Merge with wpa_supplicant 1.0 stable release
authorarron.wang <arron.wang@intel.com>
Thu, 7 Jun 2012 23:52:41 +0000 (07:52 +0800)
committerarron.wang <arron.wang@intel.com>
Thu, 7 Jun 2012 23:57:47 +0000 (07:57 +0800)
358 files changed:
README
packaging/wpa_supplicant.changes
packaging/wpa_supplicant.spec
src/Makefile
src/ap/accounting.c
src/ap/ap_config.c
src/ap/ap_config.h
src/ap/ap_drv_ops.c
src/ap/ap_drv_ops.h
src/ap/ap_list.c
src/ap/ap_list.h
src/ap/authsrv.c
src/ap/beacon.c
src/ap/beacon.h
src/ap/ctrl_iface_ap.c
src/ap/drv_callbacks.c
src/ap/hostapd.c
src/ap/hostapd.h
src/ap/hw_features.c
src/ap/hw_features.h
src/ap/ieee802_11.c
src/ap/ieee802_11.h
src/ap/ieee802_11_auth.c
src/ap/ieee802_11_ht.c
src/ap/ieee802_11_shared.c [new file with mode: 0644]
src/ap/ieee802_1x.c
src/ap/p2p_hostapd.c [new file with mode: 0644]
src/ap/p2p_hostapd.h [new file with mode: 0644]
src/ap/peerkey_auth.c
src/ap/sta_info.c
src/ap/sta_info.h
src/ap/tkip_countermeasures.c
src/ap/tkip_countermeasures.h
src/ap/utils.c
src/ap/vlan_init.c
src/ap/wmm.c
src/ap/wpa_auth.c
src/ap/wpa_auth.h
src/ap/wpa_auth_ft.c
src/ap/wpa_auth_glue.c
src/ap/wpa_auth_i.h
src/ap/wpa_auth_ie.c
src/ap/wps_hostapd.c
src/ap/wps_hostapd.h
src/common/defs.h
src/common/gas.c [new file with mode: 0644]
src/common/gas.h [new file with mode: 0644]
src/common/ieee802_11_common.c
src/common/ieee802_11_common.h
src/common/ieee802_11_defs.h
src/common/version.h
src/common/wpa_common.c
src/common/wpa_common.h
src/common/wpa_ctrl.c
src/common/wpa_ctrl.h
src/crypto/Makefile
src/crypto/crypto_internal.c
src/crypto/dh_groups.c
src/crypto/fips_prf_internal.c
src/crypto/md5-internal.c
src/crypto/ms_funcs.c
src/crypto/random.c [new file with mode: 0644]
src/crypto/random.h [new file with mode: 0644]
src/crypto/sha256-internal.c
src/crypto/tls.h
src/crypto/tls_gnutls.c
src/crypto/tls_internal.c
src/crypto/tls_none.c
src/crypto/tls_nss.c
src/crypto/tls_openssl.c
src/crypto/tls_schannel.c
src/drivers/driver.h
src/drivers/driver_atheros.c
src/drivers/driver_atmel.c [deleted file]
src/drivers/driver_bsd.c
src/drivers/driver_common.c [new file with mode: 0644]
src/drivers/driver_hostap.c
src/drivers/driver_ipw.c [deleted file]
src/drivers/driver_madwifi.c
src/drivers/driver_ndis.c
src/drivers/driver_ndiswrapper.c [deleted file]
src/drivers/driver_nl80211.c
src/drivers/driver_ralink.c
src/drivers/driver_roboswitch.c
src/drivers/driver_test.c
src/drivers/driver_wext.c
src/drivers/driver_wext.h
src/drivers/driver_wired.c
src/drivers/drivers.c
src/drivers/drivers.mak
src/drivers/drivers.mk [new file with mode: 0644]
src/drivers/linux_ioctl.c
src/drivers/linux_ioctl.h
src/drivers/netlink.c
src/drivers/netlink.h
src/drivers/nl80211_copy.h
src/drivers/rfkill.c [new file with mode: 0644]
src/drivers/rfkill.h [new file with mode: 0644]
src/drivers/wireless_copy.h
src/eap_common/eap_defs.h
src/eap_common/eap_peap_common.c
src/eap_common/eap_peap_common.h
src/eap_common/eap_pwd_common.c [new file with mode: 0644]
src/eap_common/eap_pwd_common.h [new file with mode: 0644]
src/eap_common/eap_sim_common.c
src/eap_common/ikev2_common.c
src/eap_common/ikev2_common.h
src/eap_peer/eap.c
src/eap_peer/eap.h
src/eap_peer/eap_aka.c
src/eap_peer/eap_fast.c
src/eap_peer/eap_fast_pac.c
src/eap_peer/eap_gpsk.c
src/eap_peer/eap_i.h
src/eap_peer/eap_leap.c
src/eap_peer/eap_methods.c
src/eap_peer/eap_methods.h
src/eap_peer/eap_mschapv2.c
src/eap_peer/eap_pax.c
src/eap_peer/eap_peap.c
src/eap_peer/eap_psk.c
src/eap_peer/eap_pwd.c [new file with mode: 0644]
src/eap_peer/eap_sake.c
src/eap_peer/eap_sim.c
src/eap_peer/eap_tls_common.c
src/eap_peer/eap_tls_common.h
src/eap_peer/eap_ttls.c
src/eap_peer/eap_wsc.c
src/eap_peer/ikev2.c
src/eap_peer/tncc.c
src/eap_server/eap.h
src/eap_server/eap_i.h
src/eap_server/eap_methods.h
src/eap_server/eap_server.c
src/eap_server/eap_server_aka.c
src/eap_server/eap_server_fast.c
src/eap_server/eap_server_gpsk.c
src/eap_server/eap_server_ikev2.c
src/eap_server/eap_server_md5.c
src/eap_server/eap_server_methods.c
src/eap_server/eap_server_mschapv2.c
src/eap_server/eap_server_pax.c
src/eap_server/eap_server_peap.c
src/eap_server/eap_server_psk.c
src/eap_server/eap_server_pwd.c [new file with mode: 0644]
src/eap_server/eap_server_sake.c
src/eap_server/eap_server_sim.c
src/eap_server/eap_server_tls_common.c
src/eap_server/eap_server_tnc.c
src/eap_server/eap_server_ttls.c
src/eap_server/eap_server_wsc.c
src/eap_server/eap_sim_db.c
src/eap_server/ikev2.c
src/eap_server/tncs.c
src/eapol_auth/eapol_auth_sm.c
src/eapol_auth/eapol_auth_sm.h
src/eapol_supp/eapol_supp_sm.c
src/eapol_supp/eapol_supp_sm.h
src/l2_packet/l2_packet_freebsd.c
src/l2_packet/l2_packet_linux.c
src/p2p/Makefile [new file with mode: 0644]
src/p2p/p2p.c [new file with mode: 0644]
src/p2p/p2p.h [new file with mode: 0644]
src/p2p/p2p_build.c [new file with mode: 0644]
src/p2p/p2p_dev_disc.c [new file with mode: 0644]
src/p2p/p2p_go_neg.c [new file with mode: 0644]
src/p2p/p2p_group.c [new file with mode: 0644]
src/p2p/p2p_i.h [new file with mode: 0644]
src/p2p/p2p_invitation.c [new file with mode: 0644]
src/p2p/p2p_parse.c [new file with mode: 0644]
src/p2p/p2p_pd.c [new file with mode: 0644]
src/p2p/p2p_sd.c [new file with mode: 0644]
src/p2p/p2p_utils.c [new file with mode: 0644]
src/radius/radius.c
src/radius/radius_client.c
src/radius/radius_client.h
src/radius/radius_server.c
src/radius/radius_server.h
src/rsn_supp/peerkey.c
src/rsn_supp/pmksa_cache.c
src/rsn_supp/pmksa_cache.h
src/rsn_supp/tdls.c [new file with mode: 0644]
src/rsn_supp/wpa.c
src/rsn_supp/wpa.h
src/rsn_supp/wpa_ft.c
src/rsn_supp/wpa_i.h
src/rsn_supp/wpa_ie.c
src/rsn_supp/wpa_ie.h
src/tls/libtommath.c
src/tls/tlsv1_client.c
src/tls/tlsv1_client.h
src/tls/tlsv1_client_i.h
src/tls/tlsv1_client_read.c
src/tls/tlsv1_client_write.c
src/tls/tlsv1_common.h
src/tls/tlsv1_cred.c
src/tls/tlsv1_record.c
src/tls/tlsv1_record.h
src/tls/tlsv1_server.c
src/tls/tlsv1_server_read.c
src/tls/tlsv1_server_write.c
src/tls/x509v3.c
src/tls/x509v3.h
src/utils/Makefile
src/utils/base64.c
src/utils/base64.h
src/utils/common.c
src/utils/common.h
src/utils/edit.c [new file with mode: 0644]
src/utils/edit.h [new file with mode: 0644]
src/utils/edit_readline.c [new file with mode: 0644]
src/utils/edit_simple.c [new file with mode: 0644]
src/utils/eloop.c
src/utils/eloop.h
src/utils/eloop_win.c
src/utils/includes.h
src/utils/list.h
src/utils/os.h
src/utils/os_internal.c
src/utils/os_none.c
src/utils/os_unix.c
src/utils/os_win32.c
src/utils/pcsc_funcs.c
src/utils/radiotap_iter.h
src/utils/wpa_debug.c
src/utils/wpa_debug.h
src/utils/wpabuf.h
src/wps/http_client.c
src/wps/upnp_xml.c
src/wps/upnp_xml.h
src/wps/wps.c
src/wps/wps.h
src/wps/wps_attr_build.c
src/wps/wps_attr_parse.c
src/wps/wps_attr_process.c
src/wps/wps_common.c
src/wps/wps_defs.h
src/wps/wps_dev_attr.c
src/wps/wps_dev_attr.h
src/wps/wps_enrollee.c
src/wps/wps_er.c
src/wps/wps_er.h
src/wps/wps_er_ssdp.c
src/wps/wps_i.h
src/wps/wps_registrar.c
src/wps/wps_upnp.c
src/wps/wps_upnp.h
src/wps/wps_upnp_ap.c
src/wps/wps_upnp_event.c
src/wps/wps_upnp_i.h
src/wps/wps_upnp_ssdp.c
src/wps/wps_upnp_web.c
src/wps/wps_validate.c [new file with mode: 0644]
wpa_supplicant/.gitignore
wpa_supplicant/Android.mk [new file with mode: 0644]
wpa_supplicant/ChangeLog
wpa_supplicant/Makefile
wpa_supplicant/README
wpa_supplicant/README-P2P [new file with mode: 0644]
wpa_supplicant/README-WPS
wpa_supplicant/ap.c
wpa_supplicant/ap.h
wpa_supplicant/bgscan.c
wpa_supplicant/bgscan.h
wpa_supplicant/bgscan_learn.c [new file with mode: 0644]
wpa_supplicant/bgscan_simple.c
wpa_supplicant/blacklist.c
wpa_supplicant/bss.c
wpa_supplicant/bss.h
wpa_supplicant/config.c
wpa_supplicant/config.h
wpa_supplicant/config_file.c
wpa_supplicant/config_ssid.h
wpa_supplicant/config_winreg.c
wpa_supplicant/ctrl_iface.c
wpa_supplicant/ctrl_iface.h
wpa_supplicant/ctrl_iface_unix.c
wpa_supplicant/dbus/Makefile
wpa_supplicant/dbus/dbus_dict_helpers.c
wpa_supplicant/dbus/dbus_dict_helpers.h
wpa_supplicant/dbus/dbus_new.c
wpa_supplicant/dbus/dbus_new.h
wpa_supplicant/dbus/dbus_new_handlers.c
wpa_supplicant/dbus/dbus_new_handlers.h
wpa_supplicant/dbus/dbus_new_handlers_p2p.c [new file with mode: 0644]
wpa_supplicant/dbus/dbus_new_handlers_p2p.h [new file with mode: 0644]
wpa_supplicant/dbus/dbus_new_handlers_wps.c
wpa_supplicant/dbus/dbus_new_helpers.c
wpa_supplicant/dbus/dbus_new_helpers.h
wpa_supplicant/dbus/dbus_new_introspect.c
wpa_supplicant/dbus/dbus_old.c
wpa_supplicant/dbus/dbus_old.h
wpa_supplicant/dbus/dbus_old_handlers.c
wpa_supplicant/dbus/dbus_old_handlers.h
wpa_supplicant/dbus/dbus_old_handlers_wps.c
wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service [deleted file]
wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service.in [new file with mode: 0644]
wpa_supplicant/dbus/fi.w1.wpa_supplicant1.service [deleted file]
wpa_supplicant/dbus/fi.w1.wpa_supplicant1.service.in [new file with mode: 0644]
wpa_supplicant/defconfig
wpa_supplicant/doc/docbook/wpa_supplicant.sgml
wpa_supplicant/driver_i.h
wpa_supplicant/eap_register.c
wpa_supplicant/eapol_test.c
wpa_supplicant/events.c
wpa_supplicant/examples/p2p-action-udhcp.sh [new file with mode: 0644]
wpa_supplicant/examples/p2p-action.sh [new file with mode: 0644]
wpa_supplicant/examples/udhcpd-p2p.conf [new file with mode: 0644]
wpa_supplicant/examples/wpas-dbus-new-signals.py
wpa_supplicant/gas_query.c [new file with mode: 0644]
wpa_supplicant/gas_query.h [new file with mode: 0644]
wpa_supplicant/ibss_rsn.c
wpa_supplicant/ibss_rsn.h
wpa_supplicant/interworking.c [new file with mode: 0644]
wpa_supplicant/interworking.h [new file with mode: 0644]
wpa_supplicant/main.c
wpa_supplicant/mlme.c [deleted file]
wpa_supplicant/mlme.h [deleted file]
wpa_supplicant/notify.c
wpa_supplicant/notify.h
wpa_supplicant/offchannel.c [new file with mode: 0644]
wpa_supplicant/offchannel.h [new file with mode: 0644]
wpa_supplicant/p2p_supplicant.c [new file with mode: 0644]
wpa_supplicant/p2p_supplicant.h [new file with mode: 0644]
wpa_supplicant/scan.c
wpa_supplicant/scan.h
wpa_supplicant/sme.c
wpa_supplicant/sme.h
wpa_supplicant/symbian/wpa_supplicant.mmp
wpa_supplicant/systemd/wpa_supplicant-nl80211@.service.in [new file with mode: 0644]
wpa_supplicant/systemd/wpa_supplicant-wired@.service.in [new file with mode: 0644]
wpa_supplicant/systemd/wpa_supplicant.service.in [new file with mode: 0644]
wpa_supplicant/systemd/wpa_supplicant@.service.in [new file with mode: 0644]
wpa_supplicant/vs2005/eapol_test/eapol_test.vcproj [new file with mode: 0644]
wpa_supplicant/vs2005/wpa_cli/wpa_cli.vcproj [new file with mode: 0644]
wpa_supplicant/vs2005/wpa_passphrase/wpa_passphrase.vcproj [new file with mode: 0644]
wpa_supplicant/vs2005/wpa_supplicant/wpa_supplicant.vcproj [new file with mode: 0644]
wpa_supplicant/vs2005/wpasvc/wpasvc.vcproj
wpa_supplicant/wpa_cli.c
wpa_supplicant/wpa_gui-qt4/.gitignore
wpa_supplicant/wpa_gui-qt4/icons.qrc
wpa_supplicant/wpa_gui-qt4/icons/Makefile [new file with mode: 0644]
wpa_supplicant/wpa_gui-qt4/icons/README
wpa_supplicant/wpa_gui-qt4/icons/group.svg [new file with mode: 0644]
wpa_supplicant/wpa_gui-qt4/icons/invitation.svg [new file with mode: 0644]
wpa_supplicant/wpa_gui-qt4/icons_png.qrc
wpa_supplicant/wpa_gui-qt4/peers.cpp
wpa_supplicant/wpa_gui-qt4/peers.h
wpa_supplicant/wpa_gui-qt4/wpagui.cpp
wpa_supplicant/wpa_gui/.gitignore
wpa_supplicant/wpa_priv.c
wpa_supplicant/wpa_supplicant.c
wpa_supplicant/wpa_supplicant.conf
wpa_supplicant/wpa_supplicant_i.h
wpa_supplicant/wpas_glue.c
wpa_supplicant/wpas_glue.h
wpa_supplicant/wps_supplicant.c
wpa_supplicant/wps_supplicant.h

diff --git a/README b/README
index 9c6be85..abdc18c 100644 (file)
--- a/README
+++ b/README
@@ -1,19 +1,75 @@
-wpa_supplicant and hostapd v0.6.x
----------------------------------
+wpa_supplicant and hostapd
+--------------------------
 
-Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> and contributors
+Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi> and contributors
 All Rights Reserved.
 
-These program is dual-licensed under both the GPL version 2 and BSD
-license. Either license may be used at your option.
+These programs are dual-licensed under both the GPL version 2 and BSD
+license (the one with advertisement clause removed). Either license
+may be used at your option.
+
+If you are submitting changes to the project, please see CONTRIBUTIONS
+file for more instructions.
 
 
 This package may include either wpa_supplicant, hostapd, or both. See
 README file respective subdirectories (wpa_supplicant/README or
 hostapd/README) for more details.
 
-Source code files have been moved around in v0.6.x releases and
-compared to earlier releases, the programs are now build by first
-going to a subdirectory (wpa_supplicant or hostapd) and creating
-build configuration (.config) and running 'make' there (for
-Linux/BSD/cygwin builds).
+Source code files were moved around in v0.6.x releases and compared to
+earlier releases, the programs are now built by first going to a
+subdirectory (wpa_supplicant or hostapd) and creating build
+configuration (.config) and running 'make' there (for Linux/BSD/cygwin
+builds).
+
+
+License
+-------
+
+GPL v2:
+
+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.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+(this copy of the license is in COPYING file)
+
+
+Alternatively, this software may be distributed, used, and modified
+under the terms of BSD license:
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+3. Neither the name(s) of the above-listed copyright holder(s) 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.
index 010796e..443ac6d 100644 (file)
@@ -1,3 +1,6 @@
+* Fri Jun  8 2012 Arron <arron.wang@intel.com> - 1.0
+- Merge with wpa_supplicant 1.0 stable release
+
 * Wed May 30 2012 Junfeng Dong <junfeng.dong@intel.com>
 - Clean up spec file for packaging.
 
index 43b4db9..697d225 100644 (file)
@@ -1,6 +1,6 @@
 Name:       wpa_supplicant
 Summary:    WPA/WPA2/IEEE 802.1X Supplicant
-Version:    0.7.3
+Version:    1.0
 Release:    1
 Group:      System/Base
 License:    BSD
index f47da7b..d73a175 100644 (file)
@@ -1,4 +1,4 @@
-SUBDIRS=ap common crypto drivers eapol_auth eapol_supp eap_common eap_peer eap_server l2_packet radius rsn_supp tls utils wps
+SUBDIRS=ap common crypto drivers eapol_auth eapol_supp eap_common eap_peer eap_server l2_packet p2p radius rsn_supp tls utils wps
 
 all:
        for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d; done
index 7939c68..ad20fea 100644 (file)
@@ -23,6 +23,7 @@
 #include "ieee802_1x.h"
 #include "ap_config.h"
 #include "sta_info.h"
+#include "ap_drv_ops.h"
 #include "accounting.h"
 
 
@@ -186,7 +187,7 @@ static int accounting_sta_update_stats(struct hostapd_data *hapd,
                                       struct sta_info *sta,
                                       struct hostap_sta_driver_data *data)
 {
-       if (hapd->drv.read_sta_data(hapd, data, sta->addr))
+       if (hostapd_drv_read_sta_data(hapd, data, sta->addr))
                return -1;
 
        if (sta->last_rx_bytes > data->rx_bytes)
@@ -235,6 +236,7 @@ static void accounting_interim_update(void *eloop_ctx, void *timeout_ctx)
 void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta)
 {
        struct radius_msg *msg;
+       struct os_time t;
        int interval;
 
        if (sta->acct_session_started)
@@ -246,10 +248,11 @@ void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta)
                       "starting accounting session %08X-%08X",
                       sta->acct_session_id_hi, sta->acct_session_id_lo);
 
-       time(&sta->acct_session_start);
+       os_get_time(&t);
+       sta->acct_session_start = t.sec;
        sta->last_rx_bytes = sta->last_tx_bytes = 0;
        sta->acct_input_gigawords = sta->acct_output_gigawords = 0;
-       hapd->drv.sta_clear_stats(hapd, sta->addr);
+       hostapd_drv_sta_clear_stats(hapd, sta->addr);
 
        if (!hapd->conf->radius->acct_server)
                return;
@@ -262,8 +265,9 @@ void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta)
                               hapd, sta);
 
        msg = accounting_msg(hapd, sta, RADIUS_ACCT_STATUS_TYPE_START);
-       if (msg)
-               radius_client_send(hapd->radius, msg, RADIUS_ACCT, sta->addr);
+       if (msg &&
+           radius_client_send(hapd->radius, msg, RADIUS_ACCT, sta->addr) < 0)
+               radius_msg_free(msg);
 
        sta->acct_session_started = 1;
 }
@@ -275,6 +279,7 @@ static void accounting_sta_report(struct hostapd_data *hapd,
        struct radius_msg *msg;
        int cause = sta->acct_terminate_cause;
        struct hostap_sta_driver_data data;
+       struct os_time now;
        u32 gigawords;
 
        if (!hapd->conf->radius->acct_server)
@@ -288,8 +293,9 @@ static void accounting_sta_report(struct hostapd_data *hapd,
                return;
        }
 
+       os_get_time(&now);
        if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_SESSION_TIME,
-                                      time(NULL) - sta->acct_session_start)) {
+                                      now.sec - sta->acct_session_start)) {
                printf("Could not add Acct-Session-Time\n");
                goto fail;
        }
@@ -344,7 +350,7 @@ static void accounting_sta_report(struct hostapd_data *hapd,
        }
 
        if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP,
-                                      time(NULL))) {
+                                      now.sec)) {
                printf("Could not add Event-Timestamp\n");
                goto fail;
        }
@@ -359,9 +365,10 @@ static void accounting_sta_report(struct hostapd_data *hapd,
                goto fail;
        }
 
-       radius_client_send(hapd->radius, msg,
-                          stop ? RADIUS_ACCT : RADIUS_ACCT_INTERIM,
-                          sta->addr);
+       if (radius_client_send(hapd->radius, msg,
+                              stop ? RADIUS_ACCT : RADIUS_ACCT_INTERIM,
+                              sta->addr) < 0)
+               goto fail;
        return;
 
  fail:
@@ -464,7 +471,8 @@ static void accounting_report_state(struct hostapd_data *hapd, int on)
                return;
        }
 
-       radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL);
+       if (radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL) < 0)
+               radius_msg_free(msg);
 }
 
 
@@ -475,9 +483,12 @@ static void accounting_report_state(struct hostapd_data *hapd, int on)
  */
 int accounting_init(struct hostapd_data *hapd)
 {
+       struct os_time now;
+
        /* Acct-Session-Id should be unique over reboots. If reliable clock is
         * not available, this could be replaced with reboot counter, etc. */
-       hapd->acct_session_id_hi = time(NULL);
+       os_get_time(&now);
+       hapd->acct_session_id_hi = now.sec;
 
        if (radius_client_register(hapd->radius, RADIUS_ACCT,
                                   accounting_receive, hapd))
index 5996993..cfb6b2d 100644 (file)
@@ -74,6 +74,8 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
 
        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;
@@ -84,14 +86,22 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
        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 */
 }
 
 
 struct hostapd_config * hostapd_config_defaults(void)
 {
+#define ecw2cw(ecw) ((1 << (ecw)) - 1)
+
        struct hostapd_config *conf;
        struct hostapd_bss_config *bss;
-       int i;
        const int aCWmin = 4, aCWmax = 10;
        const struct hostapd_wmm_ac_params ac_bk =
                { aCWmin, aCWmax, 7, 0, 0 }; /* background traffic */
@@ -101,6 +111,17 @@ struct hostapd_config * hostapd_config_defaults(void)
                { aCWmin - 1, aCWmin, 2, 3000 / 32, 1 };
        const struct hostapd_wmm_ac_params ac_vo = /* voice traffic */
                { aCWmin - 2, aCWmin - 1, 2, 1500 / 32, 1 };
+       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));
@@ -129,14 +150,16 @@ struct hostapd_config * hostapd_config_defaults(void)
        conf->fragm_threshold = -1; /* user driver default: 2346 */
        conf->send_probe_response = 1;
 
-       for (i = 0; i < NUM_TX_QUEUES; i++)
-               conf->tx_queue[i].aifs = -1; /* use hw default */
-
        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;
 
        return conf;
@@ -403,6 +426,8 @@ static void hostapd_config_free_bss(struct hostapd_bss_config *conf)
                ssid->dyn_vlan_keys = NULL;
        }
 
+       os_free(conf->time_zone);
+
 #ifdef CONFIG_IEEE80211R
        {
                struct ft_remote_r0kh *r0kh, *r0kh_prev;
@@ -433,7 +458,6 @@ static void hostapd_config_free_bss(struct hostapd_bss_config *conf)
        os_free(conf->model_name);
        os_free(conf->model_number);
        os_free(conf->serial_number);
-       os_free(conf->device_type);
        os_free(conf->config_methods);
        os_free(conf->ap_pin);
        os_free(conf->extra_cred);
@@ -445,6 +469,8 @@ static void hostapd_config_free_bss(struct hostapd_bss_config *conf)
        os_free(conf->model_url);
        os_free(conf->upc);
 #endif /* CONFIG_WPS */
+
+       os_free(conf->roaming_consortium);
 }
 
 
index f509b5b..fe20fc2 100644 (file)
@@ -18,6 +18,7 @@
 #include "common/defs.h"
 #include "ip_addr.h"
 #include "common/wpa_common.h"
+#include "wps/wps.h"
 
 #define MAX_STA_COUNT 2007
 #define MAX_VLAN_ID 4094
@@ -123,14 +124,13 @@ struct hostapd_eap_user {
 };
 
 
-#define NUM_TX_QUEUES 8
+#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 */
-       int configured;
 };
 
 struct hostapd_wmm_ac_params {
@@ -142,12 +142,20 @@ struct hostapd_wmm_ac_params {
 };
 
 
+#define MAX_ROAMING_CONSORTIUM_LEN 15
+
+struct hostapd_roaming_consortium {
+       u8 len;
+       u8 oi[MAX_ROAMING_CONSORTIUM_LEN];
+};
+
 /**
  * struct hostapd_bss_config - Per-BSS configuration
  */
 struct hostapd_bss_config {
        char iface[IFNAMSIZ + 1];
        char bridge[IFNAMSIZ + 1];
+       char wds_bridge[IFNAMSIZ + 1];
 
        enum hostapd_logger_level logger_syslog_level, logger_stdout_level;
 
@@ -198,6 +206,7 @@ struct hostapd_bss_config {
        struct mac_acl_entry *deny_mac;
        int num_deny_mac;
        int wds_sta;
+       int isolate;
 
        int auth_algs; /* bitfield of allowed IEEE 802.11 authentication
                        * algorithms, WPA_AUTH_ALG_{OPEN,SHARED,LEAP} */
@@ -231,6 +240,7 @@ struct hostapd_bss_config {
        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 */
@@ -254,6 +264,8 @@ struct hostapd_bss_config {
        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;
@@ -283,6 +295,7 @@ struct hostapd_bss_config {
         */
        u16 max_listen_interval;
 
+       int disable_pmksa_caching;
        int okc; /* Opportunistic Key Caching */
 
        int wps_state;
@@ -295,7 +308,7 @@ struct hostapd_bss_config {
        char *model_name;
        char *model_number;
        char *serial_number;
-       char *device_type;
+       u8 device_type[WPS_DEV_TYPE_LEN];
        char *config_methods;
        u8 os_version[4];
        char *ap_pin;
@@ -311,7 +324,43 @@ struct hostapd_bss_config {
        char *model_description;
        char *model_url;
        char *upc;
+       struct wpabuf *wps_vendor_ext[MAX_WPS_VENDOR_EXTENSIONS];
 #endif /* CONFIG_WPS */
+       int pbc_in_m1;
+
+#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;
+
+       int disassoc_low_ack;
+
+#define TDLS_PROHIBIT BIT(0)
+#define TDLS_PROHIBIT_CHAN_SWITCH BIT(1)
+       int tdls;
+       int disable_11n;
+
+       /* IEEE 802.11v */
+       int time_advertisement;
+       char *time_zone;
+
+       /* 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;
 };
 
 
@@ -332,12 +381,6 @@ struct hostapd_config {
                LONG_PREAMBLE = 0,
                SHORT_PREAMBLE = 1
        } preamble;
-       enum {
-               CTS_PROTECTION_AUTOMATIC = 0,
-               CTS_PROTECTION_FORCE_ENABLED = 1,
-               CTS_PROTECTION_FORCE_DISABLED = 2,
-               CTS_PROTECTION_AUTOMATIC_NO_OLBC = 3,
-       } cts_protection_type;
 
        int *supported_rates;
        int *basic_rates;
@@ -371,6 +414,7 @@ struct hostapd_config {
        u16 ht_capab;
        int ieee80211n;
        int secondary_channel;
+       int require_ht;
 };
 
 
index f264a3e..01c28b9 100644 (file)
 #include "utils/common.h"
 #include "drivers/driver.h"
 #include "common/ieee802_11_defs.h"
+#include "wps/wps.h"
 #include "hostapd.h"
 #include "ieee802_11.h"
 #include "sta_info.h"
 #include "ap_config.h"
+#include "p2p_hostapd.h"
 #include "ap_drv_ops.h"
 
 
-static int hostapd_sta_flags_to_drv(int flags)
+u32 hostapd_sta_flags_to_drv(u32 flags)
 {
        int res = 0;
        if (flags & WLAN_STA_AUTHORIZED)
@@ -39,45 +41,166 @@ static int hostapd_sta_flags_to_drv(int flags)
 }
 
 
-static int hostapd_set_ap_wps_ie(struct hostapd_data *hapd)
+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, *proberesp;
-       int ret;
+       struct wpabuf *beacon = NULL, *proberesp = NULL, *assocresp = NULL;
+       u8 buf[200], *pos;
 
-       if (hapd->driver == NULL || hapd->driver->set_ap_wps_ie == NULL)
-               return 0;
+       *beacon_ret = *proberesp_ret = *assocresp_ret = NULL;
 
-       beacon = hapd->wps_beacon_ie;
-       proberesp = hapd->wps_probe_resp_ie;
+       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);
+       }
 
-       ret = hapd->driver->set_ap_wps_ie(hapd->drv_priv, beacon, proberesp);
+       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);
+       }
 
-       return ret;
+       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_WPS2
+       if (hapd->conf->wps_state) {
+               struct wpabuf *a = wps_build_assoc_resp_ie();
+               if (a && wpabuf_resize(&assocresp, wpabuf_len(a)) == 0)
+                       wpabuf_put_buf(assocresp, a);
+               wpabuf_free(a);
+       }
+#endif /* CONFIG_WPS2 */
+
+#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 */
+
+       *beacon_ret = beacon;
+       *proberesp_ret = proberesp;
+       *assocresp_ret = assocresp;
+
+       return 0;
+
+fail:
+       wpabuf_free(beacon);
+       wpabuf_free(proberesp);
+       wpabuf_free(assocresp);
+       return -1;
 }
 
 
-static int hostapd_send_mgmt_frame(struct hostapd_data *hapd, const void *msg,
-                          size_t len)
+void hostapd_free_ap_extra_ies(struct hostapd_data *hapd,
+                              struct wpabuf *beacon,
+                              struct wpabuf *proberesp,
+                              struct wpabuf *assocresp)
 {
-       if (hapd->driver == NULL || hapd->driver->send_mlme == NULL)
-               return 0;
-       return hapd->driver->send_mlme(hapd->drv_priv, msg, len);
+       wpabuf_free(beacon);
+       wpabuf_free(proberesp);
+       wpabuf_free(assocresp);
 }
 
 
-static int hostapd_send_eapol(struct hostapd_data *hapd, const u8 *addr,
-                             const u8 *data, size_t data_len, int encrypt)
+int hostapd_set_ap_wps_ie(struct hostapd_data *hapd)
 {
-       if (hapd->driver == NULL || hapd->driver->hapd_send_eapol == NULL)
+       struct wpabuf *beacon, *proberesp, *assocresp;
+       int ret;
+
+       if (hapd->driver == NULL || hapd->driver->set_ap_wps_ie == NULL)
                return 0;
-       return hapd->driver->hapd_send_eapol(hapd->drv_priv, addr, data,
-                                            data_len, encrypt,
-                                            hapd->own_addr);
+
+       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;
 }
 
 
-static int hostapd_set_authorized(struct hostapd_data *hapd,
-                                 struct sta_info *sta, int authorized)
+int hostapd_set_authorized(struct hostapd_data *hapd,
+                          struct sta_info *sta, int authorized)
 {
        if (authorized) {
                return hostapd_sta_set_flags(hapd, sta->addr,
@@ -92,39 +215,7 @@ static int hostapd_set_authorized(struct hostapd_data *hapd,
 }
 
 
-static int hostapd_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);
-}
-
-
-static int hostapd_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 int hostapd_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 int hostapd_set_sta_flags(struct hostapd_data *hapd,
-                                struct sta_info *sta)
+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);
@@ -140,8 +231,8 @@ static int hostapd_set_sta_flags(struct hostapd_data *hapd,
 }
 
 
-static int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd,
-                                    const char *ifname, int enabled)
+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));
@@ -154,166 +245,80 @@ static int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd,
                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);
 }
 
 
-static int hostapd_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 int hostapd_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 int hostapd_set_bss_params(struct hostapd_data *hapd,
-                                 int use_protection)
-{
-       int ret = 0;
-       int preamble;
-#ifdef CONFIG_IEEE80211N
-       u8 buf[60], *ht_capab, *ht_oper, *pos;
-
-       pos = buf;
-       ht_capab = pos;
-       pos = hostapd_eid_ht_capabilities(hapd, pos);
-       ht_oper = pos;
-       pos = hostapd_eid_ht_operation(hapd, pos);
-       if (pos > ht_oper && ht_oper > ht_capab &&
-           hostapd_set_ht_params(hapd, ht_capab + 2, ht_capab[1],
-                                 ht_oper + 2, ht_oper[1])) {
-               wpa_printf(MSG_ERROR, "Could not set HT capabilities "
-                          "for kernel driver");
-               ret = -1;
-       }
-
-#endif /* CONFIG_IEEE80211N */
-
-       if (hostapd_set_cts_protect(hapd, use_protection)) {
-               wpa_printf(MSG_ERROR, "Failed to set CTS protect in kernel "
-                          "driver");
-               ret = -1;
-       }
-
-       if (hapd->iface->current_mode &&
-           hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G &&
-           hostapd_set_short_slot_time(hapd,
-                                       hapd->iface->num_sta_no_short_slot_time
-                                       > 0 ? 0 : 1)) {
-               wpa_printf(MSG_ERROR, "Failed to set Short Slot Time option "
-                          "in kernel driver");
-               ret = -1;
-       }
-
-       if (hapd->iface->num_sta_no_short_preamble == 0 &&
-           hapd->iconf->preamble == SHORT_PREAMBLE)
-               preamble = SHORT_PREAMBLE;
-       else
-               preamble = LONG_PREAMBLE;
-       if (hostapd_set_preamble(hapd, preamble)) {
-               wpa_printf(MSG_ERROR, "Could not set preamble for kernel "
-                          "driver");
-               ret = -1;
-       }
-
-       return ret;
-}
-
-
-static int hostapd_set_beacon(struct hostapd_data *hapd,
-                             const u8 *head, size_t head_len,
-                             const u8 *tail, size_t tail_len, int dtim_period,
-                             int beacon_int)
-{
-       if (hapd->driver == NULL || hapd->driver->set_beacon == NULL)
-               return 0;
-       return hapd->driver->set_beacon(hapd->drv_priv,
-                                       head, head_len, tail, tail_len,
-                                       dtim_period, beacon_int);
-}
-
-
-static int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname)
+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, NULL, NULL, NULL,
-                             force_ifname, if_addr);
+       return hostapd_if_add(hapd, WPA_IF_AP_VLAN, ifname, hapd->own_addr,
+                             NULL, NULL, force_ifname, if_addr, NULL);
 }
 
-static int hostapd_vlan_if_remove(struct hostapd_data *hapd,
-                                 const char *ifname)
+
+int hostapd_vlan_if_remove(struct hostapd_data *hapd, const char *ifname)
 {
        return hostapd_if_remove(hapd, WPA_IF_AP_VLAN, ifname);
 }
 
 
-static int hostapd_set_wds_sta(struct hostapd_data *hapd, const u8 *addr,
-                              int aid, int val)
+int hostapd_set_wds_sta(struct hostapd_data *hapd, const u8 *addr, int aid,
+                       int val)
 {
+       const char *bridge = NULL;
+
        if (hapd->driver == NULL || hapd->driver->set_wds_sta == NULL)
                return 0;
-       return hapd->driver->set_wds_sta(hapd->drv_priv, addr, aid, val);
+       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);
 }
 
 
-static int hostapd_set_sta_vlan(const char *ifname, struct hostapd_data *hapd,
-                               const u8 *addr, int vlan_id)
+int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr,
+                        u16 auth_alg)
 {
-       if (hapd->driver == NULL || hapd->driver->set_sta_vlan == NULL)
+       if (hapd->driver == NULL || hapd->driver->add_sta_node == NULL)
                return 0;
-       return hapd->driver->set_sta_vlan(hapd->drv_priv, addr, ifname,
-                                         vlan_id);
+       return hapd->driver->add_sta_node(hapd->drv_priv, addr, auth_alg);
 }
 
 
-static int hostapd_get_inact_sec(struct hostapd_data *hapd, const u8 *addr)
+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->get_inact_sec == NULL)
+       if (hapd->driver == NULL || hapd->driver->sta_auth == NULL)
                return 0;
-       return hapd->driver->get_inact_sec(hapd->drv_priv, addr);
+       return hapd->driver->sta_auth(hapd->drv_priv, hapd->own_addr, addr,
+                                     seq, status, ie, len);
 }
 
 
-static int hostapd_sta_deauth(struct hostapd_data *hapd, const u8 *addr,
-                             int reason)
+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_deauth == NULL)
+       if (hapd->driver == NULL || hapd->driver->sta_assoc == NULL)
                return 0;
-       return hapd->driver->sta_deauth(hapd->drv_priv, hapd->own_addr, addr,
-                                       reason);
+       return hapd->driver->sta_assoc(hapd->drv_priv, hapd->own_addr, addr,
+                                      reassoc, status, ie, len);
 }
 
 
-static int hostapd_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);
-}
-
-
-static 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)
+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,
+                   u32 flags)
 {
        struct hostapd_sta_add_params params;
 
@@ -330,52 +335,18 @@ static int hostapd_sta_add(struct hostapd_data *hapd,
        params.supp_rates_len = supp_rates_len;
        params.listen_interval = listen_interval;
        params.ht_capabilities = ht_capab;
+       params.flags = hostapd_sta_flags_to_drv(flags);
        return hapd->driver->sta_add(hapd->drv_priv, &params);
 }
 
 
-static int hostapd_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 int hostapd_set_countermeasures(struct hostapd_data *hapd, int enabled)
+int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr,
+                     u8 *tspec_ie, size_t tspec_ielen)
 {
-       if (hapd->driver == NULL ||
-           hapd->driver->hapd_set_countermeasures == NULL)
+       if (hapd->driver == NULL || hapd->driver->add_tspec == NULL)
                return 0;
-       return hapd->driver->hapd_set_countermeasures(hapd->drv_priv, enabled);
-}
-
-
-void hostapd_set_driver_ops(struct hostapd_driver_ops *ops)
-{
-       ops->set_ap_wps_ie = hostapd_set_ap_wps_ie;
-       ops->send_mgmt_frame = hostapd_send_mgmt_frame;
-       ops->send_eapol = hostapd_send_eapol;
-       ops->set_authorized = hostapd_set_authorized;
-       ops->set_key = hostapd_set_key;
-       ops->read_sta_data = hostapd_read_sta_data;
-       ops->sta_clear_stats = hostapd_sta_clear_stats;
-       ops->set_sta_flags = hostapd_set_sta_flags;
-       ops->set_drv_ieee8021x = hostapd_set_drv_ieee8021x;
-       ops->set_radius_acl_auth = hostapd_set_radius_acl_auth;
-       ops->set_radius_acl_expire = hostapd_set_radius_acl_expire;
-       ops->set_bss_params = hostapd_set_bss_params;
-       ops->set_beacon = hostapd_set_beacon;
-       ops->vlan_if_add = hostapd_vlan_if_add;
-       ops->vlan_if_remove = hostapd_vlan_if_remove;
-       ops->set_wds_sta = hostapd_set_wds_sta;
-       ops->set_sta_vlan = hostapd_set_sta_vlan;
-       ops->get_inact_sec = hostapd_get_inact_sec;
-       ops->sta_deauth = hostapd_sta_deauth;
-       ops->sta_disassoc = hostapd_sta_disassoc;
-       ops->sta_add = hostapd_sta_add;
-       ops->sta_remove = hostapd_sta_remove;
-       ops->set_countermeasures = hostapd_set_countermeasures;
+       return hapd->driver->add_tspec(hapd->drv_priv, addr, tspec_ie,
+                                      tspec_ielen);
 }
 
 
@@ -414,12 +385,14 @@ 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)
+                  void **drv_priv, char *force_ifname, u8 *if_addr,
+                  const char *bridge)
 {
        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);
+                                   bss_ctx, drv_priv, force_ifname, if_addr,
+                                   bridge);
 }
 
 
@@ -521,30 +494,6 @@ int hostapd_set_country(struct hostapd_data *hapd, const char *country)
 }
 
 
-int hostapd_set_cts_protect(struct hostapd_data *hapd, int value)
-{
-       if (hapd->driver == NULL || hapd->driver->set_cts_protect == NULL)
-               return 0;
-       return hapd->driver->set_cts_protect(hapd->drv_priv, value);
-}
-
-
-int hostapd_set_preamble(struct hostapd_data *hapd, int value)
-{
-       if (hapd->driver == NULL || hapd->driver->set_preamble == NULL)
-               return 0;
-       return hapd->driver->set_preamble(hapd->drv_priv, value);
-}
-
-
-int hostapd_set_short_slot_time(struct hostapd_data *hapd, int value)
-{
-       if (hapd->driver == NULL || hapd->driver->set_short_slot_time == NULL)
-               return 0;
-       return hapd->driver->set_short_slot_time(hapd->drv_priv, value);
-}
-
-
 int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs,
                                int cw_min, int cw_max, int burst_time)
 {
@@ -555,15 +504,6 @@ int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs,
 }
 
 
-int hostapd_valid_bss_mask(struct hostapd_data *hapd, const u8 *addr,
-                          const u8 *mask)
-{
-       if (hapd->driver == NULL || hapd->driver->valid_bss_mask == NULL)
-               return 1;
-       return hapd->driver->valid_bss_mask(hapd->drv_priv, addr, mask);
-}
-
-
 struct hostapd_hw_modes *
 hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes,
                            u16 *flags)
@@ -584,19 +524,6 @@ int hostapd_driver_commit(struct hostapd_data *hapd)
 }
 
 
-int hostapd_set_ht_params(struct hostapd_data *hapd,
-                         const u8 *ht_capab, size_t ht_capab_len,
-                         const u8 *ht_oper, size_t ht_oper_len)
-{
-       if (hapd->driver == NULL || hapd->driver->set_ht_params == NULL ||
-           ht_capab == NULL || ht_oper == NULL)
-               return 0;
-       return hapd->driver->set_ht_params(hapd->drv_priv,
-                                          ht_capab, ht_capab_len,
-                                          ht_oper, ht_oper_len);
-}
-
-
 int hostapd_drv_none(struct hostapd_data *hapd)
 {
        return hapd->driver && os_strcmp(hapd->driver->name, "none") == 0;
@@ -619,3 +546,56 @@ struct wpa_scan_results * hostapd_driver_get_scan_results(
                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)
+{
+       if (hapd->driver == NULL || hapd->driver->send_mlme == NULL)
+               return 0;
+       return hapd->driver->send_mlme(hapd->drv_priv, msg, len);
+}
+
+
+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);
+}
index 9b75d09..66e2cb8 100644 (file)
 enum wpa_driver_if_type;
 struct wpa_bss_params;
 struct wpa_driver_scan_params;
+struct ieee80211_ht_capabilities;
 
-void hostapd_set_driver_ops(struct hostapd_driver_ops *ops);
+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_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, 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,
+                   u32 flags);
 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);
@@ -27,7 +51,8 @@ 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);
+                  void **drv_priv, char *force_ifname, u8 *if_addr,
+                  const char *bridge);
 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,
@@ -44,24 +69,149 @@ int hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr,
 int hostapd_set_rate_sets(struct hostapd_data *hapd, int *supp_rates,
                          int *basic_rates, int mode);
 int hostapd_set_country(struct hostapd_data *hapd, const char *country);
-int hostapd_set_cts_protect(struct hostapd_data *hapd, int value);
-int hostapd_set_preamble(struct hostapd_data *hapd, int value);
-int hostapd_set_short_slot_time(struct hostapd_data *hapd, int value);
 int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs,
                                int cw_min, int cw_max, int burst_time);
-int hostapd_valid_bss_mask(struct hostapd_data *hapd, const u8 *addr,
-                          const u8 *mask);
 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_set_ht_params(struct hostapd_data *hapd,
-                         const u8 *ht_capab, size_t ht_capab_len,
-                         const u8 *ht_oper, size_t ht_oper_len);
 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 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_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);
+
+
+#include "drivers/driver.h"
+
+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_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);
+}
 
 #endif /* AP_DRV_OPS */
index 5297dbf..9b9fc9e 100644 (file)
@@ -227,6 +227,7 @@ void ap_list_process_beacon(struct hostapd_iface *iface,
                            struct hostapd_frame_info *fi)
 {
        struct ap_info *ap;
+       struct os_time now;
        int new_ap = 0;
        size_t len;
        int set_beacon = 0;
@@ -292,7 +293,8 @@ void ap_list_process_beacon(struct hostapd_iface *iface,
                ap->ht_support = 0;
 
        ap->num_beacons++;
-       time(&ap->last_beacon);
+       os_get_time(&now);
+       ap->last_beacon = now.sec;
        if (fi) {
                ap->ssi_signal = fi->ssi_signal;
                ap->datarate = fi->datarate;
@@ -331,7 +333,7 @@ void ap_list_process_beacon(struct hostapd_iface *iface,
 static void ap_list_timer(void *eloop_ctx, void *timeout_ctx)
 {
        struct hostapd_iface *iface = eloop_ctx;
-       time_t now;
+       struct os_time now;
        struct ap_info *ap;
        int set_beacon = 0;
 
@@ -340,12 +342,12 @@ static void ap_list_timer(void *eloop_ctx, void *timeout_ctx)
        if (!iface->ap_list)
                return;
 
-       time(&now);
+       os_get_time(&now);
 
        while (iface->ap_list) {
                ap = iface->ap_list->prev;
                if (ap->last_beacon + iface->conf->ap_table_expiration_time >=
-                   now)
+                   now.sec)
                        break;
 
                ap_free_ap(iface, ap);
index f49f58b..6df8981 100644 (file)
@@ -45,7 +45,7 @@ struct ap_info {
        int ht_support;
 
        unsigned int num_beacons; /* number of beacon frames received */
-       time_t last_beacon;
+       os_time_t last_beacon;
 
        int already_seen; /* whether API call AP-NEW has already fetched
                           * information about this AP */
index 0ab0668..7c87fde 100644 (file)
@@ -119,6 +119,7 @@ static int hostapd_setup_radius_srv(struct hostapd_data *hapd)
        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;
 
        hapd->radius_srv = radius_server_init(&srv);
        if (hapd->radius_srv == NULL) {
index 004cc8a..63b708a 100644 (file)
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
 #include "drivers/driver.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"
 
 
+#ifdef NEED_AP_MLME
+
 static u8 ieee802_11_erp_info(struct hostapd_data *hapd)
 {
        u8 erp = 0;
@@ -39,23 +45,11 @@ static u8 ieee802_11_erp_info(struct hostapd_data *hapd)
            hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
                return 0;
 
-       switch (hapd->iconf->cts_protection_type) {
-       case CTS_PROTECTION_FORCE_ENABLED:
-               erp |= ERP_INFO_NON_ERP_PRESENT | ERP_INFO_USE_PROTECTION;
-               break;
-       case CTS_PROTECTION_FORCE_DISABLED:
-               erp = 0;
-               break;
-       case CTS_PROTECTION_AUTOMATIC:
-               if (hapd->iface->olbc)
-                       erp |= ERP_INFO_USE_PROTECTION;
-               /* continue */
-       case CTS_PROTECTION_AUTOMATIC_NO_OLBC:
-               if (hapd->iface->num_sta_non_erp > 0) {
-                       erp |= ERP_INFO_NON_ERP_PRESENT |
-                               ERP_INFO_USE_PROTECTION;
-               }
-               break;
+       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)
@@ -178,8 +172,7 @@ static u8 * hostapd_eid_country(struct hostapd_data *hapd, u8 *eid,
 }
 
 
-static u8 * hostapd_eid_wpa(struct hostapd_data *hapd, u8 *eid, size_t len,
-                           struct sta_info *sta)
+static u8 * hostapd_eid_wpa(struct hostapd_data *hapd, u8 *eid, size_t len)
 {
        const u8 *ie;
        size_t ielen;
@@ -207,11 +200,14 @@ void handle_probe_req(struct hostapd_data *hapd,
        size_t i;
 
        ie = mgmt->u.probe_req.variable;
+       if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req))
+               return;
        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, ie, ie_len) > 0)
+                                           mgmt->sa, mgmt->da, mgmt->bssid,
+                                           ie, ie_len) > 0)
                        return;
 
        if (!hapd->iconf->send_probe_response)
@@ -233,6 +229,33 @@ void handle_probe_req(struct hostapd_data *hapd,
                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) {
                wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR " for "
                           "broadcast SSID ignored", MAC2STR(mgmt->sa));
@@ -241,6 +264,16 @@ void handle_probe_req(struct hostapd_data *hapd,
 
        sta = ap_get_sta(hapd, mgmt->sa);
 
+#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 */
+
        if (elems.ssid_len == 0 ||
            (elems.ssid_len == hapd->conf->ssid.ssid_len &&
             os_memcmp(elems.ssid, hapd->conf->ssid.ssid, elems.ssid_len) ==
@@ -257,12 +290,43 @@ void handle_probe_req(struct hostapd_data *hapd,
                        ieee802_11_print_ssid(ssid_txt, elems.ssid,
                                              elems.ssid_len);
                        wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR
-                                  " for foreign SSID '%s'",
-                                  MAC2STR(mgmt->sa), ssid_txt);
+                                  " for foreign SSID '%s' (DA " MACSTR ")",
+                                  MAC2STR(mgmt->sa), ssid_txt,
+                                  MAC2STR(mgmt->da));
                }
                return;
        }
 
+#ifdef CONFIG_INTERWORKING
+       if (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 (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 */
+
        /* TODO: verify that supp_rates contains at least one matching rate
         * with AP configuration */
 #define MAX_PROBERESP_LEN 768
@@ -271,6 +335,10 @@ void handle_probe_req(struct hostapd_data *hapd,
        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 */
        resp = os_zalloc(buflen);
        if (resp == NULL)
                return;
@@ -310,13 +378,22 @@ void handle_probe_req(struct hostapd_data *hapd,
        pos = hostapd_eid_ext_supp_rates(hapd, pos);
 
        /* RSN, MDIE, WPA */
-       pos = hostapd_eid_wpa(hapd, pos, epos - pos, sta);
+       pos = hostapd_eid_wpa(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);
+
        /* Wi-Fi Alliance WMM */
        pos = hostapd_eid_wmm(hapd, pos);
 
@@ -328,23 +405,48 @@ void handle_probe_req(struct hostapd_data *hapd,
        }
 #endif /* CONFIG_WPS */
 
-       if (hapd->drv.send_mgmt_frame(hapd, resp, pos - (u8 *) resp) < 0)
+#ifdef CONFIG_P2P
+       if ((hapd->conf->p2p & P2P_ENABLED) && elems.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 */
+
+       if (hostapd_drv_send_mlme(hapd, resp, pos - (u8 *) resp) < 0)
                perror("handle_probe_req: send");
 
        os_free(resp);
 
-       wpa_printf(MSG_MSGDUMP, "STA " MACSTR " sent probe request for %s "
+       wpa_printf(MSG_EXCESSIVE, "STA " MACSTR " sent probe request for %s "
                   "SSID", MAC2STR(mgmt->sa),
                   elems.ssid_len == 0 ? "broadcast" : "our");
 }
 
+#endif /* NEED_AP_MLME */
+
 
 void ieee802_11_set_beacon(struct hostapd_data *hapd)
 {
-       struct ieee80211_mgmt *head;
-       u8 *pos, *tail, *tailpos;
+       struct ieee80211_mgmt *head = NULL;
+       u8 *tail = NULL;
+       size_t head_len = 0, tail_len = 0;
+       struct wpa_driver_ap_params params;
+       struct wpabuf *beacon, *proberesp, *assocresp;
+#ifdef NEED_AP_MLME
        u16 capab_info;
-       size_t head_len, tail_len;
+       u8 *pos, *tailpos;
+#endif /* NEED_AP_MLME */
+
+       hapd->beacon_set_done = 1;
+
+#ifdef NEED_AP_MLME
 
 #define BEACON_HEAD_BUF_SIZE 256
 #define BEACON_TAIL_BUF_SIZE 512
@@ -354,6 +456,10 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd)
        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 */
        tailpos = tail = os_malloc(tail_len);
        if (head == NULL || tail == NULL) {
                wpa_printf(MSG_ERROR, "Failed to set beacon data");
@@ -412,13 +518,25 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd)
 
        /* RSN, MDIE, WPA */
        tailpos = hostapd_eid_wpa(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE -
-                                 tailpos, NULL);
+                                 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);
+
        /* Wi-Fi Alliance WMM */
        tailpos = hostapd_eid_wmm(hapd, tailpos);
 
@@ -430,19 +548,85 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd)
        }
 #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 */
+
        tail_len = tailpos > tail ? tailpos - tail : 0;
 
-       if (hapd->drv.set_beacon(hapd, (u8 *) head, head_len,
-                                tail, tail_len, hapd->conf->dtim_period,
-                                hapd->iconf->beacon_int))
-               wpa_printf(MSG_ERROR, "Failed to set beacon head/tail or DTIM "
-                          "period");
+#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.dtim_period = hapd->conf->dtim_period;
+       params.beacon_int = hapd->iconf->beacon_int;
+       params.ssid = (u8 *) hapd->conf->ssid.ssid;
+       params.ssid_len = hapd->conf->ssid.ssid_len;
+       params.pairwise_ciphers = hapd->conf->rsn_pairwise ?
+               hapd->conf->rsn_pairwise : hapd->conf->wpa_pairwise;
+       params.group_cipher = hapd->conf->wpa_group;
+       params.key_mgmt_suites = hapd->conf->wpa_key_mgmt;
+       params.auth_algs = hapd->conf->auth_algs;
+       params.wpa_version = hapd->conf->wpa;
+       params.privacy = hapd->conf->ssid.wep.keys_set || hapd->conf->wpa ||
+               (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;
+       }
+       hostapd_build_ap_extra_ies(hapd, &beacon, &proberesp, &assocresp);
+       params.beacon_ies = beacon;
+       params.proberesp_ies = proberesp;
+       params.assocresp_ies = assocresp;
+       params.isolate = hapd->conf->isolate;
+#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;
+       if (hostapd_drv_set_ap(hapd, &params))
+               wpa_printf(MSG_ERROR, "Failed to set beacon parameters");
+       hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp);
 
        os_free(tail);
        os_free(head);
-
-       hapd->drv.set_bss_params(hapd, !!(ieee802_11_erp_info(hapd) &
-                                         ERP_INFO_USE_PROTECTION));
 }
 
 
index c1510e1..a944f5f 100644 (file)
@@ -20,17 +20,7 @@ struct ieee80211_mgmt;
 
 void handle_probe_req(struct hostapd_data *hapd,
                      const struct ieee80211_mgmt *mgmt, size_t len);
-#ifdef NEED_AP_MLME
 void ieee802_11_set_beacon(struct hostapd_data *hapd);
 void ieee802_11_set_beacons(struct hostapd_iface *iface);
-#else /* NEED_AP_MLME */
-static inline void ieee802_11_set_beacon(struct hostapd_data *hapd)
-{
-}
-
-static inline void ieee802_11_set_beacons(struct hostapd_iface *iface)
-{
-}
-#endif /* NEED_AP_MLME */
 
 #endif /* BEACON_H */
index e50b0a7..d348dc1 100644 (file)
@@ -21,6 +21,7 @@
 #include "ieee802_11.h"
 #include "sta_info.h"
 #include "wps_hostapd.h"
+#include "p2p_hostapd.h"
 #include "ctrl_iface_ap.h"
 
 
@@ -57,6 +58,9 @@ static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd,
                                      buflen - len);
        if (res >= 0)
                len += res;
+       res = hostapd_p2p_get_mib_sta(hapd, sta, buf + len, buflen - len);
+       if (res >= 0)
+               len += res;
 
        return len;
 }
index 26ef584..8844132 100644 (file)
@@ -20,6 +20,9 @@
 #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 "hostapd.h"
 #include "ieee802_11.h"
 #include "sta_info.h"
 #include "wpa_auth.h"
 #include "wmm.h"
 #include "wps_hostapd.h"
+#include "ap_drv_ops.h"
 #include "ap_config.h"
 
 
 int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
-                       const u8 *ie, size_t ielen)
+                       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;
+       u16 reason = WLAN_REASON_UNSPECIFIED;
 
        if (addr == NULL) {
                /*
@@ -52,11 +59,12 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
                           "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(ie, ielen, &elems, 0);
+       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;
@@ -79,15 +87,30 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
        sta = ap_get_sta(hapd, addr);
        if (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)
                        return -1;
        }
-       sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS);
+       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);
+       }
+#endif /* CONFIG_P2P */
 
        if (hapd->conf->wpa) {
                if (ie == NULL || ielen == 0) {
+#ifdef CONFIG_WPS
                        if (hapd->conf->wps_state) {
                                wpa_printf(MSG_DEBUG, "STA did not include "
                                           "WPA/RSN IE in (Re)Association "
@@ -95,15 +118,29 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
                                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_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,
@@ -116,36 +153,55 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
                res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
                                          ie, ielen, NULL, 0);
                if (res != WPA_IE_OK) {
-                       int resp;
                        wpa_printf(MSG_DEBUG, "WPA/RSN information element "
                                   "rejected? (res %u)", res);
                        wpa_hexdump(MSG_DEBUG, "IE", ie, ielen);
                        if (res == WPA_INVALID_GROUP)
-                               resp = WLAN_REASON_GROUP_CIPHER_NOT_VALID;
+                               reason = WLAN_REASON_GROUP_CIPHER_NOT_VALID;
                        else if (res == WPA_INVALID_PAIRWISE)
-                               resp = WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID;
+                               reason = WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID;
                        else if (res == WPA_INVALID_AKMP)
-                               resp = WLAN_REASON_AKMP_NOT_VALID;
+                               reason = WLAN_REASON_AKMP_NOT_VALID;
 #ifdef CONFIG_IEEE80211W
                        else if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION)
-                               resp = WLAN_REASON_INVALID_IE;
+                               reason = WLAN_REASON_INVALID_IE;
                        else if (res == WPA_INVALID_MGMT_GROUP_CIPHER)
-                               resp = WLAN_REASON_GROUP_CIPHER_NOT_VALID;
+                               reason = WLAN_REASON_GROUP_CIPHER_NOT_VALID;
 #endif /* CONFIG_IEEE80211W */
                        else
-                               resp = WLAN_REASON_INVALID_IE;
-                       hapd->drv.sta_disassoc(hapd, sta->addr, resp);
-                       ap_free_sta(hapd, sta);
-                       return -1;
+                               reason = WLAN_REASON_INVALID_IE;
+                       goto fail;
                }
        } else if (hapd->conf->wps_state) {
-               if (ie && ielen > 4 && ie[0] == 0xdd && ie[1] >= 4 &&
-                   os_memcmp(ie + 2, "\x00\x50\xf2\x04", 4) == 0) {
+#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;
+                       wpabuf_free(wps);
+                       goto fail;
+               }
+#endif /* CONFIG_WPS_STRICT */
+               if (wps) {
                        sta->flags |= WLAN_STA_WPS;
+                       if (wps_is_20(wps)) {
+                               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_WPS
 skip_wpa_check:
+#endif /* CONFIG_WPS */
 
        new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0;
        sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
@@ -155,7 +211,19 @@ skip_wpa_check:
 
        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:
+       hostapd_drv_sta_disassoc(hapd, sta->addr, reason);
+       ap_free_sta(hapd, sta);
+       return -1;
 }
 
 
@@ -163,6 +231,19 @@ 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, "hostapd_notif_disassoc: Skip event "
+                          "with no address");
+               return;
+       }
+
        hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
                       HOSTAPD_LEVEL_INFO, "disassociated");
 
@@ -173,9 +254,8 @@ void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr)
                return;
        }
 
+       ap_sta_set_authorized(hapd, sta, 0);
        sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
-       wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED MACSTR,
-               MAC2STR(sta->addr));
        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);
@@ -183,50 +263,47 @@ void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr)
 }
 
 
-#ifdef HOSTAPD
+void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr)
+{
+       struct sta_info *sta = ap_get_sta(hapd, addr);
 
-#ifdef NEED_AP_MLME
+       if (!sta || !hapd->conf->disassoc_low_ack)
+               return;
+
+       hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
+                      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);
+}
 
-static const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len)
+
+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)
 {
-       u16 fc, type, stype;
+       size_t i;
+       int ret = 0;
 
-       /*
-        * PS-Poll frames are 16 bytes. All other frames are
-        * 24 bytes or longer.
-        */
-       if (len < 16)
-               return NULL;
+       if (sa == NULL || ie == NULL)
+               return -1;
 
-       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;
+       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) > 0) {
+                       ret = 1;
+                       break;
                }
-       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;
        }
+       return ret;
 }
 
 
+#ifdef HOSTAPD
+
+#ifdef NEED_AP_MLME
+
 #define HAPD_BROADCAST ((struct hostapd_data *) -1)
 
 static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
@@ -250,17 +327,14 @@ static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
 
 
 static void hostapd_rx_from_unknown_sta(struct hostapd_data *hapd,
-                                       const u8 *frame, size_t len)
+                                       const u8 *bssid, const u8 *addr,
+                                       int wds)
 {
-       const struct ieee80211_hdr *hdr = (const struct ieee80211_hdr *) frame;
-       u16 fc = le_to_host16(hdr->frame_control);
-       hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len));
+       hapd = get_hapd_bssid(hapd->iface, bssid);
        if (hapd == NULL || hapd == HAPD_BROADCAST)
                return;
 
-       ieee802_11_rx_from_unknown(hapd, hdr->addr2,
-                                  (fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) ==
-                                  (WLAN_FC_TODS | WLAN_FC_FROMDS));
+       ieee802_11_rx_from_unknown(hapd, addr, wds);
 }
 
 
@@ -303,6 +377,48 @@ static void hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt)
                                        rx_mgmt->frame_len, &fi);
        } else
                ieee802_11_mgmt(hapd, rx_mgmt->frame, rx_mgmt->frame_len, &fi);
+
+       random_add_randomness(&fi, sizeof(fi));
+}
+
+
+static void hostapd_rx_action(struct hostapd_data *hapd,
+                             struct rx_action *rx_action)
+{
+       struct rx_mgmt rx_mgmt;
+       u8 *buf;
+       struct ieee80211_hdr *hdr;
+
+       wpa_printf(MSG_DEBUG, "EVENT_RX_ACTION DA=" MACSTR " SA=" MACSTR
+                  " BSSID=" MACSTR " category=%u",
+                  MAC2STR(rx_action->da), MAC2STR(rx_action->sa),
+                  MAC2STR(rx_action->bssid), rx_action->category);
+       wpa_hexdump(MSG_MSGDUMP, "Received action frame contents",
+                   rx_action->data, rx_action->len);
+
+       buf = os_zalloc(24 + 1 + rx_action->len);
+       if (buf == NULL)
+               return;
+       hdr = (struct ieee80211_hdr *) buf;
+       hdr->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+                                         WLAN_FC_STYPE_ACTION);
+       if (rx_action->category == WLAN_ACTION_SA_QUERY) {
+               /*
+                * Assume frame was protected; it would have been dropped if
+                * not.
+                */
+               hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP);
+       }
+       os_memcpy(hdr->addr1, rx_action->da, ETH_ALEN);
+       os_memcpy(hdr->addr2, rx_action->sa, ETH_ALEN);
+       os_memcpy(hdr->addr3, rx_action->bssid, ETH_ALEN);
+       buf[24] = rx_action->category;
+       os_memcpy(buf + 24 + 1, rx_action->data, rx_action->len);
+       os_memset(&rx_mgmt, 0, sizeof(rx_mgmt));
+       rx_mgmt.frame = buf;
+       rx_mgmt.frame_len = 24 + 1 + rx_action->len;
+       hostapd_mgmt_rx(hapd, &rx_mgmt);
+       os_free(buf);
 }
 
 
@@ -320,23 +436,6 @@ static void hostapd_mgmt_tx_cb(struct hostapd_data *hapd, const u8 *buf,
 #endif /* NEED_AP_MLME */
 
 
-static int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa,
-                               const u8 *ie, size_t ie_len)
-{
-       size_t i;
-       int ret = 0;
-
-       for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++) {
-               if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx,
-                                           sa, ie, ie_len) > 0) {
-                       ret = 1;
-                       break;
-               }
-       }
-       return ret;
-}
-
-
 static int hostapd_event_new_sta(struct hostapd_data *hapd, const u8 *addr)
 {
        struct sta_info *sta = ap_get_sta(hapd, addr);
@@ -395,7 +494,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                break;
 #endif /* CONFIG_IEEE80211R */
        case EVENT_WPS_BUTTON_PUSHED:
-               hostapd_wps_button_pushed(hapd);
+               hostapd_wps_button_pushed(hapd, NULL);
                break;
 #ifdef NEED_AP_MLME
        case EVENT_TX_STATUS:
@@ -414,16 +513,25 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                        break;
                }
                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.frame,
-                                           data->rx_from_unknown.len);
+               hostapd_rx_from_unknown_sta(hapd, data->rx_from_unknown.bssid,
+                                           data->rx_from_unknown.addr,
+                                           data->rx_from_unknown.wds);
                break;
        case EVENT_RX_MGMT:
                hostapd_mgmt_rx(hapd, &data->rx_mgmt);
                break;
 #endif /* NEED_AP_MLME */
        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);
                break;
@@ -438,7 +546,8 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
        case EVENT_ASSOC:
                hostapd_notif_assoc(hapd, data->assoc_info.addr,
                                    data->assoc_info.req_ies,
-                                   data->assoc_info.req_ies_len);
+                                   data->assoc_info.req_ies_len,
+                                   data->assoc_info.reassoc);
                break;
        case EVENT_DISASSOC:
                if (data)
@@ -448,6 +557,19 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                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;
+#ifdef NEED_AP_MLME
+       case EVENT_RX_ACTION:
+               if (data->rx_action.da == NULL || data->rx_action.sa == NULL ||
+                   data->rx_action.bssid == NULL)
+                       break;
+               hostapd_rx_action(hapd, &data->rx_action);
+               break;
+#endif /* NEED_AP_MLME */
        default:
                wpa_printf(MSG_DEBUG, "Unknown event %d", event);
                break;
index 841f9c5..95aa008 100644 (file)
 #include "wpa_auth_glue.h"
 #include "ap_drv_ops.h"
 #include "ap_config.h"
+#include "p2p_hostapd.h"
 
 
-static int hostapd_flush_old_stations(struct hostapd_data *hapd);
+static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason);
 static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd);
+static int hostapd_broadcast_wep_clear(struct hostapd_data *hapd);
 
 extern int wpa_debug_level;
 
 
-int hostapd_reload_config(struct hostapd_iface *iface)
+static void hostapd_reload_bss(struct hostapd_data *hapd)
 {
-       struct hostapd_data *hapd = iface->bss[0];
-       struct hostapd_config *newconf, *oldconf;
-       size_t j;
-
-       if (iface->config_read_cb == NULL)
-               return -1;
-       newconf = iface->config_read_cb(iface->config_fname);
-       if (newconf == NULL)
-               return -1;
-
-       /*
-        * 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]);
-
 #ifndef CONFIG_NO_RADIUS
-       /* TODO: update dynamic data based on changed configuration
-        * items (e.g., open/close sockets, etc.) */
-       radius_client_flush(hapd->radius, 0);
+       radius_client_reconfig(hapd->radius, hapd->conf->radius);
 #endif /* CONFIG_NO_RADIUS */
 
-       oldconf = hapd->iconf;
-       hapd->iconf = newconf;
-       hapd->conf = &newconf->bss[0];
-       iface->conf = newconf;
-
        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)
-               hapd->drv.set_drv_ieee8021x(hapd, hapd->conf->iface, 1);
+               hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 1);
        else
-               hapd->drv.set_drv_ieee8021x(hapd, hapd->conf->iface, 0);
+               hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 0);
 
-       if (hapd->conf->wpa && hapd->wpa_auth == NULL)
+       if (hapd->conf->wpa && hapd->wpa_auth == NULL) {
                hostapd_setup_wpa(hapd);
-       else if (hapd->conf->wpa) {
+               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);
@@ -110,10 +90,50 @@ int hostapd_reload_config(struct hostapd_iface *iface)
                wpa_printf(MSG_ERROR, "Could not set SSID for kernel driver");
                /* try to continue */
        }
+       wpa_printf(MSG_DEBUG, "Reconfigured interface %s", hapd->conf->iface);
+}
+
+
+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_read_cb == NULL)
+               return -1;
+       newconf = iface->config_read_cb(iface->config_fname);
+       if (newconf == NULL)
+               return -1;
+
+       /*
+        * 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 */
+       }
+
+       oldconf = hapd->iconf;
+       iface->conf = newconf;
+
+       for (j = 0; j < iface->num_bss; j++) {
+               hapd = iface->bss[j];
+               hapd->iconf = newconf;
+               hapd->conf = &newconf->bss[j];
+               hostapd_reload_bss(hapd);
+       }
 
        hostapd_config_free(oldconf);
 
-       wpa_printf(MSG_DEBUG, "Reconfigured interface %s", hapd->conf->iface);
 
        return 0;
 }
@@ -125,8 +145,8 @@ static void hostapd_broadcast_key_clear_iface(struct hostapd_data *hapd,
        int i;
 
        for (i = 0; i < NUM_WEP_KEYS; i++) {
-               if (hapd->drv.set_key(ifname, hapd, WPA_ALG_NONE, NULL, i,
-                                     i == 0 ? 1 : 0, NULL, 0, NULL, 0)) {
+               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);
@@ -135,9 +155,9 @@ static void hostapd_broadcast_key_clear_iface(struct hostapd_data *hapd,
 #ifdef CONFIG_IEEE80211W
        if (hapd->conf->ieee80211w) {
                for (i = NUM_WEP_KEYS; i < NUM_WEP_KEYS + 2; i++) {
-                       if (hapd->drv.set_key(ifname, hapd, WPA_ALG_NONE, NULL,
-                                             i, i == 0 ? 1 : 0, NULL, 0,
-                                             NULL, 0)) {
+                       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);
@@ -162,11 +182,10 @@ static int hostapd_broadcast_wep_set(struct hostapd_data *hapd)
 
        idx = ssid->wep.idx;
        if (ssid->wep.default_len &&
-           hapd->drv.set_key(hapd->conf->iface,
-                             hapd, WPA_ALG_WEP, NULL, idx,
-                             idx == ssid->wep.idx,
-                             NULL, 0, ssid->wep.key[idx],
-                             ssid->wep.len[idx])) {
+           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++;
        }
@@ -184,9 +203,10 @@ static int hostapd_broadcast_wep_set(struct hostapd_data *hapd)
                                continue;
 
                        idx = key->idx;
-                       if (hapd->drv.set_key(ifname, hapd, WPA_ALG_WEP, NULL,
-                                             idx, idx == key->idx, NULL, 0,
-                                             key->key[idx], key->len[idx])) {
+                       if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_WEP,
+                                               broadcast_ether_addr, idx, 1,
+                                               NULL, 0, key->key[idx],
+                                               key->len[idx])) {
                                wpa_printf(MSG_WARNING, "Could not set "
                                           "dynamic VLAN WEP encryption.");
                                errors++;
@@ -235,6 +255,15 @@ static void hostapd_cleanup(struct hostapd_data *hapd)
 
        os_free(hapd->probereq_cb);
        hapd->probereq_cb = NULL;
+
+#ifdef CONFIG_P2P
+       wpabuf_free(hapd->p2p_beacon_ie);
+       hapd->p2p_beacon_ie = NULL;
+       wpabuf_free(hapd->p2p_probe_resp_ie);
+       hapd->p2p_probe_resp_ie = NULL;
+#endif /* CONFIG_P2P */
+
+       wpabuf_free(hapd->time_adv);
 }
 
 
@@ -284,12 +313,18 @@ static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd)
                return 0;
        }
 
+       /*
+        * When IEEE 802.1X is not enabled, the driver may need to know how to
+        * set authentication algorithms for static WEP.
+        */
+       hostapd_drv_set_authmode(hapd, hapd->conf->auth_algs);
+
        for (i = 0; i < 4; i++) {
                if (hapd->conf->ssid.wep.key[i] &&
-                   hapd->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])) {
+                   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;
@@ -303,9 +338,10 @@ static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd)
 }
 
 
-static int hostapd_flush_old_stations(struct hostapd_data *hapd)
+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;
@@ -316,17 +352,9 @@ static int hostapd_flush_old_stations(struct hostapd_data *hapd)
                ret = -1;
        }
        wpa_printf(MSG_DEBUG, "Deauthenticate all stations");
-
-       /* New Prism2.5/3 STA firmware versions seem to have issues with this
-        * broadcast deauth frame. This gets the firmware in odd state where
-        * nothing works correctly, so let's skip sending this for the hostap
-        * driver. */
-       if (hapd->driver && os_strcmp(hapd->driver->name, "hostap") != 0) {
-               u8 addr[ETH_ALEN];
-               os_memset(addr, 0xff, ETH_ALEN);
-               hapd->drv.sta_deauth(hapd, addr,
-                                    WLAN_REASON_PREV_AUTH_NOT_VALID);
-       }
+       os_memset(addr, 0xff, ETH_ALEN);
+       hostapd_drv_sta_deauth(hapd, addr, reason);
+       hostapd_free_stas(hapd);
 
        return ret;
 }
@@ -344,7 +372,6 @@ 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 res;
        int auto_addr = 0;
 
        if (hostapd_drv_none(hapd))
@@ -408,17 +435,6 @@ skip_mask_ext:
        wpa_printf(MSG_DEBUG, "BSS count %lu, BSSID mask " MACSTR " (%d bits)",
                   (unsigned long) iface->conf->num_bss, MAC2STR(mask), bits);
 
-       res = hostapd_valid_bss_mask(hapd, hapd->own_addr, mask);
-       if (res == 0)
-               return 0;
-
-       if (res < 0) {
-               wpa_printf(MSG_ERROR, "Driver did not accept BSSID mask "
-                          MACSTR " for start address " MACSTR ".",
-                          MAC2STR(mask), MAC2STR(hapd->own_addr));
-               return -1;
-       }
-
        if (!auto_addr)
                return 0;
 
@@ -495,14 +511,19 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
                hapd->interface_added = 1;
                if (hostapd_if_add(hapd->iface->bss[0], WPA_IF_AP_BSS,
                                   hapd->conf->iface, hapd->own_addr, hapd,
-                                  &hapd->drv_priv, force_ifname, if_addr)) {
+                                  &hapd->drv_priv, force_ifname, if_addr,
+                                  hapd->conf->bridge[0] ? hapd->conf->bridge :
+                                  NULL)) {
                        wpa_printf(MSG_ERROR, "Failed to add BSS (BSSID="
                                   MACSTR ")", MAC2STR(hapd->own_addr));
                        return -1;
                }
        }
 
-       hostapd_flush_old_stations(hapd);
+       if (conf->wmm_enabled < 0)
+               conf->wmm_enabled = hapd->iconf->ieee80211n;
+
+       hostapd_flush_old_stations(hapd, WLAN_REASON_PREV_AUTH_NOT_VALID);
        hostapd_set_privacy(hapd, 0);
 
        hostapd_broadcast_wep_clear(hapd);
@@ -611,6 +632,12 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
 
        ieee802_11_set_beacon(hapd);
 
+       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;
 }
 
@@ -624,9 +651,6 @@ static void hostapd_tx_queue_params(struct hostapd_iface *iface)
        for (i = 0; i < NUM_TX_QUEUES; i++) {
                p = &iface->conf->tx_queue[i];
 
-               if (!p->configured)
-                       continue;
-
                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 "
@@ -717,6 +741,17 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
                }
        }
 
+       if (iface->current_mode) {
+               if (hostapd_prepare_rates(hapd, iface->current_mode)) {
+                       wpa_printf(MSG_ERROR, "Failed to prepare rates "
+                                  "table.");
+                       hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+                                      HOSTAPD_LEVEL_WARNING,
+                                      "Failed to prepare rates table.");
+                       return -1;
+               }
+       }
+
        if (hapd->iconf->rts_threshold > -1 &&
            hostapd_set_rts(hapd, hapd->iconf->rts_threshold)) {
                wpa_printf(MSG_ERROR, "Could not set RTS threshold for "
@@ -753,6 +788,20 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
                return -1;
        }
 
+       /*
+        * 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]))
+                       return -1;
+       }
+
+       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);
 
@@ -807,7 +856,6 @@ hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
        if (hapd == NULL)
                return NULL;
 
-       hostapd_set_driver_ops(&hapd->drv);
        hapd->new_assoc_sta_cb = hostapd_new_assoc_sta;
        hapd->iconf = conf;
        hapd->conf = bss;
@@ -829,7 +877,7 @@ void hostapd_interface_deinit(struct hostapd_iface *iface)
        for (j = 0; j < iface->num_bss; j++) {
                struct hostapd_data *hapd = iface->bss[j];
                hostapd_free_stas(hapd);
-               hostapd_flush_old_stations(hapd);
+               hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING);
                hostapd_cleanup(hapd);
        }
 }
@@ -859,8 +907,8 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
                           int reassoc)
 {
        if (hapd->tkip_countermeasures) {
-               hapd->drv.sta_deauth(hapd, sta->addr,
-                                    WLAN_REASON_MICHAEL_MIC_FAILURE);
+               hostapd_drv_sta_deauth(hapd, sta->addr,
+                                      WLAN_REASON_MICHAEL_MIC_FAILURE);
                return;
        }
 
@@ -870,6 +918,15 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
        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. */
index d0d67c8..72cf012 100644 (file)
@@ -27,9 +27,12 @@ struct sta_info;
 struct hostap_sta_driver_data;
 struct ieee80211_ht_capabilities;
 struct full_dynamic_vlan;
+enum wps_event;
+union wps_event_data;
 
 struct hostapd_probereq_cb {
-       int (*cb)(void *ctx, const u8 *sa, const u8 *ie, size_t ie_len);
+       int (*cb)(void *ctx, const u8 *sa, const u8 *da, const u8 *bssid,
+                 const u8 *ie, size_t ie_len);
        void *ctx;
 };
 
@@ -47,55 +50,6 @@ struct hostapd_frame_info {
 };
 
 
-struct hostapd_driver_ops {
-       int (*set_ap_wps_ie)(struct hostapd_data *hapd);
-       int (*send_mgmt_frame)(struct hostapd_data *hapd, const void *msg,
-                              size_t len);
-       int (*send_eapol)(struct hostapd_data *hapd, const u8 *addr,
-                         const u8 *data, size_t data_len, int encrypt);
-       int (*set_authorized)(struct hostapd_data *hapd, struct sta_info *sta,
-                             int authorized);
-       int (*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 (*read_sta_data)(struct hostapd_data *hapd,
-                            struct hostap_sta_driver_data *data,
-                            const u8 *addr);
-       int (*sta_clear_stats)(struct hostapd_data *hapd, const u8 *addr);
-       int (*set_sta_flags)(struct hostapd_data *hapd, struct sta_info *sta);
-       int (*set_drv_ieee8021x)(struct hostapd_data *hapd, const char *ifname,
-                                int enabled);
-       int (*set_radius_acl_auth)(struct hostapd_data *hapd,
-                                  const u8 *mac, int accepted,
-                                  u32 session_timeout);
-       int (*set_radius_acl_expire)(struct hostapd_data *hapd,
-                                    const u8 *mac);
-       int (*set_bss_params)(struct hostapd_data *hapd, int use_protection);
-       int (*set_beacon)(struct hostapd_data *hapd,
-                         const u8 *head, size_t head_len,
-                         const u8 *tail, size_t tail_len, int dtim_period,
-                         int beacon_int);
-       int (*vlan_if_add)(struct hostapd_data *hapd, const char *ifname);
-       int (*vlan_if_remove)(struct hostapd_data *hapd, const char *ifname);
-       int (*set_wds_sta)(struct hostapd_data *hapd, const u8 *addr, int aid,
-                          int val);
-       int (*set_sta_vlan)(const char *ifname, struct hostapd_data *hapd,
-                           const u8 *addr, int vlan_id);
-       int (*get_inact_sec)(struct hostapd_data *hapd, const u8 *addr);
-       int (*sta_deauth)(struct hostapd_data *hapd, const u8 *addr,
-                         int reason);
-       int (*sta_disassoc)(struct hostapd_data *hapd, const u8 *addr,
-                           int reason);
-       int (*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);
-       int (*sta_remove)(struct hostapd_data *hapd, const u8 *addr);
-       int (*set_countermeasures)(struct hostapd_data *hapd, int enabled);
-};
-
 /**
  * struct hostapd_data - hostapd per-BSS data structure
  */
@@ -123,12 +77,12 @@ struct hostapd_data {
 
        const struct wpa_driver_ops *driver;
        void *drv_priv;
-       struct hostapd_driver_ops drv;
 
        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;
@@ -155,6 +109,10 @@ struct hostapd_data {
 
        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 */
@@ -162,10 +120,12 @@ struct hostapd_data {
        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;
 #endif /* CONFIG_WPS */
@@ -177,9 +137,39 @@ struct hostapd_data {
                                 int freq);
        void *public_action_cb_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;
+
+#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 */
 };
 
 
@@ -202,6 +192,7 @@ struct hostapd_iface {
        struct ap_info *ap_hash[STA_HASH_SIZE];
        struct ap_info *ap_iter_list;
 
+       unsigned int drv_flags;
        struct hostapd_hw_modes *hw_features;
        int num_hw_features;
        struct hostapd_hw_modes *current_mode;
@@ -264,13 +255,17 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
 /* 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),
                                 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);
+                       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);
+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);
 
 #endif /* HOSTAPD_H */
index 0159c72..e723543 100644 (file)
@@ -101,8 +101,8 @@ int hostapd_get_hw_features(struct hostapd_iface *iface)
 }
 
 
-static int hostapd_prepare_rates(struct hostapd_data *hapd,
-                                struct hostapd_hw_modes *mode)
+int hostapd_prepare_rates(struct hostapd_data *hapd,
+                         struct hostapd_hw_modes *mode)
 {
        int i, num_basic_rates = 0;
        int basic_rates_a[] = { 60, 120, 240, -1 };
@@ -162,7 +162,8 @@ static int hostapd_prepare_rates(struct hostapd_data *hapd,
                hapd->iface->num_rates++;
        }
 
-       if (hapd->iface->num_rates == 0 || num_basic_rates == 0) {
+       if ((hapd->iface->num_rates == 0 || num_basic_rates == 0) &&
+           (!hapd->iconf->ieee80211n || !hapd->iconf->require_ht)) {
                wpa_printf(MSG_ERROR, "No rates remaining in supported/basic "
                           "rate sets (%d,%d).",
                           hapd->iface->num_rates, num_basic_rates);
@@ -265,11 +266,11 @@ static void ieee80211n_get_pri_sec_chan(struct wpa_scan_res *bss,
                oper = (struct ieee80211_ht_operation *) elems.ht_operation;
                *pri_chan = oper->control_chan;
                if (oper->ht_param & HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH) {
-                       if (oper->ht_param &
-                           HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
+                       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 (oper->ht_param &
-                                HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
+                       else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
                                *sec_chan = *pri_chan - 4;
                }
        }
@@ -406,24 +407,11 @@ static int ieee80211n_check_40mhz_2g4(struct hostapd_iface *iface,
 }
 
 
-static void wpa_scan_results_free(struct wpa_scan_results *res)
-{
-       size_t i;
-
-       if (res == NULL)
-               return;
-
-       for (i = 0; i < res->num; i++)
-               os_free(res->res[i]);
-       os_free(res->res);
-       os_free(res);
-}
-
-
 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 802.11n/D7.0, 11.14.3.2 */
@@ -452,7 +440,8 @@ static void ieee80211n_check_scan(struct hostapd_iface *iface)
                iface->conf->ht_capab &= ~HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
        }
 
-       hostapd_setup_interface_complete(iface, 0);
+       res = ieee80211n_allowed_ht40_channel_pair(iface);
+       hostapd_setup_interface_complete(iface, !res);
 }
 
 
@@ -483,9 +472,6 @@ static int ieee80211n_supported_ht_capab(struct hostapd_iface *iface)
        u16 hw = iface->current_mode->ht_capab;
        u16 conf = iface->conf->ht_capab;
 
-       if (!iface->conf->ieee80211n)
-               return 1;
-
        if ((conf & HT_CAP_INFO_LDPC_CODING_CAP) &&
            !(hw & HT_CAP_INFO_LDPC_CODING_CAP)) {
                wpa_printf(MSG_ERROR, "Driver does not support configured "
@@ -585,13 +571,15 @@ int hostapd_check_ht_capab(struct hostapd_iface *iface)
 {
 #ifdef CONFIG_IEEE80211N
        int ret;
+       if (!iface->conf->ieee80211n)
+               return 0;
+       if (!ieee80211n_supported_ht_capab(iface))
+               return -1;
        ret = ieee80211n_check_40mhz(iface);
        if (ret)
                return ret;
        if (!ieee80211n_allowed_ht40_channel_pair(iface))
                return -1;
-       if (!ieee80211n_supported_ht_capab(iface))
-               return -1;
 #endif /* CONFIG_IEEE80211N */
 
        return 0;
@@ -601,7 +589,7 @@ int hostapd_check_ht_capab(struct hostapd_iface *iface)
 /**
  * hostapd_select_hw_mode - Select the hardware mode
  * @iface: Pointer to interface data.
- * Returns: 0 on success, -1 on failure
+ * Returns: 0 on success, < 0 on failure
  *
  * Sets up the hardware mode, channel, rates, and passive scanning
  * based on the configuration.
@@ -628,18 +616,51 @@ int hostapd_select_hw_mode(struct hostapd_iface *iface)
                hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
                               HOSTAPD_LEVEL_WARNING,
                               "Hardware does not support configured mode "
-                              "(%d)", (int) iface->conf->hw_mode);
-               return -1;
+                              "(%d) (hw_mode in hostapd.conf)",
+                              (int) iface->conf->hw_mode);
+               return -2;
        }
 
        ok = 0;
        for (j = 0; j < iface->current_mode->num_channels; j++) {
                struct hostapd_channel_data *chan =
                        &iface->current_mode->channels[j];
-               if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
-                   (chan->chan == iface->conf->channel)) {
-                       ok = 1;
-                       break;
+               if (chan->chan == iface->conf->channel) {
+                       if (chan->flag & HOSTAPD_CHAN_DISABLED) {
+                               wpa_printf(MSG_ERROR,
+                                          "channel [%i] (%i) is disabled for "
+                                          "use in AP mode, flags: 0x%x",
+                                          j, chan->chan, chan->flag);
+                       } else {
+                               ok = 1;
+                               break;
+                       }
+               }
+       }
+       if (ok && iface->conf->secondary_channel) {
+               int sec_ok = 0;
+               int sec_chan = iface->conf->channel +
+                       iface->conf->secondary_channel * 4;
+               for (j = 0; j < iface->current_mode->num_channels; j++) {
+                       struct hostapd_channel_data *chan =
+                               &iface->current_mode->channels[j];
+                       if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
+                           (chan->chan == sec_chan)) {
+                               sec_ok = 1;
+                               break;
+                       }
+               }
+               if (!sec_ok) {
+                       hostapd_logger(iface->bss[0], NULL,
+                                      HOSTAPD_MODULE_IEEE80211,
+                                      HOSTAPD_LEVEL_WARNING,
+                                      "Configured HT40 secondary channel "
+                                      "(%d) not found from the channel list "
+                                      "of current mode (%d) %s",
+                                      sec_chan, iface->current_mode->mode,
+                                      hostapd_hw_mode_txt(
+                                              iface->current_mode->mode));
+                       ok = 0;
                }
        }
        if (iface->conf->channel == 0) {
@@ -647,7 +668,7 @@ int hostapd_select_hw_mode(struct hostapd_iface *iface)
                 * the channel automatically */
                wpa_printf(MSG_ERROR, "Channel not configured "
                           "(hw_mode/channel in hostapd.conf)");
-               return -1;
+               return -3;
        }
        if (ok == 0 && iface->conf->channel != 0) {
                hostapd_logger(iface->bss[0], NULL,
@@ -665,15 +686,7 @@ int hostapd_select_hw_mode(struct hostapd_iface *iface)
                hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
                               HOSTAPD_LEVEL_WARNING,
                               "Hardware does not support configured channel");
-               return -1;
-       }
-
-       if (hostapd_prepare_rates(iface->bss[0], iface->current_mode)) {
-               wpa_printf(MSG_ERROR, "Failed to prepare rates table.");
-               hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
-                                          HOSTAPD_LEVEL_WARNING,
-                                          "Failed to prepare rates table.");
-               return -1;
+               return -4;
        }
 
        return 0;
index 0295549..b84bca6 100644 (file)
@@ -25,6 +25,8 @@ 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_data *hapd,
+                         struct hostapd_hw_modes *mode);
 #else /* NEED_AP_MLME */
 static inline void
 hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
@@ -39,7 +41,7 @@ static inline int hostapd_get_hw_features(struct hostapd_iface *iface)
 
 static inline int hostapd_select_hw_mode(struct hostapd_iface *iface)
 {
-       return -1;
+       return -100;
 }
 
 static inline const char * hostapd_hw_mode_txt(int mode)
@@ -57,6 +59,12 @@ static inline int hostapd_check_ht_capab(struct hostapd_iface *iface)
        return 0;
 }
 
+static inline int hostapd_prepare_rates(struct hostapd_data *hapd,
+                                       struct hostapd_hw_modes *mode)
+{
+       return 0;
+}
+
 #endif /* NEED_AP_MLME */
 
 #endif /* HW_FEATURES_H */
index 3375aa2..d30d5ae 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * hostapd / IEEE 802.11 Management
- * Copyright (c) 2002-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2011, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -25,6 +25,7 @@
 #include "common/wpa_ctrl.h"
 #include "radius/radius.h"
 #include "radius/radius_client.h"
+#include "p2p/p2p.h"
 #include "wps/wps.h"
 #include "hostapd.h"
 #include "beacon.h"
@@ -37,6 +38,8 @@
 #include "accounting.h"
 #include "ap_config.h"
 #include "ap_mlme.h"
+#include "p2p_hostapd.h"
+#include "ap_drv_ops.h"
 #include "ieee802_11.h"
 
 
@@ -50,6 +53,8 @@ u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid)
 
        *pos++ = WLAN_EID_SUPP_RATES;
        num = hapd->iface->num_rates;
+       if (hapd->iconf->ieee80211n && hapd->iconf->require_ht)
+               num++;
        if (num > 8) {
                /* rest of the rates are encoded in Extended supported
                 * rates element */
@@ -67,6 +72,10 @@ u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid)
                pos++;
        }
 
+       if (hapd->iconf->ieee80211n && hapd->iconf->require_ht &&
+           hapd->iface->num_rates < 8)
+               *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY;
+
        return pos;
 }
 
@@ -80,6 +89,8 @@ u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid)
                return eid;
 
        num = hapd->iface->num_rates;
+       if (hapd->iconf->ieee80211n && hapd->iconf->require_ht)
+               num++;
        if (num <= 8)
                return eid;
        num -= 8;
@@ -98,6 +109,10 @@ u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid)
                pos++;
        }
 
+       if (hapd->iconf->ieee80211n && hapd->iconf->require_ht &&
+           hapd->iface->num_rates >= 8)
+               *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY;
+
        return pos;
 }
 
@@ -148,34 +163,6 @@ u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta,
 }
 
 
-#ifdef CONFIG_IEEE80211W
-static u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd,
-                                           struct sta_info *sta, u8 *eid)
-{
-       u8 *pos = eid;
-       u32 timeout, tu;
-       struct os_time now, passed;
-
-       *pos++ = WLAN_EID_TIMEOUT_INTERVAL;
-       *pos++ = 5;
-       *pos++ = WLAN_TIMEOUT_ASSOC_COMEBACK;
-       os_get_time(&now);
-       os_time_sub(&now, &sta->sa_query_start, &passed);
-       tu = (passed.sec * 1000000 + passed.usec) / 1024;
-       if (hapd->conf->assoc_sa_query_max_timeout > tu)
-               timeout = hapd->conf->assoc_sa_query_max_timeout - tu;
-       else
-               timeout = 0;
-       if (timeout < hapd->conf->assoc_sa_query_max_timeout)
-               timeout++; /* add some extra time for local timers */
-       WPA_PUT_LE32(pos, timeout);
-       pos += 4;
-
-       return pos;
-}
-#endif /* CONFIG_IEEE80211W */
-
-
 void ieee802_11_print_ssid(char *buf, const u8 *ssid, u8 len)
 {
        int i;
@@ -204,15 +191,15 @@ static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta,
                if (!sta->challenge) {
                        /* Generate a pseudo-random challenge */
                        u8 key[8];
-                       time_t now;
+                       struct os_time now;
                        int r;
                        sta->challenge = os_zalloc(WLAN_AUTH_CHALLENGE_LEN);
                        if (sta->challenge == NULL)
                                return WLAN_STATUS_UNSPECIFIED_FAILURE;
 
-                       now = time(NULL);
-                       r = random();
-                       os_memcpy(key, &now, 4);
+                       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);
@@ -282,7 +269,7 @@ static void send_auth_reply(struct hostapd_data *hapd,
                   " auth_alg=%d auth_transaction=%d resp=%d (IE len=%lu)",
                   MAC2STR(dst), auth_alg, auth_transaction,
                   resp, (unsigned long) ies_len);
-       if (hapd->drv.send_mgmt_frame(hapd, reply, rlen) < 0)
+       if (hostapd_drv_send_mlme(hapd, reply, rlen) < 0)
                perror("send_auth_reply: send");
 
        os_free(buf);
@@ -553,7 +540,7 @@ static u16 check_wmm(struct hostapd_data *hapd, struct sta_info *sta,
 {
        sta->flags &= ~WLAN_STA_WMM;
        if (wmm_ie && hapd->conf->wmm_enabled) {
-               if (hostapd_eid_wmm_valid(hapd, wmm_ie, wmm_ie_len))
+               if (!hostapd_eid_wmm_valid(hapd, wmm_ie, wmm_ie_len))
                        hostapd_logger(hapd, sta->addr,
                                       HOSTAPD_MODULE_WPA,
                                       HOSTAPD_LEVEL_DEBUG,
@@ -635,10 +622,17 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
        if (resp != WLAN_STATUS_SUCCESS)
                return resp;
 #ifdef CONFIG_IEEE80211N
-       resp = copy_sta_ht_capab(sta, elems.ht_capabilities,
+       resp = copy_sta_ht_capab(hapd, sta, elems.ht_capabilities,
                                 elems.ht_capabilities_len);
        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 */
 
        if ((hapd->conf->wpa & WPA_PROTO_RSN) && elems.rsn_ie) {
@@ -654,7 +648,7 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
        }
 
 #ifdef CONFIG_WPS
-       sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_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");
@@ -662,8 +656,17 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
                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");
@@ -768,6 +771,20 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
        } else
                wpa_auth_sta_no_wpa(sta->wpa_sm);
 
+#ifdef CONFIG_P2P
+       if (elems.p2p) {
+               wpabuf_free(sta->p2p_ie);
+               sta->p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len,
+                                                         P2P_IE_VENDOR_TYPE);
+
+       } else {
+               wpabuf_free(sta->p2p_ie);
+               sta->p2p_ie = NULL;
+       }
+
+       p2p_group_notif_assoc(hapd->p2p_group, sta->addr, ies, ies_len);
+#endif /* CONFIG_P2P */
+
        return WLAN_STATUS_SUCCESS;
 }
 
@@ -788,7 +805,7 @@ static void send_deauth(struct hostapd_data *hapd, const u8 *addr,
        send_len = IEEE80211_HDRLEN + sizeof(reply.u.deauth);
        reply.u.deauth.reason_code = host_to_le16(reason_code);
 
-       if (hapd->drv.send_mgmt_frame(hapd, &reply, send_len) < 0)
+       if (hostapd_drv_send_mlme(hapd, &reply, send_len) < 0)
                wpa_printf(MSG_INFO, "Failed to send deauth: %s",
                           strerror(errno));
 }
@@ -845,11 +862,14 @@ static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
        p = hostapd_eid_ht_operation(hapd, p);
 #endif /* CONFIG_IEEE80211N */
 
+       p = hostapd_eid_ext_capab(hapd, p);
+
        if (sta->flags & WLAN_STA_WMM)
                p = hostapd_eid_wmm(hapd, p);
 
 #ifdef CONFIG_WPS
-       if (sta->flags & WLAN_STA_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));
@@ -859,9 +879,39 @@ static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
        }
 #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 (hapd->drv.send_mgmt_frame(hapd, reply, send_len) < 0)
+       if (hostapd_drv_send_mlme(hapd, reply, send_len) < 0)
                wpa_printf(MSG_INFO, "Failed to send assoc resp: %s",
                           strerror(errno));
 }
@@ -1005,6 +1055,7 @@ static void handle_assoc(struct hostapd_data *hapd,
                       "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) {
@@ -1061,9 +1112,8 @@ static void handle_disassoc(struct hostapd_data *hapd,
                return;
        }
 
-       sta->flags &= ~WLAN_STA_ASSOC;
-       wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED MACSTR,
-               MAC2STR(sta->addr));
+       ap_sta_set_authorized(hapd, sta, 0);
+       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");
@@ -1073,7 +1123,7 @@ static void handle_disassoc(struct hostapd_data *hapd,
         * authenticated. */
        accounting_sta_stop(hapd, sta);
        ieee802_1x_free_station(sta);
-       hapd->drv.sta_remove(hapd, sta->addr);
+       hostapd_drv_sta_remove(hapd, sta->addr);
 
        if (sta->timeout_next == STA_NULLFUNC ||
            sta->timeout_next == STA_DISASSOC) {
@@ -1094,26 +1144,26 @@ static void handle_deauth(struct hostapd_data *hapd,
        struct sta_info *sta;
 
        if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.deauth)) {
-               printf("handle_deauth - too short payload (len=%lu)\n",
-                      (unsigned long) len);
+               wpa_msg(hapd->msg_ctx, MSG_DEBUG, "handle_deauth - too short "
+                       "payload (len=%lu)", (unsigned long) len);
                return;
        }
 
-       wpa_printf(MSG_DEBUG, "deauthentication: STA=" MACSTR
-                  " reason_code=%d",
-                  MAC2STR(mgmt->sa),
-                  le_to_host16(mgmt->u.deauth.reason_code));
+       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) {
-               printf("Station " MACSTR " trying to deauthenticate, but it "
-                      "is not authenticated.\n", MAC2STR(mgmt->sa));
+               wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR " trying "
+                       "to deauthenticate, but it is not authenticated",
+                       MAC2STR(mgmt->sa));
                return;
        }
 
-       sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
-       wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED MACSTR,
-               MAC2STR(sta->addr));
+       ap_sta_set_authorized(hapd, sta, 0);
+       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");
@@ -1148,81 +1198,11 @@ static void handle_beacon(struct hostapd_data *hapd,
 
 #ifdef CONFIG_IEEE80211W
 
-/* MLME-SAQuery.request */
-void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
-                                 const u8 *addr, const u8 *trans_id)
-{
-       struct ieee80211_mgmt mgmt;
-       u8 *end;
-
-       wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Request to "
-                  MACSTR, MAC2STR(addr));
-       wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
-                   trans_id, WLAN_SA_QUERY_TR_ID_LEN);
-
-       os_memset(&mgmt, 0, sizeof(mgmt));
-       mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
-                                         WLAN_FC_STYPE_ACTION);
-       os_memcpy(mgmt.da, addr, ETH_ALEN);
-       os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
-       os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
-       mgmt.u.action.category = WLAN_ACTION_SA_QUERY;
-       mgmt.u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST;
-       os_memcpy(mgmt.u.action.u.sa_query_req.trans_id, trans_id,
-                 WLAN_SA_QUERY_TR_ID_LEN);
-       end = mgmt.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN;
-       if (hapd->drv.send_mgmt_frame(hapd, &mgmt, end - (u8 *) &mgmt) < 0)
-               perror("ieee802_11_send_sa_query_req: send");
-}
-
-
-static void hostapd_sa_query_request(struct hostapd_data *hapd,
-                                    const struct ieee80211_mgmt *mgmt)
-{
-       struct sta_info *sta;
-       struct ieee80211_mgmt resp;
-       u8 *end;
-
-       wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Request from "
-                  MACSTR, MAC2STR(mgmt->sa));
-       wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
-                   mgmt->u.action.u.sa_query_resp.trans_id,
-                   WLAN_SA_QUERY_TR_ID_LEN);
-
-       sta = ap_get_sta(hapd, mgmt->sa);
-       if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) {
-               wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignore SA Query Request "
-                          "from unassociated STA " MACSTR, MAC2STR(mgmt->sa));
-               return;
-       }
-
-       wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Response to "
-                  MACSTR, MAC2STR(mgmt->sa));
-
-       os_memset(&resp, 0, sizeof(resp));
-       resp.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
-                                         WLAN_FC_STYPE_ACTION);
-       os_memcpy(resp.da, mgmt->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 = WLAN_ACTION_SA_QUERY;
-       resp.u.action.u.sa_query_req.action = WLAN_SA_QUERY_RESPONSE;
-       os_memcpy(resp.u.action.u.sa_query_req.trans_id,
-                 mgmt->u.action.u.sa_query_req.trans_id,
-                 WLAN_SA_QUERY_TR_ID_LEN);
-       end = resp.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN;
-       if (hapd->drv.send_mgmt_frame(hapd, &resp, end - (u8 *) &resp) < 0)
-               perror("hostapd_sa_query_request: send");
-}
-
-
 static void hostapd_sa_query_action(struct hostapd_data *hapd,
                                    const struct ieee80211_mgmt *mgmt,
                                    size_t len)
 {
-       struct sta_info *sta;
        const u8 *end;
-       int i;
 
        end = mgmt->u.action.u.sa_query_resp.trans_id +
                WLAN_SA_QUERY_TR_ID_LEN;
@@ -1232,50 +1212,9 @@ static void hostapd_sa_query_action(struct hostapd_data *hapd,
                return;
        }
 
-       if (mgmt->u.action.u.sa_query_resp.action == WLAN_SA_QUERY_REQUEST) {
-               hostapd_sa_query_request(hapd, mgmt);
-               return;
-       }
-
-       if (mgmt->u.action.u.sa_query_resp.action != WLAN_SA_QUERY_RESPONSE) {
-               wpa_printf(MSG_DEBUG, "IEEE 802.11: Unexpected SA Query "
-                          "Action %d", mgmt->u.action.u.sa_query_resp.action);
-               return;
-       }
-
-       wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Response from "
-                  MACSTR, MAC2STR(mgmt->sa));
-       wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
-                   mgmt->u.action.u.sa_query_resp.trans_id,
-                   WLAN_SA_QUERY_TR_ID_LEN);
-
-       /* MLME-SAQuery.confirm */
-
-       sta = ap_get_sta(hapd, mgmt->sa);
-       if (sta == NULL || sta->sa_query_trans_id == NULL) {
-               wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching STA with "
-                          "pending SA Query request found");
-               return;
-       }
-
-       for (i = 0; i < sta->sa_query_count; i++) {
-               if (os_memcmp(sta->sa_query_trans_id +
-                             i * WLAN_SA_QUERY_TR_ID_LEN,
-                             mgmt->u.action.u.sa_query_resp.trans_id,
-                             WLAN_SA_QUERY_TR_ID_LEN) == 0)
-                       break;
-       }
-
-       if (i >= sta->sa_query_count) {
-               wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching SA Query "
-                          "transaction identifier found");
-               return;
-       }
-
-       hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
-                      HOSTAPD_LEVEL_DEBUG,
-                      "Reply to pending SA Query received");
-       ap_sta_stop_sa_query(hapd, sta);
+       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);
 }
 
 
@@ -1290,7 +1229,10 @@ static int robust_action_frame(u8 category)
 static void handle_action(struct hostapd_data *hapd,
                          const struct ieee80211_mgmt *mgmt, size_t len)
 {
+#if defined(CONFIG_IEEE80211W) || defined(CONFIG_IEEE80211R)
        struct sta_info *sta;
+       sta = ap_get_sta(hapd, mgmt->sa);
+#endif /* CONFIG_IEEE80211W || CONFIG_IEEE80211R */
 
        if (len < IEEE80211_HDRLEN + 1) {
                hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
@@ -1300,7 +1242,6 @@ static void handle_action(struct hostapd_data *hapd,
                return;
        }
 
-       sta = ap_get_sta(hapd, mgmt->sa);
 #ifdef CONFIG_IEEE80211W
        if (sta && (sta->flags & WLAN_STA_MFP) &&
            !(mgmt->frame_control & host_to_le16(WLAN_FC_ISWEP) &&
@@ -1347,6 +1288,14 @@ static void handle_action(struct hostapd_data *hapd,
                        return;
                }
                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;
+               }
+               break;
        }
 
        hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
@@ -1374,7 +1323,7 @@ static void handle_action(struct hostapd_data *hapd,
                os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN);
                resp->u.action.category |= 0x80;
 
-               hapd->drv.send_mgmt_frame(hapd, resp, len);
+               hostapd_drv_send_mlme(hapd, resp, len);
                os_free(resp);
        }
 }
@@ -1400,6 +1349,9 @@ void ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
        int broadcast;
        u16 fc, stype;
 
+       if (len < 24)
+               return;
+
        mgmt = (struct ieee80211_mgmt *) buf;
        fc = le_to_host16(mgmt->frame_control);
        stype = WLAN_FC_GET_STYPE(fc);
@@ -1414,6 +1366,11 @@ void ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
                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 */
            os_memcmp(mgmt->bssid, hapd->own_addr, ETH_ALEN) != 0) {
                printf("MGMT: BSSID=" MACSTR " not our address\n",
                       MAC2STR(mgmt->bssid));
@@ -1452,7 +1409,7 @@ void ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
                handle_disassoc(hapd, mgmt, len);
                break;
        case WLAN_FC_STYPE_DEAUTH:
-               wpa_printf(MSG_DEBUG, "mgmt::deauth");
+               wpa_msg(hapd->msg_ctx, MSG_DEBUG, "mgmt::deauth");
                handle_deauth(hapd, mgmt, len);
                break;
        case WLAN_FC_STYPE_ACTION:
@@ -1565,9 +1522,7 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
                 * Open, static WEP, or FT protocol; no separate authorization
                 * step.
                 */
-               sta->flags |= WLAN_STA_AUTHORIZED;
-               wpa_msg(hapd->msg_ctx, MSG_INFO,
-                       AP_STA_CONNECTED MACSTR, MAC2STR(sta->addr));
+               ap_sta_set_authorized(hapd, sta, 1);
        }
 
        if (reassoc)
@@ -1584,22 +1539,31 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
         * cleared and configuration gets updated in case of reassociation back
         * to the same AP.
         */
-       hapd->drv.sta_remove(hapd, sta->addr);
+       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 */
 
-       if (hapd->drv.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)) {
+       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)) {
                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);
+
+               goto fail;
        }
 
+       if (sta->flags & WLAN_STA_WDS)
+               hostapd_set_wds_sta(hapd, sta->addr, sta->aid, 1);
+
        if (sta->eapol_sm == NULL) {
                /*
                 * This STA does not use RADIUS server for EAP authentication,
@@ -1614,7 +1578,7 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
                        goto fail;
        }
 
-       hapd->drv.set_sta_flags(hapd, sta);
+       hostapd_set_sta_flags(hapd, sta);
 
        if (sta->auth_alg == WLAN_AUTH_FT)
                wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT);
@@ -1633,6 +1597,54 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
 }
 
 
+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
@@ -1662,10 +1674,15 @@ void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len,
                handle_assoc_cb(hapd, mgmt, len, 1, ok);
                break;
        case WLAN_FC_STYPE_PROBE_RESP:
-               wpa_printf(MSG_DEBUG, "mgmt::proberesp cb");
+               wpa_printf(MSG_EXCESSIVE, "mgmt::proberesp cb");
                break;
        case WLAN_FC_STYPE_DEAUTH:
-               /* ignore */
+               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");
@@ -1708,7 +1725,7 @@ void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
                                break;
                }
        }
-       if (sta == NULL)
+       if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC))
                return;
        if (sta->flags & WLAN_STA_PENDING_POLL) {
                wpa_printf(MSG_DEBUG, "STA " MACSTR " %s pending "
@@ -1722,6 +1739,32 @@ void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
 }
 
 
+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)
 {
@@ -1734,19 +1777,32 @@ void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
                                   "STA " MACSTR " (aid %u)",
                                   MAC2STR(sta->addr), sta->aid);
                        sta->flags |= WLAN_STA_WDS;
-                       hapd->drv.set_wds_sta(hapd, sta->addr, sta->aid, 1);
+                       hostapd_set_wds_sta(hapd, sta->addr, sta->aid, 1);
                }
                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))
-               hapd->drv.sta_disassoc(
+               hostapd_drv_sta_disassoc(
                        hapd, src,
                        WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA);
        else
-               hapd->drv.sta_deauth(
+               hostapd_drv_sta_deauth(
                        hapd, src,
                        WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA);
 }
index cfc069c..b358060 100644 (file)
@@ -46,6 +46,7 @@ static inline int ieee802_11_get_mib_sta(struct hostapd_data *hapd,
 #endif /* NEED_AP_MLME */
 u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta,
                           int probe);
+u8 * hostapd_eid_ext_capab(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);
@@ -56,12 +57,24 @@ void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
 void hostapd_get_ht_capab(struct hostapd_data *hapd,
                          struct ieee80211_ht_capabilities *ht_cap,
                          struct ieee80211_ht_capabilities *neg_ht_cap);
-u16 copy_sta_ht_capab(struct sta_info *sta, const u8 *ht_capab,
-                     size_t ht_capab_len);
+u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
+                     const u8 *ht_capab, size_t ht_capab_len);
 void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta);
 void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
                       const u8 *buf, 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);
 
 #endif /* IEEE802_11_H */
index dec56d1..88f97b7 100644 (file)
@@ -25,6 +25,7 @@
 #include "radius/radius_client.h"
 #include "hostapd.h"
 #include "ap_config.h"
+#include "ap_drv_ops.h"
 #include "ieee802_11.h"
 #include "ieee802_11_auth.h"
 
@@ -32,7 +33,7 @@
 
 
 struct hostapd_cached_radius_acl {
-       time_t timestamp;
+       os_time_t timestamp;
        macaddr addr;
        int accepted; /* HOSTAPD_ACL_* */
        struct hostapd_cached_radius_acl *next;
@@ -43,7 +44,7 @@ struct hostapd_cached_radius_acl {
 
 
 struct hostapd_acl_query_data {
-       time_t timestamp;
+       os_time_t timestamp;
        u8 radius_id;
        macaddr addr;
        u8 *auth_msg; /* IEEE 802.11 authentication frame from station */
@@ -70,14 +71,14 @@ static int hostapd_acl_cache_get(struct hostapd_data *hapd, const u8 *addr,
                                 u32 *acct_interim_interval, int *vlan_id)
 {
        struct hostapd_cached_radius_acl *entry;
-       time_t now;
+       struct os_time now;
 
-       time(&now);
+       os_get_time(&now);
        entry = hapd->acl_cache;
 
        while (entry) {
                if (os_memcmp(entry->addr, addr, ETH_ALEN) == 0) {
-                       if (now - entry->timestamp > RADIUS_ACL_TIMEOUT)
+                       if (now.sec - entry->timestamp > RADIUS_ACL_TIMEOUT)
                                return -1; /* entry has expired */
                        if (entry->accepted == HOSTAPD_ACL_ACCEPT_TIMEOUT)
                                if (session_timeout)
@@ -190,7 +191,8 @@ static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr,
                goto fail;
        }
 
-       radius_client_send(hapd->radius, msg, RADIUS_AUTH, addr);
+       if (radius_client_send(hapd->radius, msg, RADIUS_AUTH, addr) < 0)
+               goto fail;
        return 0;
 
  fail:
@@ -240,6 +242,7 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
                return HOSTAPD_ACL_REJECT;
 #else /* CONFIG_NO_RADIUS */
                struct hostapd_acl_query_data *query;
+               struct os_time t;
 
                /* Check whether ACL cache has an entry for this station */
                int res = hostapd_acl_cache_get(hapd, addr, session_timeout,
@@ -270,7 +273,8 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
                        wpa_printf(MSG_ERROR, "malloc for query data failed");
                        return HOSTAPD_ACL_REJECT;
                }
-               time(&query->timestamp);
+               os_get_time(&t);
+               query->timestamp = t.sec;
                os_memcpy(query->addr, addr, ETH_ALEN);
                if (hostapd_radius_acl_query(hapd, addr, query)) {
                        wpa_printf(MSG_DEBUG, "Failed to send Access-Request "
@@ -302,7 +306,7 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
 
 
 #ifndef CONFIG_NO_RADIUS
-static void hostapd_acl_expire_cache(struct hostapd_data *hapd, time_t now)
+static void hostapd_acl_expire_cache(struct hostapd_data *hapd, os_time_t now)
 {
        struct hostapd_cached_radius_acl *prev, *entry, *tmp;
 
@@ -317,9 +321,7 @@ static void hostapd_acl_expire_cache(struct hostapd_data *hapd, time_t now)
                                prev->next = entry->next;
                        else
                                hapd->acl_cache = entry->next;
-#ifdef CONFIG_DRIVER_RADIUS_ACL
-                       hapd->drv.set_radius_acl_expire(hapd, entry->addr);
-#endif /* CONFIG_DRIVER_RADIUS_ACL */
+                       hostapd_drv_set_radius_acl_expire(hapd, entry->addr);
                        tmp = entry;
                        entry = entry->next;
                        os_free(tmp);
@@ -332,7 +334,8 @@ static void hostapd_acl_expire_cache(struct hostapd_data *hapd, time_t now)
 }
 
 
-static void hostapd_acl_expire_queries(struct hostapd_data *hapd, time_t now)
+static void hostapd_acl_expire_queries(struct hostapd_data *hapd,
+                                      os_time_t now)
 {
        struct hostapd_acl_query_data *prev, *entry, *tmp;
 
@@ -368,11 +371,11 @@ static void hostapd_acl_expire_queries(struct hostapd_data *hapd, time_t now)
 static void hostapd_acl_expire(void *eloop_ctx, void *timeout_ctx)
 {
        struct hostapd_data *hapd = eloop_ctx;
-       time_t now;
+       struct os_time now;
 
-       time(&now);
-       hostapd_acl_expire_cache(hapd, now);
-       hostapd_acl_expire_queries(hapd, now);
+       os_get_time(&now);
+       hostapd_acl_expire_cache(hapd, now.sec);
+       hostapd_acl_expire_queries(hapd, now.sec);
 
        eloop_register_timeout(10, 0, hostapd_acl_expire, hapd, NULL);
 }
@@ -397,6 +400,7 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req,
        struct hostapd_acl_query_data *query, *prev;
        struct hostapd_cached_radius_acl *cache;
        struct radius_hdr *hdr = radius_msg_get_hdr(msg);
+       struct os_time t;
 
        query = hapd->acl_queries;
        prev = NULL;
@@ -431,7 +435,8 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req,
                wpa_printf(MSG_DEBUG, "Failed to add ACL cache entry");
                goto done;
        }
-       time(&cache->timestamp);
+       os_get_time(&t);
+       cache->timestamp = t.sec;
        os_memcpy(cache->addr, query->addr, sizeof(cache->addr));
        if (hdr->code == RADIUS_CODE_ACCESS_ACCEPT) {
                if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_SESSION_TIMEOUT,
@@ -458,8 +463,8 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req,
        hapd->acl_cache = cache;
 
 #ifdef CONFIG_DRIVER_RADIUS_ACL
-       hapd->drv.set_radius_acl_auth(hapd, query->addr, cache->accepted,
-                                     cache->session_timeout);
+       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 */
index 7541b83..6c3696f 100644 (file)
@@ -30,7 +30,8 @@ u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid)
        struct ieee80211_ht_capabilities *cap;
        u8 *pos = eid;
 
-       if (!hapd->iconf->ieee80211n || !hapd->iface->current_mode)
+       if (!hapd->iconf->ieee80211n || !hapd->iface->current_mode ||
+           hapd->conf->disable_11n)
                return eid;
 
        *pos++ = WLAN_EID_HT_CAP;
@@ -58,7 +59,7 @@ u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid)
        struct ieee80211_ht_operation *oper;
        u8 *pos = eid;
 
-       if (!hapd->iconf->ieee80211n)
+       if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n)
                return eid;
 
        *pos++ = WLAN_EID_HT_OPERATION;
@@ -92,7 +93,6 @@ Set to 1 (HT non-member protection) if there may be non-HT STAs
 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
-       (currently non-GF HT station is considered as non-HT STA also)
 */
 int hostapd_ht_operation_update(struct hostapd_iface *iface)
 {
@@ -130,13 +130,8 @@ int hostapd_ht_operation_update(struct hostapd_iface *iface)
                op_mode_changes++;
        }
 
-       /* Note: currently we switch to the MIXED op mode if HT non-greenfield
-        * station is associated. Probably it's a theoretical case, since
-        * it looks like all known HT STAs support greenfield.
-        */
        new_op_mode = 0;
-       if (iface->num_sta_no_ht ||
-           (iface->ht_op_mode & HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT))
+       if (iface->num_sta_no_ht)
                new_op_mode = OP_MODE_MIXED;
        else if ((iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)
                 && iface->num_sta_ht_20mhz)
@@ -160,11 +155,13 @@ int hostapd_ht_operation_update(struct hostapd_iface *iface)
 }
 
 
-u16 copy_sta_ht_capab(struct sta_info *sta, const u8 *ht_capab,
-                     size_t ht_capab_len)
+u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
+                     const u8 *ht_capab, size_t ht_capab_len)
 {
+       /* Disable HT caps for STAs associated to no-HT BSSes. */
        if (!ht_capab ||
-           ht_capab_len < sizeof(struct ieee80211_ht_capabilities)) {
+           ht_capab_len < sizeof(struct ieee80211_ht_capabilities) ||
+           hapd->conf->disable_11n) {
                sta->flags &= ~WLAN_STA_HT;
                os_free(sta->ht_capabilities);
                sta->ht_capabilities = NULL;
@@ -253,8 +250,14 @@ void hostapd_get_ht_capab(struct hostapd_data *hapd,
                return;
        os_memcpy(neg_ht_cap, ht_cap, sizeof(*neg_ht_cap));
        cap = le_to_host16(neg_ht_cap->ht_capabilities_info);
-       cap &= hapd->iconf->ht_capab;
-       cap |= (hapd->iconf->ht_capab & HT_CAP_INFO_SMPS_DISABLED);
+
+       /*
+        * 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
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
new file mode 100644 (file)
index 0000000..c447bce
--- /dev/null
@@ -0,0 +1,405 @@
+/*
+ * hostapd / IEEE 802.11 Management
+ * Copyright (c) 2002-2010, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "ieee802_11.h"
+
+
+#ifdef CONFIG_IEEE80211W
+
+u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd,
+                                    struct sta_info *sta, u8 *eid)
+{
+       u8 *pos = eid;
+       u32 timeout, tu;
+       struct os_time now, passed;
+
+       *pos++ = WLAN_EID_TIMEOUT_INTERVAL;
+       *pos++ = 5;
+       *pos++ = WLAN_TIMEOUT_ASSOC_COMEBACK;
+       os_get_time(&now);
+       os_time_sub(&now, &sta->sa_query_start, &passed);
+       tu = (passed.sec * 1000000 + passed.usec) / 1024;
+       if (hapd->conf->assoc_sa_query_max_timeout > tu)
+               timeout = hapd->conf->assoc_sa_query_max_timeout - tu;
+       else
+               timeout = 0;
+       if (timeout < hapd->conf->assoc_sa_query_max_timeout)
+               timeout++; /* add some extra time for local timers */
+       WPA_PUT_LE32(pos, timeout);
+       pos += 4;
+
+       return pos;
+}
+
+
+/* MLME-SAQuery.request */
+void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
+                                 const u8 *addr, const u8 *trans_id)
+{
+       struct ieee80211_mgmt mgmt;
+       u8 *end;
+
+       wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Request to "
+                  MACSTR, MAC2STR(addr));
+       wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
+                   trans_id, WLAN_SA_QUERY_TR_ID_LEN);
+
+       os_memset(&mgmt, 0, sizeof(mgmt));
+       mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+                                         WLAN_FC_STYPE_ACTION);
+       os_memcpy(mgmt.da, addr, ETH_ALEN);
+       os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
+       os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
+       mgmt.u.action.category = WLAN_ACTION_SA_QUERY;
+       mgmt.u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST;
+       os_memcpy(mgmt.u.action.u.sa_query_req.trans_id, trans_id,
+                 WLAN_SA_QUERY_TR_ID_LEN);
+       end = mgmt.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN;
+       if (hostapd_drv_send_mlme(hapd, &mgmt, end - (u8 *) &mgmt) < 0)
+               perror("ieee802_11_send_sa_query_req: send");
+}
+
+
+void ieee802_11_send_sa_query_resp(struct hostapd_data *hapd,
+                                  const u8 *sa, const u8 *trans_id)
+{
+       struct sta_info *sta;
+       struct ieee80211_mgmt resp;
+       u8 *end;
+
+       wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Request from "
+                  MACSTR, MAC2STR(sa));
+       wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
+                   trans_id, WLAN_SA_QUERY_TR_ID_LEN);
+
+       sta = ap_get_sta(hapd, sa);
+       if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) {
+               wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignore SA Query Request "
+                          "from unassociated STA " MACSTR, MAC2STR(sa));
+               return;
+       }
+
+       wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Response to "
+                  MACSTR, MAC2STR(sa));
+
+       os_memset(&resp, 0, sizeof(resp));
+       resp.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+                                         WLAN_FC_STYPE_ACTION);
+       os_memcpy(resp.da, 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 = WLAN_ACTION_SA_QUERY;
+       resp.u.action.u.sa_query_req.action = WLAN_SA_QUERY_RESPONSE;
+       os_memcpy(resp.u.action.u.sa_query_req.trans_id, trans_id,
+                 WLAN_SA_QUERY_TR_ID_LEN);
+       end = resp.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN;
+       if (hostapd_drv_send_mlme(hapd, &resp, end - (u8 *) &resp) < 0)
+               perror("ieee80211_mgmt_sa_query_request: send");
+}
+
+
+void ieee802_11_sa_query_action(struct hostapd_data *hapd, const u8 *sa,
+                               const u8 action_type, const u8 *trans_id)
+{
+       struct sta_info *sta;
+       int i;
+
+       if (action_type == WLAN_SA_QUERY_REQUEST) {
+               ieee802_11_send_sa_query_resp(hapd, sa, trans_id);
+               return;
+       }
+
+       if (action_type != WLAN_SA_QUERY_RESPONSE) {
+               wpa_printf(MSG_DEBUG, "IEEE 802.11: Unexpected SA Query "
+                          "Action %d", action_type);
+               return;
+       }
+
+       wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Response from "
+                  MACSTR, MAC2STR(sa));
+       wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
+                   trans_id, WLAN_SA_QUERY_TR_ID_LEN);
+
+       /* MLME-SAQuery.confirm */
+
+       sta = ap_get_sta(hapd, sa);
+       if (sta == NULL || sta->sa_query_trans_id == NULL) {
+               wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching STA with "
+                          "pending SA Query request found");
+               return;
+       }
+
+       for (i = 0; i < sta->sa_query_count; i++) {
+               if (os_memcmp(sta->sa_query_trans_id +
+                             i * WLAN_SA_QUERY_TR_ID_LEN,
+                             trans_id, WLAN_SA_QUERY_TR_ID_LEN) == 0)
+                       break;
+       }
+
+       if (i >= sta->sa_query_count) {
+               wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching SA Query "
+                          "transaction identifier found");
+               return;
+       }
+
+       hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+                      HOSTAPD_LEVEL_DEBUG,
+                      "Reply to pending SA Query received");
+       ap_sta_stop_sa_query(hapd, sta);
+}
+
+#endif /* CONFIG_IEEE80211W */
+
+
+u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid)
+{
+       u8 *pos = eid;
+       u8 len = 0;
+
+       if (hapd->conf->tdls & (TDLS_PROHIBIT | TDLS_PROHIBIT_CHAN_SWITCH))
+               len = 5;
+       if (len < 4 && hapd->conf->interworking)
+               len = 4;
+       if (len == 0)
+               return eid;
+
+       *pos++ = WLAN_EID_EXT_CAPAB;
+       *pos++ = len;
+       *pos++ = 0x00;
+       *pos++ = 0x00;
+       *pos++ = 0x00;
+
+       *pos = 0x00;
+       if (hapd->conf->time_advertisement == 2)
+               *pos |= 0x08; /* Bit 27 - UTC TSF Offset */
+       if (hapd->conf->interworking)
+               *pos |= 0x80; /* Bit 31 - Interworking */
+       pos++;
+
+       if (len < 5)
+               return pos;
+       *pos = 0x00;
+       if (hapd->conf->tdls & TDLS_PROHIBIT)
+               *pos |= 0x40; /* Bit 38 - TDLS Prohibited */
+       if (hapd->conf->tdls & TDLS_PROHIBIT_CHAN_SWITCH)
+               *pos |= 0x80; /* Bit 39 - TDLS Channel Switching Prohibited */
+       pos++;
+
+       return pos;
+}
+
+
+u8 * hostapd_eid_interworking(struct hostapd_data *hapd, u8 *eid)
+{
+       u8 *pos = eid;
+#ifdef CONFIG_INTERWORKING
+       u8 *len;
+
+       if (!hapd->conf->interworking)
+               return eid;
+
+       *pos++ = WLAN_EID_INTERWORKING;
+       len = pos++;
+
+       *pos = hapd->conf->access_network_type;
+       if (hapd->conf->internet)
+               *pos |= INTERWORKING_ANO_INTERNET;
+       if (hapd->conf->asra)
+               *pos |= INTERWORKING_ANO_ASRA;
+       if (hapd->conf->esr)
+               *pos |= INTERWORKING_ANO_ESR;
+       if (hapd->conf->uesa)
+               *pos |= INTERWORKING_ANO_UESA;
+       pos++;
+
+       if (hapd->conf->venue_info_set) {
+               *pos++ = hapd->conf->venue_group;
+               *pos++ = hapd->conf->venue_type;
+       }
+
+       if (!is_zero_ether_addr(hapd->conf->hessid)) {
+               os_memcpy(pos, hapd->conf->hessid, ETH_ALEN);
+               pos += ETH_ALEN;
+       }
+
+       *len = pos - len - 1;
+#endif /* CONFIG_INTERWORKING */
+
+       return pos;
+}
+
+
+u8 * hostapd_eid_adv_proto(struct hostapd_data *hapd, u8 *eid)
+{
+       u8 *pos = eid;
+#ifdef CONFIG_INTERWORKING
+
+       /* TODO: Separate configuration for ANQP? */
+       if (!hapd->conf->interworking)
+               return eid;
+
+       *pos++ = WLAN_EID_ADV_PROTO;
+       *pos++ = 2;
+       *pos++ = 0x7F; /* Query Response Length Limit | PAME-BI */
+       *pos++ = ACCESS_NETWORK_QUERY_PROTOCOL;
+#endif /* CONFIG_INTERWORKING */
+
+       return pos;
+}
+
+
+u8 * hostapd_eid_roaming_consortium(struct hostapd_data *hapd, u8 *eid)
+{
+       u8 *pos = eid;
+#ifdef CONFIG_INTERWORKING
+       u8 *len;
+       unsigned int i, count;
+
+       if (!hapd->conf->interworking ||
+           hapd->conf->roaming_consortium == NULL ||
+           hapd->conf->roaming_consortium_count == 0)
+               return eid;
+
+       *pos++ = WLAN_EID_ROAMING_CONSORTIUM;
+       len = pos++;
+
+       /* Number of ANQP OIs (in addition to the max 3 listed here) */
+       if (hapd->conf->roaming_consortium_count > 3 + 255)
+               *pos++ = 255;
+       else if (hapd->conf->roaming_consortium_count > 3)
+               *pos++ = hapd->conf->roaming_consortium_count - 3;
+       else
+               *pos++ = 0;
+
+       /* OU #1 and #2 Lengths */
+       *pos = hapd->conf->roaming_consortium[0].len;
+       if (hapd->conf->roaming_consortium_count > 1)
+               *pos |= hapd->conf->roaming_consortium[1].len << 4;
+       pos++;
+
+       if (hapd->conf->roaming_consortium_count > 3)
+               count = 3;
+       else
+               count = hapd->conf->roaming_consortium_count;
+
+       for (i = 0; i < count; i++) {
+               os_memcpy(pos, hapd->conf->roaming_consortium[i].oi,
+                         hapd->conf->roaming_consortium[i].len);
+               pos += hapd->conf->roaming_consortium[i].len;
+       }
+
+       *len = pos - len - 1;
+#endif /* CONFIG_INTERWORKING */
+
+       return pos;
+}
+
+
+u8 * hostapd_eid_time_adv(struct hostapd_data *hapd, u8 *eid)
+{
+       if (hapd->conf->time_advertisement != 2)
+               return eid;
+
+       if (hapd->time_adv == NULL &&
+           hostapd_update_time_adv(hapd) < 0)
+               return eid;
+
+       if (hapd->time_adv == NULL)
+               return eid;
+
+       os_memcpy(eid, wpabuf_head(hapd->time_adv),
+                 wpabuf_len(hapd->time_adv));
+       eid += wpabuf_len(hapd->time_adv);
+
+       return eid;
+}
+
+
+u8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid)
+{
+       size_t len;
+
+       if (hapd->conf->time_advertisement != 2)
+               return eid;
+
+       len = os_strlen(hapd->conf->time_zone);
+
+       *eid++ = WLAN_EID_TIME_ZONE;
+       *eid++ = len;
+       os_memcpy(eid, hapd->conf->time_zone, len);
+       eid += len;
+
+       return eid;
+}
+
+
+int hostapd_update_time_adv(struct hostapd_data *hapd)
+{
+       const int elen = 2 + 1 + 10 + 5 + 1;
+       struct os_time t;
+       struct os_tm tm;
+       u8 *pos;
+
+       if (hapd->conf->time_advertisement != 2)
+               return 0;
+
+       if (os_get_time(&t) < 0 || os_gmtime(t.sec, &tm) < 0)
+               return -1;
+
+       if (!hapd->time_adv) {
+               hapd->time_adv = wpabuf_alloc(elen);
+               if (hapd->time_adv == NULL)
+                       return -1;
+               pos = wpabuf_put(hapd->time_adv, elen);
+       } else
+               pos = wpabuf_mhead_u8(hapd->time_adv);
+
+       *pos++ = WLAN_EID_TIME_ADVERTISEMENT;
+       *pos++ = 1 + 10 + 5 + 1;
+
+       *pos++ = 2; /* UTC time at which the TSF timer is 0 */
+
+       /* Time Value at TSF 0 */
+       /* FIX: need to calculate this based on the current TSF value */
+       WPA_PUT_LE16(pos, tm.year); /* Year */
+       pos += 2;
+       *pos++ = tm.month; /* Month */
+       *pos++ = tm.day; /* Day of month */
+       *pos++ = tm.hour; /* Hours */
+       *pos++ = tm.min; /* Minutes */
+       *pos++ = tm.sec; /* Seconds */
+       WPA_PUT_LE16(pos, 0); /* Milliseconds (not used) */
+       pos += 2;
+       *pos++ = 0; /* Reserved */
+
+       /* Time Error */
+       /* TODO: fill in an estimate on the error */
+       *pos++ = 0;
+       *pos++ = 0;
+       *pos++ = 0;
+       *pos++ = 0;
+       *pos++ = 0;
+
+       *pos++ = hapd->time_update_counter++;
+
+       return 0;
+}
index eb160f8..f9a0cbb 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * hostapd / IEEE 802.1X-2004 Authenticator
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -18,6 +18,7 @@
 #include "utils/eloop.h"
 #include "crypto/md5.h"
 #include "crypto/crypto.h"
+#include "crypto/random.h"
 #include "common/ieee802_11_defs.h"
 #include "common/wpa_ctrl.h"
 #include "radius/radius.h"
@@ -26,6 +27,7 @@
 #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"
@@ -33,6 +35,7 @@
 #include "preauth_auth.h"
 #include "pmksa_cache_auth.h"
 #include "ap_config.h"
+#include "ap_drv_ops.h"
 #include "ieee802_1x.h"
 
 
@@ -70,7 +73,8 @@ static void ieee802_1x_send(struct hostapd_data *hapd, struct sta_info *sta,
        if (sta->flags & WLAN_STA_PREAUTH) {
                rsn_preauth_send(hapd, sta, buf, len);
        } else {
-               hapd->drv.send_eapol(hapd, sta->addr, buf, len, encrypt);
+               hostapd_drv_hapd_send_eapol(hapd, sta->addr, buf, len,
+                                           encrypt, sta->flags);
        }
 
        os_free(buf);
@@ -86,21 +90,13 @@ void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd,
                return;
 
        if (authorized) {
-               if (!(sta->flags & WLAN_STA_AUTHORIZED))
-                       wpa_msg(hapd->msg_ctx, MSG_INFO,
-                               AP_STA_CONNECTED MACSTR, MAC2STR(sta->addr));
-               sta->flags |= WLAN_STA_AUTHORIZED;
-               res = hapd->drv.set_authorized(hapd, sta, 1);
+               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 {
-               if ((sta->flags & (WLAN_STA_AUTHORIZED | WLAN_STA_ASSOC)) ==
-                   (WLAN_STA_AUTHORIZED | WLAN_STA_ASSOC))
-                       wpa_msg(hapd->msg_ctx, MSG_INFO,
-                               AP_STA_DISCONNECTED MACSTR,
-                               MAC2STR(sta->addr));
-               sta->flags &= ~WLAN_STA_AUTHORIZED;
-               res = hapd->drv.set_authorized(hapd, sta, 0);
+               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");
        }
@@ -140,7 +136,7 @@ static void ieee802_1x_tx_key_one(struct hostapd_data *hapd,
        key->key_length = htons(key_len);
        wpa_get_ntp_timestamp(key->replay_counter);
 
-       if (os_get_random(key->key_iv, sizeof(key->key_iv))) {
+       if (random_get_bytes(key->key_iv, sizeof(key->key_iv))) {
                wpa_printf(MSG_ERROR, "Could not get random numbers");
                os_free(buf);
                return;
@@ -215,7 +211,7 @@ ieee802_1x_group_alloc(struct hostapd_data *hapd, const char *ifname)
        if (!key->key[key->idx])
                key->key[key->idx] = os_malloc(key->default_len);
        if (key->key[key->idx] == NULL ||
-           os_get_random(key->key[key->idx], key->default_len)) {
+           random_get_bytes(key->key[key->idx], key->default_len)) {
                printf("Could not generate random WEP key (dynamic VLAN).\n");
                os_free(key->key[key->idx]);
                key->key[key->idx] = NULL;
@@ -229,11 +225,13 @@ ieee802_1x_group_alloc(struct hostapd_data *hapd, const char *ifname)
        wpa_hexdump_key(MSG_DEBUG, "Default WEP key (dynamic VLAN)",
                        key->key[key->idx], key->len[key->idx]);
 
-       if (hapd->drv.set_key(ifname, hapd, WPA_ALG_WEP, NULL, key->idx, 1,
-                             NULL, 0, key->key[key->idx], key->len[key->idx]))
+       if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_WEP,
+                               broadcast_ether_addr, key->idx, 1,
+                               NULL, 0, key->key[key->idx],
+                               key->len[key->idx]))
                printf("Could not set dynamic VLAN WEP encryption key.\n");
 
-       hapd->drv.set_drv_ieee8021x(hapd, ifname, 1);
+       hostapd_set_drv_ieee8021x(hapd, ifname, 1);
 
        return key;
 }
@@ -330,7 +328,8 @@ void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta)
                u8 *ikey;
                ikey = os_malloc(hapd->conf->individual_wep_key_len);
                if (ikey == NULL ||
-                   os_get_random(ikey, hapd->conf->individual_wep_key_len)) {
+                   random_get_bytes(ikey, hapd->conf->individual_wep_key_len))
+               {
                        wpa_printf(MSG_ERROR, "Could not generate random "
                                   "individual WEP key.");
                        os_free(ikey);
@@ -345,9 +344,9 @@ void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta)
 
                /* TODO: set encryption in TX callback, i.e., only after STA
                 * has ACKed EAPOL-Key frame */
-               if (hapd->drv.set_key(hapd->conf->iface, hapd, WPA_ALG_WEP,
-                                     sta->addr, 0, 1, NULL, 0, ikey,
-                                     hapd->conf->individual_wep_key_len)) {
+               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.");
                }
@@ -549,7 +548,9 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
                }
        }
 
-       radius_client_send(hapd->radius, msg, RADIUS_AUTH, sta->addr);
+       if (radius_client_send(hapd->radius, msg, RADIUS_AUTH, sta->addr) < 0)
+               goto fail;
+
        return;
 
  fail:
@@ -652,7 +653,7 @@ ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta)
                        flags |= EAPOL_SM_FROM_PMKSA_CACHE;
        }
        return eapol_auth_alloc(hapd->eapol_auth, sta->addr, flags,
-                               sta->wps_ie, sta);
+                               sta->wps_ie, sta->p2p_ie, sta);
 }
 
 
@@ -673,6 +674,7 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
        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->wps_state)
@@ -681,9 +683,10 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
        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)) {
+       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 STA");
+                          "associated/Pre-authenticating STA");
                return;
        }
 
@@ -724,10 +727,19 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
                return;
        }
 
-       if ((!hapd->conf->ieee802_1x &&
-            !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) ||
-           wpa_key_mgmt_wpa_psk(wpa_auth_sta_key_mgmt(sta->wpa_sm)))
+       if (!hapd->conf->ieee802_1x &&
+           !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) {
+               wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore EAPOL message - "
+                          "802.1X not enabled and WPS not used");
                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);
@@ -735,14 +747,24 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
                        return;
 
 #ifdef CONFIG_WPS
-               if (!hapd->conf->ieee802_1x &&
-                   ((sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)) ==
-                    WLAN_STA_MAYBE_WPS)) {
-                       /*
-                        * Delay EAPOL frame transmission until a possible WPS
-                        * STA initiates the handshake with EAPOL-Start.
-                        */
-                       sta->eapol_sm->flags |= EAPOL_SM_WAIT_START;
+               if (!hapd->conf->ieee802_1x) {
+                       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 */
 
@@ -776,6 +798,7 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
                }
                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;
 
@@ -788,11 +811,12 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
                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 (!(sta->flags & WLAN_STA_AUTHORIZED)) {
+               if (!ap_sta_is_authorized(sta)) {
                        wpa_printf(MSG_DEBUG, "   Dropped key data from "
                                   "unauthorized Supplicant");
                        break;
@@ -827,6 +851,7 @@ 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 &&
@@ -840,9 +865,27 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta)
        }
 #endif /* CONFIG_WPS */
 
-       if ((!force_1x && !hapd->conf->ieee802_1x) ||
-           wpa_key_mgmt_wpa_psk(wpa_auth_sta_key_mgmt(sta->wpa_sm)))
+       if (!force_1x && !hapd->conf->ieee802_1x) {
+               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,
@@ -860,17 +903,40 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta)
 
 #ifdef CONFIG_WPS
        sta->eapol_sm->flags &= ~EAPOL_SM_WAIT_START;
-       if (!hapd->conf->ieee802_1x && !(sta->flags & WLAN_STA_WPS)) {
+       if (!hapd->conf->ieee802_1x && !(sta->flags & WLAN_STA_WPS2)) {
                /*
-                * Delay EAPOL frame transmission until a possible WPS
-                * initiates the handshake with EAPOL-Start.
+                * 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) {
                int old_vlanid;
@@ -885,6 +951,7 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta)
                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);
                old_vlanid = sta->vlan_id;
@@ -1380,8 +1447,8 @@ static int ieee802_1x_rekey_broadcast(struct hostapd_data *hapd)
        os_free(eapol->default_wep_key);
        eapol->default_wep_key = os_malloc(hapd->conf->default_wep_key_len);
        if (eapol->default_wep_key == NULL ||
-           os_get_random(eapol->default_wep_key,
-                         hapd->conf->default_wep_key_len)) {
+           random_get_bytes(eapol->default_wep_key,
+                            hapd->conf->default_wep_key_len)) {
                printf("Could not generate random WEP key.\n");
                os_free(eapol->default_wep_key);
                eapol->default_wep_key = NULL;
@@ -1432,10 +1499,11 @@ static void ieee802_1x_rekey(void *eloop_ctx, void *timeout_ctx)
 
        /* TODO: Could setup key for RX here, but change default TX keyid only
         * after new broadcast key has been sent to all stations. */
-       if (hapd->drv.set_key(hapd->conf->iface, hapd, WPA_ALG_WEP, NULL,
-                             eapol->default_wep_key_idx, 1, NULL, 0,
-                             eapol->default_wep_key,
-                             hapd->conf->default_wep_key_len)) {
+       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");
@@ -1538,6 +1606,7 @@ static int ieee802_1x_get_eap_user(void *ctx, const u8 *identity,
                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->ttls_auth = eap_user->ttls_auth;
@@ -1651,6 +1720,9 @@ int ieee802_1x_init(struct hostapd_data *hapd)
        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;
 
        os_memset(&cb, 0, sizeof(cb));
        cb.eapol_send = ieee802_1x_eapol_send;
@@ -1669,7 +1741,7 @@ int ieee802_1x_init(struct hostapd_data *hapd)
                return -1;
 
        if ((hapd->conf->ieee802_1x || hapd->conf->wpa) &&
-           hapd->drv.set_drv_ieee8021x(hapd, hapd->conf->iface, 1))
+           hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 1))
                return -1;
 
 #ifndef CONFIG_NO_RADIUS
@@ -1680,9 +1752,9 @@ int ieee802_1x_init(struct hostapd_data *hapd)
 
        if (hapd->conf->default_wep_key_len) {
                for (i = 0; i < 4; i++)
-                       hapd->drv.set_key(hapd->conf->iface, hapd,
-                                         WPA_ALG_NONE, NULL, i, 0, NULL, 0,
-                                         NULL, 0);
+                       hostapd_drv_set_key(hapd->conf->iface, hapd,
+                                           WPA_ALG_NONE, NULL, i, 0, NULL, 0,
+                                           NULL, 0);
 
                ieee802_1x_rekey(hapd, NULL);
 
@@ -1700,7 +1772,7 @@ void ieee802_1x_deinit(struct hostapd_data *hapd)
 
        if (hapd->driver != NULL &&
            (hapd->conf->ieee802_1x || hapd->conf->wpa))
-               hapd->drv.set_drv_ieee8021x(hapd, hapd->conf->iface, 0);
+               hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 0);
 
        eapol_auth_deinit(hapd->eapol_auth);
        hapd->eapol_auth = NULL;
@@ -1739,9 +1811,19 @@ int ieee802_1x_tx_status(struct hostapd_data *hapd, struct sta_info *sta,
                   MAC2STR(sta->addr), xhdr->version, xhdr->type,
                   be_to_host16(xhdr->length), ack);
 
+       if (xhdr->type == IEEE802_1X_TYPE_EAPOL_KEY &&
+           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-sent failed EAPOL-Key
+        * 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 (xhdr->type == IEEE802_1X_TYPE_EAPOL_KEY && !ack &&
@@ -1791,6 +1873,7 @@ u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len,
 
 const u8 * ieee802_1x_get_key(struct eapol_state_machine *sm, size_t *len)
 {
+       *len = 0;
        if (sm == NULL)
                return NULL;
 
@@ -1848,6 +1931,7 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
 {
        int len = 0, ret;
        struct eapol_state_machine *sm = sta->eapol_sm;
+       struct os_time t;
 
        if (sm == NULL)
                return 0;
@@ -1962,6 +2046,7 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
        len += ret;
 
        /* dot1xAuthSessionStatsTable */
+       os_get_time(&t);
        ret = os_snprintf(buf + len, buflen - len,
                          /* TODO: dot1xAuthSessionOctetsRx */
                          /* TODO: dot1xAuthSessionOctetsTx */
@@ -1976,8 +2061,7 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
                          (wpa_key_mgmt_wpa_ieee8021x(
                                   wpa_auth_sta_key_mgmt(sta->wpa_sm))) ?
                          1 : 2,
-                         (unsigned int) (time(NULL) -
-                                         sta->acct_session_start),
+                         (unsigned int) (t.sec - sta->acct_session_start),
                          sm->identity);
        if (ret < 0 || (size_t) ret >= buflen - len)
                return len;
@@ -2004,14 +2088,18 @@ static void ieee802_1x_finished(struct hostapd_data *hapd,
                               "Added PMKSA cache entry (IEEE 802.1X)");
        }
 
-#ifdef CONFIG_WPS
-       if (!success && (sta->flags & WLAN_STA_WPS)) {
+       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.
+                * 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_printf(MSG_DEBUG, "WPS: Force disconnection after "
+               wpa_printf(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
@@ -2019,7 +2107,6 @@ static void ieee802_1x_finished(struct hostapd_data *hapd,
                 */
                os_sleep(0, 10000);
                ap_sta_disconnect(hapd, sta, sta->addr,
-                                 WLAN_REASON_PREV_AUTH_NOT_VALID);
+                                 WLAN_REASON_IEEE_802_1X_AUTH_FAILED);
        }
-#endif /* CONFIG_WPS */
 }
diff --git a/src/ap/p2p_hostapd.c b/src/ap/p2p_hostapd.c
new file mode 100644 (file)
index 0000000..6f8b778
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * hostapd / P2P integration
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "p2p/p2p.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "sta_info.h"
+#include "p2p_hostapd.h"
+
+
+#ifdef CONFIG_P2P
+
+int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+                           char *buf, size_t buflen)
+{
+       if (sta->p2p_ie == NULL)
+               return 0;
+
+       return p2p_ie_text(sta->p2p_ie, buf, buf + buflen);
+}
+
+
+int hostapd_p2p_set_noa(struct hostapd_data *hapd, u8 count, int start,
+                       int duration)
+{
+       wpa_printf(MSG_DEBUG, "P2P: Set NoA parameters: count=%u start=%d "
+                  "duration=%d", count, start, duration);
+
+       if (count == 0) {
+               hapd->noa_enabled = 0;
+               hapd->noa_start = 0;
+               hapd->noa_duration = 0;
+       }
+
+       if (count != 255) {
+               wpa_printf(MSG_DEBUG, "P2P: Non-periodic NoA - set "
+                          "NoA parameters");
+               return hostapd_driver_set_noa(hapd, count, start, duration);
+       }
+
+       hapd->noa_enabled = 1;
+       hapd->noa_start = start;
+       hapd->noa_duration = duration;
+
+       if (hapd->num_sta_no_p2p == 0) {
+               wpa_printf(MSG_DEBUG, "P2P: No legacy STAs connected - update "
+                          "periodic NoA parameters");
+               return hostapd_driver_set_noa(hapd, count, start, duration);
+       }
+
+       wpa_printf(MSG_DEBUG, "P2P: Legacy STA(s) connected - do not enable "
+                  "periodic NoA");
+
+       return 0;
+}
+
+
+void hostapd_p2p_non_p2p_sta_connected(struct hostapd_data *hapd)
+{
+       wpa_printf(MSG_DEBUG, "P2P: First non-P2P device connected");
+
+       if (hapd->noa_enabled) {
+               wpa_printf(MSG_DEBUG, "P2P: Disable periodic NoA");
+               hostapd_driver_set_noa(hapd, 0, 0, 0);
+       }
+}
+
+
+void hostapd_p2p_non_p2p_sta_disconnected(struct hostapd_data *hapd)
+{
+       wpa_printf(MSG_DEBUG, "P2P: Last non-P2P device disconnected");
+
+       if (hapd->noa_enabled) {
+               wpa_printf(MSG_DEBUG, "P2P: Enable periodic NoA");
+               hostapd_driver_set_noa(hapd, 255, hapd->noa_start,
+                                      hapd->noa_duration);
+       }
+}
+
+#endif /* CONFIG_P2P */
+
+
+#ifdef CONFIG_P2P_MANAGER
+u8 * hostapd_eid_p2p_manage(struct hostapd_data *hapd, u8 *eid)
+{
+       u8 bitmap;
+       *eid++ = WLAN_EID_VENDOR_SPECIFIC;
+       *eid++ = 4 + 3 + 1;
+       WPA_PUT_BE24(eid, OUI_WFA);
+       eid += 3;
+       *eid++ = P2P_OUI_TYPE;
+
+       *eid++ = P2P_ATTR_MANAGEABILITY;
+       WPA_PUT_LE16(eid, 1);
+       eid += 2;
+       bitmap = P2P_MAN_DEVICE_MANAGEMENT;
+       if (hapd->conf->p2p & P2P_ALLOW_CROSS_CONNECTION)
+               bitmap |= P2P_MAN_CROSS_CONNECTION_PERMITTED;
+       bitmap |= P2P_MAN_COEXISTENCE_OPTIONAL;
+       *eid++ = bitmap;
+
+       return eid;
+}
+#endif /* CONFIG_P2P_MANAGER */
diff --git a/src/ap/p2p_hostapd.h b/src/ap/p2p_hostapd.h
new file mode 100644 (file)
index 0000000..95b31d9
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * hostapd / P2P integration
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef P2P_HOSTAPD_H
+#define P2P_HOSTAPD_H
+
+#ifdef CONFIG_P2P
+
+int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+                           char *buf, size_t buflen);
+int hostapd_p2p_set_noa(struct hostapd_data *hapd, u8 count, int start,
+                       int duration);
+void hostapd_p2p_non_p2p_sta_connected(struct hostapd_data *hapd);
+void hostapd_p2p_non_p2p_sta_disconnected(struct hostapd_data *hapd);
+
+
+#else /* CONFIG_P2P */
+
+static inline int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd,
+                                         struct sta_info *sta,
+                                         char *buf, size_t buflen)
+{
+       return 0;
+}
+
+#endif /* CONFIG_P2P */
+
+u8 * hostapd_eid_p2p_manage(struct hostapd_data *hapd, u8 *eid);
+
+#endif /* P2P_HOSTAPD_H */
index f68c479..b8fa5a9 100644 (file)
@@ -18,6 +18,7 @@
 #include "utils/eloop.h"
 #include "crypto/sha1.h"
 #include "crypto/sha256.h"
+#include "crypto/random.h"
 #include "wpa_auth.h"
 #include "wpa_auth_i.h"
 #include "wpa_auth_ie.h"
@@ -294,7 +295,7 @@ void wpa_smk_m3(struct wpa_authenticator *wpa_auth,
                return;
        }
 
-       if (os_get_random(smk, PMK_LEN)) {
+       if (random_get_bytes(smk, PMK_LEN)) {
                wpa_printf(MSG_DEBUG, "RSN: Failed to generate SMK");
                return;
        }
index 335c9a5..82a3f69 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * hostapd / Station table
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2011, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
 #include "utils/common.h"
 #include "utils/eloop.h"
 #include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
 #include "radius/radius.h"
 #include "radius/radius_client.h"
 #include "drivers/driver.h"
+#include "p2p/p2p.h"
 #include "hostapd.h"
 #include "accounting.h"
 #include "ieee802_1x.h"
 #include "beacon.h"
 #include "ap_mlme.h"
 #include "vlan_init.h"
+#include "p2p_hostapd.h"
+#include "ap_drv_ops.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_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,
@@ -121,11 +128,14 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
 
        accounting_sta_stop(hapd, sta);
 
+       /* just in case */
+       ap_sta_set_authorized(hapd, sta, 0);
+
        if (sta->flags & WLAN_STA_WDS)
-               hapd->drv.set_wds_sta(hapd, sta->addr, sta->aid, 0);
+               hostapd_set_wds_sta(hapd, sta->addr, sta->aid, 0);
 
        if (!(sta->flags & WLAN_STA_PREAUTH))
-               hapd->drv.sta_remove(hapd, sta->addr);
+               hostapd_drv_sta_remove(hapd, sta->addr);
 
        ap_sta_hash_del(hapd, sta);
        ap_sta_list_del(hapd, sta);
@@ -173,6 +183,15 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
                hapd->iface->num_sta_ht_20mhz--;
        }
 
+#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++;
@@ -183,6 +202,8 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
 
        eloop_cancel_timeout(ap_handle_timer, hapd, sta);
        eloop_cancel_timeout(ap_handle_session_timer, hapd, sta);
+       eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta);
+       eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta);
 
        ieee802_1x_free_station(sta);
        wpa_auth_sta_deinit(sta->wpa_sm);
@@ -199,7 +220,12 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
        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 */
+
        wpabuf_free(sta->wps_ie);
+       wpabuf_free(sta->p2p_ie);
 
        os_free(sta->ht_capabilities);
 
@@ -253,27 +279,41 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx)
            (sta->timeout_next == STA_NULLFUNC ||
             sta->timeout_next == STA_DISASSOC)) {
                int inactive_sec;
-               wpa_printf(MSG_DEBUG, "Checking STA " MACSTR " inactivity:",
-                          MAC2STR(sta->addr));
-               inactive_sec = hapd->drv.get_inact_sec(hapd, sta->addr);
+               inactive_sec = hostapd_drv_get_inact_sec(hapd, sta->addr);
                if (inactive_sec == -1) {
-                       wpa_printf(MSG_DEBUG, "Could not get station info "
-                                  "from kernel driver for " MACSTR ".",
-                                  MAC2STR(sta->addr));
+                       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;
                } else if (inactive_sec < hapd->conf->ap_max_inactivity &&
                           sta->flags & WLAN_STA_ASSOC) {
                        /* station activity detected; reset timeout state */
-                       wpa_printf(MSG_DEBUG, "  Station has been active");
+                       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 -
                                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 ((sta->flags & WLAN_STA_ASSOC) &&
            sta->timeout_next == STA_DISASSOC &&
            !(sta->flags & WLAN_STA_PENDING_POLL)) {
-               wpa_printf(MSG_DEBUG, "  Station has ACKed data 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;
@@ -288,40 +328,10 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx)
 
        if (sta->timeout_next == STA_NULLFUNC &&
            (sta->flags & WLAN_STA_ASSOC)) {
-#ifndef CONFIG_NATIVE_WINDOWS
-               /* send data frame to poll STA and check whether this frame
-                * is ACKed */
-               struct ieee80211_hdr hdr;
-
-               wpa_printf(MSG_DEBUG, "  Polling STA with data frame");
+               wpa_printf(MSG_DEBUG, "  Polling STA");
                sta->flags |= WLAN_STA_PENDING_POLL;
-
-               os_memset(&hdr, 0, sizeof(hdr));
-               if (hapd->driver &&
-                   os_strcmp(hapd->driver->name, "hostap") == 0) {
-                       /*
-                        * WLAN_FC_STYPE_NULLFUNC would be more appropriate,
-                        * but it is apparently not retried so TX Exc events
-                        * are not received for it.
-                        */
-                       hdr.frame_control =
-                               IEEE80211_FC(WLAN_FC_TYPE_DATA,
-                                            WLAN_FC_STYPE_DATA);
-               } else {
-                       hdr.frame_control =
-                               IEEE80211_FC(WLAN_FC_TYPE_DATA,
-                                            WLAN_FC_STYPE_NULLFUNC);
-               }
-
-               hdr.frame_control |= host_to_le16(WLAN_FC_FROMDS);
-               os_memcpy(hdr.IEEE80211_DA_FROMDS, sta->addr, ETH_ALEN);
-               os_memcpy(hdr.IEEE80211_BSSID_FROMDS, hapd->own_addr,
-                         ETH_ALEN);
-               os_memcpy(hdr.IEEE80211_SA_FROMDS, hapd->own_addr, ETH_ALEN);
-
-               if (hapd->drv.send_mgmt_frame(hapd, &hdr, sizeof(hdr)) < 0)
-                       perror("ap_handle_timer: send");
-#endif /* CONFIG_NATIVE_WINDOWS */
+               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;
 
@@ -330,10 +340,11 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx)
                           MAC2STR(sta->addr));
 
                if (deauth) {
-                       hapd->drv.sta_deauth(hapd, sta->addr,
-                                            WLAN_REASON_PREV_AUTH_NOT_VALID);
+                       hostapd_drv_sta_deauth(
+                               hapd, sta->addr,
+                               WLAN_REASON_PREV_AUTH_NOT_VALID);
                } else {
-                       hapd->drv.sta_disassoc(
+                       hostapd_drv_sta_disassoc(
                                hapd, sta->addr,
                                WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY);
                }
@@ -346,6 +357,7 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx)
                                       hapd, sta);
                break;
        case STA_DISASSOC:
+               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)
@@ -397,7 +409,7 @@ static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx)
                RADIUS_ACCT_TERMINATE_CAUSE_SESSION_TIMEOUT;
        os_memcpy(addr, sta->addr, ETH_ALEN);
        ap_free_sta(hapd, sta);
-       hapd->drv.sta_deauth(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
+       hostapd_drv_sta_deauth(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
 }
 
 
@@ -463,7 +475,7 @@ static int ap_sta_remove(struct hostapd_data *hapd, struct sta_info *sta)
 
        wpa_printf(MSG_DEBUG, "Removing STA " MACSTR " from kernel driver",
                   MAC2STR(sta->addr));
-       if (hapd->drv.sta_remove(hapd, 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));
@@ -498,13 +510,23 @@ static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd,
 }
 
 
+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->flags &= ~WLAN_STA_ASSOC;
-       ap_sta_remove(hapd, sta);
+       ap_sta_set_authorized(hapd, sta, 0);
        sta->timeout_next = STA_DEAUTH;
        eloop_cancel_timeout(ap_handle_timer, hapd, sta);
        eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DISASSOC, 0,
@@ -512,7 +534,22 @@ void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta,
        accounting_sta_stop(hapd, sta);
        ieee802_1x_free_station(sta);
 
-       mlme_disassociate_indication(hapd, sta, reason);
+       sta->disassoc_reason = reason;
+       sta->flags |= WLAN_STA_PENDING_DISASSOC_CB;
+       eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta);
+       eloop_register_timeout(hapd->iface->drv_flags &
+                              WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0,
+                              ap_sta_disassoc_cb_timeout, hapd, sta);
+}
+
+
+static void ap_sta_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);
 }
 
 
@@ -522,7 +559,7 @@ void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta,
        wpa_printf(MSG_DEBUG, "%s: deauthenticate STA " MACSTR,
                   hapd->conf->iface, MAC2STR(sta->addr));
        sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
-       ap_sta_remove(hapd, sta);
+       ap_sta_set_authorized(hapd, sta, 0);
        sta->timeout_next = STA_REMOVE;
        eloop_cancel_timeout(ap_handle_timer, hapd, sta);
        eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0,
@@ -530,7 +567,12 @@ void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta,
        accounting_sta_stop(hapd, sta);
        ieee802_1x_free_station(sta);
 
-       mlme_deauthenticate_indication(hapd, sta, reason);
+       sta->deauth_reason = reason;
+       sta->flags |= WLAN_STA_PENDING_DEAUTH_CB;
+       eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta);
+       eloop_register_timeout(hapd->iface->drv_flags &
+                              WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0,
+                              ap_sta_deauth_cb_timeout, hapd, sta);
 }
 
 
@@ -636,7 +678,7 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta,
        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 = hapd->drv.set_sta_vlan(iface, hapd, sta->addr, sta->vlan_id);
+       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 "
@@ -732,6 +774,64 @@ void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta)
 #endif /* CONFIG_IEEE80211W */
 
 
+void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta,
+                          int authorized)
+{
+       const u8 *dev_addr = NULL;
+       if (!!authorized == !!(sta->flags & WLAN_STA_AUTHORIZED))
+               return;
+
+#ifdef CONFIG_P2P
+       dev_addr = p2p_group_get_dev_addr(hapd->p2p_group, sta->addr);
+#endif /* CONFIG_P2P */
+
+       if (authorized) {
+               if (dev_addr)
+                       wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED
+                               MACSTR " p2p_dev_addr=" MACSTR,
+                               MAC2STR(sta->addr), MAC2STR(dev_addr));
+               else
+                       wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED
+                               MACSTR, MAC2STR(sta->addr));
+               if (hapd->msg_ctx_parent &&
+                   hapd->msg_ctx_parent != hapd->msg_ctx && dev_addr)
+                       wpa_msg(hapd->msg_ctx_parent, MSG_INFO,
+                               AP_STA_CONNECTED MACSTR " p2p_dev_addr="
+                               MACSTR,
+                               MAC2STR(sta->addr), MAC2STR(dev_addr));
+               else if (hapd->msg_ctx_parent &&
+                        hapd->msg_ctx_parent != hapd->msg_ctx)
+                       wpa_msg(hapd->msg_ctx_parent, MSG_INFO,
+                               AP_STA_CONNECTED MACSTR, MAC2STR(sta->addr));
+
+               sta->flags |= WLAN_STA_AUTHORIZED;
+       } else {
+               if (dev_addr)
+                       wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED
+                               MACSTR " p2p_dev_addr=" MACSTR,
+                               MAC2STR(sta->addr), MAC2STR(dev_addr));
+               else
+                       wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED
+                               MACSTR, MAC2STR(sta->addr));
+               if (hapd->msg_ctx_parent &&
+                   hapd->msg_ctx_parent != hapd->msg_ctx && dev_addr)
+                       wpa_msg(hapd->msg_ctx_parent, MSG_INFO,
+                               AP_STA_DISCONNECTED MACSTR " p2p_dev_addr="
+                               MACSTR, MAC2STR(sta->addr), MAC2STR(dev_addr));
+               else if (hapd->msg_ctx_parent &&
+                        hapd->msg_ctx_parent != hapd->msg_ctx)
+                       wpa_msg(hapd->msg_ctx_parent, MSG_INFO,
+                               AP_STA_DISCONNECTED MACSTR,
+                               MAC2STR(sta->addr));
+               sta->flags &= ~WLAN_STA_AUTHORIZED;
+       }
+
+       if (hapd->sta_authorized_cb)
+               hapd->sta_authorized_cb(hapd->sta_authorized_cb_ctx,
+                                       sta->addr, authorized, dev_addr);
+}
+
+
 void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta,
                       const u8 *addr, u16 reason)
 {
@@ -740,12 +840,47 @@ void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta,
                sta = ap_get_sta(hapd, addr);
 
        if (addr)
-               hapd->drv.sta_deauth(hapd, addr, reason);
+               hostapd_drv_sta_deauth(hapd, addr, reason);
 
        if (sta == NULL)
                return;
-       sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_AUTHORIZED);
+       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);
        eloop_cancel_timeout(ap_handle_timer, hapd, sta);
-       eloop_register_timeout(0, 0, 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);
 }
index 55faa5a..3ad00e2 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * hostapd / Station table
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2011, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
 #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_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
@@ -55,6 +59,7 @@ struct sta_info {
        unsigned int no_ht_gf_set:1;
        unsigned int no_ht_set:1;
        unsigned int ht_20mhz_set:1;
+       unsigned int no_p2p_set:1;
 
        u16 auth_alg;
        u8 previous_ap[6];
@@ -63,6 +68,9 @@ struct sta_info {
                STA_NULLFUNC = 0, STA_DISASSOC, STA_DEAUTH, STA_REMOVE
        } timeout_next;
 
+       u16 deauth_reason;
+       u16 disassoc_reason;
+
        /* IEEE 802.1X related data */
        struct eapol_state_machine *eapol_sm;
 
@@ -104,6 +112,7 @@ struct sta_info {
 #endif /* CONFIG_IEEE80211W */
 
        struct wpabuf *wps_ie; /* WPS IE from (Re)Association Request */
+       struct wpabuf *p2p_ie; /* P2P IE from (Re)Association Request */
 };
 
 
@@ -111,7 +120,7 @@ struct sta_info {
  * 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. Similarily, the station will be deauthenticated
+ * 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)
@@ -152,4 +161,14 @@ 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);
+
 #endif /* STA_INFO_H */
index 9690348..fac7f4b 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * hostapd / TKIP countermeasures
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2011, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -21,6 +21,7 @@
 #include "sta_info.h"
 #include "ap_mlme.h"
 #include "wpa_auth.h"
+#include "ap_drv_ops.h"
 #include "tkip_countermeasures.h"
 
 
@@ -29,7 +30,7 @@ static void ieee80211_tkip_countermeasures_stop(void *eloop_ctx,
 {
        struct hostapd_data *hapd = eloop_ctx;
        hapd->tkip_countermeasures = 0;
-       hapd->drv.set_countermeasures(hapd, 0);
+       hostapd_drv_set_countermeasures(hapd, 0);
        hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
                       HOSTAPD_LEVEL_INFO, "TKIP countermeasures ended");
 }
@@ -44,24 +45,30 @@ static void ieee80211_tkip_countermeasures_start(struct hostapd_data *hapd)
 
        wpa_auth_countermeasures_start(hapd->wpa_auth);
        hapd->tkip_countermeasures = 1;
-       hapd->drv.set_countermeasures(hapd, 1);
+       hostapd_drv_set_countermeasures(hapd, 1);
        wpa_gtk_rekey(hapd->wpa_auth);
        eloop_cancel_timeout(ieee80211_tkip_countermeasures_stop, hapd, NULL);
        eloop_register_timeout(60, 0, ieee80211_tkip_countermeasures_stop,
                               hapd, NULL);
        for (sta = hapd->sta_list; sta != NULL; sta = sta->next) {
-               hapd->drv.sta_deauth(hapd, sta->addr,
-                                    WLAN_REASON_MICHAEL_MIC_FAILURE);
-               sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC |
-                               WLAN_STA_AUTHORIZED);
-               hapd->drv.sta_remove(hapd, sta->addr);
+               hostapd_drv_sta_deauth(hapd, sta->addr,
+                                      WLAN_REASON_MICHAEL_MIC_FAILURE);
+               ap_sta_set_authorized(hapd, sta, 0);
+               sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
+               hostapd_drv_sta_remove(hapd, sta->addr);
        }
 }
 
 
+void ieee80211_tkip_countermeasures_deinit(struct hostapd_data *hapd)
+{
+       eloop_cancel_timeout(ieee80211_tkip_countermeasures_stop, hapd, NULL);
+}
+
+
 void michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local)
 {
-       time_t now;
+       struct os_time now;
 
        if (addr && local) {
                struct sta_info *sta = ap_get_sta(hapd, addr);
@@ -81,13 +88,13 @@ void michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local)
                }
        }
 
-       time(&now);
-       if (now > hapd->michael_mic_failure + 60) {
+       os_get_time(&now);
+       if (now.sec > hapd->michael_mic_failure + 60) {
                hapd->michael_mic_failures = 1;
        } else {
                hapd->michael_mic_failures++;
                if (hapd->michael_mic_failures > 1)
                        ieee80211_tkip_countermeasures_start(hapd);
        }
-       hapd->michael_mic_failure = now;
+       hapd->michael_mic_failure = now.sec;
 }
index 5a1afce..a8ffd16 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * hostapd / TKIP countermeasures
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2011, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -16,5 +16,6 @@
 #define TKIP_COUNTERMEASURES_H
 
 void michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local);
+void ieee80211_tkip_countermeasures_deinit(struct hostapd_data *hapd);
 
 #endif /* TKIP_COUNTERMEASURES_H */
index 0ff48ae..09bc32f 100644 (file)
@@ -22,6 +22,7 @@
 
 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),
                                 void *ctx)
 {
index c9d166a..f2f766f 100644 (file)
@@ -19,6 +19,7 @@
 #include "utils/common.h"
 #include "hostapd.h"
 #include "ap_config.h"
+#include "ap_drv_ops.h"
 #include "vlan_init.h"
 
 
@@ -737,9 +738,10 @@ int vlan_setup_encryption_dyn(struct hostapd_data *hapd,
         * functions for setting up dynamic broadcast keys. */
        for (i = 0; i < 4; i++) {
                if (mssid->wep.key[i] &&
-                   hapd->drv.set_key(dyn_vlan, hapd, WPA_ALG_WEP, NULL, i,
-                                     i == mssid->wep.idx, NULL, 0,
-                                     mssid->wep.key[i], mssid->wep.len[i])) {
+                   hostapd_drv_set_key(dyn_vlan, hapd, WPA_ALG_WEP, NULL, i,
+                                       i == mssid->wep.idx, NULL, 0,
+                                       mssid->wep.key[i], mssid->wep.len[i]))
+               {
                        wpa_printf(MSG_ERROR, "VLAN: Could not set WEP "
                                   "encryption for dynamic VLAN");
                        return -1;
@@ -755,7 +757,7 @@ static int vlan_dynamic_add(struct hostapd_data *hapd,
 {
        while (vlan) {
                if (vlan->vlan_id != VLAN_ID_WILDCARD) {
-                       if (hapd->drv.vlan_if_add(hapd, vlan->ifname)) {
+                       if (hostapd_vlan_if_add(hapd, vlan->ifname)) {
                                if (errno != EEXIST) {
                                        wpa_printf(MSG_ERROR, "VLAN: Could "
                                                   "not add VLAN %s: %s",
@@ -785,7 +787,7 @@ static void vlan_dynamic_remove(struct hostapd_data *hapd,
                next = vlan->next;
 
                if (vlan->vlan_id != VLAN_ID_WILDCARD &&
-                   hapd->drv.vlan_if_remove(hapd, vlan->ifname)) {
+                   hostapd_vlan_if_remove(hapd, vlan->ifname)) {
                        wpa_printf(MSG_ERROR, "VLAN: Could not remove VLAN "
                                   "iface: %s: %s",
                                   vlan->ifname, strerror(errno));
@@ -859,7 +861,7 @@ struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
                    pos);
        os_free(ifname);
 
-       if (hapd->drv.vlan_if_add(hapd, n->ifname)) {
+       if (hostapd_vlan_if_add(hapd, n->ifname)) {
                os_free(n);
                return NULL;
        }
@@ -897,7 +899,7 @@ int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id)
                return 1;
 
        if (vlan->dynamic_vlan == 0)
-               hapd->drv.vlan_if_remove(hapd, vlan->ifname);
+               hostapd_vlan_if_remove(hapd, vlan->ifname);
 
        return 0;
 }
index 3668130..82177f3 100644 (file)
@@ -23,6 +23,7 @@
 #include "ieee802_11.h"
 #include "sta_info.h"
 #include "ap_config.h"
+#include "ap_drv_ops.h"
 #include "wmm.h"
 
 
@@ -74,6 +75,8 @@ u8 * hostapd_eid_wmm(struct hostapd_data *hapd, u8 *eid)
        if (hapd->conf->wmm_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];
@@ -94,9 +97,11 @@ u8 * hostapd_eid_wmm(struct hostapd_data *hapd, u8 *eid)
 }
 
 
-/* This function is called when a station sends an association request with
- * WMM info element. The function returns zero on success or non-zero on any
- * error in WMM element. eid does not include Element ID and Length octets. */
+/*
+ * 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;
@@ -106,7 +111,7 @@ int hostapd_eid_wmm_valid(struct hostapd_data *hapd, const u8 *eid, size_t len)
        if (len < sizeof(struct wmm_information_element)) {
                wpa_printf(MSG_DEBUG, "Too short WMM IE (len=%lu)",
                           (unsigned long) len);
-               return -1;
+               return 0;
        }
 
        wmm = (struct wmm_information_element *) eid;
@@ -117,10 +122,10 @@ int hostapd_eid_wmm_valid(struct hostapd_data *hapd, const u8 *eid, size_t len)
        if (wmm->oui_subtype != WMM_OUI_SUBTYPE_INFORMATION_ELEMENT ||
            wmm->version != WMM_VERSION) {
                wpa_printf(MSG_DEBUG, "Unsupported WMM IE Subtype/Version");
-               return -1;
+               return 0;
        }
 
-       return 0;
+       return 1;
 }
 
 
@@ -150,7 +155,7 @@ static void wmm_send_action(struct hostapd_data *hapd, const u8 *addr,
        os_memcpy(t, tspec, sizeof(struct wmm_tspec_element));
        len = ((u8 *) (t + 1)) - buf;
 
-       if (hapd->drv.send_mgmt_frame(hapd, m, len) < 0)
+       if (hostapd_drv_send_mlme(hapd, m, len) < 0)
                perror("wmm_send_action: send");
 }
 
index 36cb0f4..34202b6 100644 (file)
@@ -22,6 +22,7 @@
 #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"
@@ -44,6 +45,8 @@ static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth,
 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 const u32 dot11RSNAConfigGroupUpdateCount = 4;
 static const u32 dot11RSNAConfigPairwiseUpdateCount = 4;
@@ -215,11 +218,13 @@ static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx)
 {
        struct wpa_authenticator *wpa_auth = eloop_ctx;
 
-       if (os_get_random(wpa_auth->group->GMK, WPA_GMK_LEN)) {
+       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) {
@@ -296,13 +301,41 @@ static void wpa_group_set_key_len(struct wpa_group *group, int cipher)
 }
 
 
-static struct wpa_group * wpa_group_init(struct wpa_authenticator *wpa_auth,
-                                        int vlan_id)
+static int wpa_group_init_gmk_and_counter(struct wpa_authenticator *wpa_auth,
+                                         struct wpa_group *group)
 {
-       struct wpa_group *group;
        u8 buf[ETH_ALEN + 8 + sizeof(group)];
        u8 rkey[32];
 
+       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);
+       os_memcpy(buf + ETH_ALEN + 8, &group, sizeof(group));
+       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;
@@ -312,27 +345,35 @@ static struct wpa_group * wpa_group_init(struct wpa_authenticator *wpa_auth,
 
        wpa_group_set_key_len(group, wpa_auth->conf.wpa_group);
 
-       /* Counter = PRF-256(Random number, "Init Counter",
-        *                   Local MAC Address || Time)
+       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.
         */
-       os_memcpy(buf, wpa_auth->addr, ETH_ALEN);
-       wpa_get_ntp_timestamp(buf + ETH_ALEN);
-       os_memcpy(buf + ETH_ALEN + 8, &group, sizeof(group));
-       if (os_get_random(rkey, sizeof(rkey)) ||
-           os_get_random(group->GMK, WPA_GMK_LEN)) {
+       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;
        }
 
-       sha1_prf(rkey, sizeof(rkey), "Init Counter", buf, sizeof(buf),
-                group->Counter, WPA_NONCE_LEN);
-
        group->GInit = TRUE;
-       wpa_group_sm_step(wpa_auth, group);
-       group->GInit = FALSE;
-       wpa_group_sm_step(wpa_auth, group);
+       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;
 }
@@ -364,7 +405,7 @@ struct wpa_authenticator * wpa_init(const u8 *addr,
                return NULL;
        }
 
-       wpa_auth->group = wpa_group_init(wpa_auth, 0);
+       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);
@@ -405,6 +446,19 @@ struct wpa_authenticator * wpa_init(const u8 *addr,
 }
 
 
+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);
+       return 0;
+}
+
+
 /**
  * wpa_deinit - Deinitialize WPA authenticator
  * @wpa_auth: Pointer to WPA authenticator data from wpa_init()
@@ -539,6 +593,10 @@ void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm)
 
 static void wpa_free_sta_sm(struct wpa_state_machine *sm)
 {
+       if (sm->GUpdateStationKeys) {
+               sm->group->GKeyDoneStations--;
+               sm->GUpdateStationKeys = FALSE;
+       }
 #ifdef CONFIG_IEEE80211R
        os_free(sm->assoc_resp_ftie);
 #endif /* CONFIG_IEEE80211R */
@@ -563,6 +621,7 @@ void wpa_auth_sta_deinit(struct wpa_state_machine *sm)
        }
 
        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) {
@@ -651,6 +710,37 @@ static int ft_check_msg_2_of_4(struct wpa_authenticator *wpa_auth,
 #endif /* CONFIG_IEEE80211R */
 
 
+static void 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 {
+               wpa_auth_mic_failure_report(wpa_auth, sm->addr);
+               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);
+}
+
+
 void wpa_receive(struct wpa_authenticator *wpa_auth,
                 struct wpa_state_machine *sm,
                 u8 *data, size_t data_len)
@@ -676,6 +766,9 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
        key = (struct wpa_eapol_key *) (hdr + 1);
        key_info = WPA_GET_BE16(key->key_info);
        key_data_length = WPA_GET_BE16(key->key_data_length);
+       wpa_printf(MSG_DEBUG, "WPA: Received EAPOL-Key from " MACSTR
+                  " key_info=0x%x type=%u key_data_length=%u",
+                  MAC2STR(sm->addr), key_info, key->type, key_data_length);
        if (key_data_length > data_len - sizeof(*hdr) - sizeof(*key)) {
                wpa_printf(MSG_INFO, "WPA: Invalid EAPOL-Key frame - "
                           "key_data overflow (%d > %lu)",
@@ -686,7 +779,14 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
        }
 
        if (sm->wpa == WPA_VERSION_WPA2) {
-               if (key->type != EAPOL_KEY_TYPE_RSN) {
+               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);
@@ -701,6 +801,11 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
                }
        }
 
+       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 */
 
@@ -770,7 +875,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
        if (!(key_info & WPA_KEY_INFO_REQUEST) &&
            !wpa_replay_counter_valid(sm, key->replay_counter)) {
                int i;
-               wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+               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++) {
@@ -795,6 +900,26 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
                                         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");
+                       sm->group->reject_4way_hs_for_entropy = FALSE;
+                       random_mark_pool_ready();
+                       sm->group->first_sta_seen = FALSE;
+                       wpa_sta_disconnect(wpa_auth, sm->addr);
+                       return;
+               }
                if (wpa_parse_kde_ies((u8 *) (key + 1), key_data_length,
                                      &kde) < 0) {
                        wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
@@ -905,6 +1030,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
                }
                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) {
@@ -930,17 +1056,9 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
 #endif /* CONFIG_PEERKEY */
                        return;
                } else if (key_info & WPA_KEY_INFO_ERROR) {
-                       /* Supplicant reported a Michael MIC error */
-                       wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
-                                       "received EAPOL-Key Error Request "
-                                       "(STA detected Michael MIC failure)");
-                       wpa_auth_mic_failure_report(wpa_auth, sm->addr);
-                       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);
+                       wpa_receive_error_report(
+                               wpa_auth, sm,
+                               !(key_info & WPA_KEY_INFO_KEY_TYPE));
                } else if (key_info & WPA_KEY_INFO_KEY_TYPE) {
                        wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
                                        "received EAPOL-Key Request for new "
@@ -958,9 +1076,6 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
                        wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
                                        "received EAPOL-Key Request for GTK "
                                        "rekeying");
-                       /* FIX: why was this triggering PTK rekeying for the
-                        * STA that requested Group Key rekeying?? */
-                       /* wpa_request_new_ptk(sta->wpa_sm); */
                        eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
                        wpa_rekey_gtk(wpa_auth, NULL);
                }
@@ -987,6 +1102,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
        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);
@@ -995,25 +1111,37 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
 }
 
 
-static void wpa_gmk_to_gtk(const u8 *gmk, const u8 *addr, const u8 *gnonce,
-                          u8 *gtk, size_t gtk_len)
+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];
+       u8 data[ETH_ALEN + WPA_NONCE_LEN + 8 + 16];
+       u8 *pos;
+       int ret = 0;
 
-       /* GTK = PRF-X(GMK, "Group key expansion", AA || GNonce) */
+       /* 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, "Group key expansion",
-                  data, sizeof(data), gtk, gtk_len);
+       sha256_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), gtk, gtk_len);
 #else /* CONFIG_IEEE80211W */
-       sha1_prf(gmk, WPA_GMK_LEN, "Group key expansion",
-                data, sizeof(data), gtk, gtk_len);
+       if (sha1_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), gtk, gtk_len)
+           < 0)
+               ret = -1;
 #endif /* CONFIG_IEEE80211W */
 
-       wpa_hexdump_key(MSG_DEBUG, "GMK", gmk, WPA_GMK_LEN);
-       wpa_hexdump_key(MSG_DEBUG, "GTK", gtk, gtk_len);
+       return ret;
 }
 
 
@@ -1022,6 +1150,7 @@ 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);
@@ -1209,10 +1338,14 @@ static void wpa_send_eapol(struct wpa_authenticator *wpa_auth,
                         keyidx, encr, 0);
 
        ctr = pairwise ? sm->TimeoutCtr : sm->GTimeoutCtr;
-       if (ctr == 1)
+       if (ctr == 1 && wpa_auth->conf.tx_status)
                timeout_ms = eapol_key_timeout_first;
        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);
 }
@@ -1247,8 +1380,7 @@ 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, (u8 *) "",
-                        0);
+       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);
 }
@@ -1411,10 +1543,41 @@ SM_STATE(WPA_PTK, AUTHENTICATION)
 }
 
 
+static void wpa_group_first_station(struct wpa_authenticator *wpa_auth,
+                                   struct wpa_group *group)
+{
+       /*
+        * 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;
+       }
+       wpa_group_init_gmk_and_counter(wpa_auth, group);
+       wpa_gtk_update(wpa_auth, group);
+       wpa_group_config_group_keys(wpa_auth, group);
+}
+
+
 SM_STATE(WPA_PTK, AUTHENTICATION2)
 {
        SM_ENTRY_MA(WPA_PTK, AUTHENTICATION2, wpa_ptk);
+
+       if (!sm->group->first_sta_seen) {
+               wpa_group_first_station(sm->wpa_auth, sm->group);
+               sm->group->first_sta_seen = TRUE;
+       }
+
        os_memcpy(sm->ANonce, sm->group->Counter, WPA_NONCE_LEN);
+       wpa_hexdump(MSG_DEBUG, "WPA: Assign ANonce", sm->ANonce,
+                   WPA_NONCE_LEN);
        inc_byte_array(sm->group->Counter, WPA_NONCE_LEN);
        sm->ReAuthenticationRequest = FALSE;
        /* IEEE 802.11i does not clear TimeoutCtr here, but this is more
@@ -1605,6 +1768,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
        }
 #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)) {
@@ -1726,6 +1890,20 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
                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);
@@ -1876,8 +2054,11 @@ SM_STEP(WPA_PTK)
        if (sm->Init)
                SM_ENTER(WPA_PTK, INITIALIZE);
        else if (sm->Disconnect
-                /* || FIX: dot11RSNAConfigSALifetime timeout */)
+                /* || 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)
@@ -1913,6 +2094,8 @@ SM_STEP(WPA_PTK)
                        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;
@@ -1933,6 +2116,9 @@ SM_STEP(WPA_PTK)
                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);
@@ -1956,6 +2142,10 @@ SM_STEP(WPA_PTK)
                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);
@@ -2094,20 +2284,24 @@ static int wpa_gtk_update(struct wpa_authenticator *wpa_auth,
 {
        int ret = 0;
 
-       /* FIX: is this the correct way of getting GNonce? */
        os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN);
        inc_byte_array(group->Counter, WPA_NONCE_LEN);
-       wpa_gmk_to_gtk(group->GMK, wpa_auth->addr, group->GNonce,
-                      group->GTK[group->GN - 1], group->GTK_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) {
-               if (os_get_random(group->IGTK[group->GN_igtk - 4],
-                                 WPA_IGTK_LEN) < 0) {
-                       wpa_printf(MSG_INFO, "RSN: Failed to get new random "
-                                  "IGTK");
+               os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN);
+               inc_byte_array(group->Counter, WPA_NONCE_LEN);
+               if (wpa_gmk_to_gtk(group->GMK, "IGTK key expansion",
+                                  wpa_auth->addr, group->GNonce,
+                                  group->IGTK[group->GN_igtk - 4],
+                                  WPA_IGTK_LEN) < 0)
                        ret = -1;
-               }
                wpa_hexdump_key(MSG_DEBUG, "IGTK",
                                group->IGTK[group->GN_igtk - 4], WPA_IGTK_LEN);
        }
@@ -2143,20 +2337,23 @@ static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx)
        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, but just in case, make sure
-                * we do not count the same STA twice in GKeyDoneStations.
+                * 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 already set - do not "
-                               "increment GKeyDoneStations");
-       } else {
-               sm->group->GKeyDoneStations++;
-               sm->GUpdateStationKeys = TRUE;
+                               "GUpdateStationKeys was already set when "
+                               "marking station for GTK rekeying");
        }
+
+       sm->group->GKeyDoneStations++;
+       sm->GUpdateStationKeys = TRUE;
+
        wpa_sm_step(sm);
        return 0;
 }
@@ -2185,32 +2382,54 @@ static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,
         * 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, NULL);
        wpa_printf(MSG_DEBUG, "wpa_group_setkeys: GKeyDoneStations=%d",
                   group->GKeyDoneStations);
 }
 
 
-static void wpa_group_setkeysdone(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)
+{
+       int ret = 0;
+
+       if (wpa_auth_set_key(wpa_auth, group->vlan_id,
+                            wpa_alg_enum(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 &&
+           wpa_auth_set_key(wpa_auth, group->vlan_id, WPA_ALG_IGTK,
+                            broadcast_ether_addr, group->GN_igtk,
+                            group->IGTK[group->GN_igtk - 4],
+                            WPA_IGTK_LEN) < 0)
+               ret = -1;
+#endif /* CONFIG_IEEE80211W */
+
+       return ret;
+}
+
+
+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;
-       wpa_auth_set_key(wpa_auth, group->vlan_id,
-                        wpa_alg_enum(wpa_auth->conf.wpa_group),
-                        NULL, group->GN, group->GTK[group->GN - 1],
-                        group->GTK_len);
 
-#ifdef CONFIG_IEEE80211W
-       if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION) {
-               wpa_auth_set_key(wpa_auth, group->vlan_id, WPA_ALG_IGTK,
-                                NULL, group->GN_igtk,
-                                group->IGTK[group->GN_igtk - 4],
-                                WPA_IGTK_LEN);
-       }
-#endif /* CONFIG_IEEE80211W */
+       if (wpa_group_config_group_keys(wpa_auth, group) < 0)
+               return -1;
+
+       return 0;
 }
 
 
@@ -2310,6 +2529,7 @@ void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth)
                group->GN_igtk = tmp;
 #endif /* CONFIG_IEEE80211W */
                wpa_gtk_update(wpa_auth, group);
+               wpa_group_config_group_keys(wpa_auth, group);
        }
 }
 
@@ -2345,19 +2565,21 @@ 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"
-#ifdef CONFIG_RSN_PREAUTH
-                         "dot11RSNAPreauthenticationImplemented=TRUE\n"
-#else /* CONFIG_RSN_PREAUTH */
-                         "dot11RSNAPreauthenticationImplemented=FALSE\n"
-#endif /* CONFIG_RSN_PREAUTH */
+                         "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 (ret < 0 || (size_t) ret >= buflen - len)
@@ -2473,7 +2695,7 @@ int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen)
                "dot11RSNAStatsSelectedPairwiseCipher=" RSN_SUITE "\n"
                /* TODO: dot11RSNAStatsTKIPICVErrors */
                "dot11RSNAStatsTKIPLocalMICFailures=%u\n"
-               "dot11RSNAStatsTKIPRemoveMICFailures=%u\n"
+               "dot11RSNAStatsTKIPRemoteMICFailures=%u\n"
                /* TODO: dot11RSNAStatsCCMPReplays */
                /* TODO: dot11RSNAStatsCCMPDecryptErrors */
                /* TODO: dot11RSNAStatsTKIPReplays */,
@@ -2570,7 +2792,8 @@ 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)
 {
-       if (sm == NULL || sm->wpa != WPA_VERSION_WPA2)
+       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,
@@ -2609,7 +2832,7 @@ wpa_auth_add_group(struct wpa_authenticator *wpa_auth, int vlan_id)
 
        wpa_printf(MSG_DEBUG, "WPA: Add group state machine for VLAN-ID %d",
                   vlan_id);
-       group = wpa_group_init(wpa_auth, vlan_id);
+       group = wpa_group_init(wpa_auth, vlan_id, 0);
        if (group == NULL)
                return NULL;
 
@@ -2649,3 +2872,33 @@ int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id)
        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);
+       }
+}
index d0136c7..ce2751e 100644 (file)
@@ -143,7 +143,9 @@ struct wpa_auth_config {
        int peerkey;
        int wmm_enabled;
        int wmm_uapsd;
+       int disable_pmksa_caching;
        int okc;
+       int tx_status;
 #ifdef CONFIG_IEEE80211W
        enum mfp_options ieee80211w;
 #endif /* CONFIG_IEEE80211W */
@@ -160,6 +162,7 @@ struct wpa_auth_config {
        struct ft_remote_r0kh *r0kh_list;
        struct ft_remote_r1kh *r1kh_list;
        int pmk_r1_push;
+       int ft_over_ds;
 #endif /* CONFIG_IEEE80211R */
 };
 
@@ -205,6 +208,7 @@ struct wpa_auth_callbacks {
 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);
@@ -259,6 +263,8 @@ int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth,
                               int session_timeout,
                               struct eapol_state_machine *eapol);
 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,
index c9871d9..4a7d619 100644 (file)
@@ -18,6 +18,7 @@
 #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"
 
 #ifdef CONFIG_IEEE80211R
 
-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 *ric;
-       size_t ric_len;
-};
-
-
-static int wpa_ft_parse_ies(const u8 *ies, size_t ies_len,
-                           struct wpa_ft_ies *parse);
-
-
 static int wpa_ft_rrb_send(struct wpa_authenticator *wpa_auth, const u8 *dst,
                           const u8 *data, size_t data_len)
 {
@@ -91,7 +70,9 @@ int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len)
        *pos++ = MOBILITY_DOMAIN_ID_LEN + 1;
        os_memcpy(pos, conf->mobility_domain, MOBILITY_DOMAIN_ID_LEN);
        pos += MOBILITY_DOMAIN_ID_LEN;
-       capab = RSN_FT_CAPAB_FT_OVER_DS;
+       capab = 0;
+       if (conf->ft_over_ds)
+               capab |= RSN_FT_CAPAB_FT_OVER_DS;
        *pos++ = capab;
 
        return pos - buf;
@@ -334,7 +315,7 @@ static int wpa_ft_pull_pmk_r1(struct wpa_authenticator *wpa_auth,
 
        /* aes_wrap() does not support inplace encryption, so use a temporary
         * buffer for the data. */
-       if (os_get_random(f.nonce, sizeof(f.nonce))) {
+       if (random_get_bytes(f.nonce, sizeof(f.nonce))) {
                wpa_printf(MSG_DEBUG, "FT: Failed to get random data for "
                           "nonce");
                return -1;
@@ -725,143 +706,6 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
 }
 
 
-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;
-               }
-
-               pos += 2 + pos[1];
-       }
-
-       return 0;
-}
-
-
-static 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:
-                       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_RIC_DATA:
-                       if (parse->ric == NULL)
-                               parse->ric = pos;
-               }
-
-               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;
-}
-
-
 static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth,
                                   int vlan_id,
                                   enum wpa_alg alg, const u8 *addr, int idx,
@@ -997,7 +841,7 @@ static u16 wpa_ft_process_auth_req(struct wpa_state_machine *sm,
        sm->pmk_r1_name_valid = 1;
        os_memcpy(sm->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN);
 
-       if (os_get_random(sm->ANonce, WPA_NONCE_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;
@@ -1204,7 +1048,7 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
 
        count = 3;
        if (parse.ric)
-               count++;
+               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",
index afa13a6..d44381d 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * hostapd / WPA authenticator glue code
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2011, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
 #include "wpa_auth.h"
 
 
-#ifdef CONFIG_IEEE80211R
-static void hostapd_rrb_receive(void *ctx, const u8 *src_addr, const u8 *buf,
-                               size_t len);
-#endif /* CONFIG_IEEE80211R */
-
-
 static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
                                  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;
@@ -54,6 +49,7 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
        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;
@@ -77,6 +73,7 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
        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 */
 }
 
@@ -230,8 +227,8 @@ static int hostapd_wpa_auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg,
                        return -1;
        }
 
-       return hapd->drv.set_key(ifname, hapd, alg, addr, idx, 1, NULL, 0,
-                                key, key_len);
+       return hostapd_drv_set_key(ifname, hapd, alg, addr, idx, 1, NULL, 0,
+                                  key, key_len);
 }
 
 
@@ -248,7 +245,15 @@ static int hostapd_wpa_auth_send_eapol(void *ctx, const u8 *addr,
                                       int encrypt)
 {
        struct hostapd_data *hapd = ctx;
-       return hapd->drv.send_eapol(hapd, addr, data, data_len, encrypt);
+       struct sta_info *sta;
+       u32 flags = 0;
+
+       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);
 }
 
 
@@ -327,8 +332,9 @@ static int hostapd_wpa_auth_ft_iter(struct hostapd_iface *iface, void *ctx)
                                   MAC2STR(idata->src_hapd->own_addr),
                                   idata->src_hapd->conf->iface,
                                   MAC2STR(hapd->own_addr), hapd->conf->iface);
-                       hostapd_rrb_receive(hapd, idata->src_hapd->own_addr,
-                                           idata->data, idata->data_len);
+                       wpa_ft_rrb_rx(hapd->wpa_auth,
+                                     idata->src_hapd->own_addr,
+                                     idata->data, idata->data_len);
                        return 1;
                }
        }
@@ -343,6 +349,8 @@ 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_IEEE80211R
        if (proto == ETH_P_RRB && hapd->iface->for_each_interface) {
@@ -366,7 +374,18 @@ static int hostapd_wpa_auth_send_ether(void *ctx, const u8 *dst, u16 proto,
                                                data, data_len);
        if (hapd->l2 == NULL)
                return -1;
-       return l2_packet_send(hapd->l2, dst, proto, data, data_len);
+
+       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;
 }
 
 
@@ -396,7 +415,7 @@ static int hostapd_wpa_auth_send_ft_action(void *ctx, const u8 *dst,
        os_memcpy(m->bssid, hapd->own_addr, ETH_ALEN);
        os_memcpy(&m->u, data, data_len);
 
-       res = hapd->drv.send_mgmt_frame(hapd, (u8 *) m, mlen);
+       res = hostapd_drv_send_mlme(hapd, (u8 *) m, mlen);
        os_free(m);
        return res;
 }
@@ -431,7 +450,14 @@ static void hostapd_rrb_receive(void *ctx, const u8 *src_addr, const u8 *buf,
                                size_t len)
 {
        struct hostapd_data *hapd = ctx;
-       wpa_ft_rrb_rx(hapd->wpa_auth, src_addr, buf, len);
+       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));
 }
 
 #endif /* CONFIG_IEEE80211R */
@@ -445,6 +471,8 @@ int hostapd_setup_wpa(struct hostapd_data *hapd)
        size_t wpa_ie_len;
 
        hostapd_wpa_auth_conf(hapd->conf, &_conf);
+       if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_EAPOL_TX_STATUS)
+               _conf.tx_status = 1;
        os_memset(&cb, 0, sizeof(cb));
        cb.ctx = hapd;
        cb.logger = hostapd_wpa_auth_logger;
@@ -494,7 +522,7 @@ int hostapd_setup_wpa(struct hostapd_data *hapd)
                hapd->l2 = l2_packet_init(hapd->conf->bridge[0] ?
                                          hapd->conf->bridge :
                                          hapd->conf->iface, NULL, ETH_P_RRB,
-                                         hostapd_rrb_receive, hapd, 0);
+                                         hostapd_rrb_receive, hapd, 1);
                if (hapd->l2 == NULL &&
                    (hapd->driver == NULL ||
                     hapd->driver->send_ether == NULL)) {
@@ -520,6 +548,7 @@ void hostapd_reconfig_wpa(struct hostapd_data *hapd)
 
 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);
index b69129f..d82192a 100644 (file)
@@ -86,6 +86,7 @@ struct wpa_state_machine {
        unsigned int pending_deinit:1;
        unsigned int started:1;
        unsigned int mgmt_frame_prot:1;
+       unsigned int rx_eapol_key_secure:1;
 #ifdef CONFIG_IEEE80211R
        unsigned int ft_completed:1;
        unsigned int pmk_r1_name_valid:1;
@@ -120,6 +121,8 @@ struct wpa_state_machine {
                                               * message 2/4 */
        u8 *assoc_resp_ftie;
 #endif /* CONFIG_IEEE80211R */
+
+       int pending_1_of_4_timeout;
 };
 
 
@@ -145,6 +148,8 @@ struct wpa_group {
        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_LEN];
        int GN_igtk, GM_igtk;
index f8a1804..5e8d134 100644 (file)
 #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;
@@ -141,6 +146,14 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
        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->rsn_pairwise & WPA_CIPHER_CCMP) {
                RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
                pos += RSN_SELECTOR_LEN;
@@ -157,6 +170,14 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t 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 pairwise cipher (%d).",
                           conf->rsn_pairwise);
@@ -168,6 +189,14 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
        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;
@@ -203,6 +232,14 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
        }
 #endif /* CONFIG_IEEE80211W */
 
+#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);
@@ -227,6 +264,10 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
                        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;
 
@@ -256,6 +297,29 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t 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;
@@ -322,114 +386,6 @@ u8 * wpa_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len,
 }
 
 
-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_WEP40)
-               return WPA_CIPHER_WEP40;
-       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 (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP104)
-               return WPA_CIPHER_WEP104;
-       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;
-}
-
-
-static 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->pairwise_cipher = WPA_CIPHER_TKIP;
-       data->group_cipher = WPA_CIPHER_TKIP;
-       data->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
-       data->mgmt_group_cipher = 0;
-
-       if (wpa_ie_len < sizeof(struct wpa_ie_hdr))
-               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) {
-               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)
-                 return -3;
-
-       if (left >= 2) {
-               data->pairwise_cipher = 0;
-               count = WPA_GET_LE16(pos);
-               pos += 2;
-               left -= 2;
-               if (count == 0 || left < count * WPA_SELECTOR_LEN)
-                       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)
-               return -5;
-
-       if (left >= 2) {
-               data->key_mgmt = 0;
-               count = WPA_GET_LE16(pos);
-               pos += 2;
-               left -= 2;
-               if (count == 0 || left < count * WPA_SELECTOR_LEN)
-                       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)
-               return -7;
-
-       if (left >= 2) {
-               data->capabilities = WPA_GET_LE16(pos);
-               pos += 2;
-               left -= 2;
-       }
-
-       if (left > 0) {
-               return -8;
-       }
-
-       return 0;
-}
-
-
 struct wpa_auth_okc_iter_data {
        struct rsn_pmksa_cache_entry *pmksa;
        const u8 *aa;
index a6ffd4d..4b31d4f 100644 (file)
@@ -28,6 +28,7 @@
 #include "wps/wps_dev_attr.h"
 #include "hostapd.h"
 #include "ap_config.h"
+#include "ap_drv_ops.h"
 #include "beacon.h"
 #include "sta_info.h"
 #include "wps_hostapd.h"
@@ -40,11 +41,51 @@ static int hostapd_wps_upnp_init(struct hostapd_data *hapd,
 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,
+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);
 static void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx);
 
 
+struct wps_for_each_data {
+       int (*func)(struct hostapd_data *h, void *ctx);
+       void *ctx;
+};
+
+
+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 = 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;
+       if (iface->for_each_interface == NULL)
+               return wps_for_each(iface, &data);
+       return iface->for_each_interface(iface->interfaces, wps_for_each,
+                                        &data);
+}
+
+
 static int hostapd_wps_new_psk_cb(void *ctx, const u8 *mac_addr, const u8 *psk,
                                  size_t psk_len)
 {
@@ -100,8 +141,9 @@ static int hostapd_wps_set_ie_cb(void *ctx, struct wpabuf *beacon_ie,
        hapd->wps_beacon_ie = beacon_ie;
        wpabuf_free(hapd->wps_probe_resp_ie);
        hapd->wps_probe_resp_ie = probe_resp_ie;
-       ieee802_11_set_beacon(hapd);
-       return hapd->drv.set_ap_wps_ie(hapd);
+       if (hapd->beacon_set_done)
+               ieee802_11_set_beacon(hapd);
+       return hostapd_set_ap_wps_ie(hapd);
 }
 
 
@@ -144,11 +186,26 @@ static void hostapd_wps_pin_needed_cb(void *ctx, const u8 *uuid_e,
 }
 
 
+struct wps_stop_reg_data {
+       struct hostapd_data *current_hapd;
+       const u8 *uuid_e;
+};
+
+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);
+       return 0;
+}
+
+
 static void hostapd_wps_reg_success_cb(void *ctx, const u8 *mac_addr,
                                       const u8 *uuid_e)
 {
        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",
@@ -156,6 +213,9 @@ static void hostapd_wps_reg_success_cb(void *ctx, const u8 *mac_addr,
        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;
+       hostapd_wps_for_each(hapd, wps_stop_registrar, &data);
 }
 
 
@@ -200,9 +260,23 @@ static void wps_reload_config(void *eloop_data, void *user_ctx)
 }
 
 
-static int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred)
+static void hapd_new_ap_event(struct hostapd_data *hapd, const u8 *attr,
+                             size_t attr_len)
 {
-       struct hostapd_data *hapd = ctx;
+       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_cred_cb(struct hostapd_data *hapd, void *ctx)
+{
+       const struct wps_credential *cred = ctx;
        FILE *oconf, *nconf;
        size_t len, i;
        char *tmp_fname;
@@ -210,6 +284,9 @@ static int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred)
        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);
 
@@ -226,15 +303,15 @@ static int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred)
 
        if ((hapd->conf->wps_cred_processing == 1 ||
             hapd->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(hapd->msg_ctx, MSG_INFO, "%s%s",
-                               WPS_EVENT_NEW_AP_SETTINGS, _buf);
-                       os_free(_buf);
-               }
+               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);
 
@@ -384,6 +461,8 @@ static int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred)
                if (!multi_bss &&
                    (str_starts(buf, "ssid=") ||
                     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=") ||
@@ -414,20 +493,27 @@ static int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred)
        eloop_register_timeout(0, 100000, wps_reload_config, hapd->iface,
                               NULL);
 
-       /* TODO: dualband AP may need to update multiple configuration files */
-
        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);
@@ -436,11 +522,12 @@ static void hostapd_wps_reenable_ap_pin(void *eloop_data, void *user_ctx)
 }
 
 
-static void hostapd_pwd_auth_fail(struct hostapd_data *hapd,
-                                 struct wps_event_pwd_auth_fail *data)
+static int wps_pwd_auth_fail(struct hostapd_data *hapd, void *ctx)
 {
-       if (!data->enrollee || hapd->conf->ap_pin == NULL)
-               return;
+       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
@@ -448,17 +535,27 @@ static void hostapd_pwd_auth_fail(struct hostapd_data *hapd,
         * force attacks.
         */
        hapd->ap_pin_failures++;
-       wpa_printf(MSG_DEBUG, "WPS: AP PIN authentication failure number %u",
-                  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;
+               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) {
+       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 &&
@@ -473,7 +570,60 @@ static void hostapd_pwd_auth_fail(struct hostapd_data *hapd,
                                       NULL);
        }
 
-       /* TODO: dualband AP may need to update other interfaces */
+       return 0;
+}
+
+
+static void hostapd_pwd_auth_fail(struct hostapd_data *hapd,
+                                 struct wps_event_pwd_auth_fail *data)
+{
+       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 const char * wps_event_fail_reason[NUM_WPS_EI_VALUES] = {
+       "No Error", /* WPS_EI_NO_ERROR */
+       "TKIP Only Prohibited", /* WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED */
+       "WEP Prohibited" /* WPS_EI_SECURITY_WEP_PROHIBITED */
+};
+
+static void hostapd_wps_event_fail(struct hostapd_data *hapd,
+                                  struct wps_event_fail *fail)
+{
+       if (fail->error_indication > 0 &&
+           fail->error_indication < NUM_WPS_EI_VALUES) {
+               wpa_msg(hapd->msg_ctx, MSG_INFO,
+                       WPS_EVENT_FAIL "msg=%d config_error=%d reason=%d (%s)",
+                       fail->msg, fail->config_error, fail->error_indication,
+                       wps_event_fail_reason[fail->error_indication]);
+       } else {
+               wpa_msg(hapd->msg_ctx, MSG_INFO,
+                       WPS_EVENT_FAIL "msg=%d config_error=%d",
+                       fail->msg, fail->config_error);
+       }
 }
 
 
@@ -482,8 +632,43 @@ static void hostapd_wps_event_cb(void *ctx, enum wps_event event,
 {
        struct hostapd_data *hapd = ctx;
 
-       if (event == WPS_EV_PWD_AUTH_FAIL)
+       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:
+               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:
+               wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_OVERLAP);
+               break;
+       case WPS_EV_PBC_TIMEOUT:
+               wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_TIMEOUT);
+               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);
 }
 
 
@@ -495,7 +680,81 @@ static void hostapd_wps_clear_ies(struct hostapd_data *hapd)
        wpabuf_free(hapd->wps_probe_resp_ie);
        hapd->wps_probe_resp_ie = NULL;
 
-       hapd->drv.set_ap_wps_ie(hapd);
+       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 && !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->for_each_interface == NULL)
+               return NULL;
+       uuid = NULL;
+       iface->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->for_each_interface == NULL)
+               return 0;
+       iface->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;
 }
 
 
@@ -522,11 +781,22 @@ int hostapd_init_wps(struct hostapd_data *hapd,
        wps->wps_state = hapd->conf->wps_state;
        wps->ap_setup_locked = hapd->conf->ap_setup_locked;
        if (is_nil_uuid(hapd->conf->uuid)) {
-               uuid_gen_mac_addr(hapd->own_addr, wps->uuid);
-               wpa_hexdump(MSG_DEBUG, "WPS: UUID based on MAC address",
-                           wps->uuid, UUID_LEN);
-       } else
+               const u8 *uuid;
+               uuid = get_own_uuid(hapd->iface);
+               if (uuid) {
+                       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;
@@ -543,13 +813,30 @@ int hostapd_init_wps(struct hostapd_data *hapd,
                os_strdup(hapd->conf->serial_number) : NULL;
        wps->config_methods =
                wps_config_methods_str2bin(hapd->conf->config_methods);
-       if (hapd->conf->device_type &&
-           wps_dev_type_str2bin(hapd->conf->device_type,
-                                wps->dev.pri_dev_type) < 0) {
-               wpa_printf(MSG_ERROR, "WPS: Invalid device_type");
+#ifdef CONFIG_WPS2
+       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;
+       }
+#endif /* CONFIG_WPS2 */
+       os_memcpy(wps->dev.pri_dev_type, hapd->conf->device_type,
+                 WPS_DEV_TYPE_LEN);
+
+       if (hostapd_wps_set_vendor_ext(hapd, wps) < 0) {
                os_free(wps);
                return -1;
        }
+
        wps->dev.os_version = WPA_GET_BE32(hapd->conf->os_version);
        wps->dev.rf_bands = hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ?
                WPS_RF_50GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */
@@ -647,10 +934,13 @@ int hostapd_init_wps(struct hostapd_data *hapd,
                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 (cfg.dualband)
+               wpa_printf(MSG_DEBUG, "WPS: Dualband AP");
 
        wps->registrar = wps_registrar_init(wps, &cfg);
        if (wps->registrar == NULL) {
-               printf("Failed to initialize WPS Registrar\n");
+               wpa_printf(MSG_ERROR, "Failed to initialize WPS Registrar");
                os_free(wps->network_key);
                os_free(wps);
                return -1;
@@ -662,20 +952,34 @@ int hostapd_init_wps(struct hostapd_data *hapd,
        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;
+}
+
+
+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);
                os_free(wps->network_key);
                os_free(wps);
+               hapd->wps = NULL;
                return -1;
        }
 #endif /* CONFIG_WPS_UPNP */
 
-       hostapd_register_probereq_cb(hapd, hostapd_wps_probe_req_rx, hapd);
-
-       hapd->wps = wps;
-
        return 0;
 }
 
@@ -707,6 +1011,17 @@ 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
@@ -714,29 +1029,72 @@ void hostapd_update_wps(struct hostapd_data *hapd)
 }
 
 
-int hostapd_wps_add_pin(struct hostapd_data *hapd, const char *uuid,
-                       const char *pin, int timeout)
+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)
 {
-       u8 u[UUID_LEN];
-       int any = 0;
+       struct wps_add_pin_data *data = ctx;
+       int ret;
 
        if (hapd->wps == NULL)
-               return -1;
+               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)
-               any = 1;
-       else if (uuid_str2bin(uuid, u))
+               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 wps_registrar_add_pin(hapd->wps->registrar, any ? NULL : u,
-                                    (const u8 *) pin, os_strlen(pin),
-                                    timeout);
+       return data.added ? 0 : -1;
 }
 
 
-int hostapd_wps_button_pushed(struct hostapd_data *hapd)
+static int wps_button_pushed(struct hostapd_data *hapd, void *ctx)
 {
+       const u8 *p2p_dev_addr = ctx;
        if (hapd->wps == NULL)
-               return -1;
-       return wps_registrar_button_pushed(hapd->wps->registrar);
+               return 0;
+       return wps_registrar_button_pushed(hapd->wps->registrar, p2p_dev_addr);
+}
+
+
+int hostapd_wps_button_pushed(struct hostapd_data *hapd,
+                             const u8 *p2p_dev_addr)
+{
+       return hostapd_wps_for_each(hapd, wps_button_pushed,
+                                   (void *) p2p_dev_addr);
 }
 
 
@@ -777,7 +1135,7 @@ int hostapd_wps_start_oob(struct hostapd_data *hapd, char *device_type,
 
        if ((wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_E ||
             wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_R) &&
-           hostapd_wps_add_pin(hapd, "any",
+           hostapd_wps_add_pin(hapd, NULL, "any",
                                wpabuf_head(wps->oob_conf.dev_password), 0) <
            0)
                goto error;
@@ -794,7 +1152,8 @@ error:
 #endif /* CONFIG_WPS_OOB */
 
 
-static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr,
+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)
 {
        struct hostapd_data *hapd = ctx;
@@ -819,15 +1178,28 @@ static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr,
        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) {
-               wps_registrar_probe_req_rx(hapd->wps->registrar, addr, wps_ie);
+               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? */
-               upnp_wps_device_send_wlan_event(hapd->wps_upnp, addr,
-                                               UPNP_WPS_WLANEVENT_TYPE_PROBE,
-                                               wps_ie);
+               if (!p2p_wildcard)
+                       upnp_wps_device_send_wlan_event(
+                               hapd->wps_upnp, addr,
+                               UPNP_WPS_WLANEVENT_TYPE_PROBE, wps_ie);
 #endif /* CONFIG_WPS_UPNP */
        }
 
@@ -864,6 +1236,7 @@ static int hostapd_rx_req_put_wlan_response(
         */
 
        sta = ap_get_sta(hapd, mac_addr);
+#ifndef CONFIG_WPS_STRICT
        if (!sta) {
                /*
                 * Workaround - Intel wsccmd uses bogus NewWLANEventMAC:
@@ -877,8 +1250,9 @@ static int hostapd_rx_req_put_wlan_response(
                                break;
                }
        }
+#endif /* CONFIG_WPS_STRICT */
 
-       if (!sta) {
+       if (!sta || !(sta->flags & WLAN_STA_WPS)) {
                wpa_printf(MSG_DEBUG, "WPS UPnP: No matching STA found");
                return 0;
        }
@@ -911,26 +1285,19 @@ static int hostapd_wps_upnp_init(struct hostapd_data *hapd,
        if (hapd->conf->ap_pin)
                ctx->ap_pin = os_strdup(hapd->conf->ap_pin);
 
-       hapd->wps_upnp = upnp_wps_device_init(ctx, wps, hapd);
-       if (hapd->wps_upnp == NULL) {
-               os_free(ctx);
+       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;
 
-       if (upnp_wps_device_start(hapd->wps_upnp, hapd->conf->upnp_iface)) {
-               upnp_wps_device_deinit(hapd->wps_upnp);
-               hapd->wps_upnp = NULL;
-               return -1;
-       }
-
        return 0;
 }
 
 
 static void hostapd_wps_upnp_deinit(struct hostapd_data *hapd)
 {
-       upnp_wps_device_deinit(hapd->wps_upnp);
+       upnp_wps_device_deinit(hapd->wps_upnp, hapd);
 }
 
 #endif /* CONFIG_WPS_UPNP */
@@ -950,6 +1317,7 @@ 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);
 }
 
 
@@ -957,6 +1325,7 @@ 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);
@@ -970,31 +1339,53 @@ static void hostapd_wps_ap_pin_enable(struct hostapd_data *hapd, int timeout)
 }
 
 
-void hostapd_wps_ap_pin_disable(struct hostapd_data *hapd)
+static int wps_ap_pin_disable(struct hostapd_data *hapd, void *ctx)
 {
-       wpa_printf(MSG_DEBUG, "WPS: Disabling AP PIN");
        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;
 }
 
 
-const char * hostapd_wps_ap_pin_random(struct hostapd_data *hapd, int timeout)
+void hostapd_wps_ap_pin_disable(struct hostapd_data *hapd)
 {
-       unsigned int pin;
+       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;
+};
 
-       pin = wps_generate_pin();
-       os_snprintf(pin_txt, sizeof(pin_txt), "%u", pin);
+
+static int wps_ap_pin_set(struct hostapd_data *hapd, void *ctx)
+{
+       struct wps_ap_pin_data *data = ctx;
        os_free(hapd->conf->ap_pin);
-       hapd->conf->ap_pin = os_strdup(pin_txt);
+       hapd->conf->ap_pin = os_strdup(data->pin_txt);
 #ifdef CONFIG_WPS_UPNP
-       upnp_wps_set_ap_pin(hapd->wps_upnp, pin_txt);
+       upnp_wps_set_ap_pin(hapd->wps_upnp, data->pin_txt);
 #endif /* CONFIG_WPS_UPNP */
-       hostapd_wps_ap_pin_enable(hapd, timeout);
+       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;
 }
 
@@ -1008,13 +1399,75 @@ const char * hostapd_wps_ap_pin_get(struct hostapd_data *hapd)
 int hostapd_wps_ap_pin_set(struct hostapd_data *hapd, const char *pin,
                           int timeout)
 {
-       os_free(hapd->conf->ap_pin);
-       hapd->conf->ap_pin = os_strdup(pin);
-       if (hapd->conf->ap_pin == NULL)
+       struct wps_ap_pin_data data;
+       int ret;
+
+       ret = os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%s", pin);
+       if (ret < 0 || ret >= (int) sizeof(data.pin_txt))
                return -1;
-#ifdef CONFIG_WPS_UPNP
-       upnp_wps_set_ap_pin(hapd->wps_upnp, hapd->conf->ap_pin);
-#endif /* CONFIG_WPS_UPNP */
-       hostapd_wps_ap_pin_enable(hapd, timeout);
+       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, "WEP", 3) == 0)
+                       cred.encr_type = WPS_ENCR_WEP;
+               else if (os_strncmp(encr, "TKIP", 4) == 0)
+                       cred.encr_type = WPS_ENCR_TKIP;
+               else if (os_strncmp(encr, "CCMP", 4) == 0)
+                       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);
+}
index e978a1c..6b28c13 100644 (file)
 
 int hostapd_init_wps(struct hostapd_data *hapd,
                     struct hostapd_bss_config *conf);
+int hostapd_init_wps_complete(struct hostapd_data *hapd);
 void hostapd_deinit_wps(struct hostapd_data *hapd);
 void hostapd_update_wps(struct hostapd_data *hapd);
-int hostapd_wps_add_pin(struct hostapd_data *hapd, const char *uuid,
-                       const char *pin, int timeout);
-int hostapd_wps_button_pushed(struct hostapd_data *hapd);
+int hostapd_wps_add_pin(struct hostapd_data *hapd, const u8 *addr,
+                       const char *uuid, const char *pin, int timeout);
+int hostapd_wps_button_pushed(struct hostapd_data *hapd,
+                             const u8 *p2p_dev_addr);
 int hostapd_wps_start_oob(struct hostapd_data *hapd, char *device_type,
                          char *path, char *method, char *name);
 int hostapd_wps_get_mib_sta(struct hostapd_data *hapd, const u8 *addr,
@@ -33,6 +35,9 @@ const char * hostapd_wps_ap_pin_random(struct hostapd_data *hapd, int timeout);
 const char * hostapd_wps_ap_pin_get(struct hostapd_data *hapd);
 int hostapd_wps_ap_pin_set(struct hostapd_data *hapd, const char *pin,
                           int timeout);
+void hostapd_wps_update_ie(struct hostapd_data *hapd);
+int hostapd_wps_config_ap(struct hostapd_data *hapd, const char *ssid,
+                         const char *auth, const char *encr, const char *key);
 
 #else /* CONFIG_WPS */
 
@@ -46,6 +51,11 @@ static inline void hostapd_deinit_wps(struct hostapd_data *hapd)
 {
 }
 
+static inline int hostapd_init_wps_complete(struct hostapd_data *hapd)
+{
+    return 0;
+}
+
 static inline void hostapd_update_wps(struct hostapd_data *hapd)
 {
 }
@@ -57,7 +67,8 @@ static inline int hostapd_wps_get_mib_sta(struct hostapd_data *hapd,
        return 0;
 }
 
-static inline int hostapd_wps_button_pushed(struct hostapd_data *hapd)
+static inline int hostapd_wps_button_pushed(struct hostapd_data *hapd,
+                                           const u8 *p2p_dev_addr)
 {
        return 0;
 }
index 173bbd1..bfbb4b7 100644 (file)
@@ -46,28 +46,34 @@ typedef enum { FALSE = 0, TRUE = 1 } Boolean;
 
 static inline int wpa_key_mgmt_wpa_ieee8021x(int akm)
 {
-       return akm == WPA_KEY_MGMT_IEEE8021X ||
-               akm == WPA_KEY_MGMT_FT_IEEE8021X ||
-               akm == WPA_KEY_MGMT_IEEE8021X_SHA256;
+       return !!(akm & (WPA_KEY_MGMT_IEEE8021X |
+                        WPA_KEY_MGMT_FT_IEEE8021X |
+                        WPA_KEY_MGMT_IEEE8021X_SHA256));
 }
 
 static inline int wpa_key_mgmt_wpa_psk(int akm)
 {
-       return akm == WPA_KEY_MGMT_PSK ||
-               akm == WPA_KEY_MGMT_FT_PSK ||
-               akm == WPA_KEY_MGMT_PSK_SHA256;
+       return !!(akm & (WPA_KEY_MGMT_PSK |
+                        WPA_KEY_MGMT_FT_PSK |
+                        WPA_KEY_MGMT_PSK_SHA256));
 }
 
 static inline int wpa_key_mgmt_ft(int akm)
 {
-       return akm == WPA_KEY_MGMT_FT_PSK ||
-               akm == WPA_KEY_MGMT_FT_IEEE8021X;
+       return !!(akm & (WPA_KEY_MGMT_FT_PSK |
+                        WPA_KEY_MGMT_FT_IEEE8021X));
 }
 
 static inline int wpa_key_mgmt_sha256(int akm)
 {
-       return akm == WPA_KEY_MGMT_PSK_SHA256 ||
-               akm == WPA_KEY_MGMT_IEEE8021X_SHA256;
+       return !!(akm & (WPA_KEY_MGMT_PSK_SHA256 |
+                        WPA_KEY_MGMT_IEEE8021X_SHA256));
+}
+
+static inline int wpa_key_mgmt_wpa(int akm)
+{
+       return wpa_key_mgmt_wpa_ieee8021x(akm) ||
+               wpa_key_mgmt_wpa_psk(akm);
 }
 
 
@@ -137,6 +143,15 @@ enum wpa_states {
        WPA_DISCONNECTED,
 
        /**
+        * WPA_INTERFACE_DISABLED - Interface disabled
+        *
+        * This stat eis 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
@@ -252,4 +267,18 @@ enum hostapd_hw_mode {
        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,
+       NUM_WPA_CTRL_REQS
+};
+
 #endif /* DEFS_H */
diff --git a/src/common/gas.c b/src/common/gas.c
new file mode 100644 (file)
index 0000000..babdaa3
--- /dev/null
@@ -0,0 +1,279 @@
+/*
+ * Generic advertisement service (GAS) (IEEE 802.11u)
+ * Copyright (c) 2009, Atheros Communications
+ * Copyright (c) 2011, Qualcomm Atheros
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "ieee802_11_defs.h"
+#include "gas.h"
+
+
+static struct wpabuf *
+gas_build_req(u8 action, u8 dialog_token, size_t size)
+{
+       struct wpabuf *buf;
+
+       buf = wpabuf_alloc(100 + size);
+       if (buf == NULL)
+               return NULL;
+
+       wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
+       wpabuf_put_u8(buf, action);
+       wpabuf_put_u8(buf, dialog_token);
+
+       return buf;
+}
+
+
+static struct wpabuf * gas_build_initial_req(u8 dialog_token, size_t size)
+{
+       return gas_build_req(WLAN_PA_GAS_INITIAL_REQ, dialog_token,
+                            size);
+}
+
+
+struct wpabuf * gas_build_comeback_req(u8 dialog_token)
+{
+       return gas_build_req(WLAN_PA_GAS_COMEBACK_REQ, dialog_token, 0);
+}
+
+
+static struct wpabuf *
+gas_build_resp(u8 action, u8 dialog_token, u16 status_code, u8 frag_id,
+              u8 more, u16 comeback_delay, size_t size)
+{
+       struct wpabuf *buf;
+
+       buf = wpabuf_alloc(100 + size);
+       if (buf == NULL)
+               return NULL;
+
+       wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
+       wpabuf_put_u8(buf, action);
+       wpabuf_put_u8(buf, dialog_token);
+       wpabuf_put_le16(buf, status_code);
+       if (action == WLAN_PA_GAS_COMEBACK_RESP)
+               wpabuf_put_u8(buf, frag_id | (more ? 0x80 : 0));
+       wpabuf_put_le16(buf, comeback_delay);
+
+       return buf;
+}
+
+
+struct wpabuf *
+gas_build_initial_resp(u8 dialog_token, u16 status_code, u16 comeback_delay,
+                      size_t size)
+{
+       return gas_build_resp(WLAN_PA_GAS_INITIAL_RESP, dialog_token,
+                             status_code, 0, 0, comeback_delay, size);
+}
+
+
+static struct wpabuf *
+gas_build_comeback_resp(u8 dialog_token, u16 status_code, u8 frag_id, u8 more,
+                       u16 comeback_delay, size_t size)
+{
+       return gas_build_resp(WLAN_PA_GAS_COMEBACK_RESP, dialog_token,
+                             status_code, frag_id, more, comeback_delay,
+                             size);
+}
+
+
+/**
+ * gas_add_adv_proto_anqp - Add an Advertisement Protocol element
+ * @buf: Buffer to which the element is added
+ * @query_resp_len_limit: Query Response Length Limit in units of 256 octets
+ * @pame_bi: Pre-Association Message Exchange BSSID Independent (0/1)
+ *
+ *
+ * @query_resp_len_limit is 0 for request and 1-0x7f for response. 0x7f means
+ * that the maximum limit is determined by the maximum allowable number of
+ * fragments in the GAS Query Response Fragment ID.
+ */
+static void gas_add_adv_proto_anqp(struct wpabuf *buf, u8 query_resp_len_limit,
+                                  u8 pame_bi)
+{
+       /* Advertisement Protocol IE */
+       wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO);
+       wpabuf_put_u8(buf, 2); /* Length */
+       wpabuf_put_u8(buf, (query_resp_len_limit & 0x7f) |
+                     (pame_bi ? 0x80 : 0));
+       /* Advertisement Protocol */
+       wpabuf_put_u8(buf, ACCESS_NETWORK_QUERY_PROTOCOL);
+}
+
+
+struct wpabuf * gas_anqp_build_initial_req(u8 dialog_token, size_t size)
+{
+       struct wpabuf *buf;
+
+       buf = gas_build_initial_req(dialog_token, 4 + size);
+       if (buf == NULL)
+               return NULL;
+
+       gas_add_adv_proto_anqp(buf, 0, 0);
+
+       wpabuf_put(buf, 2); /* Query Request Length to be filled */
+
+       return buf;
+}
+
+
+struct wpabuf * gas_anqp_build_initial_resp(u8 dialog_token, u16 status_code,
+                                           u16 comeback_delay, size_t size)
+{
+       struct wpabuf *buf;
+
+       buf = gas_build_initial_resp(dialog_token, status_code, comeback_delay,
+                                    4 + size);
+       if (buf == NULL)
+               return NULL;
+
+       gas_add_adv_proto_anqp(buf, 0x7f, 0);
+
+       wpabuf_put(buf, 2); /* Query Response Length to be filled */
+
+       return buf;
+}
+
+
+struct wpabuf * gas_anqp_build_initial_resp_buf(u8 dialog_token,
+                                               u16 status_code,
+                                               u16 comeback_delay,
+                                               struct wpabuf *payload)
+{
+       struct wpabuf *buf;
+
+       buf = gas_anqp_build_initial_resp(dialog_token, status_code,
+                                         comeback_delay,
+                                         payload ? wpabuf_len(payload) : 0);
+       if (buf == NULL)
+               return NULL;
+
+       if (payload)
+               wpabuf_put_buf(buf, payload);
+
+       gas_anqp_set_len(buf);
+
+       return buf;
+}
+
+
+struct wpabuf * gas_anqp_build_comeback_resp(u8 dialog_token, u16 status_code,
+                                            u8 frag_id, u8 more,
+                                            u16 comeback_delay, size_t size)
+{
+       struct wpabuf *buf;
+
+       buf = gas_build_comeback_resp(dialog_token, status_code,
+                                     frag_id, more, comeback_delay, 4 + size);
+       if (buf == NULL)
+               return NULL;
+
+       gas_add_adv_proto_anqp(buf, 0x7f, 0);
+
+       wpabuf_put(buf, 2); /* Query Response Length to be filled */
+
+       return buf;
+}
+
+
+struct wpabuf * gas_anqp_build_comeback_resp_buf(u8 dialog_token,
+                                                u16 status_code,
+                                                u8 frag_id, u8 more,
+                                                u16 comeback_delay,
+                                                struct wpabuf *payload)
+{
+       struct wpabuf *buf;
+
+       buf = gas_anqp_build_comeback_resp(dialog_token, status_code, frag_id,
+                                          more, comeback_delay,
+                                          payload ? wpabuf_len(payload) : 0);
+       if (buf == NULL)
+               return NULL;
+
+       if (payload)
+               wpabuf_put_buf(buf, payload);
+
+       gas_anqp_set_len(buf);
+
+       return buf;
+}
+
+
+/**
+ * gas_anqp_set_len - Set Query Request/Response Length
+ * @buf: GAS message
+ *
+ * This function is used to update the Query Request/Response Length field once
+ * the payload has been filled.
+ */
+void gas_anqp_set_len(struct wpabuf *buf)
+{
+       u8 action;
+       size_t offset;
+       u8 *len;
+
+       if (buf == NULL || wpabuf_len(buf) < 2)
+               return;
+
+       action = *(wpabuf_head_u8(buf) + 1);
+       switch (action) {
+       case WLAN_PA_GAS_INITIAL_REQ:
+               offset = 3 + 4;
+               break;
+       case WLAN_PA_GAS_INITIAL_RESP:
+               offset = 7 + 4;
+               break;
+       case WLAN_PA_GAS_COMEBACK_RESP:
+               offset = 8 + 4;
+               break;
+       default:
+               return;
+       }
+
+       if (wpabuf_len(buf) < offset + 2)
+               return;
+
+       len = wpabuf_mhead_u8(buf) + offset;
+       WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
+}
+
+
+/**
+ * gas_anqp_add_element - Add ANQP element header
+ * @buf: GAS message
+ * @info_id: ANQP Info ID
+ * Returns: Pointer to the Length field for gas_anqp_set_element_len()
+ */
+u8 * gas_anqp_add_element(struct wpabuf *buf, u16 info_id)
+{
+       wpabuf_put_le16(buf, info_id);
+       return wpabuf_put(buf, 2); /* Length to be filled */
+}
+
+
+/**
+ * gas_anqp_set_element_len - Update ANQP element Length field
+ * @buf: GAS message
+ * @len_pos: Length field position from gas_anqp_add_element()
+ *
+ * This function is called after the ANQP element payload has been added to the
+ * buffer.
+ */
+void gas_anqp_set_element_len(struct wpabuf *buf, u8 *len_pos)
+{
+       WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(buf, 0) - len_pos - 2);
+}
diff --git a/src/common/gas.h b/src/common/gas.h
new file mode 100644 (file)
index 0000000..2f8d2cb
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Generic advertisement service (GAS) (IEEE 802.11u)
+ * Copyright (c) 2009, Atheros Communications
+ * Copyright (c) 2011, Qualcomm Atheros
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef GAS_H
+#define GAS_H
+
+struct wpabuf * gas_build_comeback_req(u8 dialog_token);
+struct wpabuf * gas_build_initial_resp(u8 dialog_token, u16 status_code,
+                                      u16 comeback_delay, size_t size);
+struct wpabuf * gas_anqp_build_initial_req(u8 dialog_token, size_t size);
+struct wpabuf * gas_anqp_build_initial_resp(u8 dialog_token, u16 status_code,
+                                           u16 comeback_delay, size_t size);
+struct wpabuf * gas_anqp_build_initial_resp_buf(u8 dialog_token,
+                                               u16 status_code,
+                                               u16 comeback_delay,
+                                               struct wpabuf *payload);
+struct wpabuf * gas_anqp_build_comeback_resp(u8 dialog_token, u16 status_code,
+                                            u8 frag_id, u8 more,
+                                            u16 comeback_delay, size_t size);
+struct wpabuf * gas_anqp_build_comeback_resp_buf(u8 dialog_token,
+                                                u16 status_code,
+                                                u8 frag_id, u8 more,
+                                                u16 comeback_delay,
+                                                struct wpabuf *payload);
+void gas_anqp_set_len(struct wpabuf *buf);
+
+u8 * gas_anqp_add_element(struct wpabuf *buf, u16 info_id);
+void gas_anqp_set_element_len(struct wpabuf *buf, u8 *len_pos);
+
+#endif /* GAS_H */
index 96ef5b6..43cb2c6 100644 (file)
@@ -75,7 +75,7 @@ static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen,
                                elems->wmm_tspec_len = elen;
                                break;
                        default:
-                               wpa_printf(MSG_MSGDUMP, "unknown WMM "
+                               wpa_printf(MSG_EXCESSIVE, "unknown WMM "
                                           "information element ignored "
                                           "(subtype=%d len=%lu)",
                                           pos[4], (unsigned long) elen);
@@ -88,7 +88,23 @@ static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen,
                        elems->wps_ie_len = elen;
                        break;
                default:
-                       wpa_printf(MSG_MSGDUMP, "Unknown Microsoft "
+                       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;
+               default:
+                       wpa_printf(MSG_MSGDUMP, "Unknown WFA "
                                   "information element ignored "
                                   "(type=%d len=%lu)\n",
                                   pos[3], (unsigned long) elen);
@@ -103,18 +119,18 @@ static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen,
                        elems->vendor_ht_cap_len = elen;
                        break;
                default:
-                       wpa_printf(MSG_MSGDUMP, "Unknown Broadcom "
+                       wpa_printf(MSG_EXCESSIVE, "Unknown Broadcom "
                                   "information element ignored "
-                                  "(type=%d len=%lu)\n",
+                                  "(type=%d len=%lu)",
                                   pos[3], (unsigned long) elen);
                        return -1;
                }
                break;
 
        default:
-               wpa_printf(MSG_MSGDUMP, "unknown vendor specific information "
-                          "element ignored (vendor OUI %02x:%02x:%02x "
-                          "len=%lu)",
+               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;
        }
@@ -238,6 +254,15 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len,
                        elems->ht_operation = pos;
                        elems->ht_operation_len = elen;
                        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;
                default:
                        unknown++;
                        if (!show_errors)
@@ -324,3 +349,43 @@ struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len,
 
        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;
+       }
+}
index 4a4f5a7..60f0974 100644 (file)
@@ -40,6 +40,9 @@ struct ieee802_11_elems {
        const u8 *ht_capabilities;
        const u8 *ht_operation;
        const u8 *vendor_ht_cap;
+       const u8 *p2p;
+       const u8 *link_id;
+       const u8 *interworking;
 
        u8 ssid_len;
        u8 supp_rates_len;
@@ -64,6 +67,8 @@ struct ieee802_11_elems {
        u8 ht_capabilities_len;
        u8 ht_operation_len;
        u8 vendor_ht_cap_len;
+       u8 p2p_len;
+       u8 interworking_len;
 };
 
 typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes;
@@ -74,5 +79,7 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len,
 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);
 
 #endif /* IEEE802_11_COMMON_H */
index 4881e39..4cbc535 100644 (file)
 #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
 /* 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_SUPPORTED_CHANNEL_NOT_VALID 24
 /* IEEE 802.11g */
 #define WLAN_STATUS_ASSOC_DENIED_NO_SHORT_SLOT_TIME 25
-#define WLAN_STATUS_ASSOC_DENIED_NO_ER_PBCC 26
-#define WLAN_STATUS_ASSOC_DENIED_NO_DSSS_OFDM 27
+#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_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_TRANSMISSION_FAILURE 79
 
 /* Reason codes (IEEE 802.11-2007, 7.3.1.7, Table 7-22) */
 #define WLAN_REASON_UNSPECIFIED 1
 #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
 
 
 /* Information Element IDs */
 #define WLAN_EID_RIC_DATA 57
 #define WLAN_EID_HT_OPERATION 61
 #define WLAN_EID_SECONDARY_CHANNEL_OFFSET 62
+#define WLAN_EID_TIME_ADVERTISEMENT 69
 #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_TIME_ZONE 98
+#define WLAN_EID_LINK_ID 101
+#define WLAN_EID_INTERWORKING 107
+#define WLAN_EID_ADV_PROTO 108
+#define WLAN_EID_ROAMING_CONSORTIUM 111
+#define WLAN_EID_EXT_CAPAB 127
 #define WLAN_EID_VENDOR_SPECIFIC 221
 
 
 #define WLAN_ACTION_FT 6
 #define WLAN_ACTION_HT 7
 #define WLAN_ACTION_SA_QUERY 8
+#define WLAN_ACTION_WNM 10
+#define WLAN_ACTION_UNPROTECTED_WNM 11
+#define WLAN_ACTION_TDLS 12
 #define WLAN_ACTION_WMM 17 /* WMM Specification 1.1 */
+#define WLAN_ACTION_VENDOR_SPECIFIC 127
+
+/* Public action codes */
+#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
 
 /* SA Query Action frame (IEEE 802.11w/D8.0, 7.4.9) */
 #define WLAN_SA_QUERY_REQUEST 0
 
 #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
+
 /* 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)
@@ -273,6 +407,7 @@ struct ieee80211_mgmt {
                } STRUCT_PACKED auth;
                struct {
                        le16 reason_code;
+                       u8 variable[0];
                } STRUCT_PACKED deauth;
                struct {
                        le16 capab_info;
@@ -296,6 +431,7 @@ struct ieee80211_mgmt {
                } STRUCT_PACKED reassoc_req;
                struct {
                        le16 reason_code;
+                       u8 variable[0];
                } STRUCT_PACKED disassoc;
                struct {
                        u8 timestamp[8];
@@ -355,6 +491,28 @@ struct ieee80211_mgmt {
                                        u8 action; /* */
                                        u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN];
                                } STRUCT_PACKED sa_query_resp;
+                               struct {
+                                       u8 action;
+                                       u8 variable[0];
+                               } STRUCT_PACKED public_action;
+                               struct {
+                                       u8 action; /* 9 */
+                                       u8 oui[3];
+                                       /* Vendor-specific content */
+                                       u8 variable[0];
+                               } 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];
+                               } STRUCT_PACKED bss_tm_req;
                        } u;
                } STRUCT_PACKED action;
        } u;
@@ -473,11 +631,14 @@ struct ieee80211_ht_operation {
 #define HT_INFO_STBC_PARAM_PCO_ACTIVE                  ((u16) BIT(10))
 #define HT_INFO_STBC_PARAM_PCO_PHASE                   ((u16) BIT(11))
 
+#define BSS_MEMBERSHIP_SELECTOR_HT_PHY 127
 
 #define OUI_MICROSOFT 0x0050f2 /* Microsoft (also used in Wi-Fi specs)
                                * 00:50:F2 */
 #define WPA_IE_VENDOR_TYPE 0x0050f201
 #define WPS_IE_VENDOR_TYPE 0x0050f204
+#define OUI_WFA 0x506f9a
+#define P2P_IE_VENDOR_TYPE 0x506f9a09
 
 #define WMM_OUI_TYPE 2
 #define WMM_OUI_SUBTYPE_INFORMATION_ELEMENT 0
@@ -544,7 +705,7 @@ struct wmm_parameter_element {
        u8 oui_type; /* 2 */
        u8 oui_subtype; /* 1 */
        u8 version; /* 1 for WMM version 1.0 */
-       u8 qos_info; /* AP/STA specif QoS info */
+       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 */
 
@@ -587,6 +748,115 @@ enum {
 };
 
 
+/* 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_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)
+
+/* 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,
+};
+
+#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_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
+};
+
+
 #define OUI_BROADCOM 0x00904c /* Broadcom (Epigram) */
 
 #define VENDOR_HT_CAPAB_OUI_TYPE 0x33 /* 00-90-4c:0x33 */
@@ -604,4 +874,44 @@ enum {
 #define WLAN_AKM_SUITE_8021X           0x000FAC01
 #define WLAN_AKM_SUITE_PSK             0x000FAC02
 
+
+/* 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)
+
 #endif /* IEEE802_11_DEFS_H */
index 02f34be..d3d6915 100644 (file)
@@ -1,6 +1,10 @@
 #ifndef VERSION_H
 #define VERSION_H
 
-#define VERSION_STR "0.7.3"
+#ifndef VERSION_STR_POSTFIX
+#define VERSION_STR_POSTFIX ""
+#endif /* VERSION_STR_POSTFIX */
+
+#define VERSION_STR "1.0" VERSION_STR_POSTFIX
 
 #endif /* VERSION_H */
index b295f31..24a61e4 100644 (file)
@@ -126,6 +126,8 @@ void wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
 
        wpa_printf(MSG_DEBUG, "WPA: PTK derivation - A1=" MACSTR " A2=" MACSTR,
                   MAC2STR(addr1), MAC2STR(addr2));
+       wpa_hexdump(MSG_DEBUG, "WPA: Nonce1", nonce1, WPA_NONCE_LEN);
+       wpa_hexdump(MSG_DEBUG, "WPA: Nonce2", nonce2, WPA_NONCE_LEN);
        wpa_hexdump_key(MSG_DEBUG, "WPA: PMK", pmk, pmk_len);
        wpa_hexdump_key(MSG_DEBUG, "WPA: PTK", ptk, ptk_len);
 }
@@ -186,6 +188,154 @@ int wpa_ft_mic(const u8 *kck, const u8 *sta_addr, const u8 *ap_addr,
 
        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:
+                       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:
+                       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 */
 
 
@@ -403,6 +553,144 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len,
 }
 
 
+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_WEP40)
+               return WPA_CIPHER_WEP40;
+       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 (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP104)
+               return WPA_CIPHER_WEP104;
+       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;
+
+       if (wpa_ie_len == 0) {
+               /* No WPA IE - fail silently */
+               return -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 || left < count * 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 || left < count * 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_printf(MSG_DEBUG, "%s: ie has %u trailing bytes - ignored",
+                          __func__, left);
+       }
+
+       return 0;
+}
+
+
 #ifdef CONFIG_IEEE80211R
 
 /**
index fd8a79f..69437a7 100644 (file)
@@ -56,6 +56,7 @@
 #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_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x0f, 0xac, 0)
 #define RSN_CIPHER_SUITE_WEP40 RSN_SELECTOR(0x00, 0x0f, 0xac, 1)
@@ -68,6 +69,7 @@
 #ifdef CONFIG_IEEE80211W
 #define RSN_CIPHER_SUITE_AES_128_CMAC RSN_SELECTOR(0x00, 0x0f, 0xac, 6)
 #endif /* CONFIG_IEEE80211W */
+#define RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED RSN_SELECTOR(0x00, 0x0f, 0xac, 7)
 
 /* EAPOL-Key Key Data Encapsulation
  * GroupKey and PeerKey require encryption, otherwise, encryption is optional.
 /* 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 */
@@ -337,6 +345,8 @@ struct wpa_ie_data {
 
 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);
@@ -348,4 +358,27 @@ int wpa_compare_rsn_ie(int ft_initial_assoc,
                       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);
+
 #endif /* WPA_COMMON_H */
index 2b4e3aa..3b25f77 100644 (file)
 #include <sys/un.h>
 #endif /* CONFIG_CTRL_IFACE_UNIX */
 
+#ifdef ANDROID
+#include <dirent.h>
+#include <cutils/sockets.h>
+#include "private/android_filesystem_config.h"
+#endif /* ANDROID */
+
 #include "wpa_ctrl.h"
 #include "common.h"
 
@@ -58,6 +64,14 @@ struct wpa_ctrl {
 
 #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)
 {
        struct wpa_ctrl *ctrl;
@@ -81,7 +95,9 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path)
        counter++;
 try_again:
        ret = os_snprintf(ctrl->local.sun_path, sizeof(ctrl->local.sun_path),
-                         "/tmp/wpa_ctrl_%d-%d", getpid(), counter);
+                         CONFIG_CTRL_IFACE_CLIENT_DIR "/"
+                         CONFIG_CTRL_IFACE_CLIENT_PREFIX "%d-%d",
+                         (int) getpid(), counter);
        if (ret < 0 || (size_t) ret >= sizeof(ctrl->local.sun_path)) {
                close(ctrl->s);
                os_free(ctrl);
@@ -105,6 +121,31 @@ try_again:
                return NULL;
        }
 
+#ifdef ANDROID
+       chmod(ctrl->local.sun_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+       chown(ctrl->local.sun_path, AID_SYSTEM, AID_WIFI);
+       /*
+        * 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 != NULL && *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;
        res = os_strlcpy(ctrl->dest.sun_path, ctrl_path,
                         sizeof(ctrl->dest.sun_path));
@@ -127,11 +168,64 @@ try_again:
 
 void wpa_ctrl_close(struct wpa_ctrl *ctrl)
 {
+       if (ctrl == NULL)
+               return;
        unlink(ctrl->local.sun_path);
-       close(ctrl->s);
+       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;
+       int prefixlen = os_strlen(CONFIG_CTRL_IFACE_CLIENT_PREFIX);
+       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_strncmp(entry.d_name, CONFIG_CTRL_IFACE_CLIENT_PREFIX,
+                              prefixlen) == 0) {
+                       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 */
 
 
@@ -234,11 +328,13 @@ int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len,
        os_free(cmd_buf);
 
        for (;;) {
-               tv.tv_sec = 2;
+               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)
index d770fd4..d13ba02 100644 (file)
@@ -32,6 +32,8 @@ extern "C" {
 #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 "
 /** wpa_supplicant is exiting */
 #define WPA_EVENT_TERMINATING "CTRL-EVENT-TERMINATING "
 /** Password change was completed successfully */
@@ -54,6 +56,8 @@ extern "C" {
 #define WPA_EVENT_EAP_FAILURE "CTRL-EVENT-EAP-FAILURE "
 /** New scan results available */
 #define WPA_EVENT_SCAN_RESULTS "CTRL-EVENT-SCAN-RESULTS "
+/** 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) */
@@ -63,6 +67,8 @@ extern "C" {
 #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 "
@@ -81,11 +87,51 @@ extern "C" {
 
 #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 "
+
+/** 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: <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_INVITATION_RECEIVED "P2P-INVITATION-RECEIVED "
+#define P2P_EVENT_INVITATION_RESULT "P2P-INVITATION-RESULT "
+#define P2P_EVENT_FIND_STOPPED "P2P-FIND-STOPPED "
+
+#define INTERWORKING_AP "INTERWORKING-AP "
+#define INTERWORKING_NO_MATCH "INTERWORKING-NO-MATCH "
 
 /* hostapd control interface - fixed message prefixes */
 #define WPS_EVENT_PIN_NEEDED "WPS-PIN-NEEDED "
@@ -223,6 +269,17 @@ int wpa_ctrl_pending(struct wpa_ctrl *ctrl);
  */
 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
 #define WPA_CTRL_IFACE_PORT 9877
 #define WPA_GLOBAL_CTRL_IFACE_PORT 9878
index 69aa16a..0454827 100644 (file)
@@ -48,6 +48,7 @@ LIB_OBJS += crypto_internal-modexp.o
 LIB_OBJS += crypto_internal-rsa.o
 LIB_OBJS += tls_internal.o
 LIB_OBJS += fips_prf_internal.o
+LIB_OBJS += random.o
 
 
 libcrypto.a: $(LIB_OBJS)
index 8fdba65..5f715a8 100644 (file)
@@ -63,7 +63,8 @@ struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
                ctx->key_len = key_len;
 
                os_memcpy(k_pad, key, key_len);
-               os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len);
+               if (key_len < sizeof(k_pad))
+                       os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len);
                for (i = 0; i < sizeof(k_pad); i++)
                        k_pad[i] ^= 0x36;
                MD5Init(&ctx->u.md5);
@@ -81,7 +82,8 @@ struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
                ctx->key_len = key_len;
 
                os_memcpy(k_pad, key, key_len);
-               os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len);
+               if (key_len < sizeof(k_pad))
+                       os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len);
                for (i = 0; i < sizeof(k_pad); i++)
                        k_pad[i] ^= 0x36;
                SHA1Init(&ctx->u.sha1);
index 7bd2fb7..e5b7d4c 100644 (file)
@@ -16,6 +16,7 @@
 
 #include "common.h"
 #include "crypto.h"
+#include "random.h"
 #include "dh_groups.h"
 
 
@@ -564,7 +565,8 @@ struct wpabuf * dh_init(const struct dh_group *dh, struct wpabuf **priv)
        if (*priv == NULL)
                return NULL;
 
-       if (os_get_random(wpabuf_put(*priv, dh->prime_len), dh->prime_len)) {
+       if (random_get_bytes(wpabuf_put(*priv, dh->prime_len), dh->prime_len))
+       {
                wpabuf_free(*priv);
                *priv = NULL;
                return NULL;
index a85cb14..1e0c453 100644 (file)
@@ -28,13 +28,14 @@ int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen)
        u8 *xpos = x;
        u32 carry;
 
-       if (seed_len > sizeof(xkey))
+       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);
-       os_memset(xkey + seed_len, 0, 64 - seed_len);
        t[0] = 0x67452301;
        t[1] = 0xEFCDAB89;
        t[2] = 0x98BADCFE;
index f8692a9..137ad91 100644 (file)
@@ -188,7 +188,7 @@ void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
     MD5Transform(ctx->buf, (u32 *) ctx->in);
     byteReverse((unsigned char *) ctx->buf, 4);
     os_memcpy(digest, ctx->buf, 16);
-    os_memset(ctx, 0, sizeof(ctx));    /* In case it's sensitive */
+    os_memset(ctx, 0, sizeof(*ctx));   /* In case it's sensitive */
 }
 
 /* The four core functions - F1 is optimized somewhat */
index dae15ab..c439ae9 100644 (file)
 #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));
+                       }
+               }
+       }
+
+       if (ucs2_string_size)
+               *ucs2_string_size = j / 2;
+       return 0;
+}
+
 
 /**
  * challenge_hash - ChallengeHash() - RFC 2759, Sect. 8.2
@@ -53,7 +107,7 @@ static int challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge,
 
 /**
  * nt_password_hash - NtPasswordHash() - RFC 2759, Sect. 8.3
- * @password: 0-to-256-unicode-char Password (IN; ASCII)
+ * @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
@@ -62,18 +116,13 @@ int nt_password_hash(const u8 *password, size_t password_len,
                      u8 *password_hash)
 {
        u8 buf[512], *pos;
-       size_t i, len;
+       size_t len, max_len;
 
-       if (password_len > 256)
-               password_len = 256;
-
-       /* Convert password into unicode */
-       for (i = 0; i < password_len; i++) {
-               buf[2 * i] = password[i];
-               buf[2 * i + 1] = 0;
-       }
+       max_len = sizeof(buf);
+       if (utf8_to_ucs2(password, password_len, buf, max_len, &len) < 0)
+               return -1;
 
-       len = password_len * 2;
+       len *= 2;
        pos = buf;
        return md4_vector(1, (const u8 **) &pos, &len, password_hash);
 }
@@ -117,7 +166,7 @@ void challenge_response(const u8 *challenge, const u8 *password_hash,
  * @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; ASCII)
+ * @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
@@ -225,7 +274,7 @@ int generate_authenticator_response_pwhash(
 
 /**
  * generate_authenticator_response - GenerateAuthenticatorResponse() - RFC 2759, Sect. 8.7
- * @password: 0-to-256-unicode-char Password (IN; ASCII)
+ * @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)
@@ -254,7 +303,7 @@ int generate_authenticator_response(const u8 *password, size_t password_len,
 /**
  * nt_challenge_response - NtChallengeResponse() - RFC 2433, Sect. A.5
  * @challenge: 8-octet Challenge (IN)
- * @password: 0-to-256-unicode-char Password (IN; ASCII)
+ * @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
@@ -375,7 +424,7 @@ int get_asymetric_start_key(const u8 *master_key, u8 *session_key,
 
 /**
  * encrypt_pw_block_with_password_hash - EncryptPwBlockWithPasswordHash() - RFC 2759, Sect. 8.10
- * @password: 0-to-256-unicode-char Password (IN; ASCII)
+ * @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)
@@ -385,18 +434,23 @@ int encrypt_pw_block_with_password_hash(
        const u8 *password, size_t password_len,
        const u8 *password_hash, u8 *pw_block)
 {
-       size_t i, offset;
+       size_t ucs2_len, offset;
        u8 *pos;
 
-       if (password_len > 256)
+       os_memset(pw_block, 0, PWBLOCK_LEN);
+
+       if (utf8_to_ucs2(password, password_len, pw_block, 512, &ucs2_len) < 0)
                return -1;
 
-       os_memset(pw_block, 0, PWBLOCK_LEN);
-       offset = (256 - password_len) * 2;
-       if (os_get_random(pw_block, offset) < 0)
+       if (ucs2_len > 256)
                return -1;
-       for (i = 0; i < password_len; i++)
-               pw_block[offset + i * 2] = password[i];
+
+       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.
@@ -410,9 +464,9 @@ int encrypt_pw_block_with_password_hash(
 
 /**
  * new_password_encrypted_with_old_nt_password_hash - NewPasswordEncryptedWithOldNtPasswordHash() - RFC 2759, Sect. 8.9
- * @new_password: 0-to-256-unicode-char NewPassword (IN; ASCII)
+ * @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; ASCII)
+ * @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
@@ -450,9 +504,9 @@ void nt_password_hash_encrypted_with_block(const u8 *password_hash,
 
 /**
  * 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; ASCII)
+ * @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; ASCII)
+ * @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
diff --git a/src/crypto/random.c b/src/crypto/random.c
new file mode 100644 (file)
index 0000000..a54e197
--- /dev/null
@@ -0,0 +1,430 @@
+/*
+ * Random number generator
+ * Copyright (c) 2010-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ *
+ * This 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 "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++;
+       wpa_printf(MSG_MSGDUMP, "Add randomness: count=%u entropy=%u",
+                  count, entropy);
+       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;
+       }
+
+       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;
+       }
+       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) {
+#ifndef CONFIG_NO_STDOUT_DEBUG
+               int error = errno;
+               perror("open(/dev/random)");
+               wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s",
+                          strerror(error));
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+               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) {
+#ifndef CONFIG_NO_STDOUT_DEBUG
+               int error = errno;
+               perror("open(/dev/random)");
+               wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s",
+                          strerror(error));
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+               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;
+}
diff --git a/src/crypto/random.h b/src/crypto/random.h
new file mode 100644 (file)
index 0000000..1048bb4
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Random number generator
+ * Copyright (c) 2010-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef RANDOM_H
+#define RANDOM_H
+
+#ifdef CONFIG_NO_RANDOM_POOL
+#define random_init(e) do { } while (0)
+#define random_deinit() do { } while (0)
+#define random_add_randomness(b, l) do { } while (0)
+#define random_get_bytes(b, l) os_get_random((b), (l))
+#define random_pool_ready() 1
+#define random_mark_pool_ready() do { } while (0)
+#else /* CONFIG_NO_RANDOM_POOL */
+void random_init(const char *entropy_file);
+void random_deinit(void);
+void random_add_randomness(const void *buf, size_t len);
+int random_get_bytes(void *buf, size_t len);
+int random_pool_ready(void);
+void random_mark_pool_ready(void);
+#endif /* CONFIG_NO_RANDOM_POOL */
+
+#endif /* RANDOM_H */
index b061373..c3a74c3 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * SHA-256 hash implementation and interface functions
- * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2011, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
 #include "sha256.h"
 #include "crypto.h"
 
+#define SHA256_BLOCK_SIZE 64
+
 struct sha256_state {
        u64 length;
        u32 state[8], curlen;
-       u8 buf[64];
+       u8 buf[SHA256_BLOCK_SIZE];
 };
 
 static void sha256_init(struct sha256_state *md);
@@ -162,28 +164,27 @@ static int sha256_process(struct sha256_state *md, const unsigned char *in,
                          unsigned long inlen)
 {
        unsigned long n;
-#define block_size 64
 
-       if (md->curlen > sizeof(md->buf))
+       if (md->curlen >= sizeof(md->buf))
                return -1;
 
        while (inlen > 0) {
-               if (md->curlen == 0 && inlen >= block_size) {
+               if (md->curlen == 0 && inlen >= SHA256_BLOCK_SIZE) {
                        if (sha256_compress(md, (unsigned char *) in) < 0)
                                return -1;
-                       md->length += block_size * 8;
-                       in += block_size;
-                       inlen -= block_size;
+                       md->length += SHA256_BLOCK_SIZE * 8;
+                       in += SHA256_BLOCK_SIZE;
+                       inlen -= SHA256_BLOCK_SIZE;
                } else {
-                       n = MIN(inlen, (block_size - md->curlen));
+                       n = MIN(inlen, (SHA256_BLOCK_SIZE - md->curlen));
                        os_memcpy(md->buf + md->curlen, in, n);
                        md->curlen += n;
                        in += n;
                        inlen -= n;
-                       if (md->curlen == block_size) {
+                       if (md->curlen == SHA256_BLOCK_SIZE) {
                                if (sha256_compress(md, md->buf) < 0)
                                        return -1;
-                               md->length += 8 * block_size;
+                               md->length += 8 * SHA256_BLOCK_SIZE;
                                md->curlen = 0;
                        }
                }
@@ -217,14 +218,14 @@ static int sha256_done(struct sha256_state *md, unsigned char *out)
         * encoding like normal.
         */
        if (md->curlen > 56) {
-               while (md->curlen < 64) {
+               while (md->curlen < SHA256_BLOCK_SIZE) {
                        md->buf[md->curlen++] = (unsigned char) 0;
                }
                sha256_compress(md, md->buf);
                md->curlen = 0;
        }
 
-       /* pad upto 56 bytes of zeroes */
+       /* pad up to 56 bytes of zeroes */
        while (md->curlen < 56) {
                md->buf[md->curlen++] = (unsigned char) 0;
        }
index 0928b5b..a5de3fb 100644 (file)
@@ -24,8 +24,6 @@ struct tls_keys {
        size_t client_random_len;
        const u8 *server_random;
        size_t server_random_len;
-       const u8 *inner_secret; /* TLS/IA inner secret */
-       size_t inner_secret_len;
 };
 
 enum tls_event {
@@ -72,6 +70,7 @@ struct tls_config {
        const char *pkcs11_engine_path;
        const char *pkcs11_module_path;
        int fips_mode;
+       int cert_in_cb;
 
        void (*event_cb)(void *ctx, enum tls_event ev,
                         union tls_event_data *data);
@@ -114,7 +113,6 @@ struct tls_config {
  * specific for now)
  * @cert_id: the certificate's id when using engine
  * @ca_cert_id: the CA certificate's id when using engine
- * @tls_ia: Whether to enable TLS/IA (for EAP-TTLSv1)
  * @flags: Parameter options (TLS_CONN_*)
  *
  * TLS connection parameters to be configured with tls_connection_set_params()
@@ -142,7 +140,6 @@ struct tls_connection_params {
        const char *dh_file;
        const u8 *dh_blob;
        size_t dh_blob_len;
-       int tls_ia;
 
        /* OpenSSL specific variables */
        int engine;
@@ -282,20 +279,6 @@ int __must_check tls_connection_set_verify(void *tls_ctx,
                                           int verify_peer);
 
 /**
- * tls_connection_set_ia - Set TLS/IA parameters
- * @tls_ctx: TLS context data from tls_init()
- * @conn: Connection context data from tls_connection_init()
- * @tls_ia: 1 = enable TLS/IA
- * Returns: 0 on success, -1 on failure
- *
- * This function is used to configure TLS/IA in server mode where
- * tls_connection_set_params() is not used.
- */
-int __must_check tls_connection_set_ia(void *tls_ctx,
-                                      struct tls_connection *conn,
-                                      int tls_ia);
-
-/**
  * tls_connection_get_keys - Get master key and random data from TLS connection
  * @tls_ctx: TLS context data from tls_init()
  * @conn: Connection context data from tls_connection_init()
@@ -514,7 +497,6 @@ int tls_connection_get_write_alerts(void *tls_ctx,
 int tls_connection_get_keyblock_size(void *tls_ctx,
                                     struct tls_connection *conn);
 
-#define TLS_CAPABILITY_IA 0x0001 /* TLS Inner Application (TLS/IA) */
 /**
  * tls_capabilities - Get supported TLS capabilities
  * @tls_ctx: TLS context data from tls_init()
@@ -522,42 +504,6 @@ int tls_connection_get_keyblock_size(void *tls_ctx,
  */
 unsigned int tls_capabilities(void *tls_ctx);
 
-/**
- * tls_connection_ia_send_phase_finished - Send a TLS/IA PhaseFinished message
- * @tls_ctx: TLS context data from tls_init()
- * @conn: Connection context data from tls_connection_init()
- * @final: 1 = FinalPhaseFinished, 0 = IntermediatePhaseFinished
- * Returns: Encrypted TLS/IA data, %NULL on failure
- *
- * This function is used to send the TLS/IA end phase message, e.g., when the
- * EAP server completes EAP-TTLSv1.
- */
-struct wpabuf * tls_connection_ia_send_phase_finished(
-       void *tls_ctx, struct tls_connection *conn, int final);
-
-/**
- * tls_connection_ia_final_phase_finished - Has final phase been completed
- * @tls_ctx: TLS context data from tls_init()
- * @conn: Connection context data from tls_connection_init()
- * Returns: 1 if valid FinalPhaseFinished has been received, 0 if not, or -1
- * on failure
- */
-int __must_check tls_connection_ia_final_phase_finished(
-       void *tls_ctx, struct tls_connection *conn);
-
-/**
- * tls_connection_ia_permute_inner_secret - Permute TLS/IA inner secret
- * @tls_ctx: TLS context data from tls_init()
- * @conn: Connection context data from tls_connection_init()
- * @key: Session key material (session_key vectors with 2-octet length), or
- * %NULL if no session key was generating in the current phase
- * @key_len: Length of session key material
- * Returns: 0 on success, -1 on failure
- */
-int __must_check tls_connection_ia_permute_inner_secret(
-       void *tls_ctx, struct tls_connection *conn,
-       const u8 *key, size_t key_len);
-
 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);
index c3a7358..afa5268 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * SSL/TLS interface functions for GnuTLS
- * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
 #include <gnutls/pkcs12.h>
 #endif /* PKCS12_FUNCS */
 
-#ifdef CONFIG_GNUTLS_EXTRA
-#if LIBGNUTLS_VERSION_NUMBER >= 0x010302
-#define GNUTLS_IA
-#include <gnutls/extra.h>
-#if LIBGNUTLS_VERSION_NUMBER == 0x010302
-/* This function is not included in the current gnutls/extra.h even though it
- * should be, so define it here as a workaround for the time being. */
-int gnutls_ia_verify_endphase(gnutls_session_t session, char *checksum);
-#endif /* LIBGNUTLS_VERSION_NUMBER == 0x010302 */
-#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */
-#endif /* CONFIG_GNUTLS_EXTRA */
-
 #include "common.h"
 #include "tls.h"
 
 
-#ifndef TLS_RANDOM_SIZE
-#define TLS_RANDOM_SIZE 32
-#endif
-#ifndef TLS_MASTER_SIZE
-#define TLS_MASTER_SIZE 48
-#endif
+#define WPA_TLS_RANDOM_SIZE 32
+#define WPA_TLS_MASTER_SIZE 48
 
 
 #if LIBGNUTLS_VERSION_NUMBER < 0x010302
@@ -77,9 +61,9 @@ typedef struct {
        gnutls_mac_algorithm_t write_mac_algorithm;
        gnutls_compression_method_t write_compression_algorithm;
        cipher_suite_st current_cipher_suite;
-       opaque master_secret[TLS_MASTER_SIZE];
-       opaque client_random[TLS_RANDOM_SIZE];
-       opaque server_random[TLS_RANDOM_SIZE];
+       opaque master_secret[WPA_TLS_MASTER_SIZE];
+       opaque client_random[WPA_TLS_RANDOM_SIZE];
+       opaque server_random[WPA_TLS_RANDOM_SIZE];
        /* followed by stuff we are not interested in */
 } security_parameters_st;
 
@@ -118,21 +102,6 @@ struct tls_connection {
 
        int params_set;
        gnutls_certificate_credentials_t xcred;
-
-       int tls_ia;
-       int final_phase_finished;
-
-#ifdef GNUTLS_IA
-       gnutls_ia_server_credentials_t iacred_srv;
-       gnutls_ia_client_credentials_t iacred_cli;
-
-       /* Session keys generated in the current phase for inner secret
-        * permutation before generating/verifying PhaseFinished. */
-       u8 *session_keys;
-       size_t session_keys_len;
-
-       u8 inner_secret[TLS_MASTER_SIZE];
-#endif /* GNUTLS_IA */
 };
 
 
@@ -285,8 +254,12 @@ static ssize_t tls_push_func(gnutls_transport_ptr ptr, const void *buf,
 static int tls_gnutls_init_session(struct tls_global *global,
                                   struct tls_connection *conn)
 {
+#if LIBGNUTLS_VERSION_NUMBER >= 0x020200
+       const char *err;
+#else /* LIBGNUTLS_VERSION_NUMBER >= 0x020200 */
        const int cert_types[2] = { GNUTLS_CRT_X509, 0 };
        const int protos[2] = { GNUTLS_TLS1, 0 };
+#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020200 */
        int ret;
 
        ret = gnutls_init(&conn->session,
@@ -301,6 +274,15 @@ static int tls_gnutls_init_session(struct tls_global *global,
        if (ret < 0)
                goto fail;
 
+#if LIBGNUTLS_VERSION_NUMBER >= 0x020200
+       ret = gnutls_priority_set_direct(conn->session, "NORMAL:-VERS-SSL3.0",
+                                        &err);
+       if (ret < 0) {
+               wpa_printf(MSG_ERROR, "GnuTLS: Priority string failure at "
+                          "'%s'", err);
+               goto fail;
+       }
+#else /* LIBGNUTLS_VERSION_NUMBER >= 0x020200 */
        ret = gnutls_certificate_type_set_priority(conn->session, cert_types);
        if (ret < 0)
                goto fail;
@@ -308,6 +290,7 @@ static int tls_gnutls_init_session(struct tls_global *global,
        ret = gnutls_protocol_set_priority(conn->session, protos);
        if (ret < 0)
                goto fail;
+#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020200 */
 
        gnutls_transport_set_pull_function(conn->session, tls_pull_func);
        gnutls_transport_set_push_function(conn->session, tls_push_func);
@@ -364,17 +347,6 @@ void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn)
        if (conn == NULL)
                return;
 
-#ifdef GNUTLS_IA
-       if (conn->iacred_srv)
-               gnutls_ia_free_server_credentials(conn->iacred_srv);
-       if (conn->iacred_cli)
-               gnutls_ia_free_client_credentials(conn->iacred_cli);
-       if (conn->session_keys) {
-               os_memset(conn->session_keys, 0, conn->session_keys_len);
-               os_free(conn->session_keys);
-       }
-#endif /* GNUTLS_IA */
-
        gnutls_certificate_free_credentials(conn->xcred);
        gnutls_deinit(conn->session);
        os_free(conn->pre_shared_secret);
@@ -407,14 +379,6 @@ int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn)
        wpabuf_free(conn->push_buf);
        conn->push_buf = NULL;
        conn->established = 0;
-       conn->final_phase_finished = 0;
-#ifdef GNUTLS_IA
-       if (conn->session_keys) {
-               os_memset(conn->session_keys, 0, conn->session_keys_len);
-               os_free(conn->session_keys);
-       }
-       conn->session_keys_len = 0;
-#endif /* GNUTLS_IA */
 
        gnutls_deinit(conn->session);
        if (tls_gnutls_init_session(global, conn)) {
@@ -597,11 +561,13 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
                                conn->xcred, GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5);
                }
 
+#if LIBGNUTLS_VERSION_NUMBER >= 0x020800
                if (params->flags & TLS_CONN_DISABLE_TIME_CHECKS) {
                        gnutls_certificate_set_verify_flags(
                                conn->xcred,
                                GNUTLS_VERIFY_DISABLE_TIME_CHECKS);
                }
+#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */
        }
 
        if (params->client_cert && params->private_key) {
@@ -646,7 +612,6 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
                }
        }
 
-       conn->tls_ia = params->tls_ia;
        conn->params_set = 1;
 
        ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE,
@@ -656,28 +621,6 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
                           gnutls_strerror(ret));
        }
 
-#ifdef GNUTLS_IA
-       if (conn->iacred_cli)
-               gnutls_ia_free_client_credentials(conn->iacred_cli);
-
-       ret = gnutls_ia_allocate_client_credentials(&conn->iacred_cli);
-       if (ret) {
-               wpa_printf(MSG_DEBUG, "Failed to allocate IA credentials: %s",
-                          gnutls_strerror(ret));
-               return -1;
-       }
-
-       ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_IA,
-                                    conn->iacred_cli);
-       if (ret) {
-               wpa_printf(MSG_DEBUG, "Failed to configure IA credentials: %s",
-                          gnutls_strerror(ret));
-               gnutls_ia_free_client_credentials(conn->iacred_cli);
-               conn->iacred_cli = NULL;
-               return -1;
-       }
-#endif /* GNUTLS_IE */
-
        return ret;
 }
 
@@ -729,11 +672,13 @@ int tls_global_set_params(void *tls_ctx,
                                GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5);
                }
 
+#if LIBGNUTLS_VERSION_NUMBER >= 0x020800
                if (params->flags & TLS_CONN_DISABLE_TIME_CHECKS) {
                        gnutls_certificate_set_verify_flags(
                                global->xcred,
                                GNUTLS_VERIFY_DISABLE_TIME_CHECKS);
                }
+#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */
        }
 
        if (params->client_cert && params->private_key) {
@@ -822,10 +767,11 @@ int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn,
 
        os_memset(keys, 0, sizeof(*keys));
 
+#if LIBGNUTLS_VERSION_NUMBER < 0x020c00
 #ifdef GNUTLS_INTERNAL_STRUCTURE_HACK
        sec = &conn->session->security_parameters;
        keys->master_key = sec->master_secret;
-       keys->master_key_len = TLS_MASTER_SIZE;
+       keys->master_key_len = WPA_TLS_MASTER_SIZE;
        keys->client_random = sec->client_random;
        keys->server_random = sec->server_random;
 #else /* GNUTLS_INTERNAL_STRUCTURE_HACK */
@@ -835,16 +781,12 @@ int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn,
                (u8 *) gnutls_session_get_server_random(conn->session);
        /* No access to master_secret */
 #endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */
+#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020c00 */
 
-#ifdef GNUTLS_IA
-       gnutls_ia_extract_inner_secret(conn->session,
-                                      (char *) conn->inner_secret);
-       keys->inner_secret = conn->inner_secret;
-       keys->inner_secret_len = TLS_MASTER_SIZE;
-#endif /* GNUTLS_IA */
-
-       keys->client_random_len = TLS_RANDOM_SIZE;
-       keys->server_random_len = TLS_RANDOM_SIZE;
+#if LIBGNUTLS_VERSION_NUMBER < 0x020c00
+       keys->client_random_len = WPA_TLS_RANDOM_SIZE;
+       keys->server_random_len = WPA_TLS_RANDOM_SIZE;
+#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020c00 */
 
        return 0;
 }
@@ -883,11 +825,13 @@ static int tls_connection_verify_peer(struct tls_connection *conn,
 
        if (conn->verify_peer && (status & GNUTLS_CERT_INVALID)) {
                wpa_printf(MSG_INFO, "TLS: Peer certificate not trusted");
+               *err = GNUTLS_A_INTERNAL_ERROR;
                if (status & GNUTLS_CERT_INSECURE_ALGORITHM) {
                        wpa_printf(MSG_INFO, "TLS: Certificate uses insecure "
                                   "algorithm");
                        *err = GNUTLS_A_INSUFFICIENT_SECURITY;
                }
+#if LIBGNUTLS_VERSION_NUMBER >= 0x020800
                if (status & GNUTLS_CERT_NOT_ACTIVATED) {
                        wpa_printf(MSG_INFO, "TLS: Certificate not yet "
                                   "activated");
@@ -897,6 +841,7 @@ static int tls_connection_verify_peer(struct tls_connection *conn,
                        wpa_printf(MSG_INFO, "TLS: Certificate expired");
                        *err = GNUTLS_A_CERTIFICATE_EXPIRED;
                }
+#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */
                return -1;
        }
 
@@ -988,7 +933,7 @@ static struct wpabuf * gnutls_get_appl_data(struct tls_connection *conn)
                                 wpabuf_size(ad));
        wpa_printf(MSG_DEBUG, "GnuTLS: gnutls_record_recv: %d", res);
        if (res < 0) {
-               wpa_printf(MSG_DEBUG, "%s - gnutls_ia_recv failed: %d "
+               wpa_printf(MSG_DEBUG, "%s - gnutls_record_recv failed: %d "
                           "(%s)", __func__, (int) res,
                           gnutls_strerror(res));
                wpabuf_free(ad);
@@ -1062,20 +1007,7 @@ struct wpabuf * tls_connection_handshake(void *tls_ctx,
                        goto out;
                }
 
-#ifdef CONFIG_GNUTLS_EXTRA
-               if (conn->tls_ia && !gnutls_ia_handshake_p(conn->session)) {
-                       wpa_printf(MSG_INFO, "TLS: No TLS/IA negotiation");
-                       conn->failed++;
-                       return NULL;
-               }
-#endif /* CONFIG_GNUTLS_EXTRA */
-
-               if (conn->tls_ia)
-                       wpa_printf(MSG_DEBUG, "TLS: Start TLS/IA handshake");
-               else {
-                       wpa_printf(MSG_DEBUG, "TLS: Handshake completed "
-                                  "successfully");
-               }
+               wpa_printf(MSG_DEBUG, "TLS: Handshake completed successfully");
                conn->established = 1;
                if (conn->push_buf == NULL) {
                        /* Need to return something to get final TLS ACK. */
@@ -1122,12 +1054,6 @@ struct wpabuf * tls_connection_encrypt(void *tls_ctx,
        ssize_t res;
        struct wpabuf *buf;
 
-#ifdef GNUTLS_IA
-       if (conn->tls_ia)
-               res = gnutls_ia_send(conn->session, wpabuf_head(in_data),
-                                    wpabuf_len(in_data));
-       else
-#endif /* GNUTLS_IA */
        res = gnutls_record_send(conn->session, wpabuf_head(in_data),
                                 wpabuf_len(in_data));
        if (res < 0) {
@@ -1170,65 +1096,6 @@ struct wpabuf * tls_connection_decrypt(void *tls_ctx,
        if (out == NULL)
                return NULL;
 
-#ifdef GNUTLS_IA
-       if (conn->tls_ia) {
-               res = gnutls_ia_recv(conn->session, wpabuf_mhead(out),
-                                    wpabuf_size(out));
-               if (res == GNUTLS_E_WARNING_IA_IPHF_RECEIVED ||
-                   res == GNUTLS_E_WARNING_IA_FPHF_RECEIVED) {
-                       int final = res == GNUTLS_E_WARNING_IA_FPHF_RECEIVED;
-                       wpa_printf(MSG_DEBUG, "%s: Received %sPhaseFinished",
-                                  __func__, final ? "Final" : "Intermediate");
-
-                       res = gnutls_ia_permute_inner_secret(
-                               conn->session, conn->session_keys_len,
-                               (char *) conn->session_keys);
-                       if (conn->session_keys) {
-                               os_memset(conn->session_keys, 0,
-                                         conn->session_keys_len);
-                               os_free(conn->session_keys);
-                       }
-                       conn->session_keys = NULL;
-                       conn->session_keys_len = 0;
-                       if (res) {
-                               wpa_printf(MSG_DEBUG, "%s: Failed to permute "
-                                          "inner secret: %s",
-                                          __func__, gnutls_strerror(res));
-                               wpabuf_free(out);
-                               return NULL;
-                       }
-
-                       res = gnutls_ia_verify_endphase(conn->session,
-                                                       wpabuf_head(out));
-                       if (res == 0) {
-                               wpa_printf(MSG_DEBUG, "%s: Correct endphase "
-                                          "checksum", __func__);
-                       } else {
-                               wpa_printf(MSG_INFO, "%s: Endphase "
-                                          "verification failed: %s",
-                                          __func__, gnutls_strerror(res));
-                               wpabuf_free(out);
-                               return NULL;
-                       }
-
-                       if (final)
-                               conn->final_phase_finished = 1;
-
-                       return out;
-               }
-
-               if (res < 0) {
-                       wpa_printf(MSG_DEBUG, "%s - gnutls_ia_recv failed: %d "
-                                  "(%s)", __func__, (int) res,
-                                  gnutls_strerror(res));
-                       wpabuf_free(out);
-                       return NULL;
-               }
-               wpabuf_put(out, res);
-               return out;
-       }
-#endif /* GNUTLS_IA */
-
        res = gnutls_record_recv(conn->session, wpabuf_mhead(out),
                                 wpabuf_size(out));
        if (res < 0) {
@@ -1319,133 +1186,7 @@ int tls_connection_get_keyblock_size(void *tls_ctx,
 
 unsigned int tls_capabilities(void *tls_ctx)
 {
-       unsigned int capa = 0;
-
-#ifdef GNUTLS_IA
-       capa |= TLS_CAPABILITY_IA;
-#endif /* GNUTLS_IA */
-
-       return capa;
-}
-
-
-int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn,
-                         int tls_ia)
-{
-#ifdef GNUTLS_IA
-       int ret;
-
-       if (conn == NULL)
-               return -1;
-
-       conn->tls_ia = tls_ia;
-       if (!tls_ia)
-               return 0;
-
-       ret = gnutls_ia_allocate_server_credentials(&conn->iacred_srv);
-       if (ret) {
-               wpa_printf(MSG_DEBUG, "Failed to allocate IA credentials: %s",
-                          gnutls_strerror(ret));
-               return -1;
-       }
-
-       ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_IA,
-                                    conn->iacred_srv);
-       if (ret) {
-               wpa_printf(MSG_DEBUG, "Failed to configure IA credentials: %s",
-                          gnutls_strerror(ret));
-               gnutls_ia_free_server_credentials(conn->iacred_srv);
-               conn->iacred_srv = NULL;
-               return -1;
-       }
-
-       return 0;
-#else /* GNUTLS_IA */
-       return -1;
-#endif /* GNUTLS_IA */
-}
-
-
-struct wpabuf * tls_connection_ia_send_phase_finished(
-       void *tls_ctx, struct tls_connection *conn, int final)
-{
-#ifdef GNUTLS_IA
-       int ret;
-       struct wpabuf *buf;
-
-       if (conn == NULL || conn->session == NULL || !conn->tls_ia)
-               return NULL;
-
-       ret = gnutls_ia_permute_inner_secret(conn->session,
-                                            conn->session_keys_len,
-                                            (char *) conn->session_keys);
-       if (conn->session_keys) {
-               os_memset(conn->session_keys, 0, conn->session_keys_len);
-               os_free(conn->session_keys);
-       }
-       conn->session_keys = NULL;
-       conn->session_keys_len = 0;
-       if (ret) {
-               wpa_printf(MSG_DEBUG, "%s: Failed to permute inner secret: %s",
-                          __func__, gnutls_strerror(ret));
-               return NULL;
-       }
-
-       ret = gnutls_ia_endphase_send(conn->session, final);
-       if (ret) {
-               wpa_printf(MSG_DEBUG, "%s: Failed to send endphase: %s",
-                          __func__, gnutls_strerror(ret));
-               return NULL;
-       }
-
-       buf = conn->push_buf;
-       conn->push_buf = NULL;
-       return buf;
-#else /* GNUTLS_IA */
-       return NULL;
-#endif /* GNUTLS_IA */
-}
-
-
-int tls_connection_ia_final_phase_finished(void *tls_ctx,
-                                          struct tls_connection *conn)
-{
-       if (conn == NULL)
-               return -1;
-
-       return conn->final_phase_finished;
-}
-
-
-int tls_connection_ia_permute_inner_secret(void *tls_ctx,
-                                          struct tls_connection *conn,
-                                          const u8 *key, size_t key_len)
-{
-#ifdef GNUTLS_IA
-       if (conn == NULL || !conn->tls_ia)
-               return -1;
-
-       if (conn->session_keys) {
-               os_memset(conn->session_keys, 0, conn->session_keys_len);
-               os_free(conn->session_keys);
-       }
-       conn->session_keys_len = 0;
-
-       if (key) {
-               conn->session_keys = os_malloc(key_len);
-               if (conn->session_keys == NULL)
-                       return -1;
-               os_memcpy(conn->session_keys, key, key_len);
-               conn->session_keys_len = key_len;
-       } else {
-               conn->session_keys = NULL;
-               conn->session_keys_len = 0;
-       }
-
        return 0;
-#else /* GNUTLS_IA */
-       return -1;
-#endif /* GNUTLS_IA */
 }
 
 
index 64124d8..eacb9bf 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * TLS interface functions and an internal TLS implementation
- * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -211,6 +211,9 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
                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;
@@ -287,13 +290,6 @@ int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
 }
 
 
-int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn,
-                         int tls_ia)
-{
-       return -1;
-}
-
-
 int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn,
                            struct tls_keys *keys)
 {
@@ -608,28 +604,6 @@ unsigned int tls_capabilities(void *tls_ctx)
 }
 
 
-struct wpabuf * tls_connection_ia_send_phase_finished(
-       void *tls_ctx, struct tls_connection *conn, int final)
-{
-       return NULL;
-}
-
-
-int tls_connection_ia_final_phase_finished(void *tls_ctx,
-                                          struct tls_connection *conn)
-{
-       return -1;
-}
-
-
-int tls_connection_ia_permute_inner_secret(void *tls_ctx,
-                                          struct tls_connection *conn,
-                                          const u8 *key, size_t key_len)
-{
-       return -1;
-}
-
-
 int tls_connection_set_session_ticket_cb(void *tls_ctx,
                                         struct tls_connection *conn,
                                         tls_session_ticket_cb cb,
index 0c836bb..927edf5 100644 (file)
@@ -84,13 +84,6 @@ int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
 }
 
 
-int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn,
-                         int tls_ia)
-{
-       return -1;
-}
-
-
 int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn,
                            struct tls_keys *keys)
 {
@@ -205,25 +198,3 @@ unsigned int tls_capabilities(void *tls_ctx)
 {
        return 0;
 }
-
-
-struct wpabuf * tls_connection_ia_send_phase_finished(
-       void *tls_ctx, struct tls_connection *conn, int final)
-{
-       return NULL;
-}
-
-
-int tls_connection_ia_final_phase_finished(void *tls_ctx,
-                                          struct tls_connection *conn)
-{
-       return -1;
-}
-
-
-int tls_connection_ia_permute_inner_secret(void *tls_ctx,
-                                          struct tls_connection *conn,
-                                          const u8 *key, size_t key_len)
-{
-       return -1;
-}
index ad834b6..09a1e73 100644 (file)
@@ -419,13 +419,6 @@ int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
 }
 
 
-int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn,
-                         int tls_ia)
-{
-       return -1;
-}
-
-
 int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn,
                            struct tls_keys *keys)
 {
@@ -649,28 +642,6 @@ unsigned int tls_capabilities(void *tls_ctx)
 }
 
 
-struct wpabuf * tls_connection_ia_send_phase_finished(
-       void *tls_ctx, struct tls_connection *conn, int final)
-{
-       return NULL;
-}
-
-
-int tls_connection_ia_final_phase_finished(void *tls_ctx,
-                                          struct tls_connection *conn)
-{
-       return -1;
-}
-
-
-int tls_connection_ia_permute_inner_secret(void *tls_ctx,
-                                          struct tls_connection *conn,
-                                          const u8 *key, size_t key_len)
-{
-       return -1;
-}
-
-
 int tls_connection_set_session_ticket_cb(void *tls_ctx,
                                         struct tls_connection *conn,
                                         tls_session_ticket_cb cb,
index c0a40f9..6380ce0 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * SSL/TLS interface functions for OpenSSL
- * Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
 #include <openssl/engine.h>
 #endif /* OPENSSL_NO_ENGINE */
 
+#ifdef ANDROID
+#include <openssl/pem.h>
+#include "keystore_get.h"
+#endif /* ANDROID */
+
 #include "common.h"
 #include "crypto.h"
 #include "tls.h"
@@ -54,6 +59,7 @@ struct tls_global {
        void (*event_cb)(void *ctx, enum tls_event ev,
                         union tls_event_data *data);
        void *cb_ctx;
+       int cert_in_cb;
 };
 
 static struct tls_global *tls_global = NULL;
@@ -81,6 +87,8 @@ struct tls_connection {
        unsigned int server_cert_only:1;
 
        u8 srv_cert_hash[32];
+
+       unsigned int flags;
 };
 
 
@@ -687,6 +695,7 @@ void * tls_init(const struct tls_config *conf)
                if (conf) {
                        tls_global->event_cb = conf->event_cb;
                        tls_global->cb_ctx = conf->cb_ctx;
+                       tls_global->cert_in_cb = conf->cert_in_cb;
                }
 
 #ifdef CONFIG_FIPS
@@ -711,7 +720,7 @@ void * tls_init(const struct tls_config *conf)
 #endif /* CONFIG_FIPS */
                SSL_load_error_strings();
                SSL_library_init();
-#ifndef OPENSSL_NO_SHA256
+#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256)
                EVP_add_digest(EVP_sha256());
 #endif /* OPENSSL_NO_SHA256 */
                /* TODO: if /dev/urandom is available, PRNG is seeded
@@ -1137,7 +1146,7 @@ static void openssl_tls_cert_event(struct tls_connection *conn,
                return;
 
        os_memset(&ev, 0, sizeof(ev));
-       if (conn->cert_probe) {
+       if (conn->cert_probe || tls_global->cert_in_cb) {
                cert = get_x509_cert(err_cert);
                ev.peer_cert.cert = cert;
        }
@@ -1178,13 +1187,22 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
        X509_NAME_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf));
 
        conn = SSL_get_app_data(ssl);
-       match = conn ? conn->subject_match : NULL;
-       altmatch = conn ? conn->altsubject_match : NULL;
+       if (conn == NULL)
+               return 0;
+       match = conn->subject_match;
+       altmatch = conn->altsubject_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);
 
@@ -1290,6 +1308,19 @@ static int tls_load_ca_der(void *_ssl_ctx, const char *ca_cert)
 #endif /* OPENSSL_NO_STDIO */
 
 
+#ifdef ANDROID
+static BIO * BIO_from_keystore(const char *key)
+{
+       BIO *bio = NULL;
+       char value[KEYSTORE_MESSAGE_SIZE];
+       int length = keystore_get(key, strlen(key), value);
+       if (length != -1 && (bio = BIO_new(BIO_s_mem())) != NULL)
+               BIO_write(bio, value, length);
+       return bio;
+}
+#endif /* ANDROID */
+
+
 static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn,
                                  const char *ca_cert, const u8 *ca_cert_blob,
                                  size_t ca_cert_blob_len, const char *ca_path)
@@ -1380,6 +1411,36 @@ static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn,
                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;
+               int 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) {
@@ -1550,26 +1611,42 @@ static int tls_connection_client_cert(struct tls_connection *conn,
        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;
-       } else {
-               tls_show_errors(MSG_DEBUG, __func__,
-                               "SSL_use_certificate_file (DER) failed");
        }
 
        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;
-       } else {
-               tls_show_errors(MSG_DEBUG, __func__,
-                               "SSL_use_certificate_file (PEM) failed");
        }
+
+       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 */
@@ -1900,10 +1977,6 @@ static int tls_connection_private_key(void *_ssl_ctx,
                                   "ASN1(EVP_PKEY_RSA) --> OK");
                        ok = 1;
                        break;
-               } else {
-                       tls_show_errors(MSG_DEBUG, __func__,
-                                       "SSL_use_PrivateKey_ASN1(EVP_PKEY_RSA)"
-                                       " failed");
                }
 
                if (SSL_use_PrivateKey_ASN1(EVP_PKEY_DSA, conn->ssl,
@@ -1913,10 +1986,6 @@ static int tls_connection_private_key(void *_ssl_ctx,
                                   "ASN1(EVP_PKEY_DSA) --> OK");
                        ok = 1;
                        break;
-               } else {
-                       tls_show_errors(MSG_DEBUG, __func__,
-                                       "SSL_use_PrivateKey_ASN1(EVP_PKEY_DSA)"
-                                       " failed");
                }
 
                if (SSL_use_RSAPrivateKey_ASN1(conn->ssl,
@@ -1926,9 +1995,6 @@ static int tls_connection_private_key(void *_ssl_ctx,
                                   "SSL_use_RSAPrivateKey_ASN1 --> OK");
                        ok = 1;
                        break;
-               } else {
-                       tls_show_errors(MSG_DEBUG, __func__,
-                                       "SSL_use_RSAPrivateKey_ASN1 failed");
                }
 
                if (tls_read_pkcs12_blob(ssl_ctx, conn->ssl, private_key_blob,
@@ -1942,6 +2008,26 @@ static int tls_connection_private_key(void *_ssl_ctx,
                break;
        }
 
+#ifdef ANDROID
+       if (!ok && private_key &&
+           os_strncmp("keystore://", private_key, 11) == 0) {
+               BIO *bio = BIO_from_keystore(&private_key[11]);
+               EVP_PKEY *pkey = NULL;
+               if (bio) {
+                       pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
+                       BIO_free(bio);
+               }
+               if (pkey) {
+                       if (SSL_use_PrivateKey(conn->ssl, pkey) == 1) {
+                               wpa_printf(MSG_DEBUG, "OpenSSL: Private key "
+                                          "from keystore");
+                               ok = 1;
+                       }
+                       EVP_PKEY_free(pkey);
+               }
+       }
+#endif /* ANDROID */
+
        while (!ok && private_key) {
 #ifndef OPENSSL_NO_STDIO
                if (SSL_use_PrivateKey_file(conn->ssl, private_key,
@@ -1950,10 +2036,6 @@ static int tls_connection_private_key(void *_ssl_ctx,
                                   "SSL_use_PrivateKey_File (DER) --> OK");
                        ok = 1;
                        break;
-               } else {
-                       tls_show_errors(MSG_DEBUG, __func__,
-                                       "SSL_use_PrivateKey_File (DER) "
-                                       "failed");
                }
 
                if (SSL_use_PrivateKey_file(conn->ssl, private_key,
@@ -1962,10 +2044,6 @@ static int tls_connection_private_key(void *_ssl_ctx,
                                   "SSL_use_PrivateKey_File (PEM) --> OK");
                        ok = 1;
                        break;
-               } else {
-                       tls_show_errors(MSG_DEBUG, __func__,
-                                       "SSL_use_PrivateKey_File (PEM) "
-                                       "failed");
                }
 #else /* OPENSSL_NO_STDIO */
                wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO",
@@ -1991,9 +2069,9 @@ static int tls_connection_private_key(void *_ssl_ctx,
        }
 
        if (!ok) {
-               wpa_printf(MSG_INFO, "OpenSSL: Failed to load private key");
+               tls_show_errors(MSG_INFO, __func__,
+                               "Failed to load private key");
                os_free(passwd);
-               ERR_clear_error();
                return -1;
        }
        ERR_clear_error();
@@ -2663,6 +2741,8 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
                return -1;
        }
 
+       conn->flags = params->flags;
+
        tls_get_errors(tls_ctx);
 
        return 0;
@@ -2731,35 +2811,6 @@ unsigned int tls_capabilities(void *tls_ctx)
 }
 
 
-int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn,
-                         int tls_ia)
-{
-       return -1;
-}
-
-
-struct wpabuf * tls_connection_ia_send_phase_finished(
-       void *tls_ctx, struct tls_connection *conn, int final)
-{
-       return NULL;
-}
-
-
-int tls_connection_ia_final_phase_finished(void *tls_ctx,
-                                          struct tls_connection *conn)
-{
-       return -1;
-}
-
-
-int tls_connection_ia_permute_inner_secret(void *tls_ctx,
-                                          struct tls_connection *conn,
-                                          const u8 *key, size_t key_len)
-{
-       return -1;
-}
-
-
 #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
index 4a94e99..a33d24e 100644 (file)
@@ -736,32 +736,3 @@ unsigned int tls_capabilities(void *tls_ctx)
 {
        return 0;
 }
-
-
-int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn,
-                         int tls_ia)
-{
-       return -1;
-}
-
-
-struct wpabuf * tls_connection_ia_send_phase_finished(
-       void *tls_ctx, struct tls_connection *conn, int final);
-{
-       return NULL;
-}
-
-
-int tls_connection_ia_final_phase_finished(void *tls_ctx,
-                                          struct tls_connection *conn)
-{
-       return -1;
-}
-
-
-int tls_connection_ia_permute_inner_secret(void *tls_ctx,
-                                          struct tls_connection *conn,
-                                          const u8 *key, size_t key_len)
-{
-       return -1;
-}
index fa49da4..c7b7363 100644 (file)
@@ -31,6 +31,9 @@
 #define HOSTAPD_CHAN_PASSIVE_SCAN 0x00000002
 #define HOSTAPD_CHAN_NO_IBSS 0x00000004
 #define HOSTAPD_CHAN_RADAR 0x00000008
+#define HOSTAPD_CHAN_HT40PLUS 0x00000010
+#define HOSTAPD_CHAN_HT40MINUS 0x00000020
+#define HOSTAPD_CHAN_HT40 0x00000040
 
 /**
  * struct hostapd_channel_data - Channel information
@@ -57,6 +60,8 @@ struct hostapd_channel_data {
        u8 max_tx_power;
 };
 
+#define HOSTAPD_MODE_FLAG_HT_INFO_KNOWN BIT(0)
+
 /**
  * struct hostapd_hw_modes - Supported hardware mode information
  */
@@ -100,6 +105,8 @@ struct hostapd_hw_modes {
         * a_mpdu_params - A-MPDU (IEEE 802.11n) parameters
         */
        u8 a_mpdu_params;
+
+       unsigned int flags; /* HOSTAPD_MODE_FLAG_* */
 };
 
 
@@ -192,7 +199,7 @@ struct wpa_interface_info {
        const char *drv_name;
 };
 
-#define WPAS_MAX_SCAN_SSIDS 4
+#define WPAS_MAX_SCAN_SSIDS 16
 
 /**
  * struct wpa_driver_scan_params - Scan parameters
@@ -261,6 +268,15 @@ struct wpa_driver_scan_params {
         * num_filter_ssids - Number of entries in filter_ssids array
         */
        size_t num_filter_ssids;
+
+       /**
+        * 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.
+        */
+       u8 p2p_probe;
 };
 
 /**
@@ -279,6 +295,19 @@ struct wpa_driver_auth_params {
        size_t wep_key_len[4];
        int wep_tx_keyidx;
        int local_state_change;
+
+       /**
+        * p2p - Whether this connection is a P2P group
+        */
+       int p2p;
+
+};
+
+enum wps_mode {
+       WPS_MODE_NONE /* no WPS provisioning being used */,
+       WPS_MODE_OPEN /* WPS provisioning with AP that is in open mode */,
+       WPS_MODE_PRIVACY /* WPS provisioning with AP that is using protection
+                         */
 };
 
 /**
@@ -335,6 +364,11 @@ struct wpa_driver_associate_params {
        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
         *
         * This is usually ignored if @wpa_ie is used.
@@ -460,6 +494,184 @@ struct wpa_driver_associate_params {
         * 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;
+};
+
+enum hide_ssid {
+       NO_SSID_HIDING,
+       HIDDEN_SSID_ZERO_LEN,
+       HIDDEN_SSID_ZERO_CONTENTS
+};
+
+struct wpa_driver_ap_params {
+       /**
+        * head - Beacon head from IEEE 802.11 header to IEs before TIM IE
+        */
+       const u8 *head;
+
+       /**
+        * head_len - Length of the head buffer in octets
+        */
+       size_t head_len;
+
+       /**
+        * tail - Beacon tail following TIM IE
+        */
+       const 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;
+
+       /**
+        * 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 association 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;
 };
 
 /**
@@ -490,7 +702,7 @@ struct wpa_driver_capa {
 #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
-#define WPA_DRIVER_FLAGS_USER_SPACE_MLME 0x00000004
+/* unused: 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
@@ -502,14 +714,60 @@ struct wpa_driver_capa {
 #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 takes care of P2P management operations */
+#define WPA_DRIVER_FLAGS_P2P_MGMT      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 Device, GO, or P2P Client */
+#define WPA_DRIVER_FLAGS_P2P_CAPABLE   0x00000800
+/* Driver supports concurrent operations on multiple channels */
+#define WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT      0x00001000
+/*
+ * Driver 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
        unsigned int flags;
 
        int max_scan_ssids;
+       int max_sched_scan_ssids;
+       int sched_scan_supported;
+       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;
 };
 
 
@@ -535,6 +793,8 @@ struct hostapd_sta_add_params {
        size_t supp_rates_len;
        u16 listen_interval;
        const struct ieee80211_ht_capabilities *ht_capabilities;
+       u32 flags; /* bitmask of WPA_STA_* flags */
+       int set; /* Set STA parameters instead of add */
 };
 
 struct hostapd_freq_params {
@@ -567,9 +827,26 @@ enum wpa_driver_if_type {
         * 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
 };
 
 struct wpa_init_params {
+       void *global_priv;
        const u8 *bssid;
        const char *ifname;
        const u8 *ssid;
@@ -595,12 +872,46 @@ struct wpa_bss_params {
        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)
+
+/**
+ * struct p2p_params - P2P parameters for driver-based P2P management
+ */
+struct p2p_params {
+       const char *dev_name;
+       u8 pri_dev_type[8];
+#define DRV_MAX_SEC_DEV_TYPES 5
+       u8 sec_dev_type[DRV_MAX_SEC_DEV_TYPES][8];
+       size_t num_sec_dev_types;
+};
+
+enum tdls_oper {
+       TDLS_DISCOVERY_REQ,
+       TDLS_SETUP,
+       TDLS_TEARDOWN,
+       TDLS_ENABLE_LINK,
+       TDLS_DISABLE_LINK,
+       TDLS_ENABLE,
+       TDLS_DISABLE
+};
+
+/**
+ * struct wpa_signal_info - Information about channel signal quality
+ */
+struct wpa_signal_info {
+       u32 frequency;
+       int above_threshold;
+       int current_signal;
+       int current_noise;
+       int current_txrate;
+};
 
 /**
  * struct wpa_driver_ops - Driver interface API definition
@@ -652,8 +963,12 @@ struct wpa_driver_ops {
         * @alg: encryption algorithm (%WPA_ALG_NONE, %WPA_ALG_WEP,
         *      %WPA_ALG_TKIP, %WPA_ALG_CCMP, %WPA_ALG_IGTK, %WPA_ALG_PMK);
         *      %WPA_ALG_NONE clears the key.
-        * @addr: address of the peer STA or ff:ff:ff:ff:ff:ff for
-        *      broadcast/default keys
+        * @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
@@ -661,7 +976,7 @@ struct wpa_driver_ops {
         * @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)
+        *      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: 6 octets, IGTK: 6 octets
         * @key: key buffer; TKIP: 16-byte temporal key, 8-byte Tx Mic key,
@@ -684,7 +999,7 @@ struct wpa_driver_ops {
         * 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 tricker Michael MIC errors. This can be fixed by changing the
+        * 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.
@@ -951,93 +1266,21 @@ struct wpa_driver_ops {
         * 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.
-        *
-        * This function is only needed for drivers that export MLME
-        * (management frame processing) to %wpa_supplicant or hostapd.
         */
        struct hostapd_hw_modes * (*get_hw_feature_data)(void *priv,
                                                         u16 *num_modes,
                                                         u16 *flags);
 
        /**
-        * set_channel - Set channel
-        * @priv: Private driver interface data
-        * @phymode: HOSTAPD_MODE_IEEE80211B, ..
-        * @chan: IEEE 802.11 channel number
-        * @freq: Frequency of the channel in MHz
-        * Returns: 0 on success, -1 on failure
-        *
-        * This function is only needed for drivers that export MLME
-        * (management frame processing) to wpa_supplicant.
-        */
-       int (*set_channel)(void *priv, enum hostapd_hw_mode phymode, int chan,
-                          int freq);
-
-       /**
-        * set_ssid - Set SSID
-        * @priv: Private driver interface data
-        * @ssid: SSID
-        * @ssid_len: SSID length
-        * Returns: 0 on success, -1 on failure
-        *
-        * This function is only needed for drivers that export MLME
-        * (management frame processing) to wpa_supplicant.
-        */
-       int (*set_ssid)(void *priv, const u8 *ssid, size_t ssid_len);
-
-       /**
-        * set_bssid - Set BSSID
-        * @priv: Private driver interface data
-        * @bssid: BSSID
-        * Returns: 0 on success, -1 on failure
-        *
-        * This function is only needed for drivers that export MLME
-        * (management frame processing) to wpa_supplicant.
-        */
-       int (*set_bssid)(void *priv, const u8 *bssid);
-
-       /**
         * 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
         * Returns: 0 on success, -1 on failure
-        *
-        * This function is only needed for drivers that export MLME
-        * (management frame processing) to wpa_supplicant.
         */
        int (*send_mlme)(void *priv, const u8 *data, size_t data_len);
 
        /**
-        * mlme_add_sta - Add a STA entry into the driver/netstack
-        * @priv: Private driver interface data
-        * @addr: MAC address of the STA (e.g., BSSID of the AP)
-        * @supp_rates: Supported rate set (from (Re)AssocResp); in IEEE 802.11
-        * format (one octet per rate, 1 = 0.5 Mbps)
-        * @supp_rates_len: Number of entries in supp_rates
-        * Returns: 0 on success, -1 on failure
-        *
-        * This function is only needed for drivers that export MLME
-        * (management frame processing) to wpa_supplicant. When the MLME code
-        * completes association with an AP, this function is called to
-        * configure the driver/netstack with a STA entry for data frame
-        * processing (TX rate control, encryption/decryption).
-        */
-       int (*mlme_add_sta)(void *priv, const u8 *addr, const u8 *supp_rates,
-                           size_t supp_rates_len);
-
-       /**
-        * mlme_remove_sta - Remove a STA entry from the driver/netstack
-        * @priv: Private driver interface data
-        * @addr: MAC address of the STA (e.g., BSSID of the AP)
-        * Returns: 0 on success, -1 on failure
-        *
-        * This function is only needed for drivers that export MLME
-        * (management frame processing) to wpa_supplicant.
-        */
-       int (*mlme_remove_sta)(void *priv, const u8 *addr);
-
-       /**
         * update_ft_ies - Update FT (IEEE 802.11r) IEs
         * @priv: Private driver interface data
         * @md: Mobility domain (2 octets) (also included inside ies)
@@ -1164,24 +1407,25 @@ struct wpa_driver_ops {
                            struct wpa_driver_auth_params *params);
 
        /**
-        * set_beacon - Set Beacon frame template
+        * set_ap - Set Beacon and Probe Response information for AP mode
         * @priv: Private driver interface data
-        * @head: Beacon head from IEEE 802.11 header to IEs before TIM IE
-        * @head_len: Length of the head buffer in octets
-        * @tail: Beacon tail following TIM IE
-        * @tail_len: Length of the tail buffer in octets
-        * @dtim_period: DTIM period
-        * @beacon_int: Beacon interval
-        * Returns: 0 on success, -1 on failure
+        * @params: Parameters to use in AP mode
         *
-        * This function is used to configure Beacon template for the driver in
+        * 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.
+        * 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_beacon)(void *priv, const u8 *head, size_t head_len,
-                         const u8 *tail, size_t tail_len, int dtim_period,
-                         int beacon_int);
+       int (*set_ap)(void *priv, struct wpa_driver_ap_params *params);
 
        /**
         * hapd_init - Initialize driver interface (hostapd only)
@@ -1190,7 +1434,7 @@ struct wpa_driver_ops {
         * Returns: Pointer to private data, %NULL on failure
         *
         * This function is used instead of init() or init2() when the driver
-        * wrapper is used withh hostapd.
+        * wrapper is used with hostapd.
         */
        void * (*hapd_init)(struct hostapd_data *hapd,
                            struct wpa_init_params *params);
@@ -1210,8 +1454,10 @@ struct wpa_driver_ops {
         * 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_beacon() to set WPA/RSN IE
+        * 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);
 
@@ -1223,7 +1469,9 @@ struct wpa_driver_ops {
         *
         * 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_beacon().
+        * %NULL) if the driver uses the Beacon template from set_ap().
+        *
+        * DEPRECATED - use set_ap() instead
         */
        int (*set_privacy)(void *priv, int enabled);
 
@@ -1265,7 +1513,9 @@ struct wpa_driver_ops {
         * 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_beacon().
+        * set_ap().
+        *
+        * DEPRECATED - use set_ap() instead
         */
        int (*set_generic_elem)(void *priv, const u8 *elem, size_t elem_len);
 
@@ -1287,12 +1537,13 @@ struct wpa_driver_ops {
         * @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);
+                              const u8 *own_addr, u32 flags);
 
        /**
         * sta_deauth - Deauthenticate a station (AP only)
@@ -1338,8 +1589,7 @@ struct wpa_driver_ops {
         * Returns: Length of the SSID on success, -1 on failure
         *
         * This function need not be implemented if the driver uses Beacon
-        * template from set_beacon() and does not reply to Probe Request
-        * frames.
+        * template from set_ap() and does not reply to Probe Request frames.
         */
        int (*hapd_get_ssid)(void *priv, u8 *buf, int len);
 
@@ -1349,6 +1599,8 @@ struct wpa_driver_ops {
         * @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);
 
@@ -1372,6 +1624,9 @@ struct wpa_driver_ops {
         * 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);
 
@@ -1439,33 +1694,9 @@ struct wpa_driver_ops {
                             int mode);
 
        /**
-        * set_cts_protect - Set CTS protection mode (AP only)
-        * @priv: Private driver interface data
-        * @value: Whether CTS protection is enabled
-        * Returns: 0 on success, -1 on failure
-        */
-       int (*set_cts_protect)(void *priv, int value);
-
-       /**
-        * set_preamble - Set preamble mode (AP only)
-        * @priv: Private driver interface data
-        * @value: Whether short preamble is enabled
-        * Returns: 0 on success, -1 on failure
-        */
-       int (*set_preamble)(void *priv, int value);
-
-       /**
-        * set_short_slot_time - Set short slot time (AP only)
-        * @priv: Private driver interface data
-        * @value: Whether short slot time is enabled
-        * Returns: 0 on success, -1 on failure
-        */
-       int (*set_short_slot_time)(void *priv, int value);
-
-       /**
         * set_tx_queue_params - Set TX queue parameters
         * @priv: Private driver interface data
-        * @queue: Queue number
+        * @queue: Queue number (0 = VO, 1 = VI, 2 = BE, 3 = BK)
         * @aifs: AIFS
         * @cw_min: cwMin
         * @cw_max: cwMax
@@ -1475,17 +1706,6 @@ struct wpa_driver_ops {
                                   int cw_max, int burst_time);
 
        /**
-        * valid_bss_mask - Validate BSSID mask
-        * @priv: Private driver interface data
-        * @addr: Address
-        * @mask: Mask
-        * Returns: 0 if mask is valid, -1 if mask is not valid, 1 if mask can
-        * be used, but the main interface address must be the first address in
-        * the block if mask is applied
-        */
-       int (*valid_bss_mask)(void *priv, const u8 *addr, const u8 *mask);
-
-       /**
         * if_add - Add a virtual interface
         * @priv: Private driver interface data
         * @type: Interface type
@@ -1500,11 +1720,13 @@ struct wpa_driver_ops {
         * @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
         * 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);
+                     void **drv_priv, char *force_ifname, u8 *if_addr,
+                     const char *bridge);
 
        /**
         * if_remove - Remove a virtual interface
@@ -1578,33 +1800,36 @@ struct wpa_driver_ops {
        int (*set_radius_acl_expire)(void *priv, const u8 *mac);
 
        /**
-        * set_ht_params - Set HT parameters (AP only)
-        * @priv: Private driver interface data
-        * @ht_capab: HT Capabilities IE
-        * @ht_capab_len: Length of ht_capab in octets
-        * @ht_oper: HT Operation IE
-        * @ht_oper_len: Length of ht_oper in octets
-        * Returns: 0 on success, -1 on failure
-        */
-       int (*set_ht_params)(void *priv,
-                            const u8 *ht_capab, size_t ht_capab_len,
-                            const u8 *ht_oper, size_t ht_oper_len);
-
-       /**
         * 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_beacon()
-        * and does not process Probe Request frames.
+        * 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 *proberesp,
+                            const struct wpabuf *assocresp);
 
        /**
         * set_supp_port - Set IEEE 802.1X Supplicant Port status
@@ -1620,31 +1845,52 @@ struct wpa_driver_ops {
         * @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
         * Returns: 0 on success, -1 on failure
         */
-       int (*set_wds_sta)(void *priv, const u8 *addr, int aid, int val);
+       int (*set_wds_sta)(void *priv, const u8 *addr, int aid, int val,
+                          const char *bridge_ifname);
 
        /**
         * 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 a remain-on-channel duration
-        * is in progress, the frame is transmitted on that channel. Otherwise,
-        * the frame is transmitted on the current operational channel if in
-        * associated state in station mode or if operating as an AP. If none
-        * of these conditions is in effect, send_action() cannot be used.
+        * 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,
+       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);
+                          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
@@ -1701,28 +1947,25 @@ struct wpa_driver_ops {
        int (*probe_req_report)(void *priv, int report);
 
        /**
-        * disable_11b_rates - Set whether IEEE 802.11b rates are used for TX
+        * deinit_ap - Deinitialize AP mode
         * @priv: Private driver interface data
-        * @disabled: Whether IEEE 802.11b rates are disabled
         * Returns: 0 on success, -1 on failure (or if not supported)
         *
-        * This command is used to disable IEEE 802.11b rates (1, 2, 5.5, and
-        * 11 Mbps) as TX rates for data and management frames. This can be
-        * used to optimize channel use when there is no need to support IEEE
-        * 802.11b-only devices.
+        * This optional function can be used to disable AP mode related
+        * configuration and change the driver mode to station mode to allow
+        * normal station operations like scanning to be completed.
         */
-       int (*disable_11b_rates)(void *priv, int disabled);
+       int (*deinit_ap)(void *priv);
 
        /**
-        * deinit_ap - Deinitialize AP mode
+        * 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 AP mode related
-        * configuration and change the driver mode to station mode to allow
-        * normal station operations like scanning to be completed.
+        * This optional function can be used to disable P2P client mode. It
+        * can be used to change the interface type back to station mode.
         */
-       int (*deinit_ap)(void *priv);
+       int (*deinit_p2p_cli)(void *priv);
 
        /**
         * suspend - Notification on system suspend/hibernate event
@@ -1765,46 +2008,500 @@ struct wpa_driver_ops {
         */
        int (*send_frame)(void *priv, const u8 *data, size_t data_len,
                          int encrypt);
-};
-
 
-/**
- * enum wpa_event_type - Event type for wpa_supplicant_event() calls
- */
-enum wpa_event_type {
        /**
-        * EVENT_ASSOC - Association completed
+        * 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 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.
+        * 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.
         */
-       EVENT_ASSOC,
+       int (*shared_freq)(void *priv);
 
        /**
-        * EVENT_DISASSOC - Association lost
+        * 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 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.
+        * This function is used to fetch the current Notice of Absence
+        * attribute value from GO.
         */
-       EVENT_DISASSOC,
+       int (*get_noa)(void *priv, u8 *buf, size_t buf_len);
 
        /**
-        * EVENT_MICHAEL_MIC_FAILURE - Michael MIC (TKIP) detected
+        * 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 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
+        * 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);
+
+       /**
+        * p2p_find - Start P2P Device Discovery
+        * @priv: Private driver interface data
+        * @timeout: Timeout for find operation in seconds or 0 for no timeout
+        * @type: Device Discovery type (enum p2p_discovery_type)
+        * Returns: 0 on success, -1 on failure
+        *
+        * This function is only used if the driver implements P2P management,
+        * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+        * struct wpa_driver_capa.
+        */
+       int (*p2p_find)(void *priv, unsigned int timeout, int type);
+
+       /**
+        * p2p_stop_find - Stop P2P Device Discovery
+        * @priv: Private driver interface data
+        * Returns: 0 on success, -1 on failure
+        *
+        * This function is only used if the driver implements P2P management,
+        * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+        * struct wpa_driver_capa.
+        */
+       int (*p2p_stop_find)(void *priv);
+
+       /**
+        * p2p_listen - Start P2P Listen state for specified duration
+        * @priv: Private driver interface data
+        * @timeout: Listen state duration in milliseconds
+        * Returns: 0 on success, -1 on failure
+        *
+        * This function can be used to request the P2P module to keep the
+        * device discoverable on the listen channel for an extended set of
+        * time. At least in its current form, this is mainly used for testing
+        * purposes and may not be of much use for normal P2P operations.
+        *
+        * This function is only used if the driver implements P2P management,
+        * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+        * struct wpa_driver_capa.
+        */
+       int (*p2p_listen)(void *priv, unsigned int timeout);
+
+       /**
+        * p2p_connect - Start P2P group formation (GO negotiation)
+        * @priv: Private driver interface data
+        * @peer_addr: MAC address of the peer P2P client
+        * @wps_method: enum p2p_wps_method value indicating config method
+        * @go_intent: Local GO intent value (1..15)
+        * @own_interface_addr: Intended interface address to use with the
+        *      group
+        * @force_freq: The only allowed channel frequency in MHz or 0
+        * @persistent_group: Whether to create persistent group
+        * Returns: 0 on success, -1 on failure
+        *
+        * This function is only used if the driver implements P2P management,
+        * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+        * struct wpa_driver_capa.
+        */
+       int (*p2p_connect)(void *priv, const u8 *peer_addr, int wps_method,
+                          int go_intent, const u8 *own_interface_addr,
+                          unsigned int force_freq, int persistent_group);
+
+       /**
+        * wps_success_cb - Report successfully completed WPS provisioning
+        * @priv: Private driver interface data
+        * @peer_addr: Peer address
+        * Returns: 0 on success, -1 on failure
+        *
+        * This function is used to report successfully completed WPS
+        * provisioning during group formation in both GO/Registrar and
+        * client/Enrollee roles.
+        *
+        * This function is only used if the driver implements P2P management,
+        * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+        * struct wpa_driver_capa.
+        */
+       int (*wps_success_cb)(void *priv, const u8 *peer_addr);
+
+       /**
+        * p2p_group_formation_failed - Report failed WPS provisioning
+        * @priv: Private driver interface data
+        * Returns: 0 on success, -1 on failure
+        *
+        * This function is used to report failed group formation. This can
+        * happen either due to failed WPS provisioning or due to 15 second
+        * timeout during the provisioning phase.
+        *
+        * This function is only used if the driver implements P2P management,
+        * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+        * struct wpa_driver_capa.
+        */
+       int (*p2p_group_formation_failed)(void *priv);
+
+       /**
+        * p2p_set_params - Set P2P parameters
+        * @priv: Private driver interface data
+        * @params: P2P parameters
+        * Returns: 0 on success, -1 on failure
+        *
+        * This function is only used if the driver implements P2P management,
+        * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+        * struct wpa_driver_capa.
+        */
+       int (*p2p_set_params)(void *priv, const struct p2p_params *params);
+
+       /**
+        * p2p_prov_disc_req - Send Provision Discovery Request
+        * @priv: Private driver interface data
+        * @peer_addr: MAC address of the peer P2P client
+        * @config_methods: WPS Config Methods value (only one bit set)
+        * Returns: 0 on success, -1 on failure
+        *
+        * This function can be used to request a discovered P2P peer to
+        * display a PIN (config_methods = WPS_CONFIG_DISPLAY) or be prepared
+        * to enter a PIN from us (config_methods = WPS_CONFIG_KEYPAD). The
+        * Provision Discovery Request frame is transmitted once immediately
+        * and if no response is received, the frame will be sent again
+        * whenever the target device is discovered during device dsicovery
+        * (start with a p2p_find() call). Response from the peer is indicated
+        * with the EVENT_P2P_PROV_DISC_RESPONSE event.
+        *
+        * This function is only used if the driver implements P2P management,
+        * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+        * struct wpa_driver_capa.
+        */
+       int (*p2p_prov_disc_req)(void *priv, const u8 *peer_addr,
+                                u16 config_methods, int join);
+
+       /**
+        * p2p_sd_request - Schedule a service discovery query
+        * @priv: Private driver interface data
+        * @dst: Destination peer or %NULL to apply for all peers
+        * @tlvs: P2P Service Query TLV(s)
+        * Returns: Reference to the query or 0 on failure
+        *
+        * Response to the query is indicated with the
+        * EVENT_P2P_SD_RESPONSE driver event.
+        *
+        * This function is only used if the driver implements P2P management,
+        * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+        * struct wpa_driver_capa.
+        */
+       u64 (*p2p_sd_request)(void *priv, const u8 *dst,
+                             const struct wpabuf *tlvs);
+
+       /**
+        * p2p_sd_cancel_request - Cancel a pending service discovery query
+        * @priv: Private driver interface data
+        * @req: Query reference from p2p_sd_request()
+        * Returns: 0 on success, -1 on failure
+        *
+        * This function is only used if the driver implements P2P management,
+        * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+        * struct wpa_driver_capa.
+        */
+       int (*p2p_sd_cancel_request)(void *priv, u64 req);
+
+       /**
+        * p2p_sd_response - Send response to a service discovery query
+        * @priv: Private driver interface data
+        * @freq: Frequency from EVENT_P2P_SD_REQUEST event
+        * @dst: Destination address from EVENT_P2P_SD_REQUEST event
+        * @dialog_token: Dialog token from EVENT_P2P_SD_REQUEST event
+        * @resp_tlvs: P2P Service Response TLV(s)
+        * Returns: 0 on success, -1 on failure
+        *
+        * This function is called as a response to the request indicated with
+        * the EVENT_P2P_SD_REQUEST driver event.
+        *
+        * This function is only used if the driver implements P2P management,
+        * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+        * struct wpa_driver_capa.
+        */
+       int (*p2p_sd_response)(void *priv, int freq, const u8 *dst,
+                              u8 dialog_token,
+                              const struct wpabuf *resp_tlvs);
+
+       /**
+        * p2p_service_update - Indicate a change in local services
+        * @priv: Private driver interface data
+        * Returns: 0 on success, -1 on failure
+        *
+        * This function needs to be called whenever there is a change in
+        * availability of the local services. This will increment the
+        * Service Update Indicator value which will be used in SD Request and
+        * Response frames.
+        *
+        * This function is only used if the driver implements P2P management,
+        * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+        * struct wpa_driver_capa.
+        */
+       int (*p2p_service_update)(void *priv);
+
+       /**
+        * p2p_reject - Reject peer device (explicitly block connections)
+        * @priv: Private driver interface data
+        * @addr: MAC address of the peer
+        * Returns: 0 on success, -1 on failure
+        */
+       int (*p2p_reject)(void *priv, const u8 *addr);
+
+       /**
+        * p2p_invite - Invite a P2P Device into a group
+        * @priv: Private driver interface data
+        * @peer: Device Address of the peer P2P Device
+        * @role: Local role in the group
+        * @bssid: Group BSSID or %NULL if not known
+        * @ssid: Group SSID
+        * @ssid_len: Length of ssid in octets
+        * @go_dev_addr: Forced GO Device Address or %NULL if none
+        * @persistent_group: Whether this is to reinvoke a persistent group
+        * Returns: 0 on success, -1 on failure
+        */
+       int (*p2p_invite)(void *priv, const u8 *peer, int role,
+                         const u8 *bssid, const u8 *ssid, size_t ssid_len,
+                         const u8 *go_dev_addr, int persistent_group);
+
+       /**
+        * send_tdls_mgmt - for sending TDLS management packets
+        * @priv: private driver interface data
+        * @dst: Destination (peer) MAC address
+        * @action_code: TDLS action code for the mssage
+        * @dialog_token: Dialog Token to use in the message (if needed)
+        * @status_code: Status Code or Reason Code to use (if needed)
+        * @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,
+                             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);
+
+       /**
+        * 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);
+
+       /**
+        * set_rekey_info - Set rekey information
+        * @priv: Private driver interface data
+        * @kek: Current KEK
+        * @kck: Current KCK
+        * @replay_ctr: Current EAPOL-Key Replay Counter
+        *
+        * This optional function can be used to provide information for the
+        * driver/firmware to process EAPOL-Key frames in Group Key Handshake
+        * while the host (including wpa_supplicant) is sleeping.
+        */
+       void (*set_rekey_info)(void *priv, const u8 *kek, const u8 *kck,
+                              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);
+};
+
+
+/**
+ * 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,
@@ -1887,6 +2584,13 @@ enum wpa_event_type {
        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
@@ -1930,7 +2634,7 @@ enum wpa_event_type {
         * EVENT_ASSOC_REJECT - Association rejected
         *
         * This event should be called when (re)association attempt has been
-        * rejected by the AP. Information about authentication result is
+        * rejected by the AP. Information about the association response is
         * included in union wpa_event_data::assoc_reject.
         */
        EVENT_ASSOC_REJECT,
@@ -2046,7 +2750,144 @@ enum wpa_event_type {
         * observed in frames received from the current AP if signal strength
         * monitoring has been enabled with signal_monitor().
         */
-       EVENT_SIGNAL_CHANGE
+       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.
+        */
+       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_P2P_DEV_FOUND - Report a discovered P2P device
+        *
+        * This event is used only if the driver implements P2P management
+        * internally. Event data is stored in
+        * union wpa_event_data::p2p_dev_found.
+        */
+       EVENT_P2P_DEV_FOUND,
+
+       /**
+        * EVENT_P2P_GO_NEG_REQ_RX - Report reception of GO Negotiation Request
+        *
+        * This event is used only if the driver implements P2P management
+        * internally. Event data is stored in
+        * union wpa_event_data::p2p_go_neg_req_rx.
+        */
+       EVENT_P2P_GO_NEG_REQ_RX,
+
+       /**
+        * EVENT_P2P_GO_NEG_COMPLETED - Report completion of GO Negotiation
+        *
+        * This event is used only if the driver implements P2P management
+        * internally. Event data is stored in
+        * union wpa_event_data::p2p_go_neg_completed.
+        */
+       EVENT_P2P_GO_NEG_COMPLETED,
+
+       EVENT_P2P_PROV_DISC_REQUEST,
+       EVENT_P2P_PROV_DISC_RESPONSE,
+       EVENT_P2P_SD_REQUEST,
+       EVENT_P2P_SD_RESPONSE,
+
+       /**
+        * EVENT_IBSS_PEER_LOST - IBSS peer not reachable anymore
+        */
+       EVENT_IBSS_PEER_LOST,
+
+       /**
+        * 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
 };
 
 
@@ -2064,6 +2905,11 @@ union wpa_event_data {
         */
        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
@@ -2146,6 +2992,16 @@ union wpa_event_data {
                 *      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;
        } disassoc_info;
 
        /**
@@ -2162,6 +3018,16 @@ union wpa_event_data {
                 *      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;
        } deauth_info;
 
        /**
@@ -2202,6 +3068,18 @@ union wpa_event_data {
        } stkstart;
 
        /**
+        * struct tdls - Data for EVENT_TDLS
+        */
+       struct tdls {
+               u8 peer[ETH_ALEN];
+               enum {
+                       TDLS_REQUEST_SETUP,
+                       TDLS_REQUEST_TEARDOWN
+               } oper;
+               u16 reason_code; /* for teardown */
+       } tdls;
+
+       /**
         * struct ft_ies - FT information elements (EVENT_FT_RESPONSE)
         *
         * During FT (IEEE 802.11r) authentication sequence, the driver is
@@ -2233,7 +3111,9 @@ union wpa_event_data {
         */
        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;
@@ -2244,6 +3124,11 @@ union wpa_event_data {
         */
        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
@@ -2254,7 +3139,7 @@ union wpa_event_data {
                 * This should start with the first IE (fixed fields before IEs
                 * are not included).
                 */
-               u8 *resp_ies;
+               const u8 *resp_ies;
 
                /**
                 * resp_ies_len - Length of resp_ies in bytes
@@ -2296,8 +3181,9 @@ union wpa_event_data {
         * struct rx_from_unknown - Data for EVENT_RX_FROM_UNKNOWN events
         */
        struct rx_from_unknown {
-               const u8 *frame;
-               size_t len;
+               const u8 *bssid;
+               const u8 *addr;
+               int wds;
        } rx_from_unknown;
 
        /**
@@ -2405,6 +3291,18 @@ union wpa_event_data {
                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;
@@ -2432,11 +3330,126 @@ union wpa_event_data {
        } eapol_rx;
 
        /**
-        * struct signal_change - Data for EVENT_SIGNAL_CHANGE events
+        * 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 signal_change {
-               int above_threshold;
-       } signal_change;
+       struct low_ack {
+               u8 addr[ETH_ALEN];
+       } low_ack;
+
+       /**
+        * struct p2p_dev_found - Data for EVENT_P2P_DEV_FOUND
+        */
+       struct p2p_dev_found {
+               const u8 *addr;
+               const u8 *dev_addr;
+               const u8 *pri_dev_type;
+               const char *dev_name;
+               u16 config_methods;
+               u8 dev_capab;
+               u8 group_capab;
+       } p2p_dev_found;
+
+       /**
+        * struct p2p_go_neg_req_rx - Data for EVENT_P2P_GO_NEG_REQ_RX
+        */
+       struct p2p_go_neg_req_rx {
+               const u8 *src;
+               u16 dev_passwd_id;
+       } p2p_go_neg_req_rx;
+
+       /**
+        * struct p2p_go_neg_completed - Data for EVENT_P2P_GO_NEG_COMPLETED
+        */
+       struct p2p_go_neg_completed {
+               struct p2p_go_neg_results *res;
+       } p2p_go_neg_completed;
+
+       struct p2p_prov_disc_req {
+               const u8 *peer;
+               u16 config_methods;
+               const u8 *dev_addr;
+               const u8 *pri_dev_type;
+               const char *dev_name;
+               u16 supp_config_methods;
+               u8 dev_capab;
+               u8 group_capab;
+       } p2p_prov_disc_req;
+
+       struct p2p_prov_disc_resp {
+               const u8 *peer;
+               u16 config_methods;
+       } p2p_prov_disc_resp;
+
+       struct p2p_sd_req {
+               int freq;
+               const u8 *sa;
+               u8 dialog_token;
+               u16 update_indic;
+               const u8 *tlvs;
+               size_t tlvs_len;
+       } p2p_sd_req;
+
+       struct p2p_sd_resp {
+               const u8 *sa;
+               u16 update_indic;
+               const u8 *tlvs;
+               size_t tlvs_len;
+       } p2p_sd_resp;
+
+       /**
+        * struct ibss_peer_lost - Data for EVENT_IBSS_PEER_LOST
+        */
+       struct ibss_peer_lost {
+               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;
 };
 
 /**
@@ -2459,10 +3472,11 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
  */
 
 static inline void drv_event_assoc(void *ctx, const u8 *addr, const u8 *ie,
-                                  size_t ielen)
+                                  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;
@@ -2488,4 +3502,10 @@ static inline void drv_event_eapol_rx(void *ctx, const u8 *src, const u8 *data,
        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);
+
 #endif /* DRIVER_H */
index 5c25f00..788171d 100644 (file)
@@ -34,7 +34,7 @@
  */
 #define ATH_WPS_IE
 
-#include "os/linux/include/ieee80211_external.h"
+#include "ieee80211_external.h"
 
 
 #ifdef CONFIG_WPS
@@ -56,7 +56,7 @@
 #include "linux_ioctl.h"
 
 
-struct madwifi_driver_data {
+struct atheros_driver_data {
        struct hostapd_data *hapd;              /* back pointer */
 
        char    iface[IFNAMSIZ + 1];
@@ -70,11 +70,14 @@ struct madwifi_driver_data {
        struct hostap_sta_driver_data acct_data;
 
        struct l2_packet_data *sock_raw; /* raw 802.11 management frames */
+       struct wpabuf *wpa_ie;
+       struct wpabuf *wps_beacon_ie;
+       struct wpabuf *wps_probe_resp_ie;
 };
 
-static int madwifi_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
+static int atheros_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
                              int reason_code);
-static int madwifi_set_privacy(void *priv, int enabled);
+static int atheros_set_privacy(void *priv, int enabled);
 
 static const char * athr_get_ioctl_name(int op)
 {
@@ -125,16 +128,8 @@ static const char * athr_get_ioctl_name(int op)
                return "FILTERFRAME";
        case IEEE80211_IOCTL_SET_RTPARAMS:
                return "SET_RTPARAMS";
-       case IEEE80211_IOCTL_SENDADDBA:
-               return "SENDADDBA";
-       case IEEE80211_IOCTL_GETADDBASTATUS:
-               return "GETADDBASTATUS";
-       case IEEE80211_IOCTL_SENDDELBA:
-               return "SENDDELBA";
        case IEEE80211_IOCTL_SET_MEDENYENTRY:
                return "SET_MEDENYENTRY";
-       case IEEE80211_IOCTL_SET_ADDBARESP:
-               return "SET_ADDBARESP";
        case IEEE80211_IOCTL_GET_MACADDR:
                return "GET_MACADDR";
        case IEEE80211_IOCTL_SET_HBRPARAMS:
@@ -179,7 +174,7 @@ static const char * athr_get_param_name(int op)
 
 
 static int
-set80211priv(struct madwifi_driver_data *drv, int op, void *data, int len)
+set80211priv(struct atheros_driver_data *drv, int op, void *data, int len)
 {
        struct iwreq iwr;
        int do_inline = len < IFNAMSIZ;
@@ -218,7 +213,7 @@ set80211priv(struct madwifi_driver_data *drv, int op, void *data, int len)
 }
 
 static int
-set80211param(struct madwifi_driver_data *drv, int op, int arg)
+set80211param(struct atheros_driver_data *drv, int op, int arg)
 {
        struct iwreq iwr;
 
@@ -255,7 +250,7 @@ ether_sprintf(const u8 *addr)
  * Configure WPA parameters.
  */
 static int
-madwifi_configure_wpa(struct madwifi_driver_data *drv,
+atheros_configure_wpa(struct atheros_driver_data *drv,
                      struct wpa_bss_params *params)
 {
        int v;
@@ -320,6 +315,14 @@ madwifi_configure_wpa(struct madwifi_driver_data *drv,
        v = 0;
        if (params->rsn_preauth)
                v |= BIT(0);
+#ifdef CONFIG_IEEE80211W
+       if (params->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+               v |= BIT(7);
+               if (params->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED)
+                       v |= BIT(6);
+       }
+#endif /* CONFIG_IEEE80211W */
+
        wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x",
                   __func__, params->rsn_preauth);
        if (set80211param(drv, IEEE80211_PARAM_RSNCAPS, v)) {
@@ -336,9 +339,9 @@ madwifi_configure_wpa(struct madwifi_driver_data *drv,
 }
 
 static int
-madwifi_set_ieee8021x(void *priv, struct wpa_bss_params *params)
+atheros_set_ieee8021x(void *priv, struct wpa_bss_params *params)
 {
-       struct madwifi_driver_data *drv = priv;
+       struct atheros_driver_data *drv = priv;
 
        wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, params->enabled);
 
@@ -348,14 +351,14 @@ madwifi_set_ieee8021x(void *priv, struct wpa_bss_params *params)
                                  IEEE80211_AUTH_AUTO) < 0)
                        return -1;
                /* IEEE80211_AUTH_AUTO ends up enabling Privacy; clear that */
-               return madwifi_set_privacy(drv, 0);
+               return atheros_set_privacy(drv, 0);
        }
        if (!params->wpa && !params->ieee802_1x) {
                hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER,
                        HOSTAPD_LEVEL_WARNING, "No 802.1X or WPA enabled!");
                return -1;
        }
-       if (params->wpa && madwifi_configure_wpa(drv, params) != 0) {
+       if (params->wpa && atheros_configure_wpa(drv, params) != 0) {
                hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER,
                        HOSTAPD_LEVEL_WARNING, "Error configuring WPA state!");
                return -1;
@@ -371,9 +374,9 @@ madwifi_set_ieee8021x(void *priv, struct wpa_bss_params *params)
 }
 
 static int
-madwifi_set_privacy(void *priv, int enabled)
+atheros_set_privacy(void *priv, int enabled)
 {
-       struct madwifi_driver_data *drv = priv;
+       struct atheros_driver_data *drv = priv;
 
        wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
 
@@ -381,9 +384,9 @@ madwifi_set_privacy(void *priv, int enabled)
 }
 
 static int
-madwifi_set_sta_authorized(void *priv, const u8 *addr, int authorized)
+atheros_set_sta_authorized(void *priv, const u8 *addr, int authorized)
 {
-       struct madwifi_driver_data *drv = priv;
+       struct atheros_driver_data *drv = priv;
        struct ieee80211req_mlme mlme;
        int ret;
 
@@ -406,21 +409,21 @@ madwifi_set_sta_authorized(void *priv, const u8 *addr, int authorized)
 }
 
 static int
-madwifi_sta_set_flags(void *priv, const u8 *addr,
+atheros_sta_set_flags(void *priv, const u8 *addr,
                      int total_flags, int flags_or, int flags_and)
 {
        /* For now, only support setting Authorized flag */
        if (flags_or & WPA_STA_AUTHORIZED)
-               return madwifi_set_sta_authorized(priv, addr, 1);
+               return atheros_set_sta_authorized(priv, addr, 1);
        if (!(flags_and & WPA_STA_AUTHORIZED))
-               return madwifi_set_sta_authorized(priv, addr, 0);
+               return atheros_set_sta_authorized(priv, addr, 0);
        return 0;
 }
 
 static int
-madwifi_del_key(void *priv, const u8 *addr, int key_idx)
+atheros_del_key(void *priv, const u8 *addr, int key_idx)
 {
-       struct madwifi_driver_data *drv = priv;
+       struct atheros_driver_data *drv = priv;
        struct ieee80211req_del_key wk;
        int ret;
 
@@ -446,17 +449,17 @@ madwifi_del_key(void *priv, const u8 *addr, int key_idx)
 }
 
 static int
-madwifi_set_key(const char *ifname, void *priv, enum wpa_alg alg,
+atheros_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 madwifi_driver_data *drv = priv;
+       struct atheros_driver_data *drv = priv;
        struct ieee80211req_key wk;
        u_int8_t cipher;
        int ret;
 
        if (alg == WPA_ALG_NONE)
-               return madwifi_del_key(drv, addr, key_idx);
+               return atheros_del_key(drv, addr, key_idx);
 
        wpa_printf(MSG_DEBUG, "%s: alg=%d addr=%s key_idx=%d",
                   __func__, alg, ether_sprintf(addr), key_idx);
@@ -471,6 +474,11 @@ madwifi_set_key(const char *ifname, void *priv, enum wpa_alg alg,
        case WPA_ALG_CCMP:
                cipher = IEEE80211_CIPHER_AES_CCM;
                break;
+#ifdef CONFIG_IEEE80211W
+       case WPA_ALG_IGTK:
+               cipher = IEEE80211_CIPHER_AES_CMAC;
+               break;
+#endif /* CONFIG_IEEE80211W */
        default:
                printf("%s: unknown/unsupported algorithm %d\n",
                        __func__, alg);
@@ -486,10 +494,11 @@ madwifi_set_key(const char *ifname, void *priv, enum wpa_alg alg,
        memset(&wk, 0, sizeof(wk));
        wk.ik_type = cipher;
        wk.ik_flags = IEEE80211_KEY_RECV | IEEE80211_KEY_XMIT;
-       if (addr == NULL) {
+       if (addr == NULL || is_broadcast_ether_addr(addr)) {
                memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN);
                wk.ik_keyix = key_idx;
-               wk.ik_flags |= IEEE80211_KEY_DEFAULT;
+               if (set_tx)
+                       wk.ik_flags |= IEEE80211_KEY_DEFAULT;
        } else {
                memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN);
                wk.ik_keyix = IEEE80211_KEYIX_NONE;
@@ -510,10 +519,10 @@ madwifi_set_key(const char *ifname, void *priv, enum wpa_alg alg,
 
 
 static int
-madwifi_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx,
+atheros_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx,
                   u8 *seq)
 {
-       struct madwifi_driver_data *drv = priv;
+       struct atheros_driver_data *drv = priv;
        struct ieee80211req_key wk;
 
        wpa_printf(MSG_DEBUG, "%s: addr=%s idx=%d",
@@ -557,20 +566,20 @@ madwifi_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx,
 
 
 static int
-madwifi_flush(void *priv)
+atheros_flush(void *priv)
 {
        u8 allsta[IEEE80211_ADDR_LEN];
        memset(allsta, 0xff, IEEE80211_ADDR_LEN);
-       return madwifi_sta_deauth(priv, NULL, allsta,
+       return atheros_sta_deauth(priv, NULL, allsta,
                                  IEEE80211_REASON_AUTH_LEAVE);
 }
 
 
 static int
-madwifi_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data,
+atheros_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data,
                             const u8 *addr)
 {
-       struct madwifi_driver_data *drv = priv;
+       struct atheros_driver_data *drv = priv;
        struct ieee80211req_sta_stats stats;
 
        memset(data, 0, sizeof(*data));
@@ -602,9 +611,9 @@ madwifi_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data,
 
 
 static int
-madwifi_sta_clear_stats(void *priv, const u8 *addr)
+atheros_sta_clear_stats(void *priv, const u8 *addr)
 {
-       struct madwifi_driver_data *drv = priv;
+       struct atheros_driver_data *drv = priv;
        struct ieee80211req_mlme mlme;
        int ret;
 
@@ -624,20 +633,61 @@ madwifi_sta_clear_stats(void *priv, const u8 *addr)
 
 
 static int
-madwifi_set_opt_ie(void *priv, const u8 *ie, size_t ie_len)
+atheros_set_opt_ie(void *priv, const u8 *ie, size_t ie_len)
 {
-       /*
-        * Do nothing; we setup parameters at startup that define the
-        * contents of the beacon information element.
-        */
+       struct atheros_driver_data *drv = priv;
+       u8 buf[512];
+       struct ieee80211req_getset_appiebuf *app_ie;
+
+       wpa_printf(MSG_DEBUG, "%s buflen = %lu", __func__,
+                  (unsigned long) ie_len);
+       wpa_hexdump(MSG_DEBUG, "atheros: set_generic_elem", ie, ie_len);
+
+       wpabuf_free(drv->wpa_ie);
+       drv->wpa_ie = wpabuf_alloc_copy(ie, ie_len);
+
+       app_ie = (struct ieee80211req_getset_appiebuf *) buf;
+       os_memcpy(&(app_ie->app_buf[0]), ie, ie_len);
+       app_ie->app_buflen = ie_len;
+
+       app_ie->app_frmtype = IEEE80211_APPIE_FRAME_BEACON;
+
+       /* append WPS IE for Beacon */
+       if (drv->wps_beacon_ie != NULL) {
+               os_memcpy(&(app_ie->app_buf[ie_len]),
+                         wpabuf_head(drv->wps_beacon_ie),
+                         wpabuf_len(drv->wps_beacon_ie));
+               app_ie->app_buflen = ie_len + wpabuf_len(drv->wps_beacon_ie);
+       }
+       wpa_hexdump(MSG_DEBUG, "atheros: SET_APPIEBUF(Beacon)",
+                   app_ie->app_buf, app_ie->app_buflen);
+       set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, app_ie,
+                    sizeof(struct ieee80211req_getset_appiebuf) +
+                    app_ie->app_buflen);
+
+       /* append WPS IE for Probe Response */
+       app_ie->app_frmtype = IEEE80211_APPIE_FRAME_PROBE_RESP;
+       if (drv->wps_probe_resp_ie != NULL) {
+               os_memcpy(&(app_ie->app_buf[ie_len]),
+                         wpabuf_head(drv->wps_probe_resp_ie),
+                         wpabuf_len(drv->wps_probe_resp_ie));
+               app_ie->app_buflen = ie_len +
+                       wpabuf_len(drv->wps_probe_resp_ie);
+       } else
+               app_ie->app_buflen = ie_len;
+       wpa_hexdump(MSG_DEBUG, "atheros: SET_APPIEBUF(ProbeResp)",
+                   app_ie->app_buf, app_ie->app_buflen);
+       set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, app_ie,
+                    sizeof(struct ieee80211req_getset_appiebuf) +
+                    app_ie->app_buflen);
        return 0;
 }
 
 static int
-madwifi_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
+atheros_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
                   int reason_code)
 {
-       struct madwifi_driver_data *drv = priv;
+       struct atheros_driver_data *drv = priv;
        struct ieee80211req_mlme mlme;
        int ret;
 
@@ -658,10 +708,10 @@ madwifi_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
 }
 
 static int
-madwifi_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
+atheros_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
                     int reason_code)
 {
-       struct madwifi_driver_data *drv = priv;
+       struct atheros_driver_data *drv = priv;
        struct ieee80211req_mlme mlme;
        int ret;
 
@@ -682,10 +732,10 @@ madwifi_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
 }
 
 #ifdef CONFIG_WPS
-static void madwifi_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf,
+static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf,
                                size_t len)
 {
-       struct madwifi_driver_data *drv = ctx;
+       struct atheros_driver_data *drv = ctx;
        const struct ieee80211_mgmt *mgmt;
        u16 fc;
        union wpa_event_data event;
@@ -703,6 +753,8 @@ static void madwifi_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf,
 
        os_memset(&event, 0, sizeof(event));
        event.rx_probe_req.sa = mgmt->sa;
+       event.rx_probe_req.da = mgmt->da;
+       event.rx_probe_req.bssid = mgmt->bssid;
        event.rx_probe_req.ie = mgmt->u.probe_req.variable;
        event.rx_probe_req.ie_len =
                len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req));
@@ -710,7 +762,7 @@ static void madwifi_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf,
 }
 #endif /* CONFIG_WPS */
 
-static int madwifi_receive_probe_req(struct madwifi_driver_data *drv)
+static int atheros_receive_probe_req(struct atheros_driver_data *drv)
 {
        int ret = 0;
 #ifdef CONFIG_WPS
@@ -725,7 +777,7 @@ static int madwifi_receive_probe_req(struct madwifi_driver_data *drv)
                return ret;
 
        drv->sock_raw = l2_packet_init(drv->iface, NULL, ETH_P_80211_RAW,
-                                      madwifi_raw_receive, drv, 1);
+                                      atheros_raw_receive, drv, 1);
        if (drv->sock_raw == NULL)
                return -1;
 #endif /* CONFIG_WPS */
@@ -734,43 +786,74 @@ static int madwifi_receive_probe_req(struct madwifi_driver_data *drv)
 
 #ifdef CONFIG_WPS
 static int
-madwifi_set_wps_ie(void *priv, const u8 *ie, size_t len, u32 frametype)
+atheros_set_wps_ie(void *priv, const u8 *ie, size_t len, u32 frametype)
 {
-       struct madwifi_driver_data *drv = priv;
-       u8 buf[256];
+       struct atheros_driver_data *drv = priv;
+       u8 buf[512];
        struct ieee80211req_getset_appiebuf *beac_ie;
 
-       wpa_printf(MSG_DEBUG, "%s buflen = %lu", __func__,
-                  (unsigned long) len);
+       wpa_printf(MSG_DEBUG, "%s buflen = %lu frametype=%u", __func__,
+                  (unsigned long) len, frametype);
+       wpa_hexdump(MSG_DEBUG, "atheros: IE", ie, len);
 
        beac_ie = (struct ieee80211req_getset_appiebuf *) buf;
        beac_ie->app_frmtype = frametype;
        beac_ie->app_buflen = len;
-       memcpy(&(beac_ie->app_buf[0]), ie, len);
+       os_memcpy(&(beac_ie->app_buf[0]), ie, len);
+
+       /* append the WPA/RSN IE if it is set already */
+       if (((frametype == IEEE80211_APPIE_FRAME_BEACON) ||
+            (frametype == IEEE80211_APPIE_FRAME_PROBE_RESP)) &&
+           (drv->wpa_ie != NULL)) {
+               wpa_hexdump_buf(MSG_DEBUG, "atheros: Append WPA/RSN IE",
+                               drv->wpa_ie);
+               os_memcpy(&(beac_ie->app_buf[len]), wpabuf_head(drv->wpa_ie),
+                         wpabuf_len(drv->wpa_ie));
+               beac_ie->app_buflen += wpabuf_len(drv->wpa_ie);
+       }
 
+       wpa_hexdump(MSG_DEBUG, "atheros: SET_APPIEBUF",
+                   beac_ie->app_buf, beac_ie->app_buflen);
        return set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, beac_ie,
-                           sizeof(struct ieee80211req_getset_appiebuf) + len);
+                           sizeof(struct ieee80211req_getset_appiebuf) +
+                           beac_ie->app_buflen);
 }
 
 static int
-madwifi_set_ap_wps_ie(void *priv, const struct wpabuf *beacon,
-                     const struct wpabuf *proberesp)
+atheros_set_ap_wps_ie(void *priv, const struct wpabuf *beacon,
+                     const struct wpabuf *proberesp,
+                     const struct wpabuf *assocresp)
 {
-       if (madwifi_set_wps_ie(priv, beacon ? wpabuf_head(beacon) : NULL,
+       struct atheros_driver_data *drv = priv;
+
+       wpa_hexdump_buf(MSG_DEBUG, "atheros: set_ap_wps_ie - beacon", beacon);
+       wpa_hexdump_buf(MSG_DEBUG, "atheros: set_ap_wps_ie - proberesp",
+                       proberesp);
+       wpa_hexdump_buf(MSG_DEBUG, "atheros: set_ap_wps_ie - assocresp",
+                       assocresp);
+       wpabuf_free(drv->wps_beacon_ie);
+       drv->wps_beacon_ie = beacon ? wpabuf_dup(beacon) : NULL;
+       wpabuf_free(drv->wps_probe_resp_ie);
+       drv->wps_probe_resp_ie = proberesp ? wpabuf_dup(proberesp) : NULL;
+
+       atheros_set_wps_ie(priv, assocresp ? wpabuf_head(assocresp) : NULL,
+                          assocresp ? wpabuf_len(assocresp) : 0,
+                          IEEE80211_APPIE_FRAME_ASSOC_RESP);
+       if (atheros_set_wps_ie(priv, beacon ? wpabuf_head(beacon) : NULL,
                               beacon ? wpabuf_len(beacon) : 0,
                               IEEE80211_APPIE_FRAME_BEACON))
                return -1;
-       return madwifi_set_wps_ie(priv,
+       return atheros_set_wps_ie(priv,
                                  proberesp ? wpabuf_head(proberesp) : NULL,
                                  proberesp ? wpabuf_len(proberesp): 0,
                                  IEEE80211_APPIE_FRAME_PROBE_RESP);
 }
 #else /* CONFIG_WPS */
-#define madwifi_set_ap_wps_ie NULL
+#define atheros_set_ap_wps_ie NULL
 #endif /* CONFIG_WPS */
 
 static void
-madwifi_new_sta(struct madwifi_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN])
+atheros_new_sta(struct atheros_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN])
 {
        struct hostapd_data *hapd = drv->hapd;
        struct ieee80211req_wpaie ie;
@@ -791,17 +874,21 @@ madwifi_new_sta(struct madwifi_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN])
                           __func__, strerror(errno));
                goto no_ie;
        }
-       wpa_hexdump(MSG_MSGDUMP, "madwifi req WPA IE",
+       wpa_hexdump(MSG_MSGDUMP, "atheros req WPA IE",
                    ie.wpa_ie, IEEE80211_MAX_OPT_IE);
-       wpa_hexdump(MSG_MSGDUMP, "madwifi req RSN IE",
+       wpa_hexdump(MSG_MSGDUMP, "atheros req RSN IE",
                    ie.rsn_ie, IEEE80211_MAX_OPT_IE);
+#ifdef ATH_WPS_IE
+       wpa_hexdump(MSG_MSGDUMP, "atheros req WPS IE",
+                   ie.wps_ie, IEEE80211_MAX_OPT_IE);
+#endif /* ATH_WPS_IE */
        iebuf = ie.wpa_ie;
-       /* madwifi seems to return some random data if WPA/RSN IE is not set.
+       /* atheros seems to return some random data if WPA/RSN IE is not set.
         * Assume the IE was not included if the IE type is unknown. */
        if (iebuf[0] != WLAN_EID_VENDOR_SPECIFIC)
                iebuf[1] = 0;
        if (iebuf[1] == 0 && ie.rsn_ie[1] > 0) {
-               /* madwifi-ng svn #1453 added rsn_ie. Use it, if wpa_ie was not
+               /* atheros-ng svn #1453 added rsn_ie. Use it, if wpa_ie was not
                 * set. This is needed for WPA2. */
                iebuf = ie.rsn_ie;
                if (iebuf[0] != WLAN_EID_RSN)
@@ -809,13 +896,23 @@ madwifi_new_sta(struct madwifi_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN])
        }
 
        ielen = iebuf[1];
+
+#ifdef ATH_WPS_IE
+       /* if WPS IE is present, preference is given to WPS */
+       if (ie.wps_ie &&
+           (ie.wps_ie[1] > 0 && (ie.wps_ie[0] == WLAN_EID_VENDOR_SPECIFIC))) {
+               iebuf = ie.wps_ie;
+               ielen = ie.wps_ie[1];
+       }
+#endif /* ATH_WPS_IE */
+
        if (ielen == 0)
                iebuf = NULL;
        else
                ielen += 2;
 
 no_ie:
-       drv_event_assoc(hapd, addr, iebuf, ielen);
+       drv_event_assoc(hapd, addr, iebuf, ielen, 0);
 
        if (memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) {
                /* Cached accounting data is not valid anymore. */
@@ -825,7 +922,7 @@ no_ie:
 }
 
 static void
-madwifi_wireless_event_wireless_custom(struct madwifi_driver_data *drv,
+atheros_wireless_event_wireless_custom(struct atheros_driver_data *drv,
                                       char *custom, char *end)
 {
        wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom);
@@ -897,14 +994,14 @@ madwifi_wireless_event_wireless_custom(struct madwifi_driver_data *drv,
                                   "length %d", len);
                        return;
                }
-               madwifi_raw_receive(drv, NULL,
+               atheros_raw_receive(drv, NULL,
                                    (u8 *) custom + WPS_FRAM_TAG_SIZE, len);
 #endif /* CONFIG_WPS */
        }
 }
 
 static void
-madwifi_wireless_event_wireless(struct madwifi_driver_data *drv,
+atheros_wireless_event_wireless(struct atheros_driver_data *drv,
                                char *data, int len)
 {
        struct iw_event iwe_buf, *iwe = &iwe_buf;
@@ -943,7 +1040,7 @@ madwifi_wireless_event_wireless(struct madwifi_driver_data *drv,
                                           (u8 *) iwe->u.addr.sa_data);
                        break;
                case IWEVREGISTERED:
-                       madwifi_new_sta(drv, (u8 *) iwe->u.addr.sa_data);
+                       atheros_new_sta(drv, (u8 *) iwe->u.addr.sa_data);
                        break;
                case IWEVASSOCREQIE:
                        /* Driver hack.. Use IWEVASSOCREQIE to bypass
@@ -958,7 +1055,7 @@ madwifi_wireless_event_wireless(struct madwifi_driver_data *drv,
                                return;         /* XXX */
                        memcpy(buf, custom, iwe->u.data.length);
                        buf[iwe->u.data.length] = '\0';
-                       madwifi_wireless_event_wireless_custom(
+                       atheros_wireless_event_wireless_custom(
                                drv, buf, buf + iwe->u.data.length);
                        free(buf);
                        break;
@@ -970,10 +1067,10 @@ madwifi_wireless_event_wireless(struct madwifi_driver_data *drv,
 
 
 static void
-madwifi_wireless_event_rtm_newlink(void *ctx,
+atheros_wireless_event_rtm_newlink(void *ctx,
                                   struct ifinfomsg *ifi, u8 *buf, size_t len)
 {
-       struct madwifi_driver_data *drv = ctx;
+       struct atheros_driver_data *drv = ctx;
        int attrlen, rta_len;
        struct rtattr *attr;
 
@@ -986,7 +1083,7 @@ madwifi_wireless_event_rtm_newlink(void *ctx,
        rta_len = RTA_ALIGN(sizeof(struct rtattr));
        while (RTA_OK(attr, attrlen)) {
                if (attr->rta_type == IFLA_WIRELESS) {
-                       madwifi_wireless_event_wireless(
+                       atheros_wireless_event_wireless(
                                drv, ((char *) attr) + rta_len,
                                attr->rta_len - rta_len);
                }
@@ -996,7 +1093,7 @@ madwifi_wireless_event_rtm_newlink(void *ctx,
 
 
 static int
-madwifi_get_we_version(struct madwifi_driver_data *drv)
+atheros_get_we_version(struct atheros_driver_data *drv)
 {
        struct iw_range *range;
        struct iwreq iwr;
@@ -1036,23 +1133,23 @@ madwifi_get_we_version(struct madwifi_driver_data *drv)
                drv->we_version = range->we_version_compiled;
        }
 
-       free(range);
+       os_free(range);
        return 0;
 }
 
 
 static int
-madwifi_wireless_event_init(struct madwifi_driver_data *drv)
+atheros_wireless_event_init(struct atheros_driver_data *drv)
 {
        struct netlink_config *cfg;
 
-       madwifi_get_we_version(drv);
+       atheros_get_we_version(drv);
 
        cfg = os_zalloc(sizeof(*cfg));
        if (cfg == NULL)
                return -1;
        cfg->ctx = drv;
-       cfg->newlink_cb = madwifi_wireless_event_rtm_newlink;
+       cfg->newlink_cb = atheros_wireless_event_rtm_newlink;
        drv->netlink = netlink_init(cfg);
        if (drv->netlink == NULL) {
                os_free(cfg);
@@ -1064,10 +1161,10 @@ madwifi_wireless_event_init(struct madwifi_driver_data *drv)
 
 
 static int
-madwifi_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len,
-                  int encrypt, const u8 *own_addr)
+atheros_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len,
+                  int encrypt, const u8 *own_addr, u32 flags)
 {
-       struct madwifi_driver_data *drv = priv;
+       struct atheros_driver_data *drv = priv;
        unsigned char buf[3000];
        unsigned char *bp = buf;
        struct l2_ethhdr *eth;
@@ -1107,22 +1204,22 @@ madwifi_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len,
 static void
 handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len)
 {
-       struct madwifi_driver_data *drv = ctx;
+       struct atheros_driver_data *drv = ctx;
        drv_event_eapol_rx(drv->hapd, src_addr, buf + sizeof(struct l2_ethhdr),
                           len - sizeof(struct l2_ethhdr));
 }
 
 static void *
-madwifi_init(struct hostapd_data *hapd, struct wpa_init_params *params)
+atheros_init(struct hostapd_data *hapd, struct wpa_init_params *params)
 {
-       struct madwifi_driver_data *drv;
+       struct atheros_driver_data *drv;
        struct ifreq ifr;
        struct iwreq iwr;
        char brname[IFNAMSIZ];
 
-       drv = os_zalloc(sizeof(struct madwifi_driver_data));
+       drv = os_zalloc(sizeof(struct atheros_driver_data));
        if (drv == NULL) {
-               printf("Could not allocate memory for madwifi driver data\n");
+               printf("Could not allocate memory for atheros driver data\n");
                return NULL;
        }
 
@@ -1179,11 +1276,11 @@ madwifi_init(struct hostapd_data *hapd, struct wpa_init_params *params)
 
        /* mark down during setup */
        linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0);
-       madwifi_set_privacy(drv, 0); /* default to no privacy */
+       atheros_set_privacy(drv, 0); /* default to no privacy */
 
-       madwifi_receive_probe_req(drv);
+       atheros_receive_probe_req(drv);
 
-       if (madwifi_wireless_event_init(drv))
+       if (atheros_wireless_event_init(drv))
                goto bad;
 
        return drv;
@@ -1201,9 +1298,9 @@ bad:
 
 
 static void
-madwifi_deinit(void *priv)
+atheros_deinit(void *priv)
 {
-       struct madwifi_driver_data *drv = priv;
+       struct atheros_driver_data *drv = priv;
 
        netlink_deinit(drv->netlink);
        (void) linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0);
@@ -1215,13 +1312,16 @@ madwifi_deinit(void *priv)
                l2_packet_deinit(drv->sock_xmit);
        if (drv->sock_raw)
                l2_packet_deinit(drv->sock_raw);
+       wpabuf_free(drv->wpa_ie);
+       wpabuf_free(drv->wps_beacon_ie);
+       wpabuf_free(drv->wps_probe_resp_ie);
        free(drv);
 }
 
 static int
-madwifi_set_ssid(void *priv, const u8 *buf, int len)
+atheros_set_ssid(void *priv, const u8 *buf, int len)
 {
-       struct madwifi_driver_data *drv = priv;
+       struct atheros_driver_data *drv = priv;
        struct iwreq iwr;
 
        memset(&iwr, 0, sizeof(iwr));
@@ -1239,9 +1339,9 @@ madwifi_set_ssid(void *priv, const u8 *buf, int len)
 }
 
 static int
-madwifi_get_ssid(void *priv, u8 *buf, int len)
+atheros_get_ssid(void *priv, u8 *buf, int len)
 {
-       struct madwifi_driver_data *drv = priv;
+       struct atheros_driver_data *drv = priv;
        struct iwreq iwr;
        int ret = 0;
 
@@ -1249,6 +1349,8 @@ madwifi_get_ssid(void *priv, u8 *buf, int len)
        os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
        iwr.u.essid.pointer = (caddr_t) buf;
        iwr.u.essid.length = len;
+       iwr.u.essid.length = (len > IW_ESSID_MAX_SIZE) ?
+               IW_ESSID_MAX_SIZE : len;
 
        if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) {
                perror("ioctl[SIOCGIWESSID]");
@@ -1260,39 +1362,86 @@ madwifi_get_ssid(void *priv, u8 *buf, int len)
 }
 
 static int
-madwifi_set_countermeasures(void *priv, int enabled)
+atheros_set_countermeasures(void *priv, int enabled)
 {
-       struct madwifi_driver_data *drv = priv;
+       struct atheros_driver_data *drv = priv;
        wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled);
        return set80211param(drv, IEEE80211_PARAM_COUNTERMEASURES, enabled);
 }
 
 static int
-madwifi_commit(void *priv)
+atheros_commit(void *priv)
 {
-       struct madwifi_driver_data *drv = priv;
+       struct atheros_driver_data *drv = priv;
        return linux_set_iface_flags(drv->ioctl_sock, drv->iface, 1);
 }
 
+static int atheros_set_authmode(void *priv, int auth_algs)
+{
+       int authmode;
+
+       if ((auth_algs & WPA_AUTH_ALG_OPEN) &&
+           (auth_algs & WPA_AUTH_ALG_SHARED))
+               authmode = IEEE80211_AUTH_AUTO;
+       else if (auth_algs & WPA_AUTH_ALG_OPEN)
+               authmode = IEEE80211_AUTH_OPEN;
+       else if (auth_algs & WPA_AUTH_ALG_SHARED)
+               authmode = IEEE80211_AUTH_SHARED;
+       else
+               return -1;
+
+       return set80211param(priv, IEEE80211_PARAM_AUTHMODE, authmode);
+}
+
+static int atheros_set_ap(void *priv, struct wpa_driver_ap_params *params)
+{
+       /*
+        * TODO: Use this to replace set_authmode, set_privacy, set_ieee8021x,
+        * set_generic_elem, and hapd_set_ssid.
+        */
+
+       wpa_printf(MSG_DEBUG, "atheros: set_ap - pairwise_ciphers=0x%x "
+                  "group_cipher=0x%x key_mgmt_suites=0x%x auth_algs=0x%x "
+                  "wpa_version=0x%x privacy=%d interworking=%d",
+                  params->pairwise_ciphers, params->group_cipher,
+                  params->key_mgmt_suites, params->auth_algs,
+                  params->wpa_version, params->privacy, params->interworking);
+       wpa_hexdump_ascii(MSG_DEBUG, "atheros: SSID",
+                         params->ssid, params->ssid_len);
+       if (params->hessid)
+               wpa_printf(MSG_DEBUG, "atheros: HESSID " MACSTR,
+                          MAC2STR(params->hessid));
+       wpa_hexdump_buf(MSG_DEBUG, "atheros: beacon_ies",
+                       params->beacon_ies);
+       wpa_hexdump_buf(MSG_DEBUG, "atheros: proberesp_ies",
+                       params->proberesp_ies);
+       wpa_hexdump_buf(MSG_DEBUG, "atheros: assocresp_ies",
+                       params->assocresp_ies);
+
+       return 0;
+}
+
 const struct wpa_driver_ops wpa_driver_atheros_ops = {
        .name                   = "atheros",
-       .hapd_init              = madwifi_init,
-       .hapd_deinit            = madwifi_deinit,
-       .set_ieee8021x          = madwifi_set_ieee8021x,
-       .set_privacy            = madwifi_set_privacy,
-       .set_key                = madwifi_set_key,
-       .get_seqnum             = madwifi_get_seqnum,
-       .flush                  = madwifi_flush,
-       .set_generic_elem       = madwifi_set_opt_ie,
-       .sta_set_flags          = madwifi_sta_set_flags,
-       .read_sta_data          = madwifi_read_sta_driver_data,
-       .hapd_send_eapol        = madwifi_send_eapol,
-       .sta_disassoc           = madwifi_sta_disassoc,
-       .sta_deauth             = madwifi_sta_deauth,
-       .hapd_set_ssid          = madwifi_set_ssid,
-       .hapd_get_ssid          = madwifi_get_ssid,
-       .set_countermeasures    = madwifi_set_countermeasures,
-       .sta_clear_stats        = madwifi_sta_clear_stats,
-       .commit                 = madwifi_commit,
-       .set_ap_wps_ie          = madwifi_set_ap_wps_ie,
+       .hapd_init              = atheros_init,
+       .hapd_deinit            = atheros_deinit,
+       .set_ieee8021x          = atheros_set_ieee8021x,
+       .set_privacy            = atheros_set_privacy,
+       .set_key                = atheros_set_key,
+       .get_seqnum             = atheros_get_seqnum,
+       .flush                  = atheros_flush,
+       .set_generic_elem       = atheros_set_opt_ie,
+       .sta_set_flags          = atheros_sta_set_flags,
+       .read_sta_data          = atheros_read_sta_driver_data,
+       .hapd_send_eapol        = atheros_send_eapol,
+       .sta_disassoc           = atheros_sta_disassoc,
+       .sta_deauth             = atheros_sta_deauth,
+       .hapd_set_ssid          = atheros_set_ssid,
+       .hapd_get_ssid          = atheros_get_ssid,
+       .set_countermeasures    = atheros_set_countermeasures,
+       .sta_clear_stats        = atheros_sta_clear_stats,
+       .commit                 = atheros_commit,
+       .set_ap_wps_ie          = atheros_set_ap_wps_ie,
+       .set_authmode           = atheros_set_authmode,
+       .set_ap                 = atheros_set_ap,
 };
diff --git a/src/drivers/driver_atmel.c b/src/drivers/driver_atmel.c
deleted file mode 100644 (file)
index cbec6c3..0000000
+++ /dev/null
@@ -1,499 +0,0 @@
-/*
- * WPA Supplicant - Driver interaction with Atmel Wireless LAN drivers
- * Copyright (c) 2000-2005, ATMEL Corporation
- * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
- */
-
-/******************************************************************************
-       Copyright 2000-2001 ATMEL Corporation.
-       
-    WPA Supplicant - driver interaction with Atmel Wireless lan drivers.
-    
-    This is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with Atmel wireless lan drivers; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-
-******************************************************************************/
-
-/*
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- */
-
-#include "includes.h"
-#include <sys/ioctl.h>
-
-#include "wireless_copy.h"
-#include "common.h"
-#include "driver.h"
-#include "driver_wext.h"
-
-struct wpa_driver_atmel_data {
-       void *wext; /* private data for driver_wext */
-       void *ctx;
-       char ifname[IFNAMSIZ + 1];
-       int sock;
-};
-
-
-#define ATMEL_WPA_IOCTL                (SIOCIWFIRSTPRIV + 2)
-#define ATMEL_WPA_IOCTL_PARAM          (SIOCIWFIRSTPRIV + 3)
-#define ATMEL_WPA_IOCTL_GET_PARAM      (SIOCIWFIRSTPRIV + 4)
-
-
-/* ATMEL_WPA_IOCTL ioctl() cmd: */
-enum {
-    SET_WPA_ENCRYPTION  = 1,
-    SET_CIPHER_SUITES   = 2,
-    MLME_STA_DEAUTH     = 3,
-    MLME_STA_DISASSOC   = 4
-};
-
-/* ATMEL_WPA_IOCTL_PARAM ioctl() cmd: */
-enum {
-            ATMEL_PARAM_WPA = 1,
-            ATMEL_PARAM_PRIVACY_INVOKED = 2,
-            ATMEL_PARAM_WPA_TYPE = 3
-};
-
-#define MAX_KEY_LENGTH      40
-
-struct atmel_param{
-    unsigned char sta_addr[6];
-        int     cmd;
-        u8      alg;
-        u8      key_idx;
-        u8      set_tx;
-        u8      seq[8];
-        u8      seq_len;
-        u16     key_len;
-        u8      key[MAX_KEY_LENGTH];
-    struct{
-        int     reason_code;
-        u8      state;
-    }mlme;
-    u8          pairwise_suite;
-    u8          group_suite;
-    u8          key_mgmt_suite;
-};
-
-    
-    
-static int atmel_ioctl(struct wpa_driver_atmel_data *drv,
-                      struct atmel_param *param,
-                      int len, int show_err)
-{
-       struct iwreq iwr;
-
-       os_memset(&iwr, 0, sizeof(iwr));
-       os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
-       iwr.u.data.pointer = (caddr_t) param;
-       iwr.u.data.length = len;
-
-       if (ioctl(drv->sock, ATMEL_WPA_IOCTL, &iwr) < 0) {
-               int ret;
-               ret = errno;
-               if (show_err) 
-                       perror("ioctl[ATMEL_WPA_IOCTL]");
-               return ret;
-       }
-
-       return 0;
-}
-
-
-static int atmel2param(struct wpa_driver_atmel_data *drv, int param, int value)
-{
-       struct iwreq iwr;
-       int *i, ret = 0;
-
-       os_memset(&iwr, 0, sizeof(iwr));
-       os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
-       i = (int *) iwr.u.name;
-       *i++ = param;
-       *i++ = value;
-
-       if (ioctl(drv->sock, ATMEL_WPA_IOCTL_PARAM, &iwr) < 0) {
-               perror("ioctl[ATMEL_WPA_IOCTL_PARAM]");
-               ret = -1;
-       }
-       return ret;
-}
-
-
-#if 0
-static int wpa_driver_atmel_set_wpa_ie(struct wpa_driver_atmel_data *drv,
-                                      const char *wpa_ie, size_t wpa_ie_len)
-{
-       struct atmel_param *param;
-       int res;
-       size_t blen = ATMEL_GENERIC_ELEMENT_HDR_LEN + wpa_ie_len;
-       if (blen < sizeof(*param))
-               blen = sizeof(*param);
-
-       param = os_zalloc(blen);
-       if (param == NULL)
-               return -1;
-
-       param->cmd = ATMEL_SET_GENERIC_ELEMENT;
-       param->u.generic_elem.len = wpa_ie_len;
-       os_memcpy(param->u.generic_elem.data, wpa_ie, wpa_ie_len);
-       res = atmel_ioctl(drv, param, blen, 1);
-
-       os_free(param);
-
-       return res;
-}
-#endif
-
-
-static int wpa_driver_atmel_set_wpa(void *priv, int enabled)
-{
-       struct wpa_driver_atmel_data *drv = priv;
-        int ret = 0;
-       
-        printf("wpa_driver_atmel_set_wpa %s\n", drv->ifname);
-
-       wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled);
-
-#if 0
-       if (!enabled && wpa_driver_atmel_set_wpa_ie(drv, NULL, 0) < 0)
-               ret = -1;
-#endif
-       if (atmel2param(drv, ATMEL_PARAM_PRIVACY_INVOKED, enabled) < 0)
-               ret = -1;
-       if (atmel2param(drv, ATMEL_PARAM_WPA, enabled) < 0)
-               ret = -1;
-
-       return ret;
-}
-
-
-static int wpa_driver_atmel_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_atmel_data *drv = priv;
-       int ret = 0;
-        struct atmel_param *param;
-       u8 *buf;
-        u8 alg_type;
-        
-       size_t blen;
-       char *alg_name;
-
-       switch (alg) {
-       case WPA_ALG_NONE:
-               alg_name = "none";
-                alg_type = 0;
-               break;
-       case WPA_ALG_WEP:
-               alg_name = "WEP";
-               alg_type = 1;
-                break;
-       case WPA_ALG_TKIP:
-               alg_name = "TKIP";
-               alg_type = 2;
-                break;
-       case WPA_ALG_CCMP:
-               alg_name = "CCMP";
-               alg_type = 3;
-                break;
-       default:
-               return -1;
-       }
-
-       wpa_printf(MSG_DEBUG, "%s: alg=%s key_idx=%d set_tx=%d seq_len=%lu "
-                  "key_len=%lu", __FUNCTION__, alg_name, key_idx, set_tx,
-                  (unsigned long) seq_len, (unsigned long) key_len);
-
-       if (seq_len > 8)
-               return -2;
-
-       blen = sizeof(*param) + key_len;
-       buf = os_zalloc(blen);
-       if (buf == NULL)
-               return -1;
-
-       param = (struct atmel_param *) buf;
-        
-        param->cmd = SET_WPA_ENCRYPTION; 
-        
-        if (addr == NULL)
-               os_memset(param->sta_addr, 0xff, ETH_ALEN);
-       else
-               os_memcpy(param->sta_addr, addr, ETH_ALEN);
-        
-        param->alg = alg_type;
-        param->key_idx = key_idx;
-        param->set_tx = set_tx;
-        os_memcpy(param->seq, seq, seq_len);
-        param->seq_len = seq_len;
-        param->key_len = key_len;
-       os_memcpy((u8 *)param->key, key, key_len);
-       
-        if (atmel_ioctl(drv, param, blen, 1)) {
-               wpa_printf(MSG_WARNING, "Failed to set encryption.");
-               /* TODO: show key error*/
-               ret = -1;
-       }
-       os_free(buf);
-
-       return ret;
-}
-
-
-static int wpa_driver_atmel_set_countermeasures(void *priv,
-                                                int enabled)
-{
-       /* FIX */
-       printf("wpa_driver_atmel_set_countermeasures - not yet "
-              "implemented\n");
-       return 0;
-}
-
-
-static int wpa_driver_atmel_mlme(void *priv, const u8 *addr, int cmd,
-                                int reason_code)
-{
-       struct wpa_driver_atmel_data *drv = priv;
-       struct atmel_param param;
-       int ret;
-        int mgmt_error = 0xaa;
-        
-       os_memset(&param, 0, sizeof(param));
-       os_memcpy(param.sta_addr, addr, ETH_ALEN);
-       param.cmd = cmd;
-       param.mlme.reason_code = reason_code;
-        param.mlme.state = mgmt_error;
-       ret = atmel_ioctl(drv, &param, sizeof(param), 1);
-       return ret;
-}
-
-
-#if 0
-static int wpa_driver_atmel_set_suites(struct wpa_driver_atmel_data *drv,
-                                      u8 pairwise_suite, u8 group_suite,
-                                      u8 key_mgmt_suite)
-{
-       struct atmel_param param;
-       int ret;
-        
-       os_memset(&param, 0, sizeof(param));
-        param.cmd = SET_CIPHER_SUITES;
-        param.pairwise_suite = pairwise_suite;
-        param.group_suite = group_suite;
-        param.key_mgmt_suite = key_mgmt_suite;
-               
-       ret = atmel_ioctl(drv, &param, sizeof(param), 1);
-       return ret;
-}
-#endif
-
-
-static int wpa_driver_atmel_deauthenticate(void *priv, const u8 *addr,
-                                          int reason_code)
-{
-       struct wpa_driver_atmel_data *drv = priv;
-       printf("wpa_driver_atmel_deauthenticate\n");
-        wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
-       return wpa_driver_atmel_mlme(drv, addr, MLME_STA_DEAUTH,
-                                    reason_code);
-
-}
-
-
-static int wpa_driver_atmel_disassociate(void *priv, const u8 *addr,
-                                        int reason_code)
-{
-       struct wpa_driver_atmel_data *drv = priv;
-       printf("wpa_driver_atmel_disassociate\n");
-       wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
-       return wpa_driver_atmel_mlme(drv, addr, MLME_STA_DISASSOC,
-                                    reason_code);
-
-}
-
-
-#if 0
-/* Atmel driver uses specific values for each cipher suite */
-static int convertSuiteToDriver(enum wpa_cipher suite)
-{
-    u8 suite_type;
-    
-    switch(suite) {
-        case CIPHER_NONE:
-                suite_type =  0;
-                break;
-        case CIPHER_WEP40:
-                suite_type =  1;
-                break;
-        case CIPHER_TKIP:
-                suite_type = 2;
-                break;
-        case CIPHER_WEP104:
-                suite_type = 5;
-                break;
-        case CIPHER_CCMP:
-                suite_type = 3;
-                break;
-        default:
-                suite_type = 2;
-    }
-    
-    return suite_type;
-
-}
-#endif
-    
-static int
-wpa_driver_atmel_associate(void *priv,
-                          struct wpa_driver_associate_params *params)
-{
-       struct wpa_driver_atmel_data *drv = priv;
-       int ret = 0;
-#if 0
-        u8 pairwise_suite_driver;
-        u8 group_suite_driver;
-        u8 key_mgmt_suite_driver;
-
-        pairwise_suite_driver = convertSuiteToDriver(params->pairwise_suite);
-        group_suite_driver    = convertSuiteToDriver(params->group_suite);
-        key_mgmt_suite_driver = convertSuiteToDriver(params->key_mgmt_suite);
-
-        if (wpa_driver_atmel_set_suites(drv, pairwise_suite_driver,
-                                       group_suite_driver,
-                                       key_mgmt_suite_driver) < 0){
-               printf("wpa_driver_atmel_set_suites.\n");
-                ret = -1;
-        }
-        if (wpa_driver_wext_set_freq(drv->wext, params->freq) < 0) {
-               printf("wpa_driver_atmel_set_freq.\n");
-               ret = -1;
-        }
-#endif
-       if (wpa_driver_wext_set_ssid(drv->wext, params->ssid, params->ssid_len)
-           < 0) {
-               printf("FAILED : wpa_driver_atmel_set_ssid.\n");
-               ret = -1;
-        }
-       if (wpa_driver_wext_set_bssid(drv->wext, params->bssid) < 0) {
-               printf("FAILED : wpa_driver_atmel_set_bssid.\n");
-               ret = -1;
-        }
-
-       return ret;
-}
-
-
-static int wpa_driver_atmel_get_bssid(void *priv, u8 *bssid)
-{
-       struct wpa_driver_atmel_data *drv = priv;
-       return wpa_driver_wext_get_bssid(drv->wext, bssid);
-}
-
-
-static int wpa_driver_atmel_get_ssid(void *priv, u8 *ssid)
-{
-       struct wpa_driver_atmel_data *drv = priv;
-       return wpa_driver_wext_get_ssid(drv->wext, ssid);
-}
-
-
-static int wpa_driver_atmel_scan(void *priv,
-                                struct wpa_driver_scan_params *params)
-{
-       struct wpa_driver_atmel_data *drv = priv;
-       return wpa_driver_wext_scan(drv->wext, params);
-}
-
-
-static struct wpa_scan_results * wpa_driver_atmel_get_scan_results(void *priv)
-{
-       struct wpa_driver_atmel_data *drv = priv;
-       return wpa_driver_wext_get_scan_results(drv->wext);
-}
-
-
-static int wpa_driver_atmel_set_operstate(void *priv, int state)
-{
-       struct wpa_driver_atmel_data *drv = priv;
-       return wpa_driver_wext_set_operstate(drv->wext, state);
-}
-
-
-static void * wpa_driver_atmel_init(void *ctx, const char *ifname)
-{
-       struct wpa_driver_atmel_data *drv;
-
-       drv = os_zalloc(sizeof(*drv));
-       if (drv == NULL)
-               return NULL;
-       drv->wext = wpa_driver_wext_init(ctx, ifname);
-       if (drv->wext == NULL) {
-               os_free(drv);
-               return NULL;
-       }
-
-       drv->ctx = ctx;
-       os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
-       drv->sock = socket(PF_INET, SOCK_DGRAM, 0);
-       if (drv->sock < 0) {
-               wpa_driver_wext_deinit(drv->wext);
-               os_free(drv);
-               return NULL;
-       }
-
-       wpa_driver_atmel_set_wpa(drv, 1);
-
-       return drv;
-}
-
-
-static void wpa_driver_atmel_deinit(void *priv)
-{
-       struct wpa_driver_atmel_data *drv = priv;
-       wpa_driver_atmel_set_wpa(drv, 0);
-       wpa_driver_wext_deinit(drv->wext);
-       close(drv->sock);
-       os_free(drv);
-}
-
-
-const struct wpa_driver_ops wpa_driver_atmel_ops = {
-       .name = "atmel",
-       .desc = "ATMEL AT76C5XXx (USB, PCMCIA)",
-       .get_bssid = wpa_driver_atmel_get_bssid,
-       .get_ssid = wpa_driver_atmel_get_ssid,
-       .set_key = wpa_driver_atmel_set_key,
-       .init = wpa_driver_atmel_init,
-       .deinit = wpa_driver_atmel_deinit,
-       .set_countermeasures = wpa_driver_atmel_set_countermeasures,
-       .scan2 = wpa_driver_atmel_scan,
-       .get_scan_results2 = wpa_driver_atmel_get_scan_results,
-       .deauthenticate = wpa_driver_atmel_deauthenticate,
-       .disassociate = wpa_driver_atmel_disassociate,
-       .associate = wpa_driver_atmel_associate,
-       .set_operstate = wpa_driver_atmel_set_operstate,
-};
index 99de6c7..4596a51 100644 (file)
 
 #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>
@@ -295,9 +297,7 @@ bsd_set_key(const char *ifname, void *priv, enum wpa_alg alg,
 
        if (alg == WPA_ALG_NONE) {
 #ifndef HOSTAPD
-               if (addr == NULL ||
-                   os_memcmp(addr, "\xff\xff\xff\xff\xff\xff",
-                             IEEE80211_ADDR_LEN) == 0)
+               if (addr == NULL || is_broadcast_ether_addr(addr))
                        return bsd_del_key(priv, NULL, key_idx);
                else
 #endif /* HOSTAPD */
@@ -334,8 +334,7 @@ bsd_set_key(const char *ifname, void *priv, enum wpa_alg alg,
                 * the address (yech).  Note also that we can only mark global
                 * keys default; doing this for a unicast key is an error.
                 */
-               if (os_memcmp(addr, "\xff\xff\xff\xff\xff\xff",
-                             IEEE80211_ADDR_LEN) == 0) {
+               if (is_broadcast_ether_addr(addr)) {
                        wk.ik_flags |= IEEE80211_KEY_GROUP;
                        wk.ik_keyix = key_idx;
                } else {
@@ -346,7 +345,20 @@ bsd_set_key(const char *ifname, void *priv, enum wpa_alg alg,
        if (wk.ik_keyix != IEEE80211_KEYIX_NONE && set_tx)
                wk.ik_flags |= IEEE80211_KEY_DEFAULT;
        wk.ik_keylen = key_len;
-       os_memcpy(&wk.ik_keyrsc, seq, seq_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));
@@ -511,12 +523,12 @@ bsd_new_sta(void *priv, void *ctx, u8 addr[IEEE80211_ADDR_LEN])
                ielen += 2;
 
 no_ie:
-       drv_event_assoc(ctx, addr, iebuf, ielen);
+       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)
+              int encrypt, const u8 *own_addr, u32 flags)
 {
        struct bsd_driver_data *drv = priv;
 
@@ -527,20 +539,30 @@ bsd_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len,
 }
 
 static int
-bsd_set_freq(void *priv, u16 channel)
+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;
-
-       if (channel < 14)
-               mode = IFM_IEEE80211_11G;
-       else if (channel == 14)
+       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 = IFM_IEEE80211_11A;
+       } 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__);
@@ -550,7 +572,7 @@ bsd_set_freq(void *priv, u16 channel)
 #ifdef SIOCS80211CHANNEL
        os_memset(&creq, 0, sizeof(creq));
        os_strlcpy(creq.i_name, drv->ifname, sizeof(creq.i_name));
-       creq.i_channel = channel;
+       creq.i_channel = (u_int16_t)channel;
        return ioctl(drv->sock, SIOCS80211CHANNEL, &creq);
 #else /* SIOCS80211CHANNEL */
        return set80211param(priv, IEEE80211_IOC_CHANNEL, channel);
@@ -569,6 +591,21 @@ bsd_set_opt_ie(void *priv, const u8 *ie, size_t ie_len)
        return 0;
 }
 
+static int
+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\n", __func__,
+                          strerror(errno));
+               len = 2048;
+       }
+
+       return len;
+}
 
 #ifdef HOSTAPD
 
@@ -691,26 +728,37 @@ static void
 bsd_wireless_event_receive(int sock, void *ctx, void *sock_ctx)
 {
        struct bsd_driver_data *drv = ctx;
-       char buf[2048];
+       char *buf;
        struct if_announcemsghdr *ifan;
        struct rt_msghdr *rtm;
        struct ieee80211_michael_event *mic;
        struct ieee80211_join_event *join;
        struct ieee80211_leave_event *leave;
-       int n;
+       int n, len;
        union wpa_event_data data;
 
-       n = read(sock, buf, sizeof(buf));
+       len = rtbuf_len();
+
+       buf = os_malloc(len);
+       if (buf == NULL) {
+               wpa_printf(MSG_ERROR, "%s os_malloc() failed\n", __func__);
+               return;
+       }
+
+       n = read(sock, buf, len);
        if (n < 0) {
                if (errno != EINTR && errno != EAGAIN)
-                       perror("read(PF_ROUTE)");
+                       wpa_printf(MSG_ERROR, "%s read() failed: %s\n",
+                                  __func__, strerror(errno));
+               os_free(buf);
                return;
        }
 
        rtm = (struct rt_msghdr *) buf;
        if (rtm->rtm_version != RTM_VERSION) {
-               wpa_printf(MSG_DEBUG, "Routing message version %d not "
-                       "understood\n", rtm->rtm_version);
+               wpa_printf(MSG_DEBUG, "Invalid routing message version=%d",
+                          rtm->rtm_version);
+               os_free(buf);
                return;
        }
        ifan = (struct if_announcemsghdr *) rtm;
@@ -751,6 +799,7 @@ bsd_wireless_event_receive(int sock, void *ctx, void *sock_ctx)
                }
                break;
        }
+       os_free(buf);
 }
 
 static void
@@ -760,12 +809,6 @@ handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len)
        drv_event_eapol_rx(drv->hapd, src_addr, buf, len);
 }
 
-static int
-hostapd_bsd_set_freq(void *priv, struct hostapd_freq_params *freq)
-{
-       return bsd_set_freq(priv, freq->channel);
-}
-
 static void *
 bsd_init(struct hostapd_data *hapd, struct wpa_init_params *params)
 {
@@ -972,7 +1015,6 @@ wpa_driver_bsd_associate(void *priv, struct wpa_driver_associate_params *params)
        struct bsd_driver_data *drv = priv;
        struct ieee80211req_mlme mlme;
        u32 mode;
-       u16 channel;
        int privacy;
        int ret = 0;
 
@@ -1007,18 +1049,6 @@ wpa_driver_bsd_associate(void *priv, struct wpa_driver_associate_params *params)
        }
 
        if (params->mode == IEEE80211_MODE_AP) {
-               if (params->freq >= 2412 && params->freq <= 2472)
-                       channel = (params->freq - 2407) / 5;
-               else if (params->freq == 2484)
-                       channel = 14;
-               else if ((params->freq >= 5180 && params->freq <= 5240) ||
-                        (params->freq >= 5745 && params->freq <= 5825))
-                       channel = (params->freq - 5000) / 5;
-               else
-                       channel = 0;
-               if (bsd_set_freq(drv, channel) < 0)
-                       return -1;
-
                drv->sock_xmit = l2_packet_init(drv->ifname, NULL, ETH_P_EAPOL,
                                                handle_read, drv, 0);
                if (drv->sock_xmit == NULL)
@@ -1134,7 +1164,7 @@ static void
 wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx)
 {
        struct bsd_driver_data *drv = sock_ctx;
-       char buf[2048];
+       char *buf;
        struct if_announcemsghdr *ifan;
        struct if_msghdr *ifm;
        struct rt_msghdr *rtm;
@@ -1142,19 +1172,30 @@ wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx)
        struct ieee80211_michael_event *mic;
        struct ieee80211_leave_event *leave;
        struct ieee80211_join_event *join;
-       int n;
+       int n, len;
+
+       len = rtbuf_len();
+
+       buf = os_malloc(len);
+       if (buf == NULL) {
+               wpa_printf(MSG_ERROR, "%s os_malloc() failed\n", __func__);
+               return;
+       }
 
-       n = read(sock, buf, sizeof(buf));
+       n = read(sock, buf, len);
        if (n < 0) {
                if (errno != EINTR && errno != EAGAIN)
-                       perror("read(PF_ROUTE)");
+                       wpa_printf(MSG_ERROR, "%s read() failed: %s\n",
+                                  __func__, strerror(errno));
+               os_free(buf);
                return;
        }
 
        rtm = (struct rt_msghdr *) buf;
        if (rtm->rtm_version != RTM_VERSION) {
-               wpa_printf(MSG_DEBUG, "Routing message version %d not "
-                       "understood\n", rtm->rtm_version);
+               wpa_printf(MSG_DEBUG, "Invalid routing message version=%d",
+                          rtm->rtm_version);
+               os_free(buf);
                return;
        }
        os_memset(&event, 0, sizeof(event));
@@ -1169,6 +1210,7 @@ wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx)
                case IFAN_DEPARTURE:
                        event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
                default:
+                       os_free(buf);
                        return;
                }
                wpa_printf(MSG_DEBUG, "RTM_IFANNOUNCE: Interface '%s' %s",
@@ -1241,6 +1283,7 @@ wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx)
                }
                break;
        }
+       os_free(buf);
 }
 
 static void
@@ -1518,7 +1561,6 @@ const struct wpa_driver_ops wpa_driver_bsd_ops = {
        .read_sta_data          = bsd_read_sta_driver_data,
        .sta_disassoc           = bsd_sta_disassoc,
        .sta_deauth             = bsd_sta_deauth,
-       .set_freq               = hostapd_bsd_set_freq,
 #else /* HOSTAPD */
        .init                   = wpa_driver_bsd_init,
        .deinit                 = wpa_driver_bsd_deinit,
@@ -1532,6 +1574,7 @@ const struct wpa_driver_ops wpa_driver_bsd_ops = {
        .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,
diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c
new file mode 100644 (file)
index 0000000..009a983
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Common driver-related functions
+ * Copyright (c) 2003-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include "utils/common.h"
+#include "driver.h"
+
+void wpa_scan_results_free(struct wpa_scan_results *res)
+{
+       size_t i;
+
+       if (res == NULL)
+               return;
+
+       for (i = 0; i < res->num; i++)
+               os_free(res->res[i]);
+       os_free(res->res);
+       os_free(res);
+}
+
+
+const char * event_to_string(enum wpa_event_type event)
+{
+#define E2S(n) case EVENT_ ## n: return #n
+       switch (event) {
+       E2S(ASSOC);
+       E2S(DISASSOC);
+       E2S(MICHAEL_MIC_FAILURE);
+       E2S(SCAN_RESULTS);
+       E2S(ASSOCINFO);
+       E2S(INTERFACE_STATUS);
+       E2S(PMKID_CANDIDATE);
+       E2S(STKSTART);
+       E2S(TDLS);
+       E2S(FT_RESPONSE);
+       E2S(IBSS_RSN_START);
+       E2S(AUTH);
+       E2S(DEAUTH);
+       E2S(ASSOC_REJECT);
+       E2S(AUTH_TIMED_OUT);
+       E2S(ASSOC_TIMED_OUT);
+       E2S(FT_RRB_RX);
+       E2S(WPS_BUTTON_PUSHED);
+       E2S(TX_STATUS);
+       E2S(RX_FROM_UNKNOWN);
+       E2S(RX_MGMT);
+       E2S(RX_ACTION);
+       E2S(REMAIN_ON_CHANNEL);
+       E2S(CANCEL_REMAIN_ON_CHANNEL);
+       E2S(MLME_RX);
+       E2S(RX_PROBE_REQ);
+       E2S(NEW_STA);
+       E2S(EAPOL_RX);
+       E2S(SIGNAL_CHANGE);
+       E2S(INTERFACE_ENABLED);
+       E2S(INTERFACE_DISABLED);
+       E2S(CHANNEL_LIST_CHANGED);
+       E2S(INTERFACE_UNAVAILABLE);
+       E2S(BEST_CHANNEL);
+       E2S(UNPROT_DEAUTH);
+       E2S(UNPROT_DISASSOC);
+       E2S(STATION_LOW_ACK);
+       E2S(P2P_DEV_FOUND);
+       E2S(P2P_GO_NEG_REQ_RX);
+       E2S(P2P_GO_NEG_COMPLETED);
+       E2S(P2P_PROV_DISC_REQUEST);
+       E2S(P2P_PROV_DISC_RESPONSE);
+       E2S(P2P_SD_REQUEST);
+       E2S(P2P_SD_RESPONSE);
+       E2S(IBSS_PEER_LOST);
+       E2S(DRIVER_GTK_REKEY);
+       E2S(SCHED_SCAN_STOPPED);
+       E2S(DRIVER_CLIENT_POLL_OK);
+       }
+
+       return "UNKNOWN";
+#undef E2S
+}
index 952f389..85e9251 100644 (file)
@@ -32,6 +32,7 @@
 #include "netlink.h"
 #include "linux_ioctl.h"
 #include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
 
 
 /* MTU to be set for the wlan#ap device; this is mainly needed for IEEE 802.1X
@@ -84,8 +85,8 @@ static void handle_data(struct hostap_driver_data *drv, u8 *buf, size_t len,
 
        sa = hdr->addr2;
        os_memset(&event, 0, sizeof(event));
-       event.rx_from_unknown.frame = buf;
-       event.rx_from_unknown.len = len;
+       event.rx_from_unknown.bssid = get_hdr_bssid(hdr, len);
+       event.rx_from_unknown.addr = sa;
        wpa_supplicant_event(drv->hapd, EVENT_RX_FROM_UNKNOWN, &event);
 
        pos = (u8 *) (hdr + 1);
@@ -148,7 +149,6 @@ static void handle_frame(struct hostap_driver_data *drv, u8 *buf, size_t len)
 {
        struct ieee80211_hdr *hdr;
        u16 fc, extra_len, type, stype;
-       unsigned char *extra = NULL;
        size_t data_len = len;
        int ver;
        union wpa_event_data event;
@@ -185,7 +185,6 @@ static void handle_frame(struct hostap_driver_data *drv, u8 *buf, size_t len)
                        return;
                }
                len -= extra_len + 2;
-               extra = buf + len;
        } else if (ver == 1 || ver == 2) {
                handle_tx_callback(drv, buf, data_len, ver == 2 ? 1 : 0);
                return;
@@ -289,7 +288,8 @@ static int hostap_send_mlme(void *priv, const u8 *msg, size_t len)
 
 
 static int hostap_send_eapol(void *priv, const u8 *addr, const u8 *data,
-                            size_t data_len, int encrypt, const u8 *own_addr)
+                            size_t data_len, int encrypt, const u8 *own_addr,
+                            u32 flags)
 {
        struct hostap_driver_data *drv = priv;
        struct ieee80211_hdr *hdr;
@@ -763,7 +763,8 @@ static int hostap_set_generic_elem(void *priv,
 
 
 static int hostap_set_ap_wps_ie(void *priv, const struct wpabuf *beacon,
-                               const struct wpabuf *proberesp)
+                               const struct wpabuf *proberesp,
+                               const struct wpabuf *assocresp)
 {
        struct hostap_driver_data *drv = priv;
 
@@ -1034,6 +1035,16 @@ static int hostap_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
        struct hostap_driver_data *drv = priv;
        struct ieee80211_mgmt mgmt;
 
+       if (is_broadcast_ether_addr(addr)) {
+               /*
+                * New Prism2.5/3 STA firmware versions seem to have issues
+                * with this broadcast deauth frame. This gets the firmware in
+                * odd state where nothing works correctly, so let's skip
+                * sending this for the hostap driver.
+                */
+               return 0;
+       }
+
        memset(&mgmt, 0, sizeof(mgmt));
        mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
                                          WLAN_FC_STYPE_DEAUTH);
@@ -1046,6 +1057,25 @@ static int hostap_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
 }
 
 
+static int hostap_set_freq(void *priv, struct hostapd_freq_params *freq)
+{
+       struct hostap_driver_data *drv = priv;
+       struct iwreq iwr;
+
+       os_memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+       iwr.u.freq.m = freq->channel;
+       iwr.u.freq.e = 0;
+
+       if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) {
+               perror("ioctl[SIOCSIWFREQ]");
+               return -1;
+       }
+
+       return 0;
+}
+
+
 static int hostap_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
                               int reason)
 {
@@ -1114,6 +1144,33 @@ static struct hostapd_hw_modes * hostap_get_hw_feature_data(void *priv,
        return mode;
 }
 
+
+static void wpa_driver_hostap_poll_client(void *priv, const u8 *own_addr,
+                                         const u8 *addr, int qos)
+{
+       struct ieee80211_hdr hdr;
+
+       os_memset(&hdr, 0, sizeof(hdr));
+
+       /*
+        * WLAN_FC_STYPE_NULLFUNC would be more appropriate,
+        * but it is apparently not retried so TX Exc events
+        * are not received for it.
+        * This is the reason the driver overrides the default
+        * handling.
+        */
+       hdr.frame_control = IEEE80211_FC(WLAN_FC_TYPE_DATA,
+                                        WLAN_FC_STYPE_DATA);
+
+       hdr.frame_control |=
+               host_to_le16(WLAN_FC_FROMDS);
+       os_memcpy(hdr.IEEE80211_DA_FROMDS, addr, ETH_ALEN);
+       os_memcpy(hdr.IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN);
+       os_memcpy(hdr.IEEE80211_SA_FROMDS, own_addr, ETH_ALEN);
+
+       hostap_send_mlme(priv, (u8 *)&hdr, sizeof(hdr));
+}
+
 #else /* HOSTAPD */
 
 struct wpa_driver_hostap_data {
@@ -1306,7 +1363,8 @@ static int wpa_driver_hostap_set_key(const char *ifname, void *priv,
                   HOSTAP_CRYPT_ALG_NAME_LEN);
        param->u.crypt.flags = set_tx ? HOSTAP_CRYPT_FLAG_SET_TX_KEY : 0;
        param->u.crypt.idx = key_idx;
-       os_memcpy(param->u.crypt.seq, seq, seq_len);
+       if (seq)
+               os_memcpy(param->u.crypt.seq, seq, seq_len);
        param->u.crypt.key_len = key_len;
        os_memcpy((u8 *) (param + 1), key, key_len);
 
@@ -1619,6 +1677,8 @@ const struct wpa_driver_ops wpa_driver_hostap_ops = {
        .sta_clear_stats = hostap_sta_clear_stats,
        .get_hw_feature_data = hostap_get_hw_feature_data,
        .set_ap_wps_ie = hostap_set_ap_wps_ie,
+       .set_freq = hostap_set_freq,
+       .poll_client = wpa_driver_hostap_poll_client,
 #else /* HOSTAPD */
        .get_bssid = wpa_driver_hostap_get_bssid,
        .get_ssid = wpa_driver_hostap_get_ssid,
diff --git a/src/drivers/driver_ipw.c b/src/drivers/driver_ipw.c
deleted file mode 100644 (file)
index 77984f9..0000000
+++ /dev/null
@@ -1,472 +0,0 @@
-/*
- * WPA Supplicant - driver interaction with Linux ipw2100/2200 drivers
- * Copyright (c) 2005 Zhu Yi <yi.zhu@intel.com>
- * Copyright (c) 2004 Lubomir Gelo <lgelo@cnc.sk>
- * Copyright (c) 2003-2004, Jouni Malinen <j@w1.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
- *
- * Please note that ipw2100/2200 drivers change to use generic Linux wireless
- * extensions if the kernel includes support for WE-18 or newer (Linux 2.6.13
- * or newer). driver_wext.c should be used in those cases.
- */
-
-#include "includes.h"
-#include <sys/ioctl.h>
-
-#include "wireless_copy.h"
-#include "common.h"
-#include "driver.h"
-#include "driver_wext.h"
-
-struct wpa_driver_ipw_data {
-       void *wext; /* private data for driver_wext */
-       void *ctx;
-       char ifname[IFNAMSIZ + 1];
-       int sock;
-};
-
-/* following definitions must be kept in sync with ipw2100.c and ipw2200.c */
-
-#define IPW_IOCTL_WPA_SUPPLICANT               SIOCIWFIRSTPRIV+30
-
-#define IPW_CMD_SET_WPA_PARAM                  1
-#define        IPW_CMD_SET_WPA_IE                      2
-#define IPW_CMD_SET_ENCRYPTION                 3
-#define IPW_CMD_MLME                           4
-
-#define IPW_PARAM_WPA_ENABLED                  1
-#define IPW_PARAM_TKIP_COUNTERMEASURES         2
-#define IPW_PARAM_DROP_UNENCRYPTED             3
-#define IPW_PARAM_PRIVACY_INVOKED              4
-#define IPW_PARAM_AUTH_ALGS                    5
-#define IPW_PARAM_IEEE_802_1X                  6
-
-#define IPW_MLME_STA_DEAUTH                    1
-#define IPW_MLME_STA_DISASSOC                  2
-
-#define IPW_CRYPT_ERR_UNKNOWN_ALG              2
-#define IPW_CRYPT_ERR_UNKNOWN_ADDR             3
-#define IPW_CRYPT_ERR_CRYPT_INIT_FAILED                4
-#define IPW_CRYPT_ERR_KEY_SET_FAILED           5
-#define IPW_CRYPT_ERR_TX_KEY_SET_FAILED                6
-#define IPW_CRYPT_ERR_CARD_CONF_FAILED         7
-
-#define        IPW_CRYPT_ALG_NAME_LEN                  16
-
-struct ipw_param {
-       u32 cmd;
-       u8 sta_addr[ETH_ALEN];
-        union {
-               struct {
-                       u8 name;
-                       u32 value;
-               } wpa_param;
-               struct {
-                       u32 len;
-                       u8 reserved[32];
-                       u8 data[0];
-               } wpa_ie;
-               struct{
-                       u32 command;
-                       u32 reason_code;
-               } mlme;
-               struct {
-                       u8 alg[IPW_CRYPT_ALG_NAME_LEN];
-                       u8 set_tx;
-                       u32 err;
-                       u8 idx;
-                       u8 seq[8];
-                       u16 key_len;
-                       u8 key[0];
-               } crypt;
-
-       } u;
-};
-
-/* end of ipw2100.c and ipw2200.c code */
-
-static int wpa_driver_ipw_set_auth_alg(void *priv, int auth_alg);
-
-static int ipw_ioctl(struct wpa_driver_ipw_data *drv,
-                    struct ipw_param *param, int len, int show_err)
-{
-       struct iwreq iwr;
-
-       os_memset(&iwr, 0, sizeof(iwr));
-       os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
-       iwr.u.data.pointer = (caddr_t) param;
-       iwr.u.data.length = len;
-
-       if (ioctl(drv->sock, IPW_IOCTL_WPA_SUPPLICANT, &iwr) < 0) {
-               int ret = errno;
-               if (show_err) 
-                       perror("ioctl[IPW_IOCTL_WPA_SUPPLICANT]");
-               return ret;
-       }
-
-       return 0;
-}
-
-
-static void ipw_show_set_key_error(struct ipw_param *param)
-{
-       switch (param->u.crypt.err) {
-       case IPW_CRYPT_ERR_UNKNOWN_ALG:
-               wpa_printf(MSG_INFO, "Unknown algorithm '%s'.",
-                          param->u.crypt.alg);
-               wpa_printf(MSG_INFO, "You may need to load kernel module to "
-                          "register that algorithm.");
-               wpa_printf(MSG_INFO, "E.g., 'modprobe ieee80211_crypt_wep' for"
-                          " WEP.");
-               break;
-       case IPW_CRYPT_ERR_UNKNOWN_ADDR:
-               wpa_printf(MSG_INFO, "Unknown address " MACSTR ".",
-                          MAC2STR(param->sta_addr));
-               break;
-       case IPW_CRYPT_ERR_CRYPT_INIT_FAILED:
-               wpa_printf(MSG_INFO, "Crypt algorithm initialization failed.");
-               break;
-       case IPW_CRYPT_ERR_KEY_SET_FAILED:
-               wpa_printf(MSG_INFO, "Key setting failed.");
-               break;
-       case IPW_CRYPT_ERR_TX_KEY_SET_FAILED:
-               wpa_printf(MSG_INFO, "TX key index setting failed.");
-               break;
-       case IPW_CRYPT_ERR_CARD_CONF_FAILED:
-               wpa_printf(MSG_INFO, "Card configuration failed.");
-               break;
-       }
-}
-
-
-static int ipw_set_wpa_ie(struct wpa_driver_ipw_data *drv,
-                         const u8 *wpa_ie, size_t wpa_ie_len)
-{
-       struct ipw_param *param;
-       int ret;
-       size_t blen = sizeof(*param) + wpa_ie_len;
-
-       param = os_zalloc(blen);
-       if (param == NULL)
-               return -1;
-
-       param->cmd = IPW_CMD_SET_WPA_IE;
-       param->u.wpa_ie.len = wpa_ie_len;
-       os_memcpy(param->u.wpa_ie.data, wpa_ie, wpa_ie_len);
-       
-       ret = ipw_ioctl(drv, param, blen, 1);
-
-       os_free(param);
-       return ret;
-}
-
-
-static int ipw_set_wpa_param(struct wpa_driver_ipw_data *drv, u8 name,
-                            u32 value)
-{
-       struct ipw_param param;
-
-       os_memset(&param, 0, sizeof(param));
-       param.cmd = IPW_CMD_SET_WPA_PARAM;
-       param.u.wpa_param.name = name;
-       param.u.wpa_param.value = value;
-
-       return ipw_ioctl(drv, &param, sizeof(param), 1);
-}
-
-
-static int ipw_mlme(struct wpa_driver_ipw_data *drv, const u8 *addr,
-                   int cmd, int reason)
-{
-       struct ipw_param param;
-
-       os_memset(&param, 0, sizeof(param));
-       os_memcpy(param.sta_addr, addr, ETH_ALEN);      
-       param.cmd = IPW_CMD_MLME;
-       param.u.mlme.command = cmd;
-       param.u.mlme.reason_code = reason;
-
-       return ipw_ioctl(drv, &param, sizeof(param), 1);
-}
-
-
-static int wpa_driver_ipw_set_wpa(void *priv, int enabled)
-{
-       struct wpa_driver_ipw_data *drv = priv;
-       int ret = 0;
-
-       wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled);
-
-       if (!enabled && ipw_set_wpa_ie(drv, NULL, 0) < 0)
-               ret = -1;
-
-       if (ipw_set_wpa_param(drv, IPW_PARAM_WPA_ENABLED, enabled) < 0)
-               ret = -1;
-
-       return ret;
-}
-
-
-static int wpa_driver_ipw_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_ipw_data *drv = priv;
-       struct ipw_param *param;
-       u8 *buf;
-       size_t blen;
-       int ret = 0;
-       char *alg_name;
-
-       switch (alg) {
-       case WPA_ALG_NONE:
-               alg_name = "none";
-               break;
-       case WPA_ALG_WEP:
-               alg_name = "WEP";
-               break;
-       case WPA_ALG_TKIP:
-               alg_name = "TKIP";
-               break;
-       case WPA_ALG_CCMP:
-               alg_name = "CCMP";
-               break;
-       default:
-               return -1;
-       }
-
-       wpa_printf(MSG_DEBUG, "%s: alg=%s key_idx=%d set_tx=%d seq_len=%lu "
-                  "key_len=%lu", __FUNCTION__, alg_name, key_idx, set_tx,
-                  (unsigned long) seq_len, (unsigned long) key_len);
-
-       if (seq_len > 8)
-               return -2;
-
-       blen = sizeof(*param) + key_len;
-       buf = os_zalloc(blen);
-       if (buf == NULL)
-               return -1;
-
-       param = (struct ipw_param *) buf;
-       param->cmd = IPW_CMD_SET_ENCRYPTION;
-       os_memset(param->sta_addr, 0xff, ETH_ALEN);
-       os_strlcpy((char *) param->u.crypt.alg, alg_name,
-                  IPW_CRYPT_ALG_NAME_LEN);
-       param->u.crypt.set_tx = set_tx ? 1 : 0;
-       param->u.crypt.idx = key_idx;
-       os_memcpy(param->u.crypt.seq, seq, seq_len);
-       param->u.crypt.key_len = key_len;
-       os_memcpy((u8 *) (param + 1), key, key_len);
-
-       if (ipw_ioctl(drv, param, blen, 1)) {
-               wpa_printf(MSG_WARNING, "Failed to set encryption.");
-               ipw_show_set_key_error(param);
-               ret = -1;
-       }
-       os_free(buf);
-
-       return ret;
-}
-
-
-static int wpa_driver_ipw_set_countermeasures(void *priv, int enabled)
-{
-       struct wpa_driver_ipw_data *drv = priv;
-       wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled);
-       return ipw_set_wpa_param(drv, IPW_PARAM_TKIP_COUNTERMEASURES,
-                                    enabled);
-
-}
-
-
-static int wpa_driver_ipw_set_drop_unencrypted(void *priv, int enabled)
-{
-       struct wpa_driver_ipw_data *drv = priv;
-       wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled);
-       return ipw_set_wpa_param(drv, IPW_PARAM_DROP_UNENCRYPTED,
-                                    enabled);
-}
-
-
-static int wpa_driver_ipw_deauthenticate(void *priv, const u8 *addr,
-                                        int reason_code)
-{
-       struct wpa_driver_ipw_data *drv = priv;
-       return ipw_mlme(drv, addr, IPW_MLME_STA_DEAUTH, reason_code);
-}
-
-
-static int wpa_driver_ipw_disassociate(void *priv, const u8 *addr,
-                                      int reason_code)
-{
-       struct wpa_driver_ipw_data *drv = priv;
-       return ipw_mlme(drv, addr, IPW_MLME_STA_DISASSOC, reason_code);
-}
-
-
-static int
-wpa_driver_ipw_associate(void *priv, struct wpa_driver_associate_params *params)
-{
-       struct wpa_driver_ipw_data *drv = priv;
-       int ret = 0;
-       int unencrypted_eapol;
-
-       if (wpa_driver_ipw_set_auth_alg(drv, params->auth_alg) < 0)
-               ret = -1;
-       if (wpa_driver_ipw_set_drop_unencrypted(drv, params->drop_unencrypted)
-           < 0)
-               ret = -1;
-       if (ipw_set_wpa_ie(drv, params->wpa_ie, params->wpa_ie_len) < 0)
-               ret = -1;
-       if (wpa_driver_wext_set_ssid(drv->wext, params->ssid,
-                                    params->ssid_len) < 0)
-               ret = -1;
-       if (wpa_driver_wext_set_bssid(drv->wext, params->bssid) < 0)
-               ret = -1;
-
-       if (params->key_mgmt_suite == KEY_MGMT_802_1X ||
-           params->key_mgmt_suite == KEY_MGMT_PSK)
-               unencrypted_eapol = 0;
-       else
-               unencrypted_eapol = 1;
-       
-       if (ipw_set_wpa_param(drv, IPW_PARAM_IEEE_802_1X,
-                             unencrypted_eapol) < 0) {
-               wpa_printf(MSG_DEBUG, "ipw: Failed to configure "
-                          "ieee_802_1x param");
-       }
-
-       return ret;
-}
-
-
-static int wpa_driver_ipw_set_auth_alg(void *priv, int auth_alg)
-{
-       struct wpa_driver_ipw_data *drv = priv;
-       int algs = 0;
-
-       if (auth_alg & WPA_AUTH_ALG_OPEN)
-               algs |= 1;
-       if (auth_alg & WPA_AUTH_ALG_SHARED)
-               algs |= 2;
-       if (auth_alg & WPA_AUTH_ALG_LEAP)
-               algs |= 4;
-       if (algs == 0)
-               algs = 1; /* at least one algorithm should be set */
-
-       wpa_printf(MSG_DEBUG, "%s: auth_alg=0x%x", __FUNCTION__, algs);
-       return ipw_set_wpa_param(drv, IPW_PARAM_AUTH_ALGS, algs);
-}
-
-
-static int wpa_driver_ipw_get_bssid(void *priv, u8 *bssid)
-{
-       struct wpa_driver_ipw_data *drv = priv;
-       return wpa_driver_wext_get_bssid(drv->wext, bssid);
-}
-
-
-static int wpa_driver_ipw_get_ssid(void *priv, u8 *ssid)
-{
-       struct wpa_driver_ipw_data *drv = priv;
-       return wpa_driver_wext_get_ssid(drv->wext, ssid);
-}
-
-
-static int wpa_driver_ipw_scan(void *priv,
-                              struct wpa_driver_scan_params *params)
-{
-       struct wpa_driver_ipw_data *drv = priv;
-       return wpa_driver_wext_scan(drv->wext, params);
-}
-
-
-static struct wpa_scan_results * wpa_driver_ipw_get_scan_results(void *priv)
-{
-       struct wpa_driver_ipw_data *drv = priv;
-       return wpa_driver_wext_get_scan_results(drv->wext);
-}
-
-
-static int wpa_driver_ipw_set_operstate(void *priv, int state)
-{
-       struct wpa_driver_ipw_data *drv = priv;
-       return wpa_driver_wext_set_operstate(drv->wext, state);
-}
-
-
-static void * wpa_driver_ipw_init(void *ctx, const char *ifname)
-{
-       struct wpa_driver_ipw_data *drv;
-       int ver;
-
-       wpa_printf(MSG_DEBUG, "%s is called", __FUNCTION__);
-       drv = os_zalloc(sizeof(*drv));
-       if (drv == NULL)
-               return NULL;
-       drv->wext = wpa_driver_wext_init(ctx, ifname);
-       if (drv->wext == NULL) {
-               os_free(drv);
-               return NULL;
-       }
-
-       ver = wpa_driver_wext_get_version(drv->wext);
-       if (ver >= 18) {
-               wpa_printf(MSG_WARNING, "Linux wireless extensions version %d "
-                          "detected.", ver);
-               wpa_printf(MSG_WARNING, "ipw2x00 driver uses driver_wext "
-                          "(-Dwext) instead of driver_ipw.");
-       }
-
-       drv->ctx = ctx;
-       os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
-       drv->sock = socket(PF_INET, SOCK_DGRAM, 0);
-       if (drv->sock < 0) {
-               wpa_driver_wext_deinit(drv->wext);
-               os_free(drv);
-               return NULL;
-       }
-
-       wpa_driver_ipw_set_wpa(drv, 1);
-
-       return drv;
-}
-
-
-static void wpa_driver_ipw_deinit(void *priv)
-{
-       struct wpa_driver_ipw_data *drv = priv;
-       wpa_driver_ipw_set_wpa(drv, 0);
-       wpa_driver_wext_deinit(drv->wext);
-       close(drv->sock);
-       os_free(drv);
-}
-
-
-const struct wpa_driver_ops wpa_driver_ipw_ops = {
-       .name = "ipw",
-       .desc = "Intel ipw2100/2200 driver (old; use wext with Linux 2.6.13 "
-       "or newer)",
-       .get_bssid = wpa_driver_ipw_get_bssid,
-       .get_ssid = wpa_driver_ipw_get_ssid,
-       .set_key = wpa_driver_ipw_set_key,
-       .set_countermeasures = wpa_driver_ipw_set_countermeasures,
-       .scan2 = wpa_driver_ipw_scan,
-       .get_scan_results2 = wpa_driver_ipw_get_scan_results,
-       .deauthenticate = wpa_driver_ipw_deauthenticate,
-       .disassociate = wpa_driver_ipw_disassociate,
-       .associate = wpa_driver_ipw_associate,
-       .init = wpa_driver_ipw_init,
-       .deinit = wpa_driver_ipw_deinit,
-       .set_operstate = wpa_driver_ipw_set_operstate,
-};
index 8687404..0a855e7 100644 (file)
@@ -69,6 +69,7 @@
 #define MADWIFI_NG
 #endif /* IEEE80211_IOCTL_SETWMMPARAMS */
 
+#define WPA_KEY_RSC_LEN 8
 
 #ifdef HOSTAPD
 
@@ -461,7 +462,7 @@ wpa_driver_madwifi_set_key(const char *ifname, void *priv, enum wpa_alg alg,
        memset(&wk, 0, sizeof(wk));
        wk.ik_type = cipher;
        wk.ik_flags = IEEE80211_KEY_RECV | IEEE80211_KEY_XMIT;
-       if (addr == NULL) {
+       if (addr == NULL || is_broadcast_ether_addr(addr)) {
                memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN);
                wk.ik_keyix = key_idx;
                wk.ik_flags |= IEEE80211_KEY_DEFAULT;
@@ -733,6 +734,8 @@ static void madwifi_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf,
 
        os_memset(&event, 0, sizeof(event));
        event.rx_probe_req.sa = mgmt->sa;
+       event.rx_probe_req.da = mgmt->da;
+       event.rx_probe_req.bssid = mgmt->bssid;
        event.rx_probe_req.ie = mgmt->u.probe_req.variable;
        event.rx_probe_req.ie_len =
                len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req));
@@ -787,7 +790,8 @@ madwifi_set_wps_ie(void *priv, const u8 *ie, size_t len, u32 frametype)
 
 static int
 madwifi_set_ap_wps_ie(void *priv, const struct wpabuf *beacon,
-                     const struct wpabuf *proberesp)
+                     const struct wpabuf *proberesp,
+                     const struct wpabuf *assocresp)
 {
        if (madwifi_set_wps_ie(priv, beacon ? wpabuf_head(beacon) : NULL,
                               beacon ? wpabuf_len(beacon) : 0,
@@ -802,6 +806,24 @@ madwifi_set_ap_wps_ie(void *priv, const struct wpabuf *beacon,
 #define madwifi_set_ap_wps_ie NULL
 #endif /* CONFIG_WPS */
 
+static int madwifi_set_freq(void *priv, struct hostapd_freq_params *freq)
+{
+       struct madwifi_driver_data *drv = priv;
+       struct iwreq iwr;
+
+       os_memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+       iwr.u.freq.m = freq->channel;
+       iwr.u.freq.e = 0;
+
+       if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) {
+               perror("ioctl[SIOCSIWFREQ]");
+               return -1;
+       }
+
+       return 0;
+}
+
 static void
 madwifi_new_sta(struct madwifi_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN])
 {
@@ -846,7 +868,7 @@ madwifi_new_sta(struct madwifi_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN])
                ielen += 2;
 
 no_ie:
-       drv_event_assoc(hapd, addr, iebuf, ielen);
+       drv_event_assoc(hapd, addr, iebuf, ielen, 0);
 
        if (memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) {
                /* Cached accounting data is not valid anymore. */
@@ -1065,7 +1087,7 @@ madwifi_wireless_event_init(struct madwifi_driver_data *drv)
 
 static int
 madwifi_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len,
-                  int encrypt, const u8 *own_addr)
+                  int encrypt, const u8 *own_addr, u32 flags)
 {
        struct madwifi_driver_data *drv = priv;
        unsigned char buf[3000];
@@ -1509,8 +1531,7 @@ wpa_driver_madwifi_set_key(const char *ifname, void *priv, enum wpa_alg alg,
        wk.ik_keyix = key_idx;
        wk.ik_keylen = key_len;
 #ifdef WORDS_BIGENDIAN
-#define WPA_KEY_RSC_LEN 8
-       {
+       if (seq) {
                size_t i;
                u8 tmp[WPA_KEY_RSC_LEN];
                os_memset(tmp, 0, sizeof(tmp));
@@ -1519,7 +1540,8 @@ wpa_driver_madwifi_set_key(const char *ifname, void *priv, enum wpa_alg alg,
                os_memcpy(&wk.ik_keyrsc, tmp, WPA_KEY_RSC_LEN);
        }
 #else /* WORDS_BIGENDIAN */
-       os_memcpy(&wk.ik_keyrsc, seq, seq_len);
+       if (seq)
+               os_memcpy(&wk.ik_keyrsc, seq, seq_len);
 #endif /* WORDS_BIGENDIAN */
        os_memcpy(wk.ik_keydata, key, key_len);
 
@@ -1838,6 +1860,7 @@ const struct wpa_driver_ops wpa_driver_madwifi_ops = {
        .sta_clear_stats        = madwifi_sta_clear_stats,
        .commit                 = madwifi_commit,
        .set_ap_wps_ie          = madwifi_set_ap_wps_ie,
+       .set_freq               = madwifi_set_freq,
 #else /* HOSTAPD */
        .get_bssid              = wpa_driver_madwifi_get_bssid,
        .get_ssid               = wpa_driver_madwifi_get_ssid,
index 462dd81..dbe9a28 100644 (file)
@@ -1001,8 +1001,7 @@ static int wpa_driver_ndis_set_key(const char *ifname, void *priv,
        int res, pairwise;
        u8 bssid[ETH_ALEN];
 
-       if (addr == NULL || os_memcmp(addr, "\xff\xff\xff\xff\xff\xff",
-                                     ETH_ALEN) == 0) {
+       if (addr == NULL || is_broadcast_ether_addr(addr)) {
                /* Group Key */
                pairwise = 0;
                if (wpa_driver_ndis_get_bssid(drv, bssid) < 0)
@@ -1066,6 +1065,7 @@ wpa_driver_ndis_associate(void *priv,
 {
        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;
 
@@ -1091,7 +1091,6 @@ wpa_driver_ndis_associate(void *priv,
        if (params->key_mgmt_suite == KEY_MGMT_NONE ||
            params->key_mgmt_suite == KEY_MGMT_802_1X_NO_WPA) {
                /* Re-set WEP keys if static WEP configuration is used. */
-               u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
                int i;
                for (i = 0; i < 4; i++) {
                        if (!params->wep_key[i])
@@ -1125,6 +1124,22 @@ wpa_driver_ndis_associate(void *priv,
        } else if (params->key_mgmt_suite == 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;
@@ -1148,6 +1163,12 @@ wpa_driver_ndis_associate(void *priv,
                encr = Ndis802_11Encryption1Enabled;
                break;
        case CIPHER_NONE:
+#ifdef CONFIG_WPS
+               if (params->wps == WPS_MODE_PRIVACY) {
+                       encr = Ndis802_11Encryption1Enabled;
+                       break;
+               }
+#endif /* CONFIG_WPS */
                if (params->group_suite == CIPHER_CCMP)
                        encr = Ndis802_11Encryption3Enabled;
                else if (params->group_suite == CIPHER_TKIP)
@@ -1156,7 +1177,14 @@ wpa_driver_ndis_associate(void *priv,
                        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,
@@ -3185,94 +3213,33 @@ wpa_driver_ndis_get_interfaces(void *global_priv)
 }
 
 
-const struct wpa_driver_ops wpa_driver_ndis_ops = {
-       "ndis",
-       "Windows NDIS driver",
-       wpa_driver_ndis_get_bssid,
-       wpa_driver_ndis_get_ssid,
-       wpa_driver_ndis_set_key,
-       wpa_driver_ndis_init,
-       wpa_driver_ndis_deinit,
-       NULL /* set_param */,
-       NULL /* set_countermeasures */,
-       wpa_driver_ndis_deauthenticate,
-       wpa_driver_ndis_disassociate,
-       wpa_driver_ndis_associate,
-       wpa_driver_ndis_add_pmkid,
-       wpa_driver_ndis_remove_pmkid,
-       wpa_driver_ndis_flush_pmkid,
-       wpa_driver_ndis_get_capa,
-       wpa_driver_ndis_poll,
-       wpa_driver_ndis_get_ifname,
-       wpa_driver_ndis_get_mac_addr,
-       NULL /* send_eapol */,
-       NULL /* set_operstate */,
-       NULL /* mlme_setprotection */,
-       NULL /* get_hw_feature_data */,
-       NULL /* set_channel */,
-       NULL /* set_ssid */,
-       NULL /* set_bssid */,
-       NULL /* send_mlme */,
-       NULL /* mlme_add_sta */,
-       NULL /* mlme_remove_sta */,
-       NULL /* update_ft_ies */,
-       NULL /* send_ft_action */,
-       wpa_driver_ndis_get_scan_results,
-       NULL /* set_country */,
-       NULL /* global_init */,
-       NULL /* global_deinit */,
-       NULL /* init2 */,
-       wpa_driver_ndis_get_interfaces,
-       wpa_driver_ndis_scan,
-       NULL /* authenticate */,
-       NULL /* set_beacon */,
-       NULL /* hapd_init */,
-       NULL /* hapd_deinit */,
-       NULL /* set_ieee8021x */,
-       NULL /* set_privacy */,
-       NULL /* get_seqnum */,
-       NULL /* flush */,
-       NULL /* set_generic_elem */,
-       NULL /* read_sta_data */,
-       NULL /* hapd_send_eapol */,
-       NULL /* sta_deauth */,
-       NULL /* sta_disassoc */,
-       NULL /* sta_remove */,
-       NULL /* hapd_get_ssid */,
-       NULL /* hapd_set_ssid */,
-       NULL /* hapd_set_countermeasures */,
-       NULL /* sta_add */,
-       NULL /* get_inact_sec */,
-       NULL /* sta_clear_stats */,
-       NULL /* set_freq */,
-       NULL /* set_rts */,
-       NULL /* set_frag */,
-       NULL /* sta_set_flags */,
-       NULL /* set_rate_sets */,
-       NULL /* set_cts_protect */,
-       NULL /* set_preamble */,
-       NULL /* set_short_slot_time */,
-       NULL /* set_tx_queue_params */,
-       NULL /* valid_bss_mask */,
-       NULL /* if_add */,
-       NULL /* if_remove */,
-       NULL /* set_sta_vlan */,
-       NULL /* commit */,
-       NULL /* send_ether */,
-       NULL /* set_radius_acl_auth */,
-       NULL /* set_radius_acl_expire */,
-       NULL /* set_ht_params */,
-       NULL /* set_ap_wps_ie */,
-       NULL /* set_supp_port */,
-       NULL /* set_wds_sta */,
-       NULL /* send_action */,
-       NULL /* remain_on_channel */,
-       NULL /* cancel_remain_on_channel */,
-       NULL /* probe_req_report */,
-       NULL /* disable_11b_rates */,
-       NULL /* deinit_ap */,
-       NULL /* suspend */,
-       NULL /* resume */,
-       NULL /* signal_monitor */,
-       NULL /* send_frame */
-};
+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.disassociate = wpa_driver_ndis_disassociate;
+       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;
+}
diff --git a/src/drivers/driver_ndiswrapper.c b/src/drivers/driver_ndiswrapper.c
deleted file mode 100644 (file)
index cd2f61e..0000000
+++ /dev/null
@@ -1,378 +0,0 @@
-/*
- * WPA Supplicant - driver interaction with Linux ndiswrapper
- * Copyright (c) 2004-2006, Giridhar Pemmasani <giri@lmc.cs.sunysb.edu>
- * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
- *
- * Please note that ndiswrapper supports WPA configuration via Linux wireless
- * extensions and if the kernel includes support for this, driver_wext.c should
- * be used instead of this driver wrapper.
- */
-
-#include "includes.h"
-#include <sys/ioctl.h>
-
-#include "wireless_copy.h"
-#include "common.h"
-#include "driver.h"
-#include "driver_wext.h"
-
-struct wpa_driver_ndiswrapper_data {
-       void *wext; /* private data for driver_wext */
-       void *ctx;
-       char ifname[IFNAMSIZ + 1];
-       int sock;
-};
-
-
-struct wpa_key {
-       enum wpa_alg alg;
-       const u8 *addr;
-       int key_index;
-       int set_tx;
-       const u8 *seq;
-       size_t seq_len;
-       const u8 *key;
-       size_t key_len;
-};
-
-struct wpa_assoc_info {
-       const u8 *bssid;
-       const u8 *ssid;
-       size_t ssid_len;
-       int freq;
-       const u8 *wpa_ie;
-       size_t wpa_ie_len;
-       enum wpa_cipher pairwise_suite;
-       enum wpa_cipher group_suite;
-       enum wpa_key_mgmt key_mgmt_suite;
-       int auth_alg;
-       int mode;
-};
-
-#define PRIV_RESET                     SIOCIWFIRSTPRIV+0
-#define WPA_SET_WPA                    SIOCIWFIRSTPRIV+1
-#define WPA_SET_KEY                    SIOCIWFIRSTPRIV+2
-#define WPA_ASSOCIATE                  SIOCIWFIRSTPRIV+3
-#define WPA_DISASSOCIATE               SIOCIWFIRSTPRIV+4
-#define WPA_DROP_UNENCRYPTED           SIOCIWFIRSTPRIV+5
-#define WPA_SET_COUNTERMEASURES        SIOCIWFIRSTPRIV+6
-#define WPA_DEAUTHENTICATE             SIOCIWFIRSTPRIV+7
-#define WPA_SET_AUTH_ALG               SIOCIWFIRSTPRIV+8
-#define WPA_INIT                       SIOCIWFIRSTPRIV+9
-#define WPA_DEINIT                     SIOCIWFIRSTPRIV+10
-#define WPA_GET_CAPA                   SIOCIWFIRSTPRIV+11
-
-static int wpa_ndiswrapper_set_auth_alg(void *priv, int auth_alg);
-
-static int get_socket(void)
-{
-       static const int families[] = {
-               AF_INET, AF_IPX, AF_AX25, AF_APPLETALK
-       };
-       unsigned int i;
-       int sock;
-
-       for (i = 0; i < sizeof(families) / sizeof(int); ++i) {
-               sock = socket(families[i], SOCK_DGRAM, 0);
-               if (sock >= 0)
-                       return sock;
-       }
-
-       return -1;
-}
-
-static int iw_set_ext(struct wpa_driver_ndiswrapper_data *drv, int request,
-                     struct iwreq *pwrq)
-{
-       os_strlcpy(pwrq->ifr_name, drv->ifname, IFNAMSIZ);
-       return ioctl(drv->sock, request, pwrq);
-}
-
-static int wpa_ndiswrapper_set_wpa(void *priv, int enabled)
-{
-       struct wpa_driver_ndiswrapper_data *drv = priv;
-       struct iwreq priv_req;
-       int ret = 0;
-
-       os_memset(&priv_req, 0, sizeof(priv_req));
-
-       priv_req.u.data.flags = enabled;
-       if (iw_set_ext(drv, WPA_SET_WPA, &priv_req) < 0)
-               ret = -1;
-       return ret;
-}
-
-static int wpa_ndiswrapper_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_ndiswrapper_data *drv = priv;
-       struct wpa_key wpa_key;
-       int ret = 0;
-       struct iwreq priv_req;
-
-       os_memset(&priv_req, 0, sizeof(priv_req));
-
-       wpa_key.alg = alg;
-       wpa_key.addr = addr;
-       wpa_key.key_index = key_idx;
-       wpa_key.set_tx = set_tx;
-       wpa_key.seq = seq;
-       wpa_key.seq_len = seq_len;
-       wpa_key.key = key;
-       wpa_key.key_len = key_len;
-
-       priv_req.u.data.pointer = (void *)&wpa_key;
-       priv_req.u.data.length = sizeof(wpa_key);
-
-       if (iw_set_ext(drv, WPA_SET_KEY, &priv_req) < 0)
-               ret = -1;
-
-       if (alg == WPA_ALG_NONE) {
-               /*
-                * ndiswrapper did not seem to be clearing keys properly in
-                * some cases with WPA_SET_KEY. For example, roaming from WPA
-                * enabled AP to plaintext one seemed to fail since the driver
-                * did not associate. Try to make sure the keys are cleared so
-                * that plaintext APs can be used in all cases.
-                */
-               wpa_driver_wext_set_key(ifname, drv->wext, alg, addr, key_idx,
-                                       set_tx, seq, seq_len, key, key_len);
-       }
-
-       return ret;
-}
-
-static int wpa_ndiswrapper_set_countermeasures(void *priv, int enabled)
-{
-       struct wpa_driver_ndiswrapper_data *drv = priv;
-       int ret = 0;
-       struct iwreq priv_req;
-
-       os_memset(&priv_req, 0, sizeof(priv_req));
-
-       priv_req.u.param.value = enabled;
-       if (iw_set_ext(drv, WPA_SET_COUNTERMEASURES, &priv_req) < 0)
-               ret = -1;
-
-       return ret;
-}
-
-static int wpa_ndiswrapper_set_drop_unencrypted(void *priv,
-                                               int enabled)
-{
-       struct wpa_driver_ndiswrapper_data *drv = priv;
-       int ret = 0;
-       struct iwreq priv_req;
-
-       os_memset(&priv_req, 0, sizeof(priv_req));
-
-       priv_req.u.param.value = enabled;
-       if (iw_set_ext(drv, WPA_DROP_UNENCRYPTED, &priv_req) < 0)
-               ret = -1;
-       return ret;
-}
-
-static int wpa_ndiswrapper_deauthenticate(void *priv, const u8 *addr,
-                                         int reason_code)
-{
-       struct wpa_driver_ndiswrapper_data *drv = priv;
-       int ret = 0;
-       struct iwreq priv_req;
-
-       os_memset(&priv_req, 0, sizeof(priv_req));
-
-       priv_req.u.param.value = reason_code;
-       os_memcpy(&priv_req.u.ap_addr.sa_data, addr, ETH_ALEN);
-       if (iw_set_ext(drv, WPA_DEAUTHENTICATE, &priv_req) < 0)
-               ret = -1;
-       return ret;
-}
-
-static int wpa_ndiswrapper_disassociate(void *priv, const u8 *addr,
-                                       int reason_code)
-{
-       struct wpa_driver_ndiswrapper_data *drv = priv;
-       int ret = 0;
-       struct iwreq priv_req;
-
-       os_memset(&priv_req, 0, sizeof(priv_req));
-
-       os_memcpy(&priv_req.u.ap_addr.sa_data, addr, ETH_ALEN);
-       if (iw_set_ext(drv, WPA_DISASSOCIATE, &priv_req) < 0)
-               ret = -1;
-       return ret;
-}
-
-static int
-wpa_ndiswrapper_associate(void *priv,
-                         struct wpa_driver_associate_params *params)
-{
-       struct wpa_driver_ndiswrapper_data *drv = priv;
-       int ret = 0;
-       struct wpa_assoc_info wpa_assoc_info;
-       struct iwreq priv_req;
-
-       if (wpa_ndiswrapper_set_drop_unencrypted(drv,
-                                                params->drop_unencrypted) < 0)
-               ret = -1;
-       if (wpa_ndiswrapper_set_auth_alg(drv, params->auth_alg) < 0)
-               ret = -1;
-
-       os_memset(&priv_req, 0, sizeof(priv_req));
-       os_memset(&wpa_assoc_info, 0, sizeof(wpa_assoc_info));
-
-       wpa_assoc_info.bssid = params->bssid;
-       wpa_assoc_info.ssid = params->ssid;
-       wpa_assoc_info.ssid_len = params->ssid_len;
-       wpa_assoc_info.freq = params->freq;
-       wpa_assoc_info.wpa_ie = params->wpa_ie;
-       wpa_assoc_info.wpa_ie_len = params->wpa_ie_len;
-       wpa_assoc_info.pairwise_suite = params->pairwise_suite;
-       wpa_assoc_info.group_suite = params->group_suite;
-       wpa_assoc_info.key_mgmt_suite = params->key_mgmt_suite;
-       wpa_assoc_info.auth_alg = params->auth_alg;
-       wpa_assoc_info.mode = params->mode;
-
-       priv_req.u.data.pointer = (void *)&wpa_assoc_info;
-       priv_req.u.data.length = sizeof(wpa_assoc_info);
-
-       if (iw_set_ext(drv, WPA_ASSOCIATE, &priv_req) < 0)
-               ret = -1;
-       return ret;
-}
-
-static int wpa_ndiswrapper_set_auth_alg(void *priv, int auth_alg)
-{
-       struct wpa_driver_ndiswrapper_data *drv = priv;
-       int ret = 0;
-       struct iwreq priv_req;
-
-       os_memset(&priv_req, 0, sizeof(priv_req));
-
-       priv_req.u.param.value = auth_alg;
-       if (iw_set_ext(drv, WPA_SET_AUTH_ALG, &priv_req) < 0)
-               ret = -1;
-       return ret;
-}
-
-static int wpa_ndiswrapper_get_bssid(void *priv, u8 *bssid)
-{
-       struct wpa_driver_ndiswrapper_data *drv = priv;
-       return wpa_driver_wext_get_bssid(drv->wext, bssid);
-}
-
-
-static int wpa_ndiswrapper_get_ssid(void *priv, u8 *ssid)
-{
-       struct wpa_driver_ndiswrapper_data *drv = priv;
-       return wpa_driver_wext_get_ssid(drv->wext, ssid);
-}
-
-
-static int wpa_ndiswrapper_scan(void *priv,
-                               struct wpa_driver_scan_params *params)
-{
-       struct wpa_driver_ndiswrapper_data *drv = priv;
-       return wpa_driver_wext_scan(drv->wext, params);
-}
-
-
-static struct wpa_scan_results * wpa_ndiswrapper_get_scan_results(void *priv)
-{
-       struct wpa_driver_ndiswrapper_data *drv = priv;
-       return wpa_driver_wext_get_scan_results(drv->wext);
-}
-
-
-static int wpa_ndiswrapper_get_capa(void *priv, struct wpa_driver_capa *capa)
-{
-       struct wpa_driver_ndiswrapper_data *drv = priv;
-       int ret = 0;
-       struct iwreq priv_req;
-
-       os_memset(&priv_req, 0, sizeof(priv_req));
-
-       priv_req.u.data.pointer = (void *) capa;
-       priv_req.u.data.length = sizeof(*capa);
-       if (iw_set_ext(drv, WPA_GET_CAPA, &priv_req) < 0)
-               ret = -1;
-       return ret;
-       
-}
-
-
-static int wpa_ndiswrapper_set_operstate(void *priv, int state)
-{
-       struct wpa_driver_ndiswrapper_data *drv = priv;
-       return wpa_driver_wext_set_operstate(drv->wext, state);
-}
-
-
-static void * wpa_ndiswrapper_init(void *ctx, const char *ifname)
-{
-       struct wpa_driver_ndiswrapper_data *drv;
-
-       drv = os_zalloc(sizeof(*drv));
-       if (drv == NULL)
-               return NULL;
-       drv->wext = wpa_driver_wext_init(ctx, ifname);
-       if (drv->wext == NULL) {
-               os_free(drv);
-               return NULL;
-       }
-
-       drv->ctx = ctx;
-       os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
-       drv->sock = get_socket();
-       if (drv->sock < 0) {
-               wpa_driver_wext_deinit(drv->wext);
-               os_free(drv);
-               return NULL;
-       }
-
-       wpa_ndiswrapper_set_wpa(drv, 1);
-
-       return drv;
-}
-
-
-static void wpa_ndiswrapper_deinit(void *priv)
-{
-       struct wpa_driver_ndiswrapper_data *drv = priv;
-       wpa_ndiswrapper_set_wpa(drv, 0);
-       wpa_driver_wext_deinit(drv->wext);
-       close(drv->sock);
-       os_free(drv);
-}
-
-
-const struct wpa_driver_ops wpa_driver_ndiswrapper_ops = {
-       .name = "ndiswrapper",
-       .desc = "Linux ndiswrapper (deprecated; use wext)",
-       .set_key = wpa_ndiswrapper_set_key,
-       .set_countermeasures = wpa_ndiswrapper_set_countermeasures,
-       .deauthenticate = wpa_ndiswrapper_deauthenticate,
-       .disassociate = wpa_ndiswrapper_disassociate,
-       .associate = wpa_ndiswrapper_associate,
-
-       .get_bssid = wpa_ndiswrapper_get_bssid,
-       .get_ssid = wpa_ndiswrapper_get_ssid,
-       .scan2 = wpa_ndiswrapper_scan,
-       .get_scan_results2 = wpa_ndiswrapper_get_scan_results,
-       .init = wpa_ndiswrapper_init,
-       .deinit = wpa_ndiswrapper_deinit,
-       .get_capa = wpa_ndiswrapper_get_capa,
-       .set_operstate = wpa_ndiswrapper_set_operstate,
-};
index 364158c..e12e511 100644 (file)
 
 #include "includes.h"
 #include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
 #include <net/if.h>
 #include <netlink/genl/genl.h>
 #include <netlink/genl/family.h>
 #include <netlink/genl/ctrl.h>
+#include <linux/rtnetlink.h>
 #include <netpacket/packet.h>
 #include <linux/filter.h>
 #include "nl80211_copy.h"
 
 #include "common.h"
 #include "eloop.h"
+#include "utils/list.h"
 #include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "l2_packet/l2_packet.h"
 #include "netlink.h"
 #include "linux_ioctl.h"
 #include "radiotap.h"
 #include "radiotap_iter.h"
+#include "rfkill.h"
 #include "driver.h"
 
 #ifdef CONFIG_LIBNL20
 /* libnl 2.0 compatibility code */
 #define nl_handle nl_sock
-#define nl_handle_alloc_cb nl_socket_alloc_cb
-#define nl_handle_destroy nl_socket_free
+#define nl80211_handle_alloc nl_socket_alloc_cb
+#define nl80211_handle_destroy nl_socket_free
+#else
+/*
+ * libnl 1.1 has a bug, it tries to allocate socket numbers densely
+ * but when you free a socket again it will mess up its bitmap and
+ * and use the wrong number the next time it needs a socket ID.
+ * Therefore, we wrap the handle alloc/destroy and add our own pid
+ * accounting.
+ */
+static uint32_t port_bitmap[32] = { 0 };
+
+static struct nl_handle *nl80211_handle_alloc(void *cb)
+{
+       struct nl_handle *handle;
+       uint32_t pid = getpid() & 0x3FFFFF;
+       int i;
+
+       handle = nl_handle_alloc_cb(cb);
+
+       for (i = 0; i < 1024; i++) {
+               if (port_bitmap[i / 32] & (1 << (i % 32)))
+                       continue;
+               port_bitmap[i / 32] |= 1 << (i % 32);
+               pid += i << 22;
+               break;
+       }
+
+       nl_socket_set_local_port(handle, pid);
+
+       return handle;
+}
+
+static void nl80211_handle_destroy(struct nl_handle *handle)
+{
+       uint32_t port = nl_socket_get_local_port(handle);
+
+       port >>= 22;
+       port_bitmap[port / 32] &= ~(1 << (port % 32));
+
+       nl_handle_destroy(handle);
+}
+
+static inline int __genl_ctrl_alloc_cache(struct nl_handle *h,
+                                         struct nl_cache **cache)
+{
+       struct nl_cache *tmp = genl_ctrl_alloc_cache(h);
+       if (!tmp)
+               return -ENOMEM;
+       *cache = tmp;
+       return 0;
+}
+#define genl_ctrl_alloc_cache __genl_ctrl_alloc_cache
 #endif /* CONFIG_LIBNL20 */
 
 
+struct nl80211_handles {
+       struct nl_handle *handle;
+       struct nl_cache *cache;
+};
+
+
+static int nl_create_handles(struct nl80211_handles *handles, struct nl_cb *cb,
+                            const char *dbg)
+{
+       if (!handles)
+               return -1;
+
+       handles->handle = nl80211_handle_alloc(cb);
+       if (handles->handle == NULL) {
+               wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink "
+                          "callbacks (%s)", dbg);
+               return -1;
+       }
+
+       if (genl_connect(handles->handle)) {
+               wpa_printf(MSG_ERROR, "nl80211: Failed to connect to generic "
+                          "netlink (%s)", dbg);
+               goto err;
+       }
+
+       if (genl_ctrl_alloc_cache(handles->handle, &handles->cache) < 0) {
+               wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic "
+                          "netlink cache (%s)", dbg);
+               goto err;
+       }
+
+       return 0;
+err:
+       nl80211_handle_destroy(handles->handle);
+       return -1;
+}
+
+
+static void nl_destroy_handles(struct nl80211_handles *handles)
+{
+       if (handles->handle == NULL)
+               return;
+       nl_cache_free(handles->cache);
+       nl80211_handle_destroy(handles->handle);
+       handles->handle = NULL;
+}
+
+
 #ifndef IFF_LOWER_UP
 #define IFF_LOWER_UP   0x10000         /* driver signals L1 up         */
 #endif
 #define IF_OPER_UP 6
 #endif
 
+struct nl80211_global {
+       struct dl_list interfaces;
+       int if_add_ifindex;
+       struct netlink_data *netlink;
+       struct nl_cb *nl_cb;
+       struct nl80211_handles nl;
+       struct genl_family *nl80211;
+       int ioctl_sock; /* socket for ioctl() use */
+};
+
+static void nl80211_global_deinit(void *priv);
+static void wpa_driver_nl80211_deinit(void *priv);
+
 struct i802_bss {
        struct wpa_driver_nl80211_data *drv;
        struct i802_bss *next;
        int ifindex;
        char ifname[IFNAMSIZ + 1];
+       char brname[IFNAMSIZ];
        unsigned int beacon_set:1;
+       unsigned int added_if_into_bridge:1;
+       unsigned int added_bridge:1;
 };
 
 struct wpa_driver_nl80211_data {
+       struct nl80211_global *global;
+       struct dl_list list;
+       u8 addr[ETH_ALEN];
+       char phyname[32];
        void *ctx;
-       struct netlink_data *netlink;
-       int ioctl_sock; /* socket for ioctl() use */
-       char brname[IFNAMSIZ];
        int ifindex;
        int if_removed;
+       int if_disabled;
+       int ignore_if_down_event;
+       struct rfkill_data *rfkill;
        struct wpa_driver_capa capa;
        int has_capability;
 
@@ -79,39 +206,40 @@ struct wpa_driver_nl80211_data {
 
        int scan_complete_events;
 
-       struct nl_handle *nl_handle;
-       struct nl_handle *nl_handle_event;
-       struct nl_cache *nl_cache;
-       struct nl_cache *nl_cache_event;
-       struct nl_cb *nl_cb;
-       struct genl_family *nl80211;
+       struct nl80211_handles nl_event, nl_preq;
 
        u8 auth_bssid[ETH_ALEN];
        u8 bssid[ETH_ALEN];
        int associated;
        u8 ssid[32];
        size_t ssid_len;
-       int nlmode;
-       int ap_scan_as_station;
+       enum nl80211_iftype nlmode;
+       enum nl80211_iftype ap_scan_as_station;
        unsigned int assoc_freq;
 
        int monitor_sock;
        int monitor_ifidx;
-       int probe_req_report;
-       int disable_11b_rates;
+       int no_monitor_iface_capab;
 
+       unsigned int disabled_11b_rates:1;
        unsigned int pending_remain_on_chan:1;
-       unsigned int added_bridge:1;
-       unsigned int added_if_into_bridge:1;
+       unsigned int in_interface_list:1;
 
        u64 remain_on_chan_cookie;
        u64 send_action_cookie;
 
+       unsigned int last_mgmt_freq;
+       unsigned int ap_oper_freq;
+
        struct wpa_driver_scan_filter *filter_ssids;
        size_t num_filter_ssids;
 
        struct i802_bss first_bss;
 
+#ifdef CONFIG_AP
+       struct l2_packet_data *l2;
+#endif /* CONFIG_AP */
+
 #ifdef HOSTAPD
        int eapol_sock; /* socket for EAPOL frames */
 
@@ -127,7 +255,8 @@ struct wpa_driver_nl80211_data {
 
 static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx,
                                            void *timeout_ctx);
-static int wpa_driver_nl80211_set_mode(void *priv, int mode);
+static int wpa_driver_nl80211_set_mode(struct i802_bss *bss,
+                                      enum nl80211_iftype nlmode);
 static int
 wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv);
 static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv,
@@ -135,6 +264,11 @@ static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv,
                                   int local_state_change);
 static void nl80211_remove_monitor_interface(
        struct wpa_driver_nl80211_data *drv);
+static int nl80211_send_frame_cmd(struct wpa_driver_nl80211_data *drv,
+                                 unsigned int freq, unsigned int wait,
+                                 const u8 *buf, size_t buf_len, u64 *cookie,
+                                 int no_cck);
+static int wpa_driver_nl80211_probe_req_report(void *priv, int report);
 
 #ifdef HOSTAPD
 static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx);
@@ -144,18 +278,57 @@ static int wpa_driver_nl80211_if_remove(void *priv,
                                        enum wpa_driver_if_type type,
                                        const char *ifname);
 #else /* HOSTAPD */
-static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+static inline void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+{
+}
+
+static inline void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+{
+}
+
+static inline int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
 {
        return 0;
 }
 #endif /* HOSTAPD */
 
 static int i802_set_freq(void *priv, struct hostapd_freq_params *freq);
-static void wpa_driver_nl80211_probe_req_report_timeout(void *eloop_ctx,
-                                                       void *timeout_ctx);
 static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv,
                                     int ifindex, int disabled);
 
+static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv);
+
+
+static int is_ap_interface(enum nl80211_iftype nlmode)
+{
+       return (nlmode == NL80211_IFTYPE_AP ||
+               nlmode == NL80211_IFTYPE_P2P_GO);
+}
+
+
+static int is_sta_interface(enum nl80211_iftype nlmode)
+{
+       return (nlmode == NL80211_IFTYPE_STATION ||
+               nlmode == NL80211_IFTYPE_P2P_CLIENT);
+}
+
+
+static int is_p2p_interface(enum nl80211_iftype nlmode)
+{
+       return (nlmode == NL80211_IFTYPE_P2P_CLIENT ||
+               nlmode == NL80211_IFTYPE_P2P_GO);
+}
+
+
+struct nl80211_bss_info_arg {
+       struct wpa_driver_nl80211_data *drv;
+       struct wpa_scan_results *res;
+       unsigned int assoc_freq;
+       u8 assoc_bssid[ETH_ALEN];
+};
+
+static int bss_info_handler(struct nl_msg *msg, void *arg);
+
 
 /* nl80211 code */
 static int ack_handler(struct nl_msg *msg, void *arg)
@@ -195,7 +368,7 @@ static int send_and_recv(struct wpa_driver_nl80211_data *drv,
        struct nl_cb *cb;
        int err = -ENOMEM;
 
-       cb = nl_cb_clone(drv->nl_cb);
+       cb = nl_cb_clone(drv->global->nl_cb);
        if (!cb)
                goto out;
 
@@ -227,7 +400,7 @@ static int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv,
                              int (*valid_handler)(struct nl_msg *, void *),
                              void *valid_data)
 {
-       return send_and_recv(drv, drv->nl_handle, msg, valid_handler,
+       return send_and_recv(drv, drv->global->nl.handle, msg, valid_handler,
                             valid_data);
 }
 
@@ -279,7 +452,8 @@ static int nl_get_multicast_id(struct wpa_driver_nl80211_data *drv,
        msg = nlmsg_alloc();
        if (!msg)
                return -ENOMEM;
-       genlmsg_put(msg, 0, 0, genl_ctrl_resolve(drv->nl_handle, "nlctrl"),
+       genlmsg_put(msg, 0, 0,
+                   genl_ctrl_resolve(drv->global->nl.handle, "nlctrl"),
                    0, 0, CTRL_CMD_GETFAMILY, 0);
        NLA_PUT_STRING(msg, CTRL_ATTR_FAMILY_NAME, family);
 
@@ -294,6 +468,14 @@ nla_put_failure:
 }
 
 
+static void * nl80211_cmd(struct wpa_driver_nl80211_data *drv,
+                         struct nl_msg *msg, int flags, uint8_t cmd)
+{
+       return genlmsg_put(msg, 0, 0, genl_family_get_id(drv->global->nl80211),
+                          0, flags, cmd, 0);
+}
+
+
 static int wpa_driver_nl80211_get_bssid(void *priv, u8 *bssid)
 {
        struct i802_bss *bss = priv;
@@ -334,10 +516,28 @@ static void wpa_driver_nl80211_event_link(struct wpa_driver_nl80211_data *drv,
                   del ? "removed" : "added");
 
        if (os_strcmp(drv->first_bss.ifname, event.interface_status.ifname) == 0) {
-               if (del)
+               if (del) {
+                       if (drv->if_removed) {
+                               wpa_printf(MSG_DEBUG, "nl80211: if_removed "
+                                          "already set - ignore event");
+                               return;
+                       }
                        drv->if_removed = 1;
-               else
+               } else {
+                       if (if_nametoindex(drv->first_bss.ifname) == 0) {
+                               wpa_printf(MSG_DEBUG, "nl80211: Interface %s "
+                                          "does not exist - ignore "
+                                          "RTM_NEWLINK",
+                                          drv->first_bss.ifname);
+                               return;
+                       }
+                       if (!drv->if_removed) {
+                               wpa_printf(MSG_DEBUG, "nl80211: if_removed "
+                                          "already cleared - ignore event");
+                               return;
+                       }
                        drv->if_removed = 0;
+               }
        }
 
        wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
@@ -387,17 +587,33 @@ static int wpa_driver_nl80211_own_ifindex(struct wpa_driver_nl80211_data *drv,
 }
 
 
+static struct wpa_driver_nl80211_data *
+nl80211_find_drv(struct nl80211_global *global, int idx, u8 *buf, size_t len)
+{
+       struct wpa_driver_nl80211_data *drv;
+       dl_list_for_each(drv, &global->interfaces,
+                        struct wpa_driver_nl80211_data, list) {
+               if (wpa_driver_nl80211_own_ifindex(drv, idx, buf, len) ||
+                   have_ifidx(drv, idx))
+                       return drv;
+       }
+       return NULL;
+}
+
+
 static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
                                                 struct ifinfomsg *ifi,
                                                 u8 *buf, size_t len)
 {
-       struct wpa_driver_nl80211_data *drv = ctx;
+       struct nl80211_global *global = ctx;
+       struct wpa_driver_nl80211_data *drv;
        int attrlen, rta_len;
        struct rtattr *attr;
        u32 brid = 0;
+       char namebuf[IFNAMSIZ];
 
-       if (!wpa_driver_nl80211_own_ifindex(drv, ifi->ifi_index, buf, len) &&
-           !have_ifidx(drv, ifi->ifi_index)) {
+       drv = nl80211_find_drv(global, ifi->ifi_index, buf, len);
+       if (!drv) {
                wpa_printf(MSG_DEBUG, "nl80211: Ignore event for foreign "
                           "ifindex %d", ifi->ifi_index);
                return;
@@ -410,6 +626,50 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
                   (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "",
                   (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
                   (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
+
+       if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) {
+               if (if_indextoname(ifi->ifi_index, namebuf) &&
+                   linux_iface_up(drv->global->ioctl_sock,
+                                  drv->first_bss.ifname) > 0) {
+                       wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down "
+                                  "event since interface %s is up", namebuf);
+                       return;
+               }
+               wpa_printf(MSG_DEBUG, "nl80211: Interface down");
+               if (drv->ignore_if_down_event) {
+                       wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down "
+                                  "event generated by mode change");
+                       drv->ignore_if_down_event = 0;
+               } else {
+                       drv->if_disabled = 1;
+                       wpa_supplicant_event(drv->ctx,
+                                            EVENT_INTERFACE_DISABLED, NULL);
+               }
+       }
+
+       if (drv->if_disabled && (ifi->ifi_flags & IFF_UP)) {
+               if (if_indextoname(ifi->ifi_index, namebuf) &&
+                   linux_iface_up(drv->global->ioctl_sock,
+                                  drv->first_bss.ifname) == 0) {
+                       wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up "
+                                  "event since interface %s is down",
+                                  namebuf);
+               } else if (if_nametoindex(drv->first_bss.ifname) == 0) {
+                       wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up "
+                                  "event since interface %s does not exist",
+                                  drv->first_bss.ifname);
+               } else if (drv->if_removed) {
+                       wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up "
+                                  "event since interface %s is marked "
+                                  "removed", drv->first_bss.ifname);
+               } else {
+                       wpa_printf(MSG_DEBUG, "nl80211: Interface up");
+                       drv->if_disabled = 0;
+                       wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED,
+                                            NULL);
+               }
+       }
+
        /*
         * Some drivers send the association event before the operup event--in
         * this case, lifting operstate in wpa_driver_nl80211_set_operstate()
@@ -419,7 +679,7 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
        if (drv->operstate == 1 &&
            (ifi->ifi_flags & (IFF_LOWER_UP | IFF_DORMANT)) == IFF_LOWER_UP &&
            !(ifi->ifi_flags & IFF_RUNNING))
-               netlink_send_oper_ifla(drv->netlink, drv->ifindex,
+               netlink_send_oper_ifla(drv->global->netlink, drv->ifindex,
                                       -1, IF_OPER_UP);
 
        attrlen = len;
@@ -436,16 +696,13 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
                attr = RTA_NEXT(attr, attrlen);
        }
 
-#ifdef HOSTAPD
        if (ifi->ifi_family == AF_BRIDGE && brid) {
                /* device has been added to bridge */
-               char namebuf[IFNAMSIZ];
                if_indextoname(brid, namebuf);
                wpa_printf(MSG_DEBUG, "nl80211: Add ifindex %u for bridge %s",
                           brid, namebuf);
                add_ifidx(drv, brid);
        }
-#endif /* HOSTAPD */
 }
 
 
@@ -453,11 +710,19 @@ static void wpa_driver_nl80211_event_rtm_dellink(void *ctx,
                                                 struct ifinfomsg *ifi,
                                                 u8 *buf, size_t len)
 {
-       struct wpa_driver_nl80211_data *drv = ctx;
+       struct nl80211_global *global = ctx;
+       struct wpa_driver_nl80211_data *drv;
        int attrlen, rta_len;
        struct rtattr *attr;
        u32 brid = 0;
 
+       drv = nl80211_find_drv(global, ifi->ifi_index, buf, len);
+       if (!drv) {
+               wpa_printf(MSG_DEBUG, "nl80211: Ignore dellink event for "
+                          "foreign ifindex %d", ifi->ifi_index);
+               return;
+       }
+
        attrlen = len;
        attr = (struct rtattr *) buf;
 
@@ -473,7 +738,6 @@ static void wpa_driver_nl80211_event_rtm_dellink(void *ctx,
                attr = RTA_NEXT(attr, attrlen);
        }
 
-#ifdef HOSTAPD
        if (ifi->ifi_family == AF_BRIDGE && brid) {
                /* device has been removed from bridge */
                char namebuf[IFNAMSIZ];
@@ -482,7 +746,6 @@ static void wpa_driver_nl80211_event_rtm_dellink(void *ctx,
                           "%s", brid, namebuf);
                del_ifidx(drv, brid);
        }
-#endif /* HOSTAPD */
 }
 
 
@@ -513,6 +776,37 @@ static void mlme_event_auth(struct wpa_driver_nl80211_data *drv,
 }
 
 
+static unsigned int nl80211_get_assoc_freq(struct wpa_driver_nl80211_data *drv)
+{
+       struct nl_msg *msg;
+       int ret;
+       struct nl80211_bss_info_arg arg;
+
+       os_memset(&arg, 0, sizeof(arg));
+       msg = nlmsg_alloc();
+       if (!msg)
+               goto nla_put_failure;
+
+       nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SCAN);
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+
+       arg.drv = drv;
+       ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg);
+       msg = NULL;
+       if (ret == 0) {
+               wpa_printf(MSG_DEBUG, "nl80211: Operating frequency for the "
+                          "associated BSS from scan results: %u MHz",
+                          arg.assoc_freq);
+               return arg.assoc_freq ? arg.assoc_freq : drv->assoc_freq;
+       }
+       wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d "
+                  "(%s)", ret, strerror(-ret));
+nla_put_failure:
+       nlmsg_free(msg);
+       return drv->assoc_freq;
+}
+
+
 static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv,
                            const u8 *frame, size_t len)
 {
@@ -530,6 +824,7 @@ static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv,
        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;
@@ -578,6 +873,8 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv,
        os_memset(&event, 0, sizeof(event));
        if (cmd == NL80211_CMD_CONNECT &&
            nla_get_u16(status) != WLAN_STATUS_SUCCESS) {
+               if (addr)
+                       event.assoc_reject.bssid = nla_data(addr);
                if (resp_ie) {
                        event.assoc_reject.resp_ies = nla_data(resp_ie);
                        event.assoc_reject.resp_ies_len = nla_len(resp_ie);
@@ -600,10 +897,35 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv,
                event.assoc_info.resp_ies_len = nla_len(resp_ie);
        }
 
+       event.assoc_info.freq = nl80211_get_assoc_freq(drv);
+
        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)
+{
+       union wpa_event_data data;
+
+       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;
+       }
+
+       drv->associated = 0;
+       os_memset(&data, 0, sizeof(data));
+       if (reason)
+               data.disassoc_info.reason_code = nla_get_u16(reason);
+       wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, &data);
+}
+
+
 static void mlme_timeout_event(struct wpa_driver_nl80211_data *drv,
                               enum nl80211_commands cmd, struct nlattr *addr)
 {
@@ -629,8 +951,8 @@ static void mlme_timeout_event(struct wpa_driver_nl80211_data *drv,
 }
 
 
-static void mlme_event_action(struct wpa_driver_nl80211_data *drv,
-                             struct nlattr *freq, const u8 *frame, size_t len)
+static void mlme_event_mgmt(struct wpa_driver_nl80211_data *drv,
+                           struct nlattr *freq, const u8 *frame, size_t len)
 {
        const struct ieee80211_mgmt *mgmt;
        union wpa_event_data event;
@@ -646,15 +968,23 @@ static void mlme_event_action(struct wpa_driver_nl80211_data *drv,
        stype = WLAN_FC_GET_STYPE(fc);
 
        os_memset(&event, 0, sizeof(event));
-       event.rx_action.da = mgmt->da;
-       event.rx_action.sa = mgmt->sa;
-       event.rx_action.bssid = mgmt->bssid;
-       event.rx_action.category = mgmt->u.action.category;
-       event.rx_action.data = &mgmt->u.action.category + 1;
-       event.rx_action.len = frame + len - event.rx_action.data;
-       if (freq)
+       if (freq) {
                event.rx_action.freq = nla_get_u32(freq);
-       wpa_supplicant_event(drv->ctx, EVENT_RX_ACTION, &event);
+               drv->last_mgmt_freq = event.rx_action.freq;
+       }
+       if (stype == WLAN_FC_STYPE_ACTION) {
+               event.rx_action.da = mgmt->da;
+               event.rx_action.sa = mgmt->sa;
+               event.rx_action.bssid = mgmt->bssid;
+               event.rx_action.category = mgmt->u.action.category;
+               event.rx_action.data = &mgmt->u.action.category + 1;
+               event.rx_action.len = frame + len - event.rx_action.data;
+               wpa_supplicant_event(drv->ctx, EVENT_RX_ACTION, &event);
+       } else {
+               event.rx_mgmt.frame = frame;
+               event.rx_mgmt.frame_len = len;
+               wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
+       }
 }
 
 
@@ -671,10 +1001,11 @@ static void mlme_event_action_tx_status(struct wpa_driver_nl80211_data *drv,
                return;
 
        cookie_val = nla_get_u64(cookie);
-       wpa_printf(MSG_DEBUG, "nl80211: Action TX status: cookie=0%llx%s",
+       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)");
+                  " (match)" : " (unknown)", ack != NULL);
        if (cookie_val != drv->send_action_cookie)
                return;
 
@@ -730,9 +1061,51 @@ static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv,
        if (type == EVENT_DISASSOC) {
                event.disassoc_info.addr = bssid;
                event.disassoc_info.reason_code = reason_code;
+               if (frame + len > mgmt->u.disassoc.variable) {
+                       event.disassoc_info.ie = mgmt->u.disassoc.variable;
+                       event.disassoc_info.ie_len = frame + len -
+                               mgmt->u.disassoc.variable;
+               }
        } else {
                event.deauth_info.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 (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);
@@ -775,13 +1148,21 @@ static void mlme_event(struct wpa_driver_nl80211_data *drv,
                mlme_event_deauth_disassoc(drv, EVENT_DISASSOC,
                                           nla_data(frame), nla_len(frame));
                break;
-       case NL80211_CMD_ACTION:
-               mlme_event_action(drv, freq, nla_data(frame), nla_len(frame));
+       case NL80211_CMD_FRAME:
+               mlme_event_mgmt(drv, freq, nla_data(frame), nla_len(frame));
                break;
-       case NL80211_CMD_ACTION_TX_STATUS:
+       case NL80211_CMD_FRAME_TX_STATUS:
                mlme_event_action_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;
        }
@@ -877,7 +1258,8 @@ static void mlme_event_remain_on_channel(struct wpa_driver_nl80211_data *drv,
        if (cookie != drv->remain_on_chan_cookie)
                return; /* not for us */
 
-       drv->pending_remain_on_chan = !cancel_event;
+       if (cancel_event)
+               drv->pending_remain_on_chan = 0;
 
        os_memset(&data, 0, sizeof(data));
        data.remain_on_channel.freq = freq;
@@ -929,78 +1311,458 @@ static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted,
 }
 
 
-static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv,
-                             struct nlattr *tb[])
+static int get_link_signal(struct nl_msg *msg, void *arg)
 {
-       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 },
+       struct nlattr *tb[NL80211_ATTR_MAX + 1];
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1];
+       static struct nla_policy policy[NL80211_STA_INFO_MAX + 1] = {
+               [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
        };
-       struct nlattr *cqm[NL80211_ATTR_CQM_MAX + 1];
-       enum nl80211_cqm_rssi_threshold_event event;
-       union wpa_event_data ed;
-
-       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;
-       }
-
-       if (cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] == NULL)
-               return;
-       event = nla_get_u32(cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]);
+       struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1];
+       static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = {
+               [NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 },
+               [NL80211_RATE_INFO_MCS] = { .type = NLA_U8 },
+               [NL80211_RATE_INFO_40_MHZ_WIDTH] = { .type = NLA_FLAG },
+               [NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG },
+       };
+       struct wpa_signal_info *sig_change = arg;
 
-       os_memset(&ed, 0, sizeof(ed));
+       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+       if (!tb[NL80211_ATTR_STA_INFO] ||
+           nla_parse_nested(sinfo, NL80211_STA_INFO_MAX,
+                            tb[NL80211_ATTR_STA_INFO], policy))
+               return NL_SKIP;
+       if (!sinfo[NL80211_STA_INFO_SIGNAL])
+               return NL_SKIP;
 
-       if (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;
+       sig_change->current_signal =
+               (s8) nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]);
+
+       if (sinfo[NL80211_STA_INFO_TX_BITRATE]) {
+               if (nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX,
+                                    sinfo[NL80211_STA_INFO_TX_BITRATE],
+                                    rate_policy)) {
+                       sig_change->current_txrate = 0;
+               } else {
+                       if (rinfo[NL80211_RATE_INFO_BITRATE]) {
+                               sig_change->current_txrate =
+                                       nla_get_u16(rinfo[
+                                            NL80211_RATE_INFO_BITRATE]) * 100;
+                       }
+               }
+       }
 
-       wpa_supplicant_event(drv->ctx, EVENT_SIGNAL_CHANGE, &ed);
+       return NL_SKIP;
 }
 
 
-static int process_event(struct nl_msg *msg, void *arg)
+static int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv,
+                                  struct wpa_signal_info *sig)
 {
-       struct wpa_driver_nl80211_data *drv = arg;
-       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-       struct nlattr *tb[NL80211_ATTR_MAX + 1];
-       union wpa_event_data data;
+       struct nl_msg *msg;
 
-       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-                 genlmsg_attrlen(gnlh, 0), NULL);
+       sig->current_signal = -9999;
+       sig->current_txrate = 0;
 
-       if (tb[NL80211_ATTR_IFINDEX]) {
-               int ifindex = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
-               if (ifindex != drv->ifindex && !have_ifidx(drv, ifindex)) {
-                       wpa_printf(MSG_DEBUG, "nl80211: Ignored event (cmd=%d)"
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -ENOMEM;
+
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_STATION);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+       NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid);
+
+       return send_and_recv_msgs(drv, msg, get_link_signal, sig);
+ nla_put_failure:
+       nlmsg_free(msg);
+       return -ENOBUFS;
+}
+
+
+static int get_link_noise(struct nl_msg *msg, void *arg)
+{
+       struct nlattr *tb[NL80211_ATTR_MAX + 1];
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1];
+       static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = {
+               [NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 },
+               [NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 },
+       };
+       struct wpa_signal_info *sig_change = arg;
+
+       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_FREQUENCY])
+               return NL_SKIP;
+
+       if (nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) !=
+           sig_change->frequency)
+               return NL_SKIP;
+
+       if (!sinfo[NL80211_SURVEY_INFO_NOISE])
+               return NL_SKIP;
+
+       sig_change->current_noise =
+               (s8) nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]);
+
+       return NL_SKIP;
+}
+
+
+static int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv,
+                                 struct wpa_signal_info *sig_change)
+{
+       struct nl_msg *msg;
+
+       sig_change->current_noise = 9999;
+       sig_change->frequency = drv->assoc_freq;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -ENOMEM;
+
+       nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+
+       return send_and_recv_msgs(drv, msg, get_link_noise, sig_change);
+ nla_put_failure:
+       nlmsg_free(msg);
+       return -ENOBUFS;
+}
+
+
+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 = nlmsg_alloc();
+       if (!msg)
+               return -ENOMEM;
+
+       nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+
+       return send_and_recv_msgs(drv, msg, get_noise_for_scan_results,
+                                 scan_res);
+ nla_put_failure:
+       nlmsg_free(msg);
+       return -ENOBUFS;
+}
+
+
+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_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: New station " MACSTR, MAC2STR(addr));
+
+       if (is_ap_interface(drv->nlmode) && drv->no_monitor_iface_capab) {
+               u8 *ies = NULL;
+               size_t ies_len = 0;
+               if (tb[NL80211_ATTR_IE]) {
+                       ies = nla_data(tb[NL80211_ATTR_IE]);
+                       ies_len = nla_len(tb[NL80211_ATTR_IE]);
+               }
+               wpa_hexdump(MSG_DEBUG, "nl80211: Assoc Req IEs", ies, ies_len);
+               drv_event_assoc(drv->ctx, addr, ies, ies_len, 0);
+               return;
+       }
+
+       if (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(drv->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->no_monitor_iface_capab) {
+               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])
+               return;
+       if (!tb[NL80211_ATTR_REKEY_DATA])
+               return;
+       if (nla_parse_nested(rekey_info, MAX_NL80211_REKEY_DATA,
+                            tb[NL80211_ATTR_REKEY_DATA], rekey_policy))
+               return;
+       if (!rekey_info[NL80211_REKEY_DATA_REPLAY_CTR])
+               return;
+
+       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;
+
+       if (!tb[NL80211_ATTR_PMKSA_CANDIDATE])
+               return;
+       if (nla_parse_nested(cand, MAX_NL80211_PMKSA_CANDIDATE,
+                            tb[NL80211_ATTR_PMKSA_CANDIDATE], cand_policy))
+               return;
+       if (!cand[NL80211_PMKSA_CANDIDATE_INDEX] ||
+           !cand[NL80211_PMKSA_CANDIDATE_BSSID])
+               return;
+
+       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 int process_event(struct nl_msg *msg, void *arg)
+{
+       struct wpa_driver_nl80211_data *drv = 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);
+
+       if (tb[NL80211_ATTR_IFINDEX]) {
+               int ifindex = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
+               if (ifindex != drv->ifindex && !have_ifidx(drv, ifindex)) {
+                       wpa_printf(MSG_DEBUG, "nl80211: Ignored event (cmd=%d)"
                                   " for foreign interface (ifindex %d)",
                                   gnlh->cmd, ifindex);
                        return NL_SKIP;
                }
        }
 
-       if (drv->ap_scan_as_station &&
+       if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED &&
            (gnlh->cmd == NL80211_CMD_NEW_SCAN_RESULTS ||
             gnlh->cmd == NL80211_CMD_SCAN_ABORTED)) {
                wpa_driver_nl80211_set_mode(&drv->first_bss,
-                                           IEEE80211_MODE_AP);
-               drv->ap_scan_as_station = 0;
+                                           drv->ap_scan_as_station);
+               drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
        }
 
        switch (gnlh->cmd) {
        case NL80211_CMD_TRIGGER_SCAN:
                wpa_printf(MSG_DEBUG, "nl80211: Scan trigger");
                break;
+       case NL80211_CMD_START_SCHED_SCAN:
+               wpa_printf(MSG_DEBUG, "nl80211: Sched scan started");
+               break;
+       case NL80211_CMD_SCHED_SCAN_STOPPED:
+               wpa_printf(MSG_DEBUG, "nl80211: Sched scan stopped");
+               wpa_supplicant_event(drv->ctx, EVENT_SCHED_SCAN_STOPPED, NULL);
+               break;
        case NL80211_CMD_NEW_SCAN_RESULTS:
                wpa_printf(MSG_DEBUG, "nl80211: New scan results available");
                drv->scan_complete_events = 1;
@@ -1008,6 +1770,11 @@ static int process_event(struct nl_msg *msg, void *arg)
                                     drv->ctx);
                send_scan_event(drv, 0, tb);
                break;
+       case NL80211_CMD_SCHED_SCAN_RESULTS:
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: New sched scan results available");
+               send_scan_event(drv, 0, tb);
+               break;
        case NL80211_CMD_SCAN_ABORTED:
                wpa_printf(MSG_DEBUG, "nl80211: Scan aborted");
                /*
@@ -1022,8 +1789,10 @@ static int process_event(struct nl_msg *msg, void *arg)
        case NL80211_CMD_ASSOCIATE:
        case NL80211_CMD_DEAUTHENTICATE:
        case NL80211_CMD_DISASSOCIATE:
-       case NL80211_CMD_ACTION:
-       case NL80211_CMD_ACTION_TX_STATUS:
+       case NL80211_CMD_FRAME:
+       case NL80211_CMD_FRAME_TX_STATUS:
+       case NL80211_CMD_UNPROT_DEAUTHENTICATE:
+       case NL80211_CMD_UNPROT_DISASSOCIATE:
                mlme_event(drv, gnlh->cmd, tb[NL80211_ATTR_FRAME],
                           tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT],
                           tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK],
@@ -1038,21 +1807,8 @@ static int process_event(struct nl_msg *msg, void *arg)
                                   tb[NL80211_ATTR_RESP_IE]);
                break;
        case NL80211_CMD_DISCONNECT:
-               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");
-                       break;
-               }
-               drv->associated = 0;
-               os_memset(&data, 0, sizeof(data));
-               if (tb[NL80211_ATTR_REASON_CODE])
-                       data.disassoc_info.reason_code =
-                               nla_get_u16(tb[NL80211_ATTR_REASON_CODE]);
-               wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, &data);
+               mlme_event_disconnect(drv, tb[NL80211_ATTR_REASON_CODE],
+                                     tb[NL80211_ATTR_MAC]);
                break;
        case NL80211_CMD_MICHAEL_MIC_FAILURE:
                mlme_event_michael_mic_failure(drv, tb);
@@ -1069,6 +1825,28 @@ static int process_event(struct nl_msg *msg, void *arg)
        case NL80211_CMD_NOTIFY_CQM:
                nl80211_cqm_event(drv, tb);
                break;
+       case NL80211_CMD_REG_CHANGE:
+               wpa_printf(MSG_DEBUG, "nl80211: Regulatory domain change");
+               wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED,
+                                    NULL);
+               break;
+       case NL80211_CMD_REG_BEACON_HINT:
+               wpa_printf(MSG_DEBUG, "nl80211: Regulatory beacon hint");
+               wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED,
+                                    NULL);
+               break;
+       case NL80211_CMD_NEW_STATION:
+               nl80211_new_station_event(drv, tb);
+               break;
+       case NL80211_CMD_DEL_STATION:
+               nl80211_del_station_event(drv, tb);
+               break;
+       case NL80211_CMD_SET_REKEY_OFFLOAD:
+               nl80211_rekey_offload_event(drv, tb);
+               break;
+       case NL80211_CMD_PMKSA_CANDIDATE:
+               nl80211_pmksa_candidate_event(drv, tb);
+               break;
        default:
                wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event "
                           "(cmd=%d)", gnlh->cmd);
@@ -1080,19 +1858,19 @@ static int process_event(struct nl_msg *msg, void *arg)
 
 
 static void wpa_driver_nl80211_event_receive(int sock, void *eloop_ctx,
-                                            void *sock_ctx)
+                                            void *handle)
 {
        struct nl_cb *cb;
        struct wpa_driver_nl80211_data *drv = eloop_ctx;
 
        wpa_printf(MSG_DEBUG, "nl80211: Event message available");
 
-       cb = nl_cb_clone(drv->nl_cb);
+       cb = nl_cb_clone(drv->global->nl_cb);
        if (!cb)
                return;
        nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL);
        nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, process_event, drv);
-       nl_recvmsgs(drv->nl_handle_event, cb);
+       nl_recvmsgs(handle, cb);
        nl_cb_put(cb);
 }
 
@@ -1121,24 +1899,22 @@ static int wpa_driver_nl80211_set_country(void *priv, const char *alpha2_arg)
        alpha2[1] = alpha2_arg[1];
        alpha2[2] = '\0';
 
-       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
-                   0, NL80211_CMD_REQ_SET_REG, 0);
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_REQ_SET_REG);
 
        NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, alpha2);
        if (send_and_recv_msgs(drv, msg, NULL, NULL))
                return -EINVAL;
        return 0;
 nla_put_failure:
+       nlmsg_free(msg);
        return -EINVAL;
 }
 
 
-#ifndef HOSTAPD
 struct wiphy_info_data {
-       int max_scan_ssids;
-       int ap_supported;
-       int auth_supported;
-       int connect_supported;
+       struct wpa_driver_capa *capa;
+
+       unsigned int error:1;
 };
 
 
@@ -1147,37 +1923,181 @@ 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;
+       int p2p_go_supported = 0, p2p_client_supported = 0;
+       int p2p_concurrent = 0;
+       int auth_supported = 0, connect_supported = 0;
+       struct wpa_driver_capa *capa = info->capa;
+       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 },
+       },
+       iface_limit_policy[NUM_NL80211_IFACE_LIMIT] = {
+               [NL80211_IFACE_LIMIT_TYPES] = { .type = NLA_NESTED },
+               [NL80211_IFACE_LIMIT_MAX] = { .type = NLA_U32 },
+       };
 
        nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
                  genlmsg_attrlen(gnlh, 0), NULL);
 
        if (tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS])
-               info->max_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_SUPPORTED_IFTYPES]) {
                struct nlattr *nl_mode;
                int i;
                nla_for_each_nested(nl_mode,
                                    tb[NL80211_ATTR_SUPPORTED_IFTYPES], i) {
-                       if (nl_mode->nla_type == NL80211_IFTYPE_AP) {
-                               info->ap_supported = 1;
+                       switch (nla_type(nl_mode)) {
+                       case NL80211_IFTYPE_AP:
+                               capa->flags |= WPA_DRIVER_FLAGS_AP;
+                               break;
+                       case NL80211_IFTYPE_P2P_GO:
+                               p2p_go_supported = 1;
+                               break;
+                       case NL80211_IFTYPE_P2P_CLIENT:
+                               p2p_client_supported = 1;
                                break;
                        }
                }
        }
 
+       if (tb[NL80211_ATTR_INTERFACE_COMBINATIONS]) {
+               struct nlattr *nl_combi;
+               int rem_combi;
+
+               nla_for_each_nested(nl_combi,
+                                   tb[NL80211_ATTR_INTERFACE_COMBINATIONS],
+                                   rem_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;
+
+                       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])
+                               goto broken_combination;
+
+                       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])
+                                       goto 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) {
+                               p2p_concurrent = 1;
+                               break;
+                       }
+
+broken_combination:
+                       ;
+               }
+       }
+
        if (tb[NL80211_ATTR_SUPPORTED_COMMANDS]) {
                struct nlattr *nl_cmd;
                int i;
 
                nla_for_each_nested(nl_cmd,
                                    tb[NL80211_ATTR_SUPPORTED_COMMANDS], i) {
-                       u32 cmd = nla_get_u32(nl_cmd);
-                       if (cmd == NL80211_CMD_AUTHENTICATE)
-                               info->auth_supported = 1;
-                       else if (cmd == NL80211_CMD_CONNECT)
-                               info->connect_supported = 1;
+                       switch (nla_get_u32(nl_cmd)) {
+                       case NL80211_CMD_AUTHENTICATE:
+                               auth_supported = 1;
+                               break;
+                       case NL80211_CMD_CONNECT:
+                               connect_supported = 1;
+                               break;
+                       case NL80211_CMD_START_SCHED_SCAN:
+                               /*
+                                * Disabled for 1.x for now as it is
+                                * broken there due to the way it ends
+                                * up getting used.
+                               capa->sched_scan_supported = 1;
+                                */
+                               break;
+                       }
+               }
+       }
+
+       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;
+       }
+
+       /* default to 5000 since early versions of mac80211 don't set it */
+       capa->max_remain_on_chan = 5000;
+
+       if (tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION])
+               capa->max_remain_on_chan =
+                       nla_get_u32(tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION]);
+
+       if (auth_supported)
+               capa->flags |= WPA_DRIVER_FLAGS_SME;
+       else if (!connect_supported) {
+               wpa_printf(MSG_INFO, "nl80211: Driver does not support "
+                          "authentication/association or connect commands");
+               info->error = 1;
+       }
+
+       if (p2p_go_supported && p2p_client_supported)
+               capa->flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE;
+       if (p2p_concurrent) {
+               wpa_printf(MSG_DEBUG, "nl80211: Use separate P2P group "
+                          "interface (driver advertised support)");
+               capa->flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT;
+               capa->flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P;
+       }
+
+       if (tb[NL80211_ATTR_TDLS_SUPPORT]) {
+               wpa_printf(MSG_DEBUG, "nl80211: TDLS supported");
+               capa->flags |= WPA_DRIVER_FLAGS_TDLS_SUPPORT;
+
+               if (tb[NL80211_ATTR_TDLS_EXTERNAL_SETUP]) {
+                       wpa_printf(MSG_DEBUG, "nl80211: TDLS external setup");
+                       capa->flags |=
+                               WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP;
                }
        }
 
@@ -1191,12 +2111,13 @@ static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv,
        struct nl_msg *msg;
 
        os_memset(info, 0, sizeof(*info));
+       info->capa = &drv->capa;
+
        msg = nlmsg_alloc();
        if (!msg)
                return -1;
 
-       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
-                   0, NL80211_CMD_GET_WIPHY, 0);
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_WIPHY);
 
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->first_bss.ifindex);
 
@@ -1214,6 +2135,10 @@ static 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;
        /* For now, assume TKIP, CCMP, WPA, WPA2 are supported */
        drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
@@ -1228,103 +2153,88 @@ static int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv)
                WPA_DRIVER_AUTH_SHARED |
                WPA_DRIVER_AUTH_LEAP;
 
-       drv->capa.max_scan_ssids = info.max_scan_ssids;
-       if (info.ap_supported)
-               drv->capa.flags |= WPA_DRIVER_FLAGS_AP;
-
-       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");
-               return -1;
-       }
-
+       drv->capa.flags |= WPA_DRIVER_FLAGS_SANE_ERROR_CODES;
        drv->capa.flags |= WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE;
-       drv->capa.max_remain_on_chan = 5000;
+       drv->capa.flags |= WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
+       drv->capa.flags |= WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS;
 
        return 0;
 }
-#endif /* HOSTAPD */
 
 
-static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv,
-                                     void *ctx)
+#ifdef ANDROID
+static int android_genl_ctrl_resolve(struct nl_handle *handle,
+                                    const char *name)
 {
-       int ret;
-
-       /* Initialize generic netlink and nl80211 */
+       /*
+        * 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;
 
-       drv->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
-       if (drv->nl_cb == NULL) {
-               wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink "
-                          "callbacks");
-               goto err1;
+       if (genl_ctrl_alloc_cache(handle, &cache) < 0) {
+               wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic "
+                          "netlink cache");
+               goto fail;
        }
 
-       drv->nl_handle = nl_handle_alloc_cb(drv->nl_cb);
-       if (drv->nl_handle == NULL) {
-               wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink "
-                          "callbacks");
-               goto err2;
-       }
+       nl80211 = genl_ctrl_search_by_name(cache, name);
+       if (nl80211 == NULL)
+               goto fail;
 
-       drv->nl_handle_event = nl_handle_alloc_cb(drv->nl_cb);
-       if (drv->nl_handle_event == NULL) {
-               wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink "
-                          "callbacks (event)");
-               goto err2b;
-       }
+       id = genl_family_get_id(nl80211);
 
-       if (genl_connect(drv->nl_handle)) {
-               wpa_printf(MSG_ERROR, "nl80211: Failed to connect to generic "
-                          "netlink");
-               goto err3;
-       }
+fail:
+       if (nl80211)
+               genl_family_put(nl80211);
+       if (cache)
+               nl_cache_free(cache);
 
-       if (genl_connect(drv->nl_handle_event)) {
-               wpa_printf(MSG_ERROR, "nl80211: Failed to connect to generic "
-                          "netlink (event)");
-               goto err3;
-       }
+       return id;
+}
+#define genl_ctrl_resolve android_genl_ctrl_resolve
+#endif /* ANDROID */
 
-#ifdef CONFIG_LIBNL20
-       if (genl_ctrl_alloc_cache(drv->nl_handle, &drv->nl_cache) < 0) {
-               wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic "
-                          "netlink cache");
-               goto err3;
-       }
-       if (genl_ctrl_alloc_cache(drv->nl_handle_event, &drv->nl_cache_event) <
-           0) {
-               wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic "
-                          "netlink cache (event)");
-               goto err3b;
-       }
-#else /* CONFIG_LIBNL20 */
-       drv->nl_cache = genl_ctrl_alloc_cache(drv->nl_handle);
-       if (drv->nl_cache == NULL) {
-               wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic "
-                          "netlink cache");
-               goto err3;
-       }
-       drv->nl_cache_event = genl_ctrl_alloc_cache(drv->nl_handle_event);
-       if (drv->nl_cache_event == NULL) {
-               wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic "
-                          "netlink cache (event)");
-               goto err3b;
+
+static int wpa_driver_nl80211_init_nl_global(struct nl80211_global *global)
+{
+       global->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
+       if (global->nl_cb == NULL) {
+               wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink "
+                          "callbacks");
+               return -1;
        }
-#endif /* CONFIG_LIBNL20 */
 
-       drv->nl80211 = genl_ctrl_search_by_name(drv->nl_cache, "nl80211");
-       if (drv->nl80211 == NULL) {
+       if (nl_create_handles(&global->nl, global->nl_cb, "nl"))
+               return -1;
+
+       global->nl80211 = genl_ctrl_search_by_name(global->nl.cache,
+                                                  "nl80211");
+       if (global->nl80211 == NULL) {
                wpa_printf(MSG_ERROR, "nl80211: 'nl80211' generic netlink not "
                           "found");
-               goto err4;
+               return -1;
        }
 
+       return 0;
+}
+
+
+static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv)
+{
+       struct nl80211_global *global = drv->global;
+       int ret;
+
+       /* Initialize generic netlink and nl80211 */
+
+       if (nl_create_handles(&drv->nl_event, global->nl_cb, "event"))
+               goto err3;
+
        ret = nl_get_multicast_id(drv, "nl80211", "scan");
        if (ret >= 0)
-               ret = nl_socket_add_membership(drv->nl_handle_event, ret);
+               ret = nl_socket_add_membership(drv->nl_event.handle, ret);
        if (ret < 0) {
                wpa_printf(MSG_ERROR, "nl80211: Could not add multicast "
                           "membership for scan events: %d (%s)",
@@ -1334,7 +2244,7 @@ static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv,
 
        ret = nl_get_multicast_id(drv, "nl80211", "mlme");
        if (ret >= 0)
-               ret = nl_socket_add_membership(drv->nl_handle_event, ret);
+               ret = nl_socket_add_membership(drv->nl_event.handle, ret);
        if (ret < 0) {
                wpa_printf(MSG_ERROR, "nl80211: Could not add multicast "
                           "membership for mlme events: %d (%s)",
@@ -1342,95 +2252,169 @@ static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv,
                goto err4;
        }
 
-       eloop_register_read_sock(nl_socket_get_fd(drv->nl_handle_event),
-                                wpa_driver_nl80211_event_receive, drv, ctx);
+       ret = nl_get_multicast_id(drv, "nl80211", "regulatory");
+       if (ret >= 0)
+               ret = nl_socket_add_membership(drv->nl_event.handle, ret);
+       if (ret < 0) {
+               wpa_printf(MSG_DEBUG, "nl80211: Could not add multicast "
+                          "membership for regulatory events: %d (%s)",
+                          ret, strerror(-ret));
+               /* Continue without regulatory events */
+       }
+
+       eloop_register_read_sock(nl_socket_get_fd(drv->nl_event.handle),
+                                wpa_driver_nl80211_event_receive, drv,
+                                drv->nl_event.handle);
 
        return 0;
 
 err4:
-       nl_cache_free(drv->nl_cache_event);
-err3b:
-       nl_cache_free(drv->nl_cache);
+       nl_destroy_handles(&drv->nl_event);
 err3:
-       nl_handle_destroy(drv->nl_handle_event);
-err2b:
-       nl_handle_destroy(drv->nl_handle);
-err2:
-       nl_cb_put(drv->nl_cb);
-err1:
        return -1;
 }
 
 
+static void wpa_driver_nl80211_rfkill_blocked(void *ctx)
+{
+       wpa_printf(MSG_DEBUG, "nl80211: RFKILL blocked");
+       /*
+        * This may be for any interface; use ifdown event to disable
+        * interface.
+        */
+}
+
+
+static void wpa_driver_nl80211_rfkill_unblocked(void *ctx)
+{
+       struct wpa_driver_nl80211_data *drv = ctx;
+       wpa_printf(MSG_DEBUG, "nl80211: RFKILL unblocked");
+       if (linux_set_iface_flags(drv->global->ioctl_sock,
+                                 drv->first_bss.ifname, 1)) {
+               wpa_printf(MSG_DEBUG, "nl80211: Could not set interface UP "
+                          "after rfkill unblock");
+               return;
+       }
+       /* rtnetlink ifup handler will report interface as enabled */
+}
+
+
+static void nl80211_get_phy_name(struct wpa_driver_nl80211_data *drv)
+{
+       /* Find phy (radio) to which this interface belongs */
+       char buf[90], *pos;
+       int f, rv;
+
+       drv->phyname[0] = '\0';
+       snprintf(buf, sizeof(buf) - 1, "/sys/class/net/%s/phy80211/name",
+                drv->first_bss.ifname);
+       f = open(buf, O_RDONLY);
+       if (f < 0) {
+               wpa_printf(MSG_DEBUG, "Could not open file %s: %s",
+                          buf, strerror(errno));
+               return;
+       }
+
+       rv = read(f, drv->phyname, sizeof(drv->phyname) - 1);
+       close(f);
+       if (rv < 0) {
+               wpa_printf(MSG_DEBUG, "Could not read file %s: %s",
+                          buf, strerror(errno));
+               return;
+       }
+
+       drv->phyname[rv] = '\0';
+       pos = os_strchr(drv->phyname, '\n');
+       if (pos)
+               *pos = '\0';
+       wpa_printf(MSG_DEBUG, "nl80211: interface %s in phy %s",
+                  drv->first_bss.ifname, drv->phyname);
+}
+
+
+#ifdef CONFIG_AP
+static void nl80211_l2_read(void *ctx, const u8 *src_addr, const u8 *buf,
+                           size_t len)
+{
+       wpa_printf(MSG_DEBUG, "nl80211: l2_packet read %u",
+                  (unsigned int) len);
+}
+#endif /* CONFIG_AP */
+
+
 /**
  * wpa_driver_nl80211_init - Initialize nl80211 driver interface
  * @ctx: context to be used when calling wpa_supplicant functions,
  * e.g., wpa_supplicant_event()
  * @ifname: interface name, e.g., wlan0
+ * @global_priv: private driver global data from global_init()
  * Returns: Pointer to private data, %NULL on failure
  */
-static void * wpa_driver_nl80211_init(void *ctx, const char *ifname)
+static void * wpa_driver_nl80211_init(void *ctx, const char *ifname,
+                                     void *global_priv)
 {
        struct wpa_driver_nl80211_data *drv;
-       struct netlink_config *cfg;
+       struct rfkill_config *rcfg;
        struct i802_bss *bss;
 
+       if (global_priv == NULL)
+               return NULL;
        drv = os_zalloc(sizeof(*drv));
        if (drv == NULL)
                return NULL;
+       drv->global = global_priv;
        drv->ctx = ctx;
        bss = &drv->first_bss;
        bss->drv = drv;
        os_strlcpy(bss->ifname, ifname, sizeof(bss->ifname));
        drv->monitor_ifidx = -1;
        drv->monitor_sock = -1;
-       drv->ioctl_sock = -1;
+       drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
 
-       if (wpa_driver_nl80211_init_nl(drv, ctx)) {
+       if (wpa_driver_nl80211_init_nl(drv)) {
                os_free(drv);
                return NULL;
        }
 
-       drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
-       if (drv->ioctl_sock < 0) {
-               perror("socket(PF_INET,SOCK_DGRAM)");
-               goto failed;
-       }
+       nl80211_get_phy_name(drv);
 
-       cfg = os_zalloc(sizeof(*cfg));
-       if (cfg == NULL)
-               goto failed;
-       cfg->ctx = drv;
-       cfg->newlink_cb = wpa_driver_nl80211_event_rtm_newlink;
-       cfg->dellink_cb = wpa_driver_nl80211_event_rtm_dellink;
-       drv->netlink = netlink_init(cfg);
-       if (drv->netlink == NULL) {
-               os_free(cfg);
+       rcfg = os_zalloc(sizeof(*rcfg));
+       if (rcfg == NULL)
                goto failed;
+       rcfg->ctx = drv;
+       os_strlcpy(rcfg->ifname, ifname, sizeof(rcfg->ifname));
+       rcfg->blocked_cb = wpa_driver_nl80211_rfkill_blocked;
+       rcfg->unblocked_cb = wpa_driver_nl80211_rfkill_unblocked;
+       drv->rfkill = rfkill_init(rcfg);
+       if (drv->rfkill == NULL) {
+               wpa_printf(MSG_DEBUG, "nl80211: RFKILL status not available");
+               os_free(rcfg);
        }
+
        if (wpa_driver_nl80211_finish_drv_init(drv))
                goto failed;
 
-       return bss;
+#ifdef CONFIG_AP
+       drv->l2 = l2_packet_init(ifname, NULL, ETH_P_EAPOL,
+                                nl80211_l2_read, drv, 0);
+#endif /* CONFIG_AP */
 
-failed:
-       netlink_deinit(drv->netlink);
-       if (drv->ioctl_sock >= 0)
-               close(drv->ioctl_sock);
+       if (drv->global) {
+               dl_list_add(&drv->global->interfaces, &drv->list);
+               drv->in_interface_list = 1;
+       }
 
-       genl_family_put(drv->nl80211);
-       nl_cache_free(drv->nl_cache);
-       nl_handle_destroy(drv->nl_handle);
-       nl_cb_put(drv->nl_cb);
-       eloop_unregister_read_sock(nl_socket_get_fd(drv->nl_handle_event));
+       return bss;
 
-       os_free(drv);
+failed:
+       wpa_driver_nl80211_deinit(bss);
        return NULL;
 }
 
 
-static int nl80211_register_action_frame(struct wpa_driver_nl80211_data *drv,
-                                        const u8 *match, size_t match_len)
+static int nl80211_register_frame(struct wpa_driver_nl80211_data *drv,
+                                 struct nl_handle *nl_handle,
+                                 u16 type, const u8 *match, size_t match_len)
 {
        struct nl_msg *msg;
        int ret = -1;
@@ -1439,18 +2423,19 @@ static int nl80211_register_action_frame(struct wpa_driver_nl80211_data *drv,
        if (!msg)
                return -1;
 
-       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
-                   NL80211_CMD_REGISTER_ACTION, 0);
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_REGISTER_ACTION);
 
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+       NLA_PUT_U16(msg, NL80211_ATTR_FRAME_TYPE, type);
        NLA_PUT(msg, NL80211_ATTR_FRAME_MATCH, match_len, match);
 
-       ret = send_and_recv(drv, drv->nl_handle_event, msg, NULL, NULL);
+       ret = send_and_recv(drv, nl_handle, msg, NULL, NULL);
        msg = NULL;
        if (ret) {
-               wpa_printf(MSG_DEBUG, "nl80211: Register Action command "
-                          "failed: ret=%d (%s)", ret, strerror(-ret));
-               wpa_hexdump(MSG_DEBUG, "nl80211: Register Action match",
+               wpa_printf(MSG_DEBUG, "nl80211: Register frame command "
+                          "failed (type=%u): ret=%d (%s)",
+                          type, ret, strerror(-ret));
+               wpa_hexdump(MSG_DEBUG, "nl80211: Register frame match",
                            match, match_len);
                goto nla_put_failure;
        }
@@ -1461,8 +2446,57 @@ nla_put_failure:
 }
 
 
+static int nl80211_register_action_frame(struct wpa_driver_nl80211_data *drv,
+                                        const u8 *match, size_t match_len)
+{
+       u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_ACTION << 4);
+       return nl80211_register_frame(drv, drv->nl_event.handle,
+                                     type, match, match_len);
+}
+
+
 static int nl80211_register_action_frames(struct wpa_driver_nl80211_data *drv)
 {
+#if defined(CONFIG_P2P) || defined(CONFIG_INTERWORKING)
+       /* GAS Initial Request */
+       if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0a", 2) < 0)
+               return -1;
+       /* GAS Initial Response */
+       if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0b", 2) < 0)
+               return -1;
+       /* GAS Comeback Request */
+       if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0c", 2) < 0)
+               return -1;
+       /* GAS Comeback Response */
+       if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0d", 2) < 0)
+               return -1;
+#endif /* CONFIG_P2P || CONFIG_INTERWORKING */
+#ifdef CONFIG_P2P
+       /* P2P Public Action */
+       if (nl80211_register_action_frame(drv,
+                                         (u8 *) "\x04\x09\x50\x6f\x9a\x09",
+                                         6) < 0)
+               return -1;
+       /* P2P Action */
+       if (nl80211_register_action_frame(drv,
+                                         (u8 *) "\x7f\x50\x6f\x9a\x09",
+                                         5) < 0)
+               return -1;
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_IEEE80211W
+       /* SA Query Response */
+       if (nl80211_register_action_frame(drv, (u8 *) "\x08\x01", 2) < 0)
+               return -1;
+#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_TDLS
+       if ((drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) {
+               /* TDLS Discovery Response */
+               if (nl80211_register_action_frame(drv, (u8 *) "\x04\x0e", 2) <
+                   0)
+                       return -1;
+       }
+#endif /* CONFIG_TDLS */
+
        /* FT Action frames */
        if (nl80211_register_action_frame(drv, (u8 *) "\x06", 1) < 0)
                return -1;
@@ -1470,36 +2504,66 @@ static int nl80211_register_action_frames(struct wpa_driver_nl80211_data *drv)
                drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT |
                        WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK;
 
+       /* WNM - BSS Transition Management Request */
+       if (nl80211_register_action_frame(drv, (u8 *) "\x0a\x07", 2) < 0)
+               return -1;
+
        return 0;
 }
 
 
+static void wpa_driver_nl80211_send_rfkill(void *eloop_ctx, void *timeout_ctx)
+{
+       wpa_supplicant_event(timeout_ctx, EVENT_INTERFACE_DISABLED, NULL);
+}
+
+
 static int
 wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv)
 {
        struct i802_bss *bss = &drv->first_bss;
+       int send_rfkill_event = 0;
 
        drv->ifindex = if_nametoindex(bss->ifname);
        drv->first_bss.ifindex = drv->ifindex;
 
 #ifndef HOSTAPD
-       if (wpa_driver_nl80211_set_mode(bss, IEEE80211_MODE_INFRA) < 0) {
-               wpa_printf(MSG_DEBUG, "nl80211: Could not configure driver to "
+       /*
+        * Make sure the interface starts up in station mode unless this is a
+        * dynamically added interface (e.g., P2P) that was already configured
+        * with proper iftype.
+        */
+       if (drv->ifindex != drv->global->if_add_ifindex &&
+           wpa_driver_nl80211_set_mode(bss, NL80211_IFTYPE_STATION) < 0) {
+               wpa_printf(MSG_ERROR, "nl80211: Could not configure driver to "
                           "use managed mode");
+               return -1;
        }
 
-       if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1)) {
-               wpa_printf(MSG_ERROR, "Could not set interface '%s' UP",
-                          bss->ifname);
-               return -1;
+       if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1)) {
+               if (rfkill_is_blocked(drv->rfkill)) {
+                       wpa_printf(MSG_DEBUG, "nl80211: Could not yet enable "
+                                  "interface '%s' due to rfkill",
+                                  bss->ifname);
+                       drv->if_disabled = 1;
+                       send_rfkill_event = 1;
+               } else {
+                       wpa_printf(MSG_ERROR, "nl80211: Could not set "
+                                  "interface '%s' UP", bss->ifname);
+                       return -1;
+               }
        }
 
+       netlink_send_oper_ifla(drv->global->netlink, drv->ifindex,
+                              1, IF_OPER_DORMANT);
+#endif /* HOSTAPD */
+
        if (wpa_driver_nl80211_capa(drv))
                return -1;
 
-       netlink_send_oper_ifla(drv->netlink, drv->ifindex,
-                              1, IF_OPER_DORMANT);
-#endif /* HOSTAPD */
+       if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
+                              drv->addr))
+               return -1;
 
        if (nl80211_register_action_frames(drv) < 0) {
                wpa_printf(MSG_DEBUG, "nl80211: Failed to register Action "
@@ -1511,6 +2575,11 @@ wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv)
                 */
        }
 
+       if (send_rfkill_event) {
+               eloop_register_timeout(0, 0, wpa_driver_nl80211_send_rfkill,
+                                      drv, drv->ctx);
+       }
+
        return 0;
 }
 
@@ -1523,12 +2592,12 @@ static int wpa_driver_nl80211_del_beacon(struct wpa_driver_nl80211_data *drv)
        if (!msg)
                return -ENOMEM;
 
-       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
-                   0, NL80211_CMD_DEL_BEACON, 0);
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_BEACON);
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
 
        return send_and_recv_msgs(drv, msg, NULL, NULL);
  nla_put_failure:
+       nlmsg_free(msg);
        return -ENOBUFS;
 }
 
@@ -1545,23 +2614,30 @@ static void wpa_driver_nl80211_deinit(void *priv)
        struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
 
-       if (drv->added_if_into_bridge) {
-               if (linux_br_del_if(drv->ioctl_sock, drv->brname, bss->ifname)
-                   < 0)
+#ifdef CONFIG_AP
+       if (drv->l2)
+               l2_packet_deinit(drv->l2);
+#endif /* CONFIG_AP */
+
+       if (drv->nl_preq.handle)
+               wpa_driver_nl80211_probe_req_report(bss, 0);
+       if (bss->added_if_into_bridge) {
+               if (linux_br_del_if(drv->global->ioctl_sock, bss->brname,
+                                   bss->ifname) < 0)
                        wpa_printf(MSG_INFO, "nl80211: Failed to remove "
                                   "interface %s from bridge %s: %s",
-                                  bss->ifname, drv->brname, strerror(errno));
+                                  bss->ifname, bss->brname, strerror(errno));
        }
-       if (drv->added_bridge) {
-               if (linux_br_del(drv->ioctl_sock, drv->brname) < 0)
+       if (bss->added_bridge) {
+               if (linux_br_del(drv->global->ioctl_sock, bss->brname) < 0)
                        wpa_printf(MSG_INFO, "nl80211: Failed to remove "
                                   "bridge %s: %s",
-                                  drv->brname, strerror(errno));
+                                  bss->brname, strerror(errno));
        }
 
        nl80211_remove_monitor_interface(drv);
 
-       if (drv->nlmode == NL80211_IFTYPE_AP)
+       if (is_ap_interface(drv->nlmode))
                wpa_driver_nl80211_del_beacon(drv);
 
 #ifdef HOSTAPD
@@ -1582,33 +2658,26 @@ static void wpa_driver_nl80211_deinit(void *priv)
                os_free(drv->if_indices);
 #endif /* HOSTAPD */
 
-       if (drv->disable_11b_rates)
+       if (drv->disabled_11b_rates)
                nl80211_disable_11b_rates(drv, drv->ifindex, 0);
 
-       netlink_send_oper_ifla(drv->netlink, drv->ifindex, 0, IF_OPER_UP);
-       netlink_deinit(drv->netlink);
+       netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, 0,
+                              IF_OPER_UP);
+       rfkill_deinit(drv->rfkill);
 
        eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
 
-       (void) linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 0);
-       wpa_driver_nl80211_set_mode(bss, IEEE80211_MODE_INFRA);
-
-       if (drv->ioctl_sock >= 0)
-               close(drv->ioctl_sock);
+       (void) linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0);
+       wpa_driver_nl80211_set_mode(bss, NL80211_IFTYPE_STATION);
 
-       eloop_unregister_read_sock(nl_socket_get_fd(drv->nl_handle_event));
-       genl_family_put(drv->nl80211);
-       nl_cache_free(drv->nl_cache);
-       nl_cache_free(drv->nl_cache_event);
-       nl_handle_destroy(drv->nl_handle);
-       nl_handle_destroy(drv->nl_handle_event);
-       nl_cb_put(drv->nl_cb);
-
-       eloop_cancel_timeout(wpa_driver_nl80211_probe_req_report_timeout,
-                            drv, NULL);
+       eloop_unregister_read_sock(nl_socket_get_fd(drv->nl_event.handle));
+       nl_destroy_handles(&drv->nl_event);
 
        os_free(drv->filter_ssids);
 
+       if (drv->in_interface_list)
+               dl_list_del(&drv->list);
+
        os_free(drv);
 }
 
@@ -1624,10 +2693,10 @@ static void wpa_driver_nl80211_deinit(void *priv)
 static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx)
 {
        struct wpa_driver_nl80211_data *drv = eloop_ctx;
-       if (drv->ap_scan_as_station) {
+       if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED) {
                wpa_driver_nl80211_set_mode(&drv->first_bss,
-                                           IEEE80211_MODE_AP);
-               drv->ap_scan_as_station = 0;
+                                           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);
@@ -1646,16 +2715,18 @@ static int wpa_driver_nl80211_scan(void *priv,
        struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
        int ret = 0, timeout;
-       struct nl_msg *msg, *ssids, *freqs;
+       struct nl_msg *msg, *ssids, *freqs, *rates;
        size_t i;
 
        msg = nlmsg_alloc();
        ssids = nlmsg_alloc();
        freqs = nlmsg_alloc();
-       if (!msg || !ssids || !freqs) {
+       rates = nlmsg_alloc();
+       if (!msg || !ssids || !freqs || !rates) {
                nlmsg_free(msg);
                nlmsg_free(ssids);
                nlmsg_free(freqs);
+               nlmsg_free(rates);
                return -1;
        }
 
@@ -1664,8 +2735,7 @@ static int wpa_driver_nl80211_scan(void *priv,
        params->filter_ssids = NULL;
        drv->num_filter_ssids = params->num_filter_ssids;
 
-       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
-                   NL80211_CMD_TRIGGER_SCAN, 0);
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_TRIGGER_SCAN);
 
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
 
@@ -1695,29 +2765,42 @@ static int wpa_driver_nl80211_scan(void *priv,
                nla_put_nested(msg, NL80211_ATTR_SCAN_FREQUENCIES, freqs);
        }
 
+       if (params->p2p_probe) {
+               /*
+                * Remove 2.4 GHz rates 1, 2, 5.5, 11 Mbps from supported rates
+                * by masking out everything else apart from the OFDM rates 6,
+                * 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS rates. All 5 GHz
+                * rates are left enabled.
+                */
+               NLA_PUT(rates, NL80211_BAND_2GHZ, 8,
+                       "\x0c\x12\x18\x24\x30\x48\x60\x6c");
+               nla_put_nested(msg, NL80211_ATTR_SCAN_SUPP_RATES, rates);
+
+               NLA_PUT_FLAG(msg, NL80211_ATTR_TX_NO_CCK_RATE);
+       }
+
        ret = send_and_recv_msgs(drv, msg, NULL, NULL);
        msg = NULL;
        if (ret) {
                wpa_printf(MSG_DEBUG, "nl80211: Scan trigger failed: ret=%d "
                           "(%s)", ret, strerror(-ret));
 #ifdef HOSTAPD
-               if (drv->nlmode == NL80211_IFTYPE_AP) {
+               if (is_ap_interface(drv->nlmode)) {
                        /*
                         * mac80211 does not allow scan requests in AP mode, so
                         * try to do this in station mode.
                         */
-                       if (wpa_driver_nl80211_set_mode(bss,
-                                                       IEEE80211_MODE_INFRA))
+                       if (wpa_driver_nl80211_set_mode(
+                                   bss, NL80211_IFTYPE_STATION))
                                goto nla_put_failure;
 
                        if (wpa_driver_nl80211_scan(drv, params)) {
-                               wpa_driver_nl80211_set_mode(bss,
-                                                           IEEE80211_MODE_AP);
+                               wpa_driver_nl80211_set_mode(bss, drv->nlmode);
                                goto nla_put_failure;
                        }
 
                        /* Restore AP mode when processing scan results */
-                       drv->ap_scan_as_station = 1;
+                       drv->ap_scan_as_station = drv->nlmode;
                        ret = 0;
                } else
                        goto nla_put_failure;
@@ -1747,6 +2830,154 @@ nla_put_failure:
        nlmsg_free(ssids);
        nlmsg_free(msg);
        nlmsg_free(freqs);
+       nlmsg_free(rates);
+       return ret;
+}
+
+
+/**
+ * wpa_driver_nl80211_sched_scan - Initiate a scheduled scan
+ * @priv: Pointer to private driver data from wpa_driver_nl80211_init()
+ * @params: Scan parameters
+ * @interval: Interval between scan cycles in milliseconds
+ * Returns: 0 on success, -1 on failure or if not supported
+ */
+static int wpa_driver_nl80211_sched_scan(void *priv,
+                                        struct wpa_driver_scan_params *params,
+                                        u32 interval)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       int ret = 0;
+       struct nl_msg *msg, *ssids, *freqs, *match_set_ssid, *match_sets;
+       size_t i;
+
+       msg = nlmsg_alloc();
+       ssids = nlmsg_alloc();
+       freqs = nlmsg_alloc();
+       if (!msg || !ssids || !freqs) {
+               nlmsg_free(msg);
+               nlmsg_free(ssids);
+               nlmsg_free(freqs);
+               return -1;
+       }
+
+       os_free(drv->filter_ssids);
+       drv->filter_ssids = params->filter_ssids;
+       params->filter_ssids = NULL;
+       drv->num_filter_ssids = params->num_filter_ssids;
+
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_START_SCHED_SCAN);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL, interval);
+
+       if (drv->num_filter_ssids) {
+               match_sets = nlmsg_alloc();
+
+               for (i = 0; i < drv->num_filter_ssids; i++) {
+                       wpa_hexdump_ascii(MSG_MSGDUMP,
+                                         "nl80211: Sched scan filter SSID",
+                                         drv->filter_ssids[i].ssid,
+                                         drv->filter_ssids[i].ssid_len);
+
+                       match_set_ssid = nlmsg_alloc();
+                       nla_put(match_set_ssid,
+                               NL80211_ATTR_SCHED_SCAN_MATCH_SSID,
+                               drv->filter_ssids[i].ssid_len,
+                               drv->filter_ssids[i].ssid);
+
+                       nla_put_nested(match_sets, i + 1, match_set_ssid);
+
+                       nlmsg_free(match_set_ssid);
+               }
+
+               nla_put_nested(msg, NL80211_ATTR_SCHED_SCAN_MATCH,
+                              match_sets);
+               nlmsg_free(match_sets);
+       }
+
+       for (i = 0; i < params->num_ssids; i++) {
+               wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Sched scan SSID",
+                                 params->ssids[i].ssid,
+                                 params->ssids[i].ssid_len);
+               NLA_PUT(ssids, i + 1, params->ssids[i].ssid_len,
+                       params->ssids[i].ssid);
+       }
+       if (params->num_ssids)
+               nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids);
+
+       if (params->extra_ies) {
+               wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Sched scan extra IEs",
+                                 params->extra_ies, params->extra_ies_len);
+               NLA_PUT(msg, NL80211_ATTR_IE, params->extra_ies_len,
+                       params->extra_ies);
+       }
+
+       if (params->freqs) {
+               for (i = 0; params->freqs[i]; i++) {
+                       wpa_printf(MSG_MSGDUMP, "nl80211: Scan frequency %u "
+                                  "MHz", params->freqs[i]);
+                       NLA_PUT_U32(freqs, i + 1, params->freqs[i]);
+               }
+               nla_put_nested(msg, NL80211_ATTR_SCAN_FREQUENCIES, freqs);
+       }
+
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+
+       /* TODO: if we get an error here, we should fall back to normal scan */
+
+       msg = NULL;
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "nl80211: Sched scan start failed: "
+                          "ret=%d (%s)", ret, strerror(-ret));
+               goto nla_put_failure;
+       }
+
+       wpa_printf(MSG_DEBUG, "nl80211: Sched scan requested (ret=%d) - "
+                  "scan interval %d msec", ret, interval);
+
+nla_put_failure:
+       nlmsg_free(ssids);
+       nlmsg_free(msg);
+       nlmsg_free(freqs);
+       return ret;
+}
+
+
+/**
+ * wpa_driver_nl80211_stop_sched_scan - Stop a scheduled scan
+ * @priv: Pointer to private driver data from wpa_driver_nl80211_init()
+ * Returns: 0 on success, -1 on failure or if not supported
+ */
+static int wpa_driver_nl80211_stop_sched_scan(void *priv)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       int ret = 0;
+       struct nl_msg *msg;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -1;
+
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_STOP_SCHED_SCAN);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       msg = NULL;
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "nl80211: Sched scan stop failed: "
+                          "ret=%d (%s)", ret, strerror(-ret));
+               goto nla_put_failure;
+       }
+
+       wpa_printf(MSG_DEBUG, "nl80211: Sched scan stop sent (ret=%d)", ret);
+
+nla_put_failure:
+       nlmsg_free(msg);
        return ret;
 }
 
@@ -1797,11 +3028,6 @@ static int nl80211_scan_filtered(struct wpa_driver_nl80211_data *drv,
 }
 
 
-struct nl80211_bss_info_arg {
-       struct wpa_driver_nl80211_data *drv;
-       struct wpa_scan_results *res;
-};
-
 static int bss_info_handler(struct nl_msg *msg, void *arg)
 {
        struct nlattr *tb[NL80211_ATTR_MAX + 1];
@@ -1827,6 +3053,7 @@ static int bss_info_handler(struct nl_msg *msg, void *arg)
        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);
@@ -1835,6 +3062,26 @@ static int bss_info_handler(struct nl_msg *msg, void *arg)
        if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS],
                             bss_policy))
                return NL_SKIP;
+       if (bss[NL80211_BSS_STATUS]) {
+               enum nl80211_bss_status status;
+               status = nla_get_u32(bss[NL80211_BSS_STATUS]);
+               if (status == NL80211_BSS_STATUS_ASSOCIATED &&
+                   bss[NL80211_BSS_FREQUENCY]) {
+                       _arg->assoc_freq =
+                               nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
+                       wpa_printf(MSG_DEBUG, "nl80211: Associated on %u MHz",
+                                  _arg->assoc_freq);
+               }
+               if (status == NL80211_BSS_STATUS_ASSOCIATED &&
+                   bss[NL80211_BSS_BSSID]) {
+                       os_memcpy(_arg->assoc_bssid,
+                                 nla_data(bss[NL80211_BSS_BSSID]), ETH_ALEN);
+                       wpa_printf(MSG_DEBUG, "nl80211: Associated with "
+                                  MACSTR, MAC2STR(_arg->assoc_bssid));
+               }
+       }
+       if (!res)
+               return NL_SKIP;
        if (bss[NL80211_BSS_INFORMATION_ELEMENTS]) {
                ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
                ie_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
@@ -1873,7 +3120,7 @@ static int bss_info_handler(struct nl_msg *msg, void *arg)
                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_LEVEL_INVALID;
+               r->flags |= WPA_SCAN_QUAL_INVALID;
        } else
                r->flags |= WPA_SCAN_LEVEL_INVALID | WPA_SCAN_QUAL_INVALID;
        if (bss[NL80211_BSS_TSF])
@@ -1905,6 +3152,38 @@ static int bss_info_handler(struct nl_msg *msg, void *arg)
                }
        }
 
+       /*
+        * cfg80211 maintains separate BSS table entries for APs if the same
+        * BSSID,SSID pair is seen on multiple channels. wpa_supplicant does
+        * not use frequency as a separate key in the BSS table, so filter out
+        * duplicated entries. Prefer associated BSS entry in such a case in
+        * order to get the correct frequency into the BSS table.
+        */
+       for (i = 0; i < res->num; i++) {
+               const u8 *s1, *s2;
+               if (os_memcmp(res->res[i]->bssid, r->bssid, ETH_ALEN) != 0)
+                       continue;
+
+               s1 = nl80211_get_ie((u8 *) (res->res[i] + 1),
+                                   res->res[i]->ie_len, WLAN_EID_SSID);
+               s2 = nl80211_get_ie((u8 *) (r + 1), r->ie_len, WLAN_EID_SSID);
+               if (s1 == NULL || s2 == NULL || s1[1] != s2[1] ||
+                   os_memcmp(s1, s2, 2 + s1[1]) != 0)
+                       continue;
+
+               /* Same BSSID,SSID was already included in scan results */
+               wpa_printf(MSG_DEBUG, "nl80211: Remove duplicated scan result "
+                          "for " MACSTR, MAC2STR(r->bssid));
+
+               if ((r->flags & WPA_SCAN_ASSOCIATED) &&
+                   !(res->res[i]->flags & WPA_SCAN_ASSOCIATED)) {
+                       os_free(res->res[i]);
+                       res->res[i] = r;
+               } else
+                       os_free(r);
+               return NL_SKIP;
+       }
+
        tmp = os_realloc(res->res,
                         (res->num + 1) * sizeof(struct wpa_scan_res *));
        if (tmp == NULL) {
@@ -1943,7 +3222,7 @@ static void wpa_driver_nl80211_check_bss_status(
                                   "indicates BSS status with " MACSTR
                                   " as authenticated",
                                   MAC2STR(r->bssid));
-                       if (drv->nlmode == NL80211_IFTYPE_STATION &&
+                       if (is_sta_interface(drv->nlmode) &&
                            os_memcmp(r->bssid, drv->bssid, ETH_ALEN) != 0 &&
                            os_memcmp(r->bssid, drv->auth_bssid, ETH_ALEN) !=
                            0) {
@@ -1961,13 +3240,13 @@ static void wpa_driver_nl80211_check_bss_status(
                                   "indicate BSS status with " MACSTR
                                   " as associated",
                                   MAC2STR(r->bssid));
-                       if (drv->nlmode == NL80211_IFTYPE_STATION &&
+                       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 (drv->nlmode == NL80211_IFTYPE_STATION &&
+                       } else if (is_sta_interface(drv->nlmode) &&
                                   os_memcmp(drv->bssid, r->bssid, ETH_ALEN) !=
                                   0) {
                                wpa_printf(MSG_DEBUG, "nl80211: Local state "
@@ -1982,20 +3261,6 @@ static void wpa_driver_nl80211_check_bss_status(
 }
 
 
-static void wpa_scan_results_free(struct wpa_scan_results *res)
-{
-       size_t i;
-
-       if (res == NULL)
-               return;
-
-       for (i = 0; i < res->num; i++)
-               os_free(res->res[i]);
-       os_free(res->res);
-       os_free(res);
-}
-
-
 static struct wpa_scan_results *
 nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv)
 {
@@ -2011,8 +3276,7 @@ nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv)
        if (!msg)
                goto nla_put_failure;
 
-       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, NLM_F_DUMP,
-                   NL80211_CMD_GET_SCAN, 0);
+       nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SCAN);
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
 
        arg.drv = drv;
@@ -2020,8 +3284,9 @@ nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv)
        ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg);
        msg = NULL;
        if (ret == 0) {
-               wpa_printf(MSG_DEBUG, "Received scan results (%lu BSSes)",
-                          (unsigned long) res->num);
+               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 "
@@ -2092,17 +3357,19 @@ static int wpa_driver_nl80211_set_key(const char *ifname, void *priv,
                   "set_tx=%d seq_len=%lu key_len=%lu",
                   __func__, ifindex, alg, addr, key_idx, set_tx,
                   (unsigned long) seq_len, (unsigned long) key_len);
+#ifdef CONFIG_TDLS
+       if (key_idx == -1)
+               key_idx = 0;
+#endif /* CONFIG_TDLS */
 
        msg = nlmsg_alloc();
        if (!msg)
                return -ENOMEM;
 
        if (alg == WPA_ALG_NONE) {
-               genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
-                           0, NL80211_CMD_DEL_KEY, 0);
+               nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_KEY);
        } else {
-               genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
-                           0, NL80211_CMD_NEW_KEY, 0);
+               nl80211_cmd(drv, msg, 0, NL80211_CMD_NEW_KEY);
                NLA_PUT(msg, NL80211_ATTR_KEY_DATA, key_len, key);
                switch (alg) {
                case WPA_ALG_WEP:
@@ -2136,10 +3403,28 @@ static int wpa_driver_nl80211_set_key(const char *ifname, void *priv,
        if (seq && seq_len)
                NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, seq_len, seq);
 
-       if (addr && os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) != 0)
-       {
+       if (addr && !is_broadcast_ether_addr(addr)) {
                wpa_printf(MSG_DEBUG, "   addr=" MACSTR, MAC2STR(addr));
                NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
+
+               if (alg != WPA_ALG_WEP && key_idx && !set_tx) {
+                       wpa_printf(MSG_DEBUG, "   RSN IBSS RX GTK");
+                       NLA_PUT_U32(msg, NL80211_ATTR_KEY_TYPE,
+                                   NL80211_KEYTYPE_GROUP);
+               }
+       } else if (addr && is_broadcast_ether_addr(addr)) {
+               struct nl_msg *types;
+               int err;
+               wpa_printf(MSG_DEBUG, "   broadcast key");
+               types = nlmsg_alloc();
+               if (!types)
+                       goto nla_put_failure;
+               NLA_PUT_FLAG(types, NL80211_KEY_DEFAULT_TYPE_MULTICAST);
+               err = nla_put_nested(msg, NL80211_ATTR_KEY_DEFAULT_TYPES,
+                                    types);
+               nlmsg_free(types);
+               if (err)
+                       goto nla_put_failure;
        }
        NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx);
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
@@ -2157,26 +3442,46 @@ static int wpa_driver_nl80211_set_key(const char *ifname, void *priv,
         */
        if (ret || !set_tx || alg == WPA_ALG_NONE)
                return ret;
-#ifdef HOSTAPD
-       if (addr)
-               return ret;
-#else /* HOSTAPD */
-       if (drv->nlmode == NL80211_IFTYPE_AP && addr)
+       if (is_ap_interface(drv->nlmode) && addr &&
+           !is_broadcast_ether_addr(addr))
                return ret;
-#endif /* HOSTAPD */
 
        msg = nlmsg_alloc();
        if (!msg)
                return -ENOMEM;
 
-       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
-                   0, NL80211_CMD_SET_KEY, 0);
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_KEY);
        NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx);
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
        if (alg == WPA_ALG_IGTK)
                NLA_PUT_FLAG(msg, NL80211_ATTR_KEY_DEFAULT_MGMT);
        else
                NLA_PUT_FLAG(msg, NL80211_ATTR_KEY_DEFAULT);
+       if (addr && is_broadcast_ether_addr(addr)) {
+               struct nl_msg *types;
+               int err;
+               types = nlmsg_alloc();
+               if (!types)
+                       goto nla_put_failure;
+               NLA_PUT_FLAG(types, NL80211_KEY_DEFAULT_TYPE_MULTICAST);
+               err = nla_put_nested(msg, NL80211_ATTR_KEY_DEFAULT_TYPES,
+                                    types);
+               nlmsg_free(types);
+               if (err)
+                       goto nla_put_failure;
+       } else if (addr) {
+               struct nl_msg *types;
+               int err;
+               types = nlmsg_alloc();
+               if (!types)
+                       goto nla_put_failure;
+               NLA_PUT_FLAG(types, NL80211_KEY_DEFAULT_TYPE_UNICAST);
+               err = nla_put_nested(msg, NL80211_ATTR_KEY_DEFAULT_TYPES,
+                                    types);
+               nlmsg_free(types);
+               if (err)
+                       goto nla_put_failure;
+       }
 
        ret = send_and_recv_msgs(drv, msg, NULL, NULL);
        if (ret == -ENOENT)
@@ -2187,6 +3492,7 @@ static int wpa_driver_nl80211_set_key(const char *ifname, void *priv,
        return ret;
 
 nla_put_failure:
+       nlmsg_free(msg);
        return -ENOBUFS;
 }
 
@@ -2257,6 +3563,12 @@ static int nl80211_set_conn_keys(struct wpa_driver_associate_params *params,
                privacy = 1;
                break;
        }
+       if (params->wps == WPS_MODE_PRIVACY)
+               privacy = 1;
+       if (params->pairwise_suite &&
+           params->pairwise_suite != WPA_CIPHER_NONE)
+               privacy = 1;
+
        if (!privacy)
                return 0;
 
@@ -2310,7 +3622,7 @@ static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv,
        if (!msg)
                return -1;
 
-       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, cmd, 0);
+       nl80211_cmd(drv, msg, 0, cmd);
 
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
        NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, reason_code);
@@ -2336,7 +3648,8 @@ nla_put_failure:
 static int wpa_driver_nl80211_disconnect(struct wpa_driver_nl80211_data *drv,
                                         const u8 *addr, int reason_code)
 {
-       wpa_printf(MSG_DEBUG, "%s", __func__);
+       wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " reason_code=%d)",
+                  __func__, MAC2STR(addr), reason_code);
        drv->associated = 0;
        return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DISCONNECT,
                                       reason_code, 0);
@@ -2350,8 +3663,11 @@ static int wpa_driver_nl80211_deauthenticate(void *priv, const u8 *addr,
        struct wpa_driver_nl80211_data *drv = bss->drv;
        if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME))
                return wpa_driver_nl80211_disconnect(drv, addr, reason_code);
-       wpa_printf(MSG_DEBUG, "%s", __func__);
+       wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " reason_code=%d)",
+                  __func__, MAC2STR(addr), reason_code);
        drv->associated = 0;
+       if (drv->nlmode == NL80211_IFTYPE_ADHOC)
+               return nl80211_leave_ibss(drv);
        return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DEAUTHENTICATE,
                                       reason_code, 0);
 }
@@ -2379,15 +3695,16 @@ static int wpa_driver_nl80211_authenticate(
        int ret = -1, i;
        struct nl_msg *msg;
        enum nl80211_auth_type type;
+       enum nl80211_iftype nlmode;
        int count = 0;
 
        drv->associated = 0;
        os_memset(drv->auth_bssid, 0, ETH_ALEN);
        /* FIX: IBSS mode */
-       if (drv->nlmode != NL80211_IFTYPE_STATION)
-               wpa_driver_nl80211_set_mode(priv, IEEE80211_MODE_INFRA);
-
-       if (wpa_driver_nl80211_set_mode(priv, IEEE80211_MODE_INFRA) < 0)
+       nlmode = params->p2p ?
+               NL80211_IFTYPE_P2P_CLIENT : NL80211_IFTYPE_STATION;
+       if (drv->nlmode != nlmode &&
+           wpa_driver_nl80211_set_mode(priv, nlmode) < 0)
                return -1;
 
 retry:
@@ -2398,8 +3715,7 @@ retry:
        wpa_printf(MSG_DEBUG, "nl80211: Authenticate (ifindex=%d)",
                   drv->ifindex);
 
-       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
-                   NL80211_CMD_AUTHENTICATE, 0);
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_AUTHENTICATE);
 
        for (i = 0; i < 4; i++) {
                if (!params->wep_key[i])
@@ -2539,6 +3855,7 @@ static int phy_info_handler(struct nl_msg *msg, void *arg)
 
                mode = &phy_info->modes[*(phy_info->num_modes)];
                memset(mode, 0, sizeof(*mode));
+               mode->flags = HOSTAPD_MODE_FLAG_HT_INFO_KNOWN;
                *(phy_info->num_modes) += 1;
 
                nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band),
@@ -2737,6 +4054,153 @@ wpa_driver_nl80211_add_11b(struct hostapd_hw_modes *modes, u16 *num_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_ht40(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;
+
+       wpa_printf(MSG_DEBUG, "nl80211: %u-%u @ %u MHz",
+                  start, end, max_bw);
+       if (max_bw < 40)
+               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(&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 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;
+       }
+
+       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)
+       {
+               nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX,
+                         nla_data(nl_rule), nla_len(nl_rule), reg_policy);
+               nl80211_reg_rule_ht40(tb_rule, results);
+       }
+
+       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);
+       }
+
+       return NL_SKIP;
+}
+
+
+static int nl80211_set_ht40_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);
+}
+
+
 static struct hostapd_hw_modes *
 wpa_driver_nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags)
 {
@@ -2755,14 +4219,17 @@ wpa_driver_nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags)
        if (!msg)
                return NULL;
 
-       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
-                   0, NL80211_CMD_GET_WIPHY, 0);
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_WIPHY);
 
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
 
-       if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0)
+       if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) {
+               nl80211_set_ht40_flags(drv, &result);
                return wpa_driver_nl80211_add_11b(result.modes, num_modes);
+       }
+       msg = NULL;
  nla_put_failure:
+       nlmsg_free(msg);
        return NULL;
 }
 
@@ -2799,11 +4266,23 @@ static int wpa_driver_nl80211_send_frame(struct wpa_driver_nl80211_data *drv,
                .msg_controllen = 0,
                .msg_flags = 0,
        };
+       int res;
 
        if (encrypt)
                rtap_hdr[8] |= IEEE80211_RADIOTAP_F_WEP;
 
-       return sendmsg(drv->monitor_sock, &msg, 0);
+       if (drv->monitor_sock < 0) {
+               wpa_printf(MSG_DEBUG, "nl80211: No monitor socket available "
+                          "for %s", __func__);
+               return -1;
+       }
+
+       res = sendmsg(drv->monitor_sock, &msg, 0);
+       if (res < 0) {
+               wpa_printf(MSG_INFO, "nl80211: sendmsg: %s", strerror(errno));
+               return -1;
+       }
+       return 0;
 }
 
 
@@ -2819,6 +4298,24 @@ static int wpa_driver_nl80211_send_mlme(void *priv, const u8 *data,
        mgmt = (struct ieee80211_mgmt *) data;
        fc = le_to_host16(mgmt->frame_control);
 
+       if (is_sta_interface(drv->nlmode) &&
+           WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+           WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_RESP) {
+               /*
+                * The use of last_mgmt_freq is a bit of a hack,
+                * but it works due to the single-threaded nature
+                * of wpa_supplicant.
+                */
+               return nl80211_send_frame_cmd(drv, drv->last_mgmt_freq, 0,
+                                             data, data_len, NULL, 1);
+       }
+
+       if (drv->no_monitor_iface_capab && is_ap_interface(drv->nlmode)) {
+               return nl80211_send_frame_cmd(drv, drv->ap_oper_freq, 0,
+                                             data, data_len,
+                                             &drv->send_action_cookie, 0);
+       }
+
        if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
            WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_AUTH) {
                /*
@@ -2837,10 +4334,57 @@ static int wpa_driver_nl80211_send_mlme(void *priv, const u8 *data,
 }
 
 
-static int wpa_driver_nl80211_set_beacon(void *priv,
-                                        const u8 *head, size_t head_len,
-                                        const u8 *tail, size_t tail_len,
-                                        int dtim_period, int beacon_int)
+static int nl80211_set_ap_isolate(struct i802_bss *bss, int enabled)
+{
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -ENOMEM;
+
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_BSS);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
+       NLA_PUT_U8(msg, NL80211_ATTR_AP_ISOLATE, enabled);
+
+       return send_and_recv_msgs(drv, msg, NULL, NULL);
+ nla_put_failure:
+       return -ENOBUFS;
+}
+
+
+static int nl80211_set_bss(struct i802_bss *bss, int cts, int preamble,
+                          int slot, int ht_opmode)
+{
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -ENOMEM;
+
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_BSS);
+
+       if (cts >= 0)
+               NLA_PUT_U8(msg, NL80211_ATTR_BSS_CTS_PROT, cts);
+       if (preamble >= 0)
+               NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_PREAMBLE, preamble);
+       if (slot >= 0)
+               NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_SLOT_TIME, slot);
+       if (ht_opmode >= 0)
+               NLA_PUT_U16(msg, NL80211_ATTR_BSS_HT_OPMODE, ht_opmode);
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
+
+       return send_and_recv_msgs(drv, msg, NULL, NULL);
+ nla_put_failure:
+       nlmsg_free(msg);
+       return -ENOBUFS;
+}
+
+
+static int wpa_driver_nl80211_set_ap(void *priv,
+                                    struct wpa_driver_ap_params *params)
 {
        struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
@@ -2849,6 +4393,9 @@ static int wpa_driver_nl80211_set_beacon(void *priv,
        int ret;
        int beacon_set;
        int ifindex = if_nametoindex(bss->ifname);
+       int num_suites;
+       u32 suites[10];
+       u32 ver;
 
        beacon_set = bss->beacon_set;
 
@@ -2861,13 +4408,105 @@ static int wpa_driver_nl80211_set_beacon(void *priv,
        if (beacon_set)
                cmd = NL80211_CMD_SET_BEACON;
 
-       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
-                   0, cmd, 0);
-       NLA_PUT(msg, NL80211_ATTR_BEACON_HEAD, head_len, head);
-       NLA_PUT(msg, NL80211_ATTR_BEACON_TAIL, tail_len, tail);
+       nl80211_cmd(drv, msg, 0, cmd);
+       NLA_PUT(msg, NL80211_ATTR_BEACON_HEAD, params->head_len, params->head);
+       NLA_PUT(msg, NL80211_ATTR_BEACON_TAIL, params->tail_len, params->tail);
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
-       NLA_PUT_U32(msg, NL80211_ATTR_BEACON_INTERVAL, beacon_int);
-       NLA_PUT_U32(msg, NL80211_ATTR_DTIM_PERIOD, dtim_period);
+       NLA_PUT_U32(msg, NL80211_ATTR_BEACON_INTERVAL, params->beacon_int);
+       NLA_PUT_U32(msg, NL80211_ATTR_DTIM_PERIOD, params->dtim_period);
+       NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len,
+               params->ssid);
+       switch (params->hide_ssid) {
+       case NO_SSID_HIDING:
+               NLA_PUT_U32(msg, NL80211_ATTR_HIDDEN_SSID,
+                           NL80211_HIDDEN_SSID_NOT_IN_USE);
+               break;
+       case HIDDEN_SSID_ZERO_LEN:
+               NLA_PUT_U32(msg, NL80211_ATTR_HIDDEN_SSID,
+                           NL80211_HIDDEN_SSID_ZERO_LEN);
+               break;
+       case HIDDEN_SSID_ZERO_CONTENTS:
+               NLA_PUT_U32(msg, NL80211_ATTR_HIDDEN_SSID,
+                           NL80211_HIDDEN_SSID_ZERO_CONTENTS);
+               break;
+       }
+       if (params->privacy)
+               NLA_PUT_FLAG(msg, NL80211_ATTR_PRIVACY);
+       if ((params->auth_algs & (WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED)) ==
+           (WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED)) {
+               /* Leave out the attribute */
+       } else if (params->auth_algs & WPA_AUTH_ALG_SHARED)
+               NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE,
+                           NL80211_AUTHTYPE_SHARED_KEY);
+       else
+               NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE,
+                           NL80211_AUTHTYPE_OPEN_SYSTEM);
+
+       ver = 0;
+       if (params->wpa_version & WPA_PROTO_WPA)
+               ver |= NL80211_WPA_VERSION_1;
+       if (params->wpa_version & WPA_PROTO_RSN)
+               ver |= NL80211_WPA_VERSION_2;
+       if (ver)
+               NLA_PUT_U32(msg, NL80211_ATTR_WPA_VERSIONS, ver);
+
+       num_suites = 0;
+       if (params->key_mgmt_suites & WPA_KEY_MGMT_IEEE8021X)
+               suites[num_suites++] = WLAN_AKM_SUITE_8021X;
+       if (params->key_mgmt_suites & WPA_KEY_MGMT_PSK)
+               suites[num_suites++] = WLAN_AKM_SUITE_PSK;
+       if (num_suites) {
+               NLA_PUT(msg, NL80211_ATTR_AKM_SUITES,
+                       num_suites * sizeof(u32), suites);
+       }
+
+       num_suites = 0;
+       if (params->pairwise_ciphers & WPA_CIPHER_CCMP)
+               suites[num_suites++] = WLAN_CIPHER_SUITE_CCMP;
+       if (params->pairwise_ciphers & WPA_CIPHER_TKIP)
+               suites[num_suites++] = WLAN_CIPHER_SUITE_TKIP;
+       if (params->pairwise_ciphers & WPA_CIPHER_WEP104)
+               suites[num_suites++] = WLAN_CIPHER_SUITE_WEP104;
+       if (params->pairwise_ciphers & WPA_CIPHER_WEP40)
+               suites[num_suites++] = WLAN_CIPHER_SUITE_WEP40;
+       if (num_suites) {
+               NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE,
+                       num_suites * sizeof(u32), suites);
+       }
+
+       switch (params->group_cipher) {
+       case WPA_CIPHER_CCMP:
+               NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP,
+                           WLAN_CIPHER_SUITE_CCMP);
+               break;
+       case WPA_CIPHER_TKIP:
+               NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP,
+                           WLAN_CIPHER_SUITE_TKIP);
+               break;
+       case WPA_CIPHER_WEP104:
+               NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP,
+                           WLAN_CIPHER_SUITE_WEP104);
+               break;
+       case WPA_CIPHER_WEP40:
+               NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP,
+                           WLAN_CIPHER_SUITE_WEP40);
+               break;
+       }
+
+       if (params->beacon_ies) {
+               NLA_PUT(msg, NL80211_ATTR_IE, wpabuf_len(params->beacon_ies),
+                       wpabuf_head(params->beacon_ies));
+       }
+       if (params->proberesp_ies) {
+               NLA_PUT(msg, NL80211_ATTR_IE_PROBE_RESP,
+                       wpabuf_len(params->proberesp_ies),
+                       wpabuf_head(params->proberesp_ies));
+       }
+       if (params->assocresp_ies) {
+               NLA_PUT(msg, NL80211_ATTR_IE_ASSOC_RESP,
+                       wpabuf_len(params->assocresp_ies),
+                       wpabuf_head(params->assocresp_ies));
+       }
 
        ret = send_and_recv_msgs(drv, msg, NULL, NULL);
        if (ret) {
@@ -2875,9 +4514,20 @@ static int wpa_driver_nl80211_set_beacon(void *priv,
                           ret, strerror(-ret));
        } else {
                bss->beacon_set = 1;
+               ret = nl80211_set_ap_isolate(bss, params->isolate);
+               if (!params->isolate && ret) {
+                       wpa_printf(MSG_DEBUG, "nl80211: Ignore AP isolation "
+                                  "configuration error since isolation is "
+                                  "not used");
+                       ret = 0;
+               }
+
+               nl80211_set_bss(bss, params->cts_protect, params->preamble,
+                               params->short_slot_time, params->ht_opmode);
        }
        return ret;
  nla_put_failure:
+       nlmsg_free(msg);
        return -ENOBUFS;
 }
 
@@ -2889,12 +4539,14 @@ static int wpa_driver_nl80211_set_freq(struct wpa_driver_nl80211_data *drv,
        struct nl_msg *msg;
        int ret;
 
+       wpa_printf(MSG_DEBUG, "nl80211: Set freq %d (ht_enabled=%d "
+                  "sec_channel_offset=%d)",
+                  freq, ht_enabled, sec_channel_offset);
        msg = nlmsg_alloc();
        if (!msg)
                return -1;
 
-       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
-                   NL80211_CMD_SET_WIPHY, 0);
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY);
 
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
        NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
@@ -2916,50 +4568,86 @@ static int wpa_driver_nl80211_set_freq(struct wpa_driver_nl80211_data *drv,
        }
 
        ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       msg = NULL;
        if (ret == 0)
                return 0;
        wpa_printf(MSG_DEBUG, "nl80211: Failed to set channel (freq=%d): "
                   "%d (%s)", freq, ret, strerror(-ret));
 nla_put_failure:
+       nlmsg_free(msg);
        return -1;
 }
 
 
+static u32 sta_flags_nl80211(int flags)
+{
+       u32 f = 0;
+
+       if (flags & WPA_STA_AUTHORIZED)
+               f |= BIT(NL80211_STA_FLAG_AUTHORIZED);
+       if (flags & WPA_STA_WMM)
+               f |= BIT(NL80211_STA_FLAG_WME);
+       if (flags & WPA_STA_SHORT_PREAMBLE)
+               f |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE);
+       if (flags & WPA_STA_MFP)
+               f |= BIT(NL80211_STA_FLAG_MFP);
+       if (flags & WPA_STA_TDLS_PEER)
+               f |= BIT(NL80211_STA_FLAG_TDLS_PEER);
+
+       return f;
+}
+
+
 static int wpa_driver_nl80211_sta_add(void *priv,
                                      struct hostapd_sta_add_params *params)
 {
        struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
        struct nl_msg *msg;
+       struct nl80211_sta_flag_update upd;
        int ret = -ENOBUFS;
 
+       if ((params->flags & WPA_STA_TDLS_PEER) &&
+           !(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT))
+               return -EOPNOTSUPP;
+
        msg = nlmsg_alloc();
        if (!msg)
                return -ENOMEM;
 
-       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
-                   0, NL80211_CMD_NEW_STATION, 0);
+       nl80211_cmd(drv, msg, 0, params->set ? NL80211_CMD_SET_STATION :
+                   NL80211_CMD_NEW_STATION);
 
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
        NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->addr);
-       NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, params->aid);
        NLA_PUT(msg, NL80211_ATTR_STA_SUPPORTED_RATES, params->supp_rates_len,
                params->supp_rates);
-       NLA_PUT_U16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL,
-                   params->listen_interval);
+       if (!params->set) {
+               NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, params->aid);
+               NLA_PUT_U16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL,
+                           params->listen_interval);
+       }
        if (params->ht_capabilities) {
                NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY,
                        sizeof(*params->ht_capabilities),
                        params->ht_capabilities);
        }
 
+       os_memset(&upd, 0, sizeof(upd));
+       upd.mask = sta_flags_nl80211(params->flags);
+       upd.set = upd.mask;
+       NLA_PUT(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd);
+
        ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       msg = NULL;
        if (ret)
-               wpa_printf(MSG_DEBUG, "nl80211: NL80211_CMD_NEW_STATION "
-                          "result: %d (%s)", ret, strerror(-ret));
+               wpa_printf(MSG_DEBUG, "nl80211: NL80211_CMD_%s_STATION "
+                          "result: %d (%s)", params->set ? "SET" : "NEW", ret,
+                          strerror(-ret));
        if (ret == -EEXIST)
                ret = 0;
  nla_put_failure:
+       nlmsg_free(msg);
        return ret;
 }
 
@@ -2975,8 +4663,7 @@ static int wpa_driver_nl80211_sta_remove(void *priv, const u8 *addr)
        if (!msg)
                return -ENOMEM;
 
-       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
-                   0, NL80211_CMD_DEL_STATION, 0);
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_STATION);
 
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
                    if_nametoindex(bss->ifname));
@@ -2987,6 +4674,7 @@ static int wpa_driver_nl80211_sta_remove(void *priv, const u8 *addr)
                return 0;
        return ret;
  nla_put_failure:
+       nlmsg_free(msg);
        return -ENOBUFS;
 }
 
@@ -2998,26 +4686,46 @@ static void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv,
 
        wpa_printf(MSG_DEBUG, "nl80211: Remove interface ifindex=%d", ifidx);
 
-#ifdef HOSTAPD
        /* stop listening for EAPOL on this interface */
        del_ifidx(drv, ifidx);
-#endif /* HOSTAPD */
 
        msg = nlmsg_alloc();
        if (!msg)
                goto nla_put_failure;
 
-       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
-                   0, NL80211_CMD_DEL_INTERFACE, 0);
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_INTERFACE);
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifidx);
 
        if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0)
                return;
+       msg = NULL;
  nla_put_failure:
+       nlmsg_free(msg);
        wpa_printf(MSG_ERROR, "Failed to remove interface (ifidx=%d)", ifidx);
 }
 
 
+static const char * nl80211_iftype_str(enum nl80211_iftype mode)
+{
+       switch (mode) {
+       case NL80211_IFTYPE_ADHOC:
+               return "ADHOC";
+       case NL80211_IFTYPE_STATION:
+               return "STATION";
+       case NL80211_IFTYPE_AP:
+               return "AP";
+       case NL80211_IFTYPE_MONITOR:
+               return "MONITOR";
+       case NL80211_IFTYPE_P2P_CLIENT:
+               return "P2P_CLIENT";
+       case NL80211_IFTYPE_P2P_GO:
+               return "P2P_GO";
+       default:
+               return "unknown";
+       }
+}
+
+
 static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv,
                                     const char *ifname,
                                     enum nl80211_iftype iftype,
@@ -3027,12 +4735,14 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv,
        int ifidx;
        int ret = -ENOBUFS;
 
+       wpa_printf(MSG_DEBUG, "nl80211: Create interface iftype %d (%s)",
+                  iftype, nl80211_iftype_str(iftype));
+
        msg = nlmsg_alloc();
        if (!msg)
                return -1;
 
-       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
-                   0, NL80211_CMD_NEW_INTERFACE, 0);
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_NEW_INTERFACE);
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
        NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, ifname);
        NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, iftype);
@@ -3057,8 +4767,10 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv,
        }
 
        ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       msg = NULL;
        if (ret) {
  nla_put_failure:
+               nlmsg_free(msg);
                wpa_printf(MSG_ERROR, "Failed to create interface %s: %d (%s)",
                           ifname, ret, strerror(-ret));
                return ret;
@@ -3071,13 +4783,11 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv,
        if (ifidx <= 0)
                return -1;
 
-#ifdef HOSTAPD
        /* start listening for EAPOL on this interface */
        add_ifidx(drv, ifidx);
-#endif /* HOSTAPD */
 
        if (addr && iftype != NL80211_IFTYPE_MONITOR &&
-           linux_set_ifhwaddr(drv->ioctl_sock, ifname, addr)) {
+           linux_set_ifhwaddr(drv->global->ioctl_sock, ifname, addr)) {
                nl80211_remove_iface(drv, ifidx);
                return -1;
        }
@@ -3094,7 +4804,7 @@ static int nl80211_create_iface(struct wpa_driver_nl80211_data *drv,
 
        ret = nl80211_create_iface_once(drv, ifname, iftype, addr, wds);
 
-       /* if error occured and interface exists already */
+       /* if error occurred and interface exists already */
        if (ret == -ENFILE && if_nametoindex(ifname)) {
                wpa_printf(MSG_INFO, "Try to remove and re-create %s", ifname);
 
@@ -3106,7 +4816,7 @@ static int nl80211_create_iface(struct wpa_driver_nl80211_data *drv,
                                                wds);
        }
 
-       if (ret >= 0 && drv->disable_11b_rates)
+       if (ret >= 0 && is_p2p_interface(iftype))
                nl80211_disable_11b_rates(drv, ret, 1);
 
        return ret;
@@ -3136,10 +4846,20 @@ static void handle_tx_callback(void *ctx, u8 *buf, size_t len, int ok)
 static void from_unknown_sta(struct wpa_driver_nl80211_data *drv,
                             u8 *buf, size_t len)
 {
+       struct ieee80211_hdr *hdr = (void *)buf;
+       u16 fc;
        union wpa_event_data event;
+
+       if (len < sizeof(*hdr))
+               return;
+
+       fc = le_to_host16(hdr->frame_control);
+
        os_memset(&event, 0, sizeof(event));
-       event.rx_from_unknown.frame = buf;
-       event.rx_from_unknown.len = len;
+       event.rx_from_unknown.bssid = get_hdr_bssid(hdr, len);
+       event.rx_from_unknown.addr = hdr->addr2;
+       event.rx_from_unknown.wds = (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) ==
+               (WLAN_FC_FROMDS | WLAN_FC_TODS);
        wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event);
 }
 
@@ -3191,12 +4911,6 @@ static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx)
                return;
        }
 
-       if (drv->nlmode == NL80211_IFTYPE_STATION && !drv->probe_req_report) {
-               wpa_printf(MSG_DEBUG, "nl80211: Ignore monitor interface "
-                          "frame since Probe Request reporting is disabled");
-               return;
-       }
-
        if (ieee80211_radiotap_iterator_init(&iter, (void*)buf, len)) {
                printf("received invalid radiotap frame\n");
                return;
@@ -3290,8 +5004,15 @@ static struct sock_filter msock_filter_insns[] = {
         * add a filter here that filters on our DA and that flag
         * to allow us to deauth frames to that bad station.
         *
-        * Not a regression -- we didn't do it before either.
+        * For now allow all To DS data frames through.
         */
+       /* load the IEEE 802.11 frame control field */
+       BPF_STMT(BPF_LD  | BPF_H | BPF_IND, 0),
+       /* mask off frame type, version and DS status */
+       BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x0F03),
+       /* accept frame if version 0, type 2 and To DS, fall through otherwise
+        */
+       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0801, PASS, 0),
 
 #if 0
        /*
@@ -3416,17 +5137,35 @@ nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv)
        int optval;
        socklen_t optlen;
 
-       snprintf(buf, IFNAMSIZ, "mon.%s", drv->first_bss.ifname);
+       if (os_strncmp(drv->first_bss.ifname, "p2p-", 4) == 0) {
+               /*
+                * P2P interface name is of the format p2p-%s-%d. For monitor
+                * interface name corresponding to P2P GO, replace "p2p-" with
+                * "mon-" to retain the same interface name length and to
+                * indicate that it is a monitor interface.
+                */
+               snprintf(buf, IFNAMSIZ, "mon-%s", drv->first_bss.ifname + 4);
+       } else {
+               /* Non-P2P interface with AP functionality. */
+               snprintf(buf, IFNAMSIZ, "mon.%s", drv->first_bss.ifname);
+       }
+
        buf[IFNAMSIZ - 1] = '\0';
 
        drv->monitor_ifidx =
                nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR, NULL,
                                     0);
 
+       if (drv->monitor_ifidx == -EOPNOTSUPP) {
+               wpa_printf(MSG_DEBUG, "nl80211: Driver does not support "
+                          "monitor interface type - try to run without it");
+               drv->no_monitor_iface_capab = 1;
+       }
+
        if (drv->monitor_ifidx < 0)
                return -1;
 
-       if (linux_set_iface_flags(drv->ioctl_sock, buf, 1))
+       if (linux_set_iface_flags(drv->global->ioctl_sock, buf, 1))
                goto error;
 
        memset(&ll, 0, sizeof(ll));
@@ -3470,11 +5209,29 @@ nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv)
 }
 
 
+#ifdef CONFIG_AP
+static int nl80211_send_eapol_data(struct i802_bss *bss,
+                                  const u8 *addr, const u8 *data,
+                                  size_t data_len, const u8 *own_addr)
+{
+       if (bss->drv->l2 == NULL) {
+               wpa_printf(MSG_DEBUG, "nl80211: No l2_packet to send EAPOL");
+               return -1;
+       }
+
+       if (l2_packet_send(bss->drv->l2, addr, ETH_P_EAPOL, data, data_len) <
+           0)
+               return -1;
+       return 0;
+}
+#endif /* CONFIG_AP */
+
+
 static const u8 rfc1042_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
 
 static int wpa_driver_nl80211_hapd_send_eapol(
        void *priv, const u8 *addr, const u8 *data,
-       size_t data_len, int encrypt, const u8 *own_addr)
+       size_t data_len, int encrypt, const u8 *own_addr, u32 flags)
 {
        struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
@@ -3482,11 +5239,13 @@ static int wpa_driver_nl80211_hapd_send_eapol(
        size_t len;
        u8 *pos;
        int res;
-#if 0 /* FIX */
-       int qos = sta->flags & WPA_STA_WMM;
-#else
-       int qos = 0;
-#endif
+       int qos = flags & WPA_STA_WMM;
+
+#ifdef CONFIG_AP
+       if (drv->no_monitor_iface_capab)
+               return nl80211_send_eapol_data(bss, addr, data, data_len,
+                                              own_addr);
+#endif /* CONFIG_AP */
 
        len = sizeof(*hdr) + (qos ? 2 : 0) + sizeof(rfc1042_header) + 2 +
                data_len;
@@ -3502,26 +5261,22 @@ static int wpa_driver_nl80211_hapd_send_eapol(
        hdr->frame_control |= host_to_le16(WLAN_FC_FROMDS);
        if (encrypt)
                hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP);
-#if 0 /* To be enabled if qos determination is added above */
        if (qos) {
                hdr->frame_control |=
                        host_to_le16(WLAN_FC_STYPE_QOS_DATA << 4);
        }
-#endif
 
        memcpy(hdr->IEEE80211_DA_FROMDS, addr, ETH_ALEN);
        memcpy(hdr->IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN);
        memcpy(hdr->IEEE80211_SA_FROMDS, own_addr, ETH_ALEN);
        pos = (u8 *) (hdr + 1);
 
-#if 0 /* To be enabled if qos determination is added above */
        if (qos) {
                /* add an empty QoS header if needed */
                pos[0] = 0;
                pos[1] = 0;
                pos += 2;
        }
-#endif
 
        memcpy(pos, rfc1042_header, sizeof(rfc1042_header));
        pos += sizeof(rfc1042_header);
@@ -3535,26 +5290,9 @@ static int wpa_driver_nl80211_hapd_send_eapol(
                           "failed: %d (%s)",
                           (unsigned long) len, errno, strerror(errno));
        }
-       os_free(hdr);
-
-       return res;
-}
-
-
-static u32 sta_flags_nl80211(int flags)
-{
-       u32 f = 0;
-
-       if (flags & WPA_STA_AUTHORIZED)
-               f |= BIT(NL80211_STA_FLAG_AUTHORIZED);
-       if (flags & WPA_STA_WMM)
-               f |= BIT(NL80211_STA_FLAG_WME);
-       if (flags & WPA_STA_SHORT_PREAMBLE)
-               f |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE);
-       if (flags & WPA_STA_MFP)
-               f |= BIT(NL80211_STA_FLAG_MFP);
+       os_free(hdr);
 
-       return f;
+       return res;
 }
 
 
@@ -3577,8 +5315,7 @@ static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr,
                return -ENOMEM;
        }
 
-       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
-                   0, NL80211_CMD_SET_STATION, 0);
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_STATION);
 
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
                    if_nametoindex(bss->ifname));
@@ -3600,6 +5337,9 @@ static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr,
        if (total_flags & WPA_STA_MFP)
                NLA_PUT_FLAG(flags, NL80211_STA_FLAG_MFP);
 
+       if (total_flags & WPA_STA_TDLS_PEER)
+               NLA_PUT_FLAG(flags, NL80211_STA_FLAG_TDLS_PEER);
+
        if (nla_put_nested(msg, NL80211_ATTR_STA_FLAGS, flags))
                goto nla_put_failure;
 
@@ -3612,6 +5352,7 @@ static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr,
 
        return send_and_recv_msgs(drv, msg, NULL, NULL);
  nla_put_failure:
+       nlmsg_free(msg);
        nlmsg_free(flags);
        return -ENOBUFS;
 }
@@ -3620,14 +5361,31 @@ static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr,
 static int wpa_driver_nl80211_ap(struct wpa_driver_nl80211_data *drv,
                                 struct wpa_driver_associate_params *params)
 {
-       if (wpa_driver_nl80211_set_mode(&drv->first_bss, params->mode) ||
+       enum nl80211_iftype nlmode;
+
+       if (params->p2p) {
+               wpa_printf(MSG_DEBUG, "nl80211: Setup AP operations for P2P "
+                          "group (GO)");
+               nlmode = NL80211_IFTYPE_P2P_GO;
+       } else
+               nlmode = NL80211_IFTYPE_AP;
+
+       if (wpa_driver_nl80211_set_mode(&drv->first_bss, nlmode) ||
            wpa_driver_nl80211_set_freq(drv, params->freq, 0, 0)) {
                nl80211_remove_monitor_interface(drv);
                return -1;
        }
 
-       /* TODO: setup monitor interface (and add code somewhere to remove this
-        * when AP mode is stopped; associate with mode != 2 or drv_deinit) */
+       if (drv->no_monitor_iface_capab) {
+               if (wpa_driver_nl80211_probe_req_report(&drv->first_bss, 1) < 0)
+               {
+                       wpa_printf(MSG_DEBUG, "nl80211: Failed to enable "
+                                  "Probe Request frame reporting in AP mode");
+                       /* Try to survive without this */
+               }
+       }
+
+       drv->ap_oper_freq = params->freq;
 
        return 0;
 }
@@ -3642,8 +5400,7 @@ static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv)
        if (!msg)
                return -1;
 
-       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
-                   NL80211_CMD_LEAVE_IBSS, 0);
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_LEAVE_IBSS);
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
        ret = send_and_recv_msgs(drv, msg, NULL, NULL);
        msg = NULL;
@@ -3671,7 +5428,8 @@ static int wpa_driver_nl80211_ibss(struct wpa_driver_nl80211_data *drv,
 
        wpa_printf(MSG_DEBUG, "nl80211: Join IBSS (ifindex=%d)", drv->ifindex);
 
-       if (wpa_driver_nl80211_set_mode(&drv->first_bss, params->mode)) {
+       if (wpa_driver_nl80211_set_mode(&drv->first_bss,
+                                       NL80211_IFTYPE_ADHOC)) {
                wpa_printf(MSG_INFO, "nl80211: Failed to set interface into "
                           "IBSS mode");
                return -1;
@@ -3682,8 +5440,7 @@ retry:
        if (!msg)
                return -1;
 
-       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
-                   NL80211_CMD_JOIN_IBSS, 0);
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_JOIN_IBSS);
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
 
        if (params->ssid == NULL || params->ssid_len > sizeof(drv->ssid))
@@ -3736,6 +5493,55 @@ nla_put_failure:
 }
 
 
+static unsigned int nl80211_get_assoc_bssid(struct wpa_driver_nl80211_data *drv,
+                                           u8 *bssid)
+{
+       struct nl_msg *msg;
+       int ret;
+       struct nl80211_bss_info_arg arg;
+
+       os_memset(&arg, 0, sizeof(arg));
+       msg = nlmsg_alloc();
+       if (!msg)
+               goto nla_put_failure;
+
+       nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SCAN);
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+
+       arg.drv = drv;
+       ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg);
+       msg = NULL;
+       if (ret == 0) {
+               if (is_zero_ether_addr(arg.assoc_bssid))
+                       return -ENOTCONN;
+               os_memcpy(bssid, arg.assoc_bssid, ETH_ALEN);
+               return 0;
+       }
+       wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d "
+                  "(%s)", ret, strerror(-ret));
+nla_put_failure:
+       nlmsg_free(msg);
+       return drv->assoc_freq;
+}
+
+
+static int nl80211_disconnect(struct wpa_driver_nl80211_data *drv,
+                             const u8 *bssid)
+{
+       u8 addr[ETH_ALEN];
+
+       if (bssid == NULL) {
+               int res = nl80211_get_assoc_bssid(drv, addr);
+               if (res)
+                       return res;
+               bssid = addr;
+       }
+
+       return wpa_driver_nl80211_disconnect(drv, bssid,
+                                            WLAN_REASON_PREV_AUTH_NOT_VALID);
+}
+
+
 static int wpa_driver_nl80211_connect(
        struct wpa_driver_nl80211_data *drv,
        struct wpa_driver_associate_params *params)
@@ -3743,14 +5549,14 @@ static int wpa_driver_nl80211_connect(
        struct nl_msg *msg;
        enum nl80211_auth_type type;
        int ret = 0;
+       int algs;
 
        msg = nlmsg_alloc();
        if (!msg)
                return -1;
 
        wpa_printf(MSG_DEBUG, "nl80211: Connect (ifindex=%d)", drv->ifindex);
-       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
-                   NL80211_CMD_CONNECT, 0);
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_CONNECT);
 
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
        if (params->bssid) {
@@ -3777,6 +5583,19 @@ static int wpa_driver_nl80211_connect(
                NLA_PUT(msg, NL80211_ATTR_IE, params->wpa_ie_len,
                        params->wpa_ie);
 
+       algs = 0;
+       if (params->auth_alg & WPA_AUTH_ALG_OPEN)
+               algs++;
+       if (params->auth_alg & WPA_AUTH_ALG_SHARED)
+               algs++;
+       if (params->auth_alg & WPA_AUTH_ALG_LEAP)
+               algs++;
+       if (algs > 1) {
+               wpa_printf(MSG_DEBUG, "  * Leave out Auth Type for automatic "
+                          "selection");
+               goto skip_auth_type;
+       }
+
        if (params->auth_alg & WPA_AUTH_ALG_OPEN)
                type = NL80211_AUTHTYPE_OPEN_SYSTEM;
        else if (params->auth_alg & WPA_AUTH_ALG_SHARED)
@@ -3791,15 +5610,16 @@ static int wpa_driver_nl80211_connect(
        wpa_printf(MSG_DEBUG, "  * Auth Type %d", type);
        NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE, type);
 
-       if (params->wpa_ie && params->wpa_ie_len) {
-               enum nl80211_wpa_versions ver;
+skip_auth_type:
+       if (params->wpa_proto) {
+               enum nl80211_wpa_versions ver = 0;
 
-               if (params->wpa_ie[0] == WLAN_EID_RSN)
-                       ver = NL80211_WPA_VERSION_2;
-               else
-                       ver = NL80211_WPA_VERSION_1;
+               if (params->wpa_proto & WPA_PROTO_WPA)
+                       ver |= NL80211_WPA_VERSION_1;
+               if (params->wpa_proto & WPA_PROTO_RSN)
+                       ver |= NL80211_WPA_VERSION_2;
 
-               wpa_printf(MSG_DEBUG, "  * WPA Version %d", ver);
+               wpa_printf(MSG_DEBUG, "  * WPA Versions 0x%x", ver);
                NLA_PUT_U32(msg, NL80211_ATTR_WPA_VERSIONS, ver);
        }
 
@@ -3870,6 +5690,14 @@ static int wpa_driver_nl80211_connect(
        if (ret) {
                wpa_printf(MSG_DEBUG, "nl80211: MLME connect failed: ret=%d "
                           "(%s)", ret, strerror(-ret));
+               /*
+                * cfg80211 does not currently accept new connection if we are
+                * already connected. As a workaround, force disconnection and
+                * try again once the driver indicates it completed
+                * disconnection.
+                */
+               if (ret == -EALREADY)
+                       nl80211_disconnect(drv, params->bssid);
                goto nla_put_failure;
        }
        ret = 0;
@@ -3897,7 +5725,10 @@ static int wpa_driver_nl80211_associate(
                return wpa_driver_nl80211_ibss(drv, params);
 
        if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) {
-               if (wpa_driver_nl80211_set_mode(priv, params->mode) < 0)
+               enum nl80211_iftype nlmode = params->p2p ?
+                       NL80211_IFTYPE_P2P_CLIENT : NL80211_IFTYPE_STATION;
+
+               if (wpa_driver_nl80211_set_mode(priv, nlmode) < 0)
                        return -1;
                return wpa_driver_nl80211_connect(drv, params);
        }
@@ -3910,8 +5741,7 @@ static int wpa_driver_nl80211_associate(
 
        wpa_printf(MSG_DEBUG, "nl80211: Associate (ifindex=%d)",
                   drv->ifindex);
-       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
-                   NL80211_CMD_ASSOCIATE, 0);
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_ASSOCIATE);
 
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
        if (params->bssid) {
@@ -3940,6 +5770,50 @@ static int wpa_driver_nl80211_associate(
                NLA_PUT(msg, NL80211_ATTR_IE, params->wpa_ie_len,
                        params->wpa_ie);
 
+       if (params->pairwise_suite != CIPHER_NONE) {
+               int cipher;
+
+               switch (params->pairwise_suite) {
+               case CIPHER_WEP40:
+                       cipher = WLAN_CIPHER_SUITE_WEP40;
+                       break;
+               case CIPHER_WEP104:
+                       cipher = WLAN_CIPHER_SUITE_WEP104;
+                       break;
+               case CIPHER_CCMP:
+                       cipher = WLAN_CIPHER_SUITE_CCMP;
+                       break;
+               case CIPHER_TKIP:
+               default:
+                       cipher = WLAN_CIPHER_SUITE_TKIP;
+                       break;
+               }
+               wpa_printf(MSG_DEBUG, "  * pairwise=0x%x", cipher);
+               NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, cipher);
+       }
+
+       if (params->group_suite != CIPHER_NONE) {
+               int cipher;
+
+               switch (params->group_suite) {
+               case CIPHER_WEP40:
+                       cipher = WLAN_CIPHER_SUITE_WEP40;
+                       break;
+               case CIPHER_WEP104:
+                       cipher = WLAN_CIPHER_SUITE_WEP104;
+                       break;
+               case CIPHER_CCMP:
+                       cipher = WLAN_CIPHER_SUITE_CCMP;
+                       break;
+               case CIPHER_TKIP:
+               default:
+                       cipher = WLAN_CIPHER_SUITE_TKIP;
+                       break;
+               }
+               wpa_printf(MSG_DEBUG, "  * group=0x%x", cipher);
+               NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, cipher);
+       }
+
 #ifdef CONFIG_IEEE80211W
        if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED)
                NLA_PUT_U32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED);
@@ -3954,6 +5828,9 @@ static int wpa_driver_nl80211_associate(
                        params->prev_bssid);
        }
 
+       if (params->p2p)
+               wpa_printf(MSG_DEBUG, "  * P2P group");
+
        ret = send_and_recv_msgs(drv, msg, NULL, NULL);
        msg = NULL;
        if (ret) {
@@ -3973,50 +5850,41 @@ nla_put_failure:
 
 
 static int nl80211_set_mode(struct wpa_driver_nl80211_data *drv,
-                           int ifindex, int mode)
+                           int ifindex, enum nl80211_iftype mode)
 {
        struct nl_msg *msg;
        int ret = -ENOBUFS;
 
+       wpa_printf(MSG_DEBUG, "nl80211: Set mode ifindex %d iftype %d (%s)",
+                  ifindex, mode, nl80211_iftype_str(mode));
+
        msg = nlmsg_alloc();
        if (!msg)
                return -ENOMEM;
 
-       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
-                   0, NL80211_CMD_SET_INTERFACE, 0);
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_INTERFACE);
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
        NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, mode);
 
        ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       msg = NULL;
        if (!ret)
                return 0;
 nla_put_failure:
+       nlmsg_free(msg);
        wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface %d to mode %d:"
                   " %d (%s)", ifindex, mode, ret, strerror(-ret));
        return ret;
 }
 
 
-static int wpa_driver_nl80211_set_mode(void *priv, int mode)
+static int wpa_driver_nl80211_set_mode(struct i802_bss *bss,
+                                      enum nl80211_iftype nlmode)
 {
-       struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
        int ret = -1;
-       int nlmode;
-
-       switch (mode) {
-       case 0:
-               nlmode = NL80211_IFTYPE_STATION;
-               break;
-       case 1:
-               nlmode = NL80211_IFTYPE_ADHOC;
-               break;
-       case 2:
-               nlmode = NL80211_IFTYPE_AP;
-               break;
-       default:
-               return -1;
-       }
+       int i;
+       int was_ap = is_ap_interface(drv->nlmode);
 
        if (nl80211_set_mode(drv, drv->ifindex, nlmode) == 0) {
                drv->nlmode = nlmode;
@@ -4035,31 +5903,62 @@ static int wpa_driver_nl80211_set_mode(void *priv, int mode)
         * take the device down, try to set the mode again, and bring the
         * device back up.
         */
-       if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 0) == 0) {
-               /* Try to set the mode again while the interface is down */
-               ret = nl80211_set_mode(drv, drv->ifindex, nlmode);
-               if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1))
-                       ret = -1;
+       wpa_printf(MSG_DEBUG, "nl80211: Try mode change after setting "
+                  "interface down");
+       for (i = 0; i < 10; i++) {
+               int res;
+               res = linux_set_iface_flags(drv->global->ioctl_sock,
+                                           bss->ifname, 0);
+               if (res == -EACCES || res == -ENODEV)
+                       break;
+               if (res == 0) {
+                       /* Try to set the mode again while the interface is
+                        * down */
+                       ret = nl80211_set_mode(drv, drv->ifindex, nlmode);
+                       if (ret == -EACCES)
+                               break;
+                       res = linux_set_iface_flags(drv->global->ioctl_sock,
+                                                   bss->ifname, 1);
+                       if (res && !ret)
+                               ret = -1;
+                       else if (ret != -EBUSY)
+                               break;
+               } else
+                       wpa_printf(MSG_DEBUG, "nl80211: Failed to set "
+                                  "interface down");
+               os_sleep(0, 100000);
        }
 
        if (!ret) {
                wpa_printf(MSG_DEBUG, "nl80211: Mode change succeeded while "
                           "interface is down");
                drv->nlmode = nlmode;
+               drv->ignore_if_down_event = 1;
        }
 
 done:
-       if (!ret && nlmode == NL80211_IFTYPE_AP) {
+       if (!ret && is_ap_interface(nlmode)) {
                /* Setup additional AP mode functionality if needed */
-               if (drv->monitor_ifidx < 0 &&
-                   nl80211_create_monitor_interface(drv))
+               if (!drv->no_monitor_iface_capab && drv->monitor_ifidx < 0 &&
+                   nl80211_create_monitor_interface(drv) &&
+                   !drv->no_monitor_iface_capab)
                        return -1;
-       } else if (!ret && nlmode != NL80211_IFTYPE_AP) {
+       } else if (!ret && !is_ap_interface(nlmode)) {
                /* Remove additional AP mode functionality */
+               if (was_ap && drv->no_monitor_iface_capab)
+                       wpa_driver_nl80211_probe_req_report(bss, 0);
                nl80211_remove_monitor_interface(drv);
                bss->beacon_set = 0;
        }
 
+       if (!ret && is_p2p_interface(drv->nlmode)) {
+               nl80211_disable_11b_rates(drv, drv->ifindex, 1);
+               drv->disabled_11b_rates = 1;
+       } else if (!ret && drv->disabled_11b_rates) {
+               nl80211_disable_11b_rates(drv, drv->ifindex, 0);
+               drv->disabled_11b_rates = 0;
+       }
+
        if (ret)
                wpa_printf(MSG_DEBUG, "nl80211: Interface mode change to %d "
                           "from %d failed", nlmode, drv->nlmode);
@@ -4088,7 +5987,7 @@ static int wpa_driver_nl80211_set_operstate(void *priv, int state)
        wpa_printf(MSG_DEBUG, "%s: operstate %d->%d (%s)",
                   __func__, drv->operstate, state, state ? "UP" : "DORMANT");
        drv->operstate = state;
-       return netlink_send_oper_ifla(drv->netlink, drv->ifindex, -1,
+       return netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, -1,
                                      state ? IF_OPER_UP : IF_OPER_DORMANT);
 }
 
@@ -4104,8 +6003,7 @@ static int wpa_driver_nl80211_set_supp_port(void *priv, int authorized)
        if (!msg)
                return -ENOMEM;
 
-       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
-                   0, NL80211_CMD_SET_STATION, 0);
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_STATION);
 
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
                    if_nametoindex(bss->ifname));
@@ -4119,74 +6017,22 @@ static int wpa_driver_nl80211_set_supp_port(void *priv, int authorized)
 
        return send_and_recv_msgs(drv, msg, NULL, NULL);
  nla_put_failure:
+       nlmsg_free(msg);
        return -ENOBUFS;
 }
 
 
-#ifdef HOSTAPD
-
-static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
-{
-       int i;
-       int *old;
-
-       wpa_printf(MSG_DEBUG, "nl80211: Add own interface ifindex %d",
-                  ifidx);
-       for (i = 0; i < drv->num_if_indices; i++) {
-               if (drv->if_indices[i] == 0) {
-                       drv->if_indices[i] = ifidx;
-                       return;
-               }
-       }
-
-       if (drv->if_indices != drv->default_if_indices)
-               old = drv->if_indices;
-       else
-               old = NULL;
-
-       drv->if_indices = os_realloc(old,
-                                    sizeof(int) * (drv->num_if_indices + 1));
-       if (!drv->if_indices) {
-               if (!old)
-                       drv->if_indices = drv->default_if_indices;
-               else
-                       drv->if_indices = old;
-               wpa_printf(MSG_ERROR, "Failed to reallocate memory for "
-                          "interfaces");
-               wpa_printf(MSG_ERROR, "Ignoring EAPOL on interface %d", ifidx);
-               return;
-       } else if (!old)
-               os_memcpy(drv->if_indices, drv->default_if_indices,
-                         sizeof(drv->default_if_indices));
-       drv->if_indices[drv->num_if_indices] = ifidx;
-       drv->num_if_indices++;
-}
-
-
-static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+/* Set kernel driver on given frequency (MHz) */
+static int i802_set_freq(void *priv, struct hostapd_freq_params *freq)
 {
-       int i;
-
-       for (i = 0; i < drv->num_if_indices; i++) {
-               if (drv->if_indices[i] == ifidx) {
-                       drv->if_indices[i] = 0;
-                       break;
-               }
-       }
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       return wpa_driver_nl80211_set_freq(drv, freq->freq, freq->ht_enabled,
+                                          freq->sec_channel_offset);
 }
 
 
-static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
-{
-       int i;
-
-       for (i = 0; i < drv->num_if_indices; i++)
-               if (drv->if_indices[i] == ifidx)
-                       return 1;
-
-       return 0;
-}
-
+#if defined(HOSTAPD) || defined(CONFIG_AP)
 
 static inline int min_int(int a, int b)
 {
@@ -4228,8 +6074,7 @@ static int i802_get_seqnum(const char *iface, void *priv, const u8 *addr,
        if (!msg)
                return -ENOMEM;
 
-       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
-                   0, NL80211_CMD_GET_KEY, 0);
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_KEY);
 
        if (addr)
                NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
@@ -4240,6 +6085,7 @@ static int i802_get_seqnum(const char *iface, void *priv, const u8 *addr,
 
        return send_and_recv_msgs(drv, msg, get_key_handler, seq);
  nla_put_failure:
+       nlmsg_free(msg);
        return -ENOBUFS;
 }
 
@@ -4258,8 +6104,7 @@ static int i802_set_rate_sets(void *priv, int *supp_rates, int *basic_rates,
        if (!msg)
                return -ENOMEM;
 
-       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
-                   NL80211_CMD_SET_BSS, 0);
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_BSS);
 
        for (i = 0; i < NL80211_MAX_SUPP_RATES && basic_rates[i] >= 0; i++)
                rates[rates_len++] = basic_rates[i] / 5;
@@ -4273,20 +6118,6 @@ static int i802_set_rate_sets(void *priv, int *supp_rates, int *basic_rates,
        return -ENOBUFS;
 }
 
-#endif /* HOSTAPD */
-
-
-/* Set kernel driver on given frequency (MHz) */
-static int i802_set_freq(void *priv, struct hostapd_freq_params *freq)
-{
-       struct i802_bss *bss = priv;
-       struct wpa_driver_nl80211_data *drv = bss->drv;
-       return wpa_driver_nl80211_set_freq(drv, freq->freq, freq->ht_enabled,
-                                          freq->sec_channel_offset);
-}
-
-
-#ifdef HOSTAPD
 
 static int i802_set_rts(void *priv, int rts)
 {
@@ -4305,15 +6136,16 @@ static int i802_set_rts(void *priv, int rts)
        else
                val = rts;
 
-       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
-                   0, NL80211_CMD_SET_WIPHY, 0);
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY);
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
        NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, val);
 
        ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       msg = NULL;
        if (!ret)
                return 0;
 nla_put_failure:
+       nlmsg_free(msg);
        wpa_printf(MSG_DEBUG, "nl80211: Failed to set RTS threshold %d: "
                   "%d (%s)", rts, ret, strerror(-ret));
        return ret;
@@ -4337,15 +6169,16 @@ static int i802_set_frag(void *priv, int frag)
        else
                val = frag;
 
-       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
-                   0, NL80211_CMD_SET_WIPHY, 0);
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY);
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
        NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD, val);
 
        ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       msg = NULL;
        if (!ret)
                return 0;
 nla_put_failure:
+       nlmsg_free(msg);
        wpa_printf(MSG_DEBUG, "nl80211: Failed to set fragmentation threshold "
                   "%d: %d (%s)", frag, ret, strerror(-ret));
        return ret;
@@ -4362,8 +6195,7 @@ static int i802_flush(void *priv)
        if (!msg)
                return -1;
 
-       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
-                   0, NL80211_CMD_DEL_STATION, 0);
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_STATION);
 
        /*
         * XXX: FIX! this needs to flush all VLANs too
@@ -4373,6 +6205,7 @@ static int i802_flush(void *priv)
 
        return send_and_recv_msgs(drv, msg, NULL, NULL);
  nla_put_failure:
+       nlmsg_free(msg);
        return -ENOBUFS;
 }
 
@@ -4440,14 +6273,14 @@ static int i802_read_sta_data(void *priv, struct hostap_sta_driver_data *data,
        if (!msg)
                return -ENOMEM;
 
-       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
-                   0, NL80211_CMD_GET_STATION, 0);
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_STATION);
 
        NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
 
        return send_and_recv_msgs(drv, msg, get_sta_handler, data);
  nla_put_failure:
+       nlmsg_free(msg);
        return -ENOBUFS;
 }
 
@@ -4464,8 +6297,7 @@ static int i802_set_tx_queue_params(void *priv, int queue, int aifs,
        if (!msg)
                return -1;
 
-       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
-                   0, NL80211_CMD_SET_WIPHY, 0);
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY);
 
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
 
@@ -4478,68 +6310,37 @@ static int i802_set_tx_queue_params(void *priv, int queue, int aifs,
        if (!params)
                goto nla_put_failure;
 
-       NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, queue);
+       switch (queue) {
+       case 0:
+               NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_VO);
+               break;
+       case 1:
+               NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_VI);
+               break;
+       case 2:
+               NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_BE);
+               break;
+       case 3:
+               NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_BK);
+               break;
+       }
        /* Burst time is configured in units of 0.1 msec and TXOP parameter in
         * 32 usec, so need to convert the value here. */
        NLA_PUT_U16(msg, NL80211_TXQ_ATTR_TXOP, (burst_time * 100 + 16) / 32);
        NLA_PUT_U16(msg, NL80211_TXQ_ATTR_CWMIN, cw_min);
-       NLA_PUT_U16(msg, NL80211_TXQ_ATTR_CWMAX, cw_max);
-       NLA_PUT_U8(msg, NL80211_TXQ_ATTR_AIFS, aifs);
-
-       nla_nest_end(msg, params);
-
-       nla_nest_end(msg, txq);
-
-       if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0)
-               return 0;
- nla_put_failure:
-       return -1;
-}
-
-
-static int i802_set_bss(void *priv, int cts, int preamble, int slot)
-{
-       struct i802_bss *bss = priv;
-       struct wpa_driver_nl80211_data *drv = bss->drv;
-       struct nl_msg *msg;
-
-       msg = nlmsg_alloc();
-       if (!msg)
-               return -ENOMEM;
-
-       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
-                   NL80211_CMD_SET_BSS, 0);
-
-       if (cts >= 0)
-               NLA_PUT_U8(msg, NL80211_ATTR_BSS_CTS_PROT, cts);
-       if (preamble >= 0)
-               NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_PREAMBLE, preamble);
-       if (slot >= 0)
-               NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_SLOT_TIME, slot);
-
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
-
-       return send_and_recv_msgs(drv, msg, NULL, NULL);
- nla_put_failure:
-       return -ENOBUFS;
-}
-
-
-static int i802_set_cts_protect(void *priv, int value)
-{
-       return i802_set_bss(priv, value, -1, -1);
-}
-
+       NLA_PUT_U16(msg, NL80211_TXQ_ATTR_CWMAX, cw_max);
+       NLA_PUT_U8(msg, NL80211_TXQ_ATTR_AIFS, aifs);
 
-static int i802_set_preamble(void *priv, int value)
-{
-       return i802_set_bss(priv, -1, value, -1);
-}
+       nla_nest_end(msg, params);
 
+       nla_nest_end(msg, txq);
 
-static int i802_set_short_slot_time(void *priv, int value)
-{
-       return i802_set_bss(priv, -1, -1, value);
+       if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0)
+               return 0;
+       msg = NULL;
+ nla_put_failure:
+       nlmsg_free(msg);
+       return -1;
 }
 
 
@@ -4555,8 +6356,7 @@ static int i802_set_sta_vlan(void *priv, const u8 *addr,
        if (!msg)
                return -ENOMEM;
 
-       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
-                   0, NL80211_CMD_SET_STATION, 0);
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_STATION);
 
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
                    if_nametoindex(bss->ifname));
@@ -4565,6 +6365,7 @@ static int i802_set_sta_vlan(void *priv, const u8 *addr,
                    if_nametoindex(ifname));
 
        ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       msg = NULL;
        if (ret < 0) {
                wpa_printf(MSG_ERROR, "nl80211: NL80211_ATTR_STA_VLAN (addr="
                           MACSTR " ifname=%s vlan_id=%d) failed: %d (%s)",
@@ -4572,53 +6373,11 @@ static int i802_set_sta_vlan(void *priv, const u8 *addr,
                           strerror(-ret));
        }
  nla_put_failure:
+       nlmsg_free(msg);
        return ret;
 }
 
 
-static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val)
-{
-       struct i802_bss *bss = priv;
-       struct wpa_driver_nl80211_data *drv = bss->drv;
-       char name[IFNAMSIZ + 1];
-
-       os_snprintf(name, sizeof(name), "%s.sta%d", bss->ifname, aid);
-       wpa_printf(MSG_DEBUG, "nl80211: Set WDS STA addr=" MACSTR
-                  " aid=%d val=%d name=%s", MAC2STR(addr), aid, val, name);
-       if (val) {
-               if (nl80211_create_iface(drv, name, NL80211_IFTYPE_AP_VLAN,
-                                        NULL, 1) < 0)
-                       return -1;
-               linux_set_iface_flags(drv->ioctl_sock, name, 1);
-               return i802_set_sta_vlan(priv, addr, name, 0);
-       } else {
-               i802_set_sta_vlan(priv, addr, bss->ifname, 0);
-               return wpa_driver_nl80211_if_remove(priv, WPA_IF_AP_VLAN,
-                                                   name);
-       }
-}
-
-
-static void handle_eapol(int sock, void *eloop_ctx, void *sock_ctx)
-{
-       struct wpa_driver_nl80211_data *drv = eloop_ctx;
-       struct sockaddr_ll lladdr;
-       unsigned char buf[3000];
-       int len;
-       socklen_t fromlen = sizeof(lladdr);
-
-       len = recvfrom(sock, buf, sizeof(buf), 0,
-                      (struct sockaddr *)&lladdr, &fromlen);
-       if (len < 0) {
-               perror("recv");
-               return;
-       }
-
-       if (have_ifidx(drv, lladdr.sll_ifindex))
-               drv_event_eapol_rx(drv->ctx, lladdr.sll_addr, buf, len);
-}
-
-
 static int i802_get_inact_sec(void *priv, const u8 *addr)
 {
        struct hostap_sta_driver_data data;
@@ -4678,27 +6437,145 @@ static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
                                            sizeof(mgmt.u.disassoc));
 }
 
+#endif /* HOSTAPD || CONFIG_AP */
+
+#ifdef HOSTAPD
+
+static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+{
+       int i;
+       int *old;
+
+       wpa_printf(MSG_DEBUG, "nl80211: Add own interface ifindex %d",
+                  ifidx);
+       for (i = 0; i < drv->num_if_indices; i++) {
+               if (drv->if_indices[i] == 0) {
+                       drv->if_indices[i] = ifidx;
+                       return;
+               }
+       }
+
+       if (drv->if_indices != drv->default_if_indices)
+               old = drv->if_indices;
+       else
+               old = NULL;
+
+       drv->if_indices = os_realloc(old,
+                                    sizeof(int) * (drv->num_if_indices + 1));
+       if (!drv->if_indices) {
+               if (!old)
+                       drv->if_indices = drv->default_if_indices;
+               else
+                       drv->if_indices = old;
+               wpa_printf(MSG_ERROR, "Failed to reallocate memory for "
+                          "interfaces");
+               wpa_printf(MSG_ERROR, "Ignoring EAPOL on interface %d", ifidx);
+               return;
+       } else if (!old)
+               os_memcpy(drv->if_indices, drv->default_if_indices,
+                         sizeof(drv->default_if_indices));
+       drv->if_indices[drv->num_if_indices] = ifidx;
+       drv->num_if_indices++;
+}
+
+
+static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+{
+       int i;
+
+       for (i = 0; i < drv->num_if_indices; i++) {
+               if (drv->if_indices[i] == ifidx) {
+                       drv->if_indices[i] = 0;
+                       break;
+               }
+       }
+}
+
+
+static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+{
+       int i;
+
+       for (i = 0; i < drv->num_if_indices; i++)
+               if (drv->if_indices[i] == ifidx)
+                       return 1;
+
+       return 0;
+}
+
+
+static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val,
+                            const char *bridge_ifname)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       char name[IFNAMSIZ + 1];
+
+       os_snprintf(name, sizeof(name), "%s.sta%d", bss->ifname, aid);
+       wpa_printf(MSG_DEBUG, "nl80211: Set WDS STA addr=" MACSTR
+                  " aid=%d val=%d name=%s", MAC2STR(addr), aid, val, name);
+       if (val) {
+               if (!if_nametoindex(name)) {
+                       if (nl80211_create_iface(drv, name,
+                                                NL80211_IFTYPE_AP_VLAN,
+                                                NULL, 1) < 0)
+                               return -1;
+                       if (bridge_ifname &&
+                           linux_br_add_if(drv->global->ioctl_sock,
+                                           bridge_ifname, name) < 0)
+                               return -1;
+               }
+               linux_set_iface_flags(drv->global->ioctl_sock, name, 1);
+               return i802_set_sta_vlan(priv, addr, name, 0);
+       } else {
+               i802_set_sta_vlan(priv, addr, bss->ifname, 0);
+               return wpa_driver_nl80211_if_remove(priv, WPA_IF_AP_VLAN,
+                                                   name);
+       }
+}
+
+
+static void handle_eapol(int sock, void *eloop_ctx, void *sock_ctx)
+{
+       struct wpa_driver_nl80211_data *drv = eloop_ctx;
+       struct sockaddr_ll lladdr;
+       unsigned char buf[3000];
+       int len;
+       socklen_t fromlen = sizeof(lladdr);
+
+       len = recvfrom(sock, buf, sizeof(buf), 0,
+                      (struct sockaddr *)&lladdr, &fromlen);
+       if (len < 0) {
+               perror("recv");
+               return;
+       }
+
+       if (have_ifidx(drv, lladdr.sll_ifindex))
+               drv_event_eapol_rx(drv->ctx, lladdr.sll_addr, buf, len);
+}
+
 
 static int i802_check_bridge(struct wpa_driver_nl80211_data *drv,
+                            struct i802_bss *bss,
                             const char *brname, const char *ifname)
 {
        int ifindex;
        char in_br[IFNAMSIZ];
 
-       os_strlcpy(drv->brname, brname, IFNAMSIZ);
+       os_strlcpy(bss->brname, brname, IFNAMSIZ);
        ifindex = if_nametoindex(brname);
        if (ifindex == 0) {
                /*
                 * Bridge was configured, but the bridge device does
                 * not exist. Try to add it now.
                 */
-               if (linux_br_add(drv->ioctl_sock, brname) < 0) {
+               if (linux_br_add(drv->global->ioctl_sock, brname) < 0) {
                        wpa_printf(MSG_ERROR, "nl80211: Failed to add the "
                                   "bridge interface %s: %s",
                                   brname, strerror(errno));
                        return -1;
                }
-               drv->added_bridge = 1;
+               bss->added_bridge = 1;
                add_ifidx(drv, if_nametoindex(brname));
        }
 
@@ -4708,7 +6585,8 @@ static int i802_check_bridge(struct wpa_driver_nl80211_data *drv,
 
                wpa_printf(MSG_DEBUG, "nl80211: Removing interface %s from "
                           "bridge %s", ifname, in_br);
-               if (linux_br_del_if(drv->ioctl_sock, in_br, ifname) < 0) {
+               if (linux_br_del_if(drv->global->ioctl_sock, in_br, ifname) <
+                   0) {
                        wpa_printf(MSG_ERROR, "nl80211: Failed to "
                                   "remove interface %s from bridge "
                                   "%s: %s",
@@ -4719,13 +6597,13 @@ static int i802_check_bridge(struct wpa_driver_nl80211_data *drv,
 
        wpa_printf(MSG_DEBUG, "nl80211: Adding interface %s into bridge %s",
                   ifname, brname);
-       if (linux_br_add_if(drv->ioctl_sock, brname, ifname) < 0) {
+       if (linux_br_add_if(drv->global->ioctl_sock, brname, ifname) < 0) {
                wpa_printf(MSG_ERROR, "nl80211: Failed to add interface %s "
                           "into bridge %s: %s",
                           ifname, brname, strerror(errno));
                return -1;
        }
-       drv->added_if_into_bridge = 1;
+       bss->added_if_into_bridge = 1;
 
        return 0;
 }
@@ -4741,11 +6619,15 @@ static void *i802_init(struct hostapd_data *hapd,
        int ifindex, br_ifindex;
        int br_added = 0;
 
-       bss = wpa_driver_nl80211_init(hapd, params->ifname);
+       bss = wpa_driver_nl80211_init(hapd, params->ifname,
+                                     params->global_priv);
        if (bss == NULL)
                return NULL;
 
        drv = bss->drv;
+       drv->nlmode = NL80211_IFTYPE_AP;
+       drv->eapol_sock = -1;
+
        if (linux_br_get(brname, params->ifname) == 0) {
                wpa_printf(MSG_DEBUG, "nl80211: Interface %s is in bridge %s",
                           params->ifname, brname);
@@ -4773,26 +6655,26 @@ static void *i802_init(struct hostapd_data *hapd,
        /* start listening for EAPOL on the default AP interface */
        add_ifidx(drv, drv->ifindex);
 
-       if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 0))
+       if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0))
                goto failed;
 
        if (params->bssid) {
-               if (linux_set_ifhwaddr(drv->ioctl_sock, bss->ifname,
+               if (linux_set_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
                                       params->bssid))
                        goto failed;
        }
 
-       if (wpa_driver_nl80211_set_mode(bss, IEEE80211_MODE_AP)) {
+       if (wpa_driver_nl80211_set_mode(bss, drv->nlmode)) {
                wpa_printf(MSG_ERROR, "nl80211: Failed to set interface %s "
                           "into AP mode", bss->ifname);
                goto failed;
        }
 
        if (params->num_bridge && params->bridge[0] &&
-           i802_check_bridge(drv, params->bridge[0], params->ifname) < 0)
+           i802_check_bridge(drv, bss, params->bridge[0], params->ifname) < 0)
                goto failed;
 
-       if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1))
+       if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1))
                goto failed;
 
        drv->eapol_sock = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_PAE));
@@ -4807,22 +6689,14 @@ static void *i802_init(struct hostapd_data *hapd,
                goto failed;
        }
 
-       if (linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, params->own_addr))
+       if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
+                              params->own_addr))
                goto failed;
 
        return bss;
 
 failed:
-       nl80211_remove_monitor_interface(drv);
-       if (drv->ioctl_sock >= 0)
-               close(drv->ioctl_sock);
-
-       genl_family_put(drv->nl80211);
-       nl_cache_free(drv->nl_cache);
-       nl_handle_destroy(drv->nl_handle);
-       nl_cb_put(drv->nl_cb);
-
-       os_free(drv);
+       wpa_driver_nl80211_deinit(bss);
        return NULL;
 }
 
@@ -4841,19 +6715,66 @@ static enum nl80211_iftype wpa_driver_nl80211_if_type(
        switch (type) {
        case WPA_IF_STATION:
                return NL80211_IFTYPE_STATION;
+       case WPA_IF_P2P_CLIENT:
+       case WPA_IF_P2P_GROUP:
+               return NL80211_IFTYPE_P2P_CLIENT;
        case WPA_IF_AP_VLAN:
                return NL80211_IFTYPE_AP_VLAN;
        case WPA_IF_AP_BSS:
                return NL80211_IFTYPE_AP;
+       case WPA_IF_P2P_GO:
+               return NL80211_IFTYPE_P2P_GO;
        }
        return -1;
 }
 
 
+#ifdef CONFIG_P2P
+
+static int nl80211_addr_in_use(struct nl80211_global *global, const u8 *addr)
+{
+       struct wpa_driver_nl80211_data *drv;
+       dl_list_for_each(drv, &global->interfaces,
+                        struct wpa_driver_nl80211_data, list) {
+               if (os_memcmp(addr, drv->addr, ETH_ALEN) == 0)
+                       return 1;
+       }
+       return 0;
+}
+
+
+static int nl80211_p2p_interface_addr(struct wpa_driver_nl80211_data *drv,
+                                     u8 *new_addr)
+{
+       unsigned int idx;
+
+       if (!drv->global)
+               return -1;
+
+       os_memcpy(new_addr, drv->addr, ETH_ALEN);
+       for (idx = 0; idx < 64; idx++) {
+               new_addr[0] = drv->addr[0] | 0x02;
+               new_addr[0] ^= idx << 2;
+               if (!nl80211_addr_in_use(drv->global, new_addr))
+                       break;
+       }
+       if (idx == 64)
+               return -1;
+
+       wpa_printf(MSG_DEBUG, "nl80211: Assigned new P2P Interface Address "
+                  MACSTR, MAC2STR(new_addr));
+
+       return 0;
+}
+
+#endif /* CONFIG_P2P */
+
+
 static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type,
                                     const char *ifname, const u8 *addr,
                                     void *bss_ctx, void **drv_priv,
-                                    char *force_ifname, u8 *if_addr)
+                                    char *force_ifname, u8 *if_addr,
+                                    const char *bridge)
 {
        struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
@@ -4881,12 +6802,56 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type,
        }
 
        if (!addr &&
-           linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, if_addr) < 0)
+           linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
+                              if_addr) < 0) {
+               nl80211_remove_iface(drv, ifidx);
                return -1;
+       }
+
+#ifdef CONFIG_P2P
+       if (!addr &&
+           (type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP ||
+            type == WPA_IF_P2P_GO)) {
+               /* Enforce unique P2P Interface Address */
+               u8 new_addr[ETH_ALEN], own_addr[ETH_ALEN];
+
+               if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
+                                      own_addr) < 0 ||
+                   linux_get_ifhwaddr(drv->global->ioctl_sock, ifname,
+                                      new_addr) < 0) {
+                       nl80211_remove_iface(drv, ifidx);
+                       return -1;
+               }
+               if (os_memcmp(own_addr, new_addr, ETH_ALEN) == 0) {
+                       wpa_printf(MSG_DEBUG, "nl80211: Allocate new address "
+                                  "for P2P group interface");
+                       if (nl80211_p2p_interface_addr(drv, new_addr) < 0) {
+                               nl80211_remove_iface(drv, ifidx);
+                               return -1;
+                       }
+                       if (linux_set_ifhwaddr(drv->global->ioctl_sock, ifname,
+                                              new_addr) < 0) {
+                               nl80211_remove_iface(drv, ifidx);
+                               return -1;
+                       }
+               }
+               os_memcpy(if_addr, new_addr, ETH_ALEN);
+       }
+#endif /* CONFIG_P2P */
 
 #ifdef HOSTAPD
+       if (bridge &&
+           i802_check_bridge(drv, new_bss, bridge, ifname) < 0) {
+               wpa_printf(MSG_ERROR, "nl80211: Failed to add the new "
+                          "interface %s to a bridge %s", ifname, bridge);
+               nl80211_remove_iface(drv, ifidx);
+               os_free(new_bss);
+               return -1;
+       }
+
        if (type == WPA_IF_AP_BSS) {
-               if (linux_set_iface_flags(drv->ioctl_sock, ifname, 1)) {
+               if (linux_set_iface_flags(drv->global->ioctl_sock, ifname, 1))
+               {
                        nl80211_remove_iface(drv, ifidx);
                        os_free(new_bss);
                        return -1;
@@ -4901,6 +6866,9 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type,
        }
 #endif /* HOSTAPD */
 
+       if (drv->global)
+               drv->global->if_add_ifindex = ifidx;
+
        return 0;
 }
 
@@ -4917,6 +6885,23 @@ static int wpa_driver_nl80211_if_remove(void *priv,
                   __func__, type, ifname, ifindex);
        if (ifindex <= 0)
                return -1;
+
+#ifdef HOSTAPD
+       if (bss->added_if_into_bridge) {
+               if (linux_br_del_if(drv->global->ioctl_sock, bss->brname,
+                                   bss->ifname) < 0)
+                       wpa_printf(MSG_INFO, "nl80211: Failed to remove "
+                                  "interface %s from bridge %s: %s",
+                                  bss->ifname, bss->brname, strerror(errno));
+       }
+       if (bss->added_bridge) {
+               if (linux_br_del(drv->global->ioctl_sock, bss->brname) < 0)
+                       wpa_printf(MSG_INFO, "nl80211: Failed to remove "
+                                  "bridge %s: %s",
+                                  bss->brname, strerror(errno));
+       }
+#endif /* HOSTAPD */
+
        nl80211_remove_iface(drv, ifindex);
 
 #ifdef HOSTAPD
@@ -4924,16 +6909,19 @@ static int wpa_driver_nl80211_if_remove(void *priv,
                return 0;
 
        if (bss != &drv->first_bss) {
-               struct i802_bss *tbss = &drv->first_bss;
-
-               while (tbss) {
-                       if (tbss->next != bss)
-                               continue;
+               struct i802_bss *tbss;
 
-                       tbss->next = bss->next;
-                       os_free(bss);
-                       break;
+               for (tbss = &drv->first_bss; tbss; tbss = tbss->next) {
+                       if (tbss->next == bss) {
+                               tbss->next = bss->next;
+                               os_free(bss);
+                               bss = NULL;
+                               break;
+                       }
                }
+               if (bss)
+                       wpa_printf(MSG_INFO, "nl80211: %s - could not find "
+                                  "BSS %p in the list", __func__, bss);
        }
 #endif /* HOSTAPD */
 
@@ -4954,21 +6942,67 @@ static int cookie_handler(struct nl_msg *msg, void *arg)
 }
 
 
+static int nl80211_send_frame_cmd(struct wpa_driver_nl80211_data *drv,
+                                 unsigned int freq, unsigned int wait,
+                                 const u8 *buf, size_t buf_len,
+                                 u64 *cookie_out, int no_cck)
+{
+       struct nl_msg *msg;
+       u64 cookie;
+       int ret = -1;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -1;
+
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_FRAME);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
+       if (wait)
+               NLA_PUT_U32(msg, NL80211_ATTR_DURATION, wait);
+       NLA_PUT_FLAG(msg, NL80211_ATTR_OFFCHANNEL_TX_OK);
+       if (no_cck)
+               NLA_PUT_FLAG(msg, NL80211_ATTR_TX_NO_CCK_RATE);
+
+       NLA_PUT(msg, NL80211_ATTR_FRAME, buf_len, buf);
+
+       cookie = 0;
+       ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie);
+       msg = NULL;
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "nl80211: Frame command failed: ret=%d "
+                          "(%s) (freq=%u wait=%u)", ret, strerror(-ret),
+                          freq, wait);
+               goto nla_put_failure;
+       }
+       wpa_printf(MSG_DEBUG, "nl80211: Frame TX command accepted; "
+                  "cookie 0x%llx", (long long unsigned int) cookie);
+
+       if (cookie_out)
+               *cookie_out = cookie;
+
+nla_put_failure:
+       nlmsg_free(msg);
+       return ret;
+}
+
+
 static int wpa_driver_nl80211_send_action(void *priv, unsigned int freq,
+                                         unsigned int wait_time,
                                          const u8 *dst, const u8 *src,
                                          const u8 *bssid,
-                                         const u8 *data, size_t data_len)
+                                         const u8 *data, size_t data_len,
+                                         int no_cck)
 {
        struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
        int ret = -1;
-       struct nl_msg *msg;
        u8 *buf;
        struct ieee80211_hdr *hdr;
-       u64 cookie;
 
-       wpa_printf(MSG_DEBUG, "nl80211: Send Action frame (ifindex=%d)",
-                  drv->ifindex);
+       wpa_printf(MSG_DEBUG, "nl80211: Send Action frame (ifindex=%d, "
+                  "wait=%d ms no_cck=%d)", drv->ifindex, wait_time, no_cck);
 
        buf = os_zalloc(24 + data_len);
        if (buf == NULL)
@@ -4981,44 +7015,43 @@ static int wpa_driver_nl80211_send_action(void *priv, unsigned int freq,
        os_memcpy(hdr->addr2, src, ETH_ALEN);
        os_memcpy(hdr->addr3, bssid, ETH_ALEN);
 
-       if (drv->nlmode == NL80211_IFTYPE_AP) {
-               ret = wpa_driver_nl80211_send_mlme(priv, buf, 24 + data_len);
-               os_free(buf);
-               return ret;
-       }
+       if (is_ap_interface(drv->nlmode))
+               ret = wpa_driver_nl80211_send_mlme(priv, buf, 24 + data_len);
+       else
+               ret = nl80211_send_frame_cmd(drv, freq, wait_time, buf,
+                                            24 + data_len,
+                                            &drv->send_action_cookie,
+                                            no_cck);
+
+       os_free(buf);
+       return ret;
+}
+
+
+static void wpa_driver_nl80211_send_action_cancel_wait(void *priv)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+       int ret;
 
        msg = nlmsg_alloc();
-       if (!msg) {
-               os_free(buf);
-               return -1;
-       }
+       if (!msg)
+               return;
 
-       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
-                   NL80211_CMD_ACTION, 0);
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_FRAME_WAIT_CANCEL);
 
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
-       NLA_PUT(msg, NL80211_ATTR_FRAME, 24 + data_len, buf);
-       os_free(buf);
-       buf = NULL;
+       NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, drv->send_action_cookie);
 
-       cookie = 0;
-       ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie);
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
        msg = NULL;
-       if (ret) {
-               wpa_printf(MSG_DEBUG, "nl80211: Action command failed: ret=%d "
+       if (ret)
+               wpa_printf(MSG_DEBUG, "nl80211: wait cancel failed: ret=%d "
                           "(%s)", ret, strerror(-ret));
-               goto nla_put_failure;
-       }
-       wpa_printf(MSG_DEBUG, "nl80211: Action TX command accepted; "
-                  "cookie 0x%llx", (long long unsigned int) cookie);
-       drv->send_action_cookie = cookie;
-       ret = 0;
 
-nla_put_failure:
-       os_free(buf);
+ nla_put_failure:
        nlmsg_free(msg);
-       return ret;
 }
 
 
@@ -5035,8 +7068,7 @@ static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq,
        if (!msg)
                return -1;
 
-       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
-                   NL80211_CMD_REMAIN_ON_CHANNEL, 0);
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_REMAIN_ON_CHANNEL);
 
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
        NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
@@ -5044,16 +7076,20 @@ static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq,
 
        cookie = 0;
        ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie);
+       msg = NULL;
        if (ret == 0) {
                wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel cookie "
                           "0x%llx for freq=%u MHz duration=%u",
                           (long long unsigned int) cookie, freq, duration);
                drv->remain_on_chan_cookie = cookie;
+               drv->pending_remain_on_chan = 1;
                return 0;
        }
        wpa_printf(MSG_DEBUG, "nl80211: Failed to request remain-on-channel "
-                  "(freq=%d): %d (%s)", freq, ret, strerror(-ret));
+                  "(freq=%d duration=%u): %d (%s)",
+                  freq, duration, ret, strerror(-ret));
 nla_put_failure:
+       nlmsg_free(msg);
        return -1;
 }
 
@@ -5079,76 +7115,72 @@ static int wpa_driver_nl80211_cancel_remain_on_channel(void *priv)
        if (!msg)
                return -1;
 
-       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
-                   NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, 0);
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL);
 
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
        NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, drv->remain_on_chan_cookie);
 
        ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       msg = NULL;
        if (ret == 0)
                return 0;
        wpa_printf(MSG_DEBUG, "nl80211: Failed to cancel remain-on-channel: "
                   "%d (%s)", ret, strerror(-ret));
 nla_put_failure:
+       nlmsg_free(msg);
        return -1;
 }
 
 
-static void wpa_driver_nl80211_probe_req_report_timeout(void *eloop_ctx,
-                                                       void *timeout_ctx)
-{
-       struct wpa_driver_nl80211_data *drv = eloop_ctx;
-       if (drv->monitor_ifidx < 0)
-               return; /* monitor interface already removed */
-
-       if (drv->nlmode != NL80211_IFTYPE_STATION)
-               return; /* not in station mode anymore */
-
-       if (drv->probe_req_report)
-               return; /* reporting enabled */
-
-       wpa_printf(MSG_DEBUG, "nl80211: Remove monitor interface due to no "
-                  "Probe Request reporting needed anymore");
-       nl80211_remove_monitor_interface(drv);
-}
-
-
 static int wpa_driver_nl80211_probe_req_report(void *priv, int report)
 {
        struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
 
-       if (drv->nlmode != NL80211_IFTYPE_STATION) {
-               wpa_printf(MSG_DEBUG, "nl80211: probe_req_report control only "
-                          "allowed in station mode (iftype=%d)",
-                          drv->nlmode);
-               return -1;
+       if (!report) {
+               if (drv->nl_preq.handle && drv->no_monitor_iface_capab &&
+                   is_ap_interface(drv->nlmode)) {
+                       /*
+                        * Do not disable Probe Request reporting that was
+                        * enabled in nl80211_setup_ap().
+                        */
+                       wpa_printf(MSG_DEBUG, "nl80211: Skip disabling of "
+                                  "Probe Request reporting nl_preq=%p while "
+                                  "in AP mode", drv->nl_preq.handle);
+               } else if (drv->nl_preq.handle) {
+                       wpa_printf(MSG_DEBUG, "nl80211: Disable Probe Request "
+                                  "reporting nl_preq=%p", drv->nl_preq.handle);
+                       eloop_unregister_read_sock(
+                               nl_socket_get_fd(drv->nl_preq.handle));
+                       nl_destroy_handles(&drv->nl_preq);
+               }
+               return 0;
        }
-       drv->probe_req_report = report;
 
-       if (report) {
-               eloop_cancel_timeout(
-                       wpa_driver_nl80211_probe_req_report_timeout,
-                       drv, NULL);
-               if (drv->monitor_ifidx < 0 &&
-                   nl80211_create_monitor_interface(drv))
-                       return -1;
-       } else {
-               /*
-                * It takes a while to remove the monitor interface, so try to
-                * avoid doing this if it is needed again shortly. Instead,
-                * schedule the interface to be removed later if no need for it
-                * is seen.
-                */
-               wpa_printf(MSG_DEBUG, "nl80211: Scheduling monitor interface "
-                          "to be removed after 10 seconds of no use");
-               eloop_register_timeout(
-                       10, 0, wpa_driver_nl80211_probe_req_report_timeout,
-                       drv, NULL);
+       if (drv->nl_preq.handle) {
+               wpa_printf(MSG_DEBUG, "nl80211: Probe Request reporting "
+                          "already on!");
+               return 0;
        }
 
+       if (nl_create_handles(&drv->nl_preq, drv->global->nl_cb, "preq"))
+               return -1;
+
+       if (nl80211_register_frame(drv, drv->nl_preq.handle,
+                                  (WLAN_FC_TYPE_MGMT << 2) |
+                                  (WLAN_FC_STYPE_PROBE_REQ << 4),
+                                  NULL, 0) < 0)
+               goto out_err;
+
+       eloop_register_read_sock(nl_socket_get_fd(drv->nl_preq.handle),
+                                wpa_driver_nl80211_event_receive, drv,
+                                drv->nl_preq.handle);
+
        return 0;
+
+ out_err:
+       nl_destroy_handles(&drv->nl_preq);
+       return -1;
 }
 
 
@@ -5163,8 +7195,7 @@ static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv,
        if (!msg)
                return -1;
 
-       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
-                   NL80211_CMD_SET_TX_BITRATE_MASK, 0);
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_TX_BITRATE_MASK);
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
 
        bands = nla_nest_start(msg, NL80211_ATTR_TX_RATES);
@@ -5179,8 +7210,10 @@ static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv,
        band = nla_nest_start(msg, NL80211_BAND_2GHZ);
        if (!band)
                goto nla_put_failure;
-       NLA_PUT(msg, NL80211_TXRATE_LEGACY, 8,
-               "\x0c\x12\x18\x24\x30\x48\x60\x6c");
+       if (disabled) {
+               NLA_PUT(msg, NL80211_TXRATE_LEGACY, 8,
+                       "\x0c\x12\x18\x24\x30\x48\x60\x6c");
+       }
        nla_nest_end(msg, band);
 
        nla_nest_end(msg, bands);
@@ -5200,23 +7233,24 @@ nla_put_failure:
 }
 
 
-static int wpa_driver_nl80211_disable_11b_rates(void *priv, int disabled)
+static int wpa_driver_nl80211_deinit_ap(void *priv)
 {
        struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
-       drv->disable_11b_rates = disabled;
-       return nl80211_disable_11b_rates(drv, drv->ifindex, disabled);
+       if (!is_ap_interface(drv->nlmode))
+               return -1;
+       wpa_driver_nl80211_del_beacon(drv);
+       return wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_STATION);
 }
 
 
-static int wpa_driver_nl80211_deinit_ap(void *priv)
+static int wpa_driver_nl80211_deinit_p2p_cli(void *priv)
 {
        struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
-       if (drv->nlmode != NL80211_IFTYPE_AP)
+       if (drv->nlmode != NL80211_IFTYPE_P2P_CLIENT)
                return -1;
-       wpa_driver_nl80211_del_beacon(drv);
-       return wpa_driver_nl80211_set_mode(priv, IEEE80211_MODE_INFRA);
+       return wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_STATION);
 }
 
 
@@ -5224,7 +7258,7 @@ static void wpa_driver_nl80211_resume(void *priv)
 {
        struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
-       if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1)) {
+       if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1)) {
                wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface up on "
                           "resume event");
        }
@@ -5241,7 +7275,8 @@ static int nl80211_send_ft_action(void *priv, u8 action, const u8 *target_ap,
        size_t data_len;
        u8 own_addr[ETH_ALEN];
 
-       if (linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, own_addr) < 0)
+       if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
+                              own_addr) < 0)
                return -1;
 
        if (action != 1) {
@@ -5272,9 +7307,9 @@ static int nl80211_send_ft_action(void *priv, u8 action, const u8 *target_ap,
        pos += ETH_ALEN;
        os_memcpy(pos, ies, ies_len);
 
-       ret = wpa_driver_nl80211_send_action(bss, drv->assoc_freq, drv->bssid,
-                                            own_addr, drv->bssid,
-                                            data, data_len);
+       ret = wpa_driver_nl80211_send_action(bss, drv->assoc_freq, 0,
+                                            drv->bssid, own_addr, drv->bssid,
+                                            data, data_len, 0);
        os_free(data);
 
        return ret;
@@ -5294,8 +7329,7 @@ static int nl80211_signal_monitor(void *priv, int threshold, int hysteresis)
        if (!msg)
                return -1;
 
-       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
-                   0, NL80211_CMD_SET_CQM, 0);
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_CQM);
 
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
 
@@ -5312,13 +7346,27 @@ static int nl80211_signal_monitor(void *priv, int threshold, int hysteresis)
        msg = NULL;
 
 nla_put_failure:
-       if (cqm)
-               nlmsg_free(cqm);
+       nlmsg_free(cqm);
        nlmsg_free(msg);
        return -1;
 }
 
 
+static int nl80211_signal_poll(void *priv, struct wpa_signal_info *si)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       int res;
+
+       os_memset(si, 0, sizeof(*si));
+       res = nl80211_get_link_signal(drv, si);
+       if (res != 0)
+               return res;
+
+       return nl80211_get_link_noise(drv, si);
+}
+
+
 static int nl80211_send_frame(void *priv, const u8 *data, size_t data_len,
                              int encrypt)
 {
@@ -5328,6 +7376,315 @@ static int nl80211_send_frame(void *priv, const u8 *data, size_t data_len,
 }
 
 
+static int nl80211_set_param(void *priv, const char *param)
+{
+       wpa_printf(MSG_DEBUG, "nl80211: driver param='%s'", param);
+       if (param == NULL)
+               return 0;
+
+#ifdef CONFIG_P2P
+       if (os_strstr(param, "use_p2p_group_interface=1")) {
+               struct i802_bss *bss = priv;
+               struct wpa_driver_nl80211_data *drv = bss->drv;
+
+               wpa_printf(MSG_DEBUG, "nl80211: Use separate P2P group "
+                          "interface");
+               drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT;
+               drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P;
+       }
+#endif /* CONFIG_P2P */
+
+       return 0;
+}
+
+
+static void * nl80211_global_init(void)
+{
+       struct nl80211_global *global;
+       struct netlink_config *cfg;
+
+       global = os_zalloc(sizeof(*global));
+       if (global == NULL)
+               return NULL;
+       global->ioctl_sock = -1;
+       dl_list_init(&global->interfaces);
+       global->if_add_ifindex = -1;
+
+       cfg = os_zalloc(sizeof(*cfg));
+       if (cfg == NULL)
+               goto err;
+
+       cfg->ctx = global;
+       cfg->newlink_cb = wpa_driver_nl80211_event_rtm_newlink;
+       cfg->dellink_cb = wpa_driver_nl80211_event_rtm_dellink;
+       global->netlink = netlink_init(cfg);
+       if (global->netlink == NULL) {
+               os_free(cfg);
+               goto err;
+       }
+
+       if (wpa_driver_nl80211_init_nl_global(global) < 0)
+               goto err;
+
+       global->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
+       if (global->ioctl_sock < 0) {
+               perror("socket(PF_INET,SOCK_DGRAM)");
+               goto err;
+       }
+
+       return global;
+
+err:
+       nl80211_global_deinit(global);
+       return NULL;
+}
+
+
+static void nl80211_global_deinit(void *priv)
+{
+       struct nl80211_global *global = priv;
+       if (global == NULL)
+               return;
+       if (!dl_list_empty(&global->interfaces)) {
+               wpa_printf(MSG_ERROR, "nl80211: %u interface(s) remain at "
+                          "nl80211_global_deinit",
+                          dl_list_len(&global->interfaces));
+       }
+
+       if (global->netlink)
+               netlink_deinit(global->netlink);
+
+       if (global->nl80211)
+               genl_family_put(global->nl80211);
+       nl_destroy_handles(&global->nl);
+
+       if (global->nl_cb)
+               nl_cb_put(global->nl_cb);
+
+       if (global->ioctl_sock >= 0)
+               close(global->ioctl_sock);
+
+       os_free(global);
+}
+
+
+static const char * nl80211_get_radio_name(void *priv)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       return drv->phyname;
+}
+
+
+static int nl80211_pmkid(struct i802_bss *bss, int cmd, const u8 *bssid,
+                        const u8 *pmkid)
+{
+       struct nl_msg *msg;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -ENOMEM;
+
+       nl80211_cmd(bss->drv, msg, 0, cmd);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
+       if (pmkid)
+               NLA_PUT(msg, NL80211_ATTR_PMKID, 16, pmkid);
+       if (bssid)
+               NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
+
+       return send_and_recv_msgs(bss->drv, msg, NULL, NULL);
+ nla_put_failure:
+       nlmsg_free(msg);
+       return -ENOBUFS;
+}
+
+
+static int nl80211_add_pmkid(void *priv, const u8 *bssid, const u8 *pmkid)
+{
+       struct i802_bss *bss = priv;
+       wpa_printf(MSG_DEBUG, "nl80211: Add PMKID for " MACSTR, MAC2STR(bssid));
+       return nl80211_pmkid(bss, NL80211_CMD_SET_PMKSA, bssid, pmkid);
+}
+
+
+static int nl80211_remove_pmkid(void *priv, const u8 *bssid, const u8 *pmkid)
+{
+       struct i802_bss *bss = priv;
+       wpa_printf(MSG_DEBUG, "nl80211: Delete PMKID for " MACSTR,
+                  MAC2STR(bssid));
+       return nl80211_pmkid(bss, NL80211_CMD_DEL_PMKSA, bssid, pmkid);
+}
+
+
+static int nl80211_flush_pmkid(void *priv)
+{
+       struct i802_bss *bss = priv;
+       wpa_printf(MSG_DEBUG, "nl80211: Flush PMKIDs");
+       return nl80211_pmkid(bss, NL80211_CMD_FLUSH_PMKSA, NULL, NULL);
+}
+
+
+static void nl80211_set_rekey_info(void *priv, const u8 *kek, const u8 *kck,
+                                  const u8 *replay_ctr)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nlattr *replay_nested;
+       struct nl_msg *msg;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return;
+
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_REKEY_OFFLOAD);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
+
+       replay_nested = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA);
+       if (!replay_nested)
+               goto nla_put_failure;
+
+       NLA_PUT(msg, NL80211_REKEY_DATA_KEK, NL80211_KEK_LEN, kek);
+       NLA_PUT(msg, NL80211_REKEY_DATA_KCK, NL80211_KCK_LEN, kck);
+       NLA_PUT(msg, NL80211_REKEY_DATA_REPLAY_CTR, NL80211_REPLAY_CTR_LEN,
+               replay_ctr);
+
+       nla_nest_end(msg, replay_nested);
+
+       send_and_recv_msgs(drv, msg, NULL, NULL);
+       return;
+ nla_put_failure:
+       nlmsg_free(msg);
+}
+
+
+static void nl80211_poll_client(void *priv, const u8 *own_addr, const u8 *addr,
+                               int qos)
+{
+       struct i802_bss *bss = priv;
+       struct {
+               struct ieee80211_hdr hdr;
+               u16 qos_ctl;
+       } STRUCT_PACKED nulldata;
+       size_t size;
+
+       /* Send data frame to poll STA and check whether this frame is ACKed */
+
+       os_memset(&nulldata, 0, sizeof(nulldata));
+
+       if (qos) {
+               nulldata.hdr.frame_control =
+                       IEEE80211_FC(WLAN_FC_TYPE_DATA,
+                                    WLAN_FC_STYPE_QOS_NULL);
+               size = sizeof(nulldata);
+       } else {
+               nulldata.hdr.frame_control =
+                       IEEE80211_FC(WLAN_FC_TYPE_DATA,
+                                    WLAN_FC_STYPE_NULLFUNC);
+               size = sizeof(struct ieee80211_hdr);
+       }
+
+       nulldata.hdr.frame_control |= host_to_le16(WLAN_FC_FROMDS);
+       os_memcpy(nulldata.hdr.IEEE80211_DA_FROMDS, addr, ETH_ALEN);
+       os_memcpy(nulldata.hdr.IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN);
+       os_memcpy(nulldata.hdr.IEEE80211_SA_FROMDS, own_addr, ETH_ALEN);
+
+       if (wpa_driver_nl80211_send_mlme(bss, (u8 *) &nulldata, size) < 0)
+               wpa_printf(MSG_DEBUG, "nl80211_send_null_frame: Failed to "
+                          "send poll frame");
+}
+
+
+#ifdef CONFIG_TDLS
+
+static int nl80211_send_tdls_mgmt(void *priv, const u8 *dst, u8 action_code,
+                                 u8 dialog_token, u16 status_code,
+                                 const u8 *buf, size_t len)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+
+       if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT))
+               return -EOPNOTSUPP;
+
+       if (!dst)
+               return -EINVAL;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -ENOMEM;
+
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_TDLS_MGMT);
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+       NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst);
+       NLA_PUT_U8(msg, NL80211_ATTR_TDLS_ACTION, action_code);
+       NLA_PUT_U8(msg, NL80211_ATTR_TDLS_DIALOG_TOKEN, dialog_token);
+       NLA_PUT_U16(msg, NL80211_ATTR_STATUS_CODE, status_code);
+       NLA_PUT(msg, NL80211_ATTR_IE, len, buf);
+
+       return send_and_recv_msgs(drv, msg, NULL, NULL);
+
+nla_put_failure:
+       nlmsg_free(msg);
+       return -ENOBUFS;
+}
+
+
+static int nl80211_tdls_oper(void *priv, enum tdls_oper oper, const u8 *peer)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+       enum nl80211_tdls_operation nl80211_oper;
+
+       if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT))
+               return -EOPNOTSUPP;
+
+       switch (oper) {
+       case TDLS_DISCOVERY_REQ:
+               nl80211_oper = NL80211_TDLS_DISCOVERY_REQ;
+               break;
+       case TDLS_SETUP:
+               nl80211_oper = NL80211_TDLS_SETUP;
+               break;
+       case TDLS_TEARDOWN:
+               nl80211_oper = NL80211_TDLS_TEARDOWN;
+               break;
+       case TDLS_ENABLE_LINK:
+               nl80211_oper = NL80211_TDLS_ENABLE_LINK;
+               break;
+       case TDLS_DISABLE_LINK:
+               nl80211_oper = NL80211_TDLS_DISABLE_LINK;
+               break;
+       case TDLS_ENABLE:
+               return 0;
+       case TDLS_DISABLE:
+               return 0;
+       default:
+               return -EINVAL;
+       }
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -ENOMEM;
+
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_TDLS_OPER);
+       NLA_PUT_U8(msg, NL80211_ATTR_TDLS_OPERATION, nl80211_oper);
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+       NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, peer);
+
+       return send_and_recv_msgs(drv, msg, NULL, NULL);
+
+nla_put_failure:
+       nlmsg_free(msg);
+       return -ENOBUFS;
+}
+
+#endif /* CONFIG TDLS */
+
+
 const struct wpa_driver_ops wpa_driver_nl80211_ops = {
        .name = "nl80211",
        .desc = "Linux nl80211/cfg80211",
@@ -5335,18 +7692,22 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
        .get_ssid = wpa_driver_nl80211_get_ssid,
        .set_key = wpa_driver_nl80211_set_key,
        .scan2 = wpa_driver_nl80211_scan,
+       .sched_scan = wpa_driver_nl80211_sched_scan,
+       .stop_sched_scan = wpa_driver_nl80211_stop_sched_scan,
        .get_scan_results2 = wpa_driver_nl80211_get_scan_results,
        .deauthenticate = wpa_driver_nl80211_deauthenticate,
        .disassociate = wpa_driver_nl80211_disassociate,
        .authenticate = wpa_driver_nl80211_authenticate,
        .associate = wpa_driver_nl80211_associate,
-       .init = wpa_driver_nl80211_init,
+       .global_init = nl80211_global_init,
+       .global_deinit = nl80211_global_deinit,
+       .init2 = wpa_driver_nl80211_init,
        .deinit = wpa_driver_nl80211_deinit,
        .get_capa = wpa_driver_nl80211_get_capa,
        .set_operstate = wpa_driver_nl80211_set_operstate,
        .set_supp_port = wpa_driver_nl80211_set_supp_port,
        .set_country = wpa_driver_nl80211_set_country,
-       .set_beacon = wpa_driver_nl80211_set_beacon,
+       .set_ap = wpa_driver_nl80211_set_ap,
        .if_add = wpa_driver_nl80211_if_add,
        .if_remove = wpa_driver_nl80211_if_remove,
        .send_mlme = wpa_driver_nl80211_send_mlme,
@@ -5358,33 +7719,45 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
 #ifdef HOSTAPD
        .hapd_init = i802_init,
        .hapd_deinit = i802_deinit,
+       .set_wds_sta = i802_set_wds_sta,
+#endif /* HOSTAPD */
+#if defined(HOSTAPD) || defined(CONFIG_AP)
        .get_seqnum = i802_get_seqnum,
        .flush = i802_flush,
        .read_sta_data = i802_read_sta_data,
-       .sta_deauth = i802_sta_deauth,
-       .sta_disassoc = i802_sta_disassoc,
        .get_inact_sec = i802_get_inact_sec,
        .sta_clear_stats = i802_sta_clear_stats,
        .set_rts = i802_set_rts,
        .set_frag = i802_set_frag,
-       .set_rate_sets = i802_set_rate_sets,
-       .set_cts_protect = i802_set_cts_protect,
-       .set_preamble = i802_set_preamble,
-       .set_short_slot_time = i802_set_short_slot_time,
        .set_tx_queue_params = i802_set_tx_queue_params,
        .set_sta_vlan = i802_set_sta_vlan,
-       .set_wds_sta = i802_set_wds_sta,
-#endif /* HOSTAPD */
+       .set_rate_sets = i802_set_rate_sets,
+       .sta_deauth = i802_sta_deauth,
+       .sta_disassoc = i802_sta_disassoc,
+#endif /* HOSTAPD || CONFIG_AP */
        .set_freq = i802_set_freq,
        .send_action = wpa_driver_nl80211_send_action,
+       .send_action_cancel_wait = wpa_driver_nl80211_send_action_cancel_wait,
        .remain_on_channel = wpa_driver_nl80211_remain_on_channel,
        .cancel_remain_on_channel =
        wpa_driver_nl80211_cancel_remain_on_channel,
        .probe_req_report = wpa_driver_nl80211_probe_req_report,
-       .disable_11b_rates = wpa_driver_nl80211_disable_11b_rates,
        .deinit_ap = wpa_driver_nl80211_deinit_ap,
+       .deinit_p2p_cli = wpa_driver_nl80211_deinit_p2p_cli,
        .resume = wpa_driver_nl80211_resume,
        .send_ft_action = nl80211_send_ft_action,
        .signal_monitor = nl80211_signal_monitor,
+       .signal_poll = nl80211_signal_poll,
        .send_frame = nl80211_send_frame,
+       .set_param = nl80211_set_param,
+       .get_radio_name = nl80211_get_radio_name,
+       .add_pmkid = nl80211_add_pmkid,
+       .remove_pmkid = nl80211_remove_pmkid,
+       .flush_pmkid = nl80211_flush_pmkid,
+       .set_rekey_info = nl80211_set_rekey_info,
+       .poll_client = nl80211_poll_client,
+#ifdef CONFIG_TDLS
+       .send_tdls_mgmt = nl80211_send_tdls_mgmt,
+       .tdls_oper = nl80211_tdls_oper,
+#endif /* CONFIG_TDLS */
 };
index 09d1ef5..a1e27be 100644 (file)
@@ -1178,8 +1178,7 @@ static int wpa_driver_ralink_set_key(const char *ifname, void *priv,
 
        drv->bAddWepKey = FALSE;
 
-       if (addr == NULL || os_memcmp(addr, "\xff\xff\xff\xff\xff\xff",
-                                     ETH_ALEN) == 0) {
+       if (addr == NULL || is_broadcast_ether_addr(addr)) {
                /* Group Key */
                pairwise = 0;
                wpa_driver_ralink_get_bssid(drv, bssid);
index 6877eda..61b75b1 100644 (file)
 
 #include "includes.h"
 #include <sys/ioctl.h>
-#include <linux/if.h>
 #include <linux/sockios.h>
 #include <linux/if_ether.h>
 #include <linux/mii.h>
+#include <net/if.h>
 
 #include "common.h"
 #include "driver.h"
@@ -364,7 +364,7 @@ static void * wpa_driver_roboswitch_init(void *ctx, const char *ifname)
        /* copy ifname and take a pointer to the second to last character */
        sep = drv->ifname +
              os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)) - 2;
-       /* find the '.' seperating <interface> and <vlan> */
+       /* find the '.' separating <interface> and <vlan> */
        while (sep > drv->ifname && *sep != '.') sep--;
        if (sep <= drv->ifname) {
                wpa_printf(MSG_INFO, "%s: No <interface>.<vlan> pair in "
index fb24673..74dedb2 100644 (file)
@@ -34,6 +34,8 @@
 #include "common/ieee802_11_defs.h"
 #include "crypto/sha1.h"
 #include "l2_packet/l2_packet.h"
+#include "p2p/p2p.h"
+#include "wps/wps.h"
 #include "driver.h"
 
 
@@ -87,7 +89,6 @@ struct wpa_driver_test_data {
        int use_associnfo;
        u8 assoc_wpa_ie[80];
        size_t assoc_wpa_ie_len;
-       int use_mlme;
        int associated;
        u8 *probe_req_ie;
        size_t probe_req_ie_len;
@@ -107,6 +108,20 @@ struct wpa_driver_test_data {
        unsigned int remain_on_channel_duration;
 
        int current_freq;
+
+       struct p2p_data *p2p;
+       unsigned int off_channel_freq;
+       struct wpabuf *pending_action_tx;
+       u8 pending_action_src[ETH_ALEN];
+       u8 pending_action_dst[ETH_ALEN];
+       u8 pending_action_bssid[ETH_ALEN];
+       unsigned int pending_action_freq;
+       unsigned int pending_action_no_cck;
+       unsigned int pending_listen_freq;
+       unsigned int pending_listen_duration;
+       int pending_p2p_scan;
+       struct sockaddr *probe_from;
+       socklen_t probe_from_len;
 };
 
 
@@ -116,6 +131,7 @@ static int wpa_driver_test_attach(struct wpa_driver_test_data *drv,
 static void wpa_driver_test_close_test_socket(
        struct wpa_driver_test_data *drv);
 static void test_remain_on_channel_timeout(void *eloop_ctx, void *timeout_ctx);
+static int wpa_driver_test_init_p2p(struct wpa_driver_test_data *drv);
 
 
 static void test_driver_free_bss(struct test_driver_bss *bss)
@@ -159,7 +175,7 @@ test_driver_get_cli(struct wpa_driver_test_data *drv, struct sockaddr_un *from,
 
 static int test_driver_send_eapol(void *priv, const u8 *addr, const u8 *data,
                                  size_t data_len, int encrypt,
-                                 const u8 *own_addr)
+                                 const u8 *own_addr, u32 flags)
 {
        struct test_driver_bss *dbss = priv;
        struct wpa_driver_test_data *drv = dbss->drv;
@@ -469,6 +485,34 @@ static int wpa_driver_test_send_mlme(void *priv, const u8 *data,
        event.tx_status.ack = ret >= 0;
        wpa_supplicant_event(drv->ctx, EVENT_TX_STATUS, &event);
 
+#ifdef CONFIG_P2P
+       if (drv->p2p &&
+           WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+           WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION) {
+               if (drv->pending_action_tx == NULL) {
+                       wpa_printf(MSG_DEBUG, "P2P: Ignore Action TX status - "
+                                  "no pending operation");
+                       return ret;
+               }
+
+               if (os_memcmp(hdr->addr1, drv->pending_action_dst, ETH_ALEN) !=
+                   0) {
+                       wpa_printf(MSG_DEBUG, "P2P: Ignore Action TX status - "
+                                  "unknown destination address");
+                       return ret;
+               }
+
+               wpabuf_free(drv->pending_action_tx);
+               drv->pending_action_tx = NULL;
+
+               p2p_send_action_cb(drv->p2p, drv->pending_action_freq,
+                                  drv->pending_action_dst,
+                                  drv->pending_action_src,
+                                  drv->pending_action_bssid,
+                                  ret >= 0);
+       }
+#endif /* CONFIG_P2P */
+
        return ret;
 }
 
@@ -515,6 +559,10 @@ static void test_driver_scan(struct wpa_driver_test_data *drv,
                event.rx_probe_req.ie = ie;
                event.rx_probe_req.ie_len = ielen;
                wpa_supplicant_event(drv->ctx, EVENT_RX_PROBE_REQ, &event);
+#ifdef CONFIG_P2P
+               if (drv->p2p)
+                       p2p_probe_req_rx(drv->p2p, sa, NULL, NULL, ie, ielen);
+#endif /* CONFIG_P2P */
        }
 
        dl_list_for_each(bss, &drv->bss, struct test_driver_bss, list) {
@@ -624,7 +672,7 @@ static void test_driver_assoc(struct wpa_driver_test_data *drv,
        sendto(drv->test_socket, cmd, strlen(cmd), 0,
               (struct sockaddr *) from, fromlen);
 
-       drv_event_assoc(bss->bss_ctx, cli->addr, ie, ielen);
+       drv_event_assoc(bss->bss_ctx, cli->addr, ie, ielen, 0);
 }
 
 
@@ -841,7 +889,8 @@ static int test_driver_set_generic_elem(void *priv,
 
 
 static int test_driver_set_ap_wps_ie(void *priv, const struct wpabuf *beacon,
-                                    const struct wpabuf *proberesp)
+                                    const struct wpabuf *proberesp,
+                                    const struct wpabuf *assocresp)
 {
        struct test_driver_bss *bss = priv;
 
@@ -1015,7 +1064,8 @@ static int test_driver_bss_remove(void *priv, const char *ifname)
 static int test_driver_if_add(void *priv, enum wpa_driver_if_type type,
                              const char *ifname, const u8 *addr,
                              void *bss_ctx, void **drv_priv,
-                             char *force_ifname, u8 *if_addr)
+                             char *force_ifname, u8 *if_addr,
+                             const char *bridge)
 {
        struct test_driver_bss *dbss = priv;
        struct wpa_driver_test_data *drv = dbss->drv;
@@ -1033,7 +1083,8 @@ static int test_driver_if_add(void *priv, enum wpa_driver_if_type type,
                         sizeof(drv->alloc_iface_idx),
                         if_addr + 1, ETH_ALEN - 1);
        }
-       if (type == WPA_IF_AP_BSS)
+       if (type == WPA_IF_AP_BSS || type == WPA_IF_P2P_GO ||
+           type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP)
                return test_driver_bss_add(priv, ifname, if_addr, bss_ctx,
                                           drv_priv);
        return 0;
@@ -1044,27 +1095,23 @@ static int test_driver_if_remove(void *priv, enum wpa_driver_if_type type,
                                 const char *ifname)
 {
        wpa_printf(MSG_DEBUG, "%s(type=%d ifname=%s)", __func__, type, ifname);
-       if (type == WPA_IF_AP_BSS)
+       if (type == WPA_IF_AP_BSS || type == WPA_IF_P2P_GO ||
+           type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP)
                return test_driver_bss_remove(priv, ifname);
        return 0;
 }
 
 
-static int test_driver_valid_bss_mask(void *priv, const u8 *addr,
-                                     const u8 *mask)
-{
-       return 0;
-}
-
-
 static int test_driver_set_ssid(void *priv, const u8 *buf, int len)
 {
        struct test_driver_bss *bss = priv;
 
        wpa_printf(MSG_DEBUG, "%s(ifname=%s)", __func__, bss->ifname);
+       if (len < 0)
+               return -1;
        wpa_hexdump_ascii(MSG_DEBUG, "test_driver_set_ssid: SSID", buf, len);
 
-       if (len < 0 || (size_t) len > sizeof(bss->ssid))
+       if ((size_t) len > sizeof(bss->ssid))
                return -1;
 
        os_memcpy(bss->ssid, buf, len);
@@ -1178,6 +1225,7 @@ static void * test_driver_init(struct hostapd_data *hapd,
                return NULL;
        drv->ap = 1;
        bss = dl_list_first(&drv->bss, struct test_driver_bss, list);
+       drv->global = params->global_priv;
 
        bss->bss_ctx = hapd;
        os_memcpy(bss->bssid, drv->own_addr, ETH_ALEN);
@@ -1271,7 +1319,23 @@ static void wpa_driver_test_poll(void *eloop_ctx, void *timeout_ctx)
 
 static void wpa_driver_test_scan_timeout(void *eloop_ctx, void *timeout_ctx)
 {
+       struct wpa_driver_test_data *drv = eloop_ctx;
        wpa_printf(MSG_DEBUG, "Scan timeout - try to get results");
+       if (drv->pending_p2p_scan && drv->p2p) {
+#ifdef CONFIG_P2P
+               size_t i;
+               for (i = 0; i < drv->num_scanres; i++) {
+                       struct wpa_scan_res *bss = drv->scanres[i];
+                       if (p2p_scan_res_handler(drv->p2p, bss->bssid,
+                                                bss->freq, bss->level,
+                                                (const u8 *) (bss + 1),
+                                                bss->ie_len) > 0)
+                               return;
+               }
+               p2p_scan_res_handled(drv->p2p);
+#endif /* CONFIG_P2P */
+               return;
+       }
        wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
 }
 
@@ -1487,6 +1551,7 @@ static int wpa_driver_test_associate(
                   __func__, priv, params->freq, params->pairwise_suite,
                   params->group_suite, params->key_mgmt_suite,
                   params->auth_alg, params->mode);
+       wpa_driver_update_mode(drv, params->mode == IEEE80211_MODE_AP);
        if (params->bssid) {
                wpa_printf(MSG_DEBUG, "   bssid=" MACSTR,
                           MAC2STR(params->bssid));
@@ -1848,6 +1913,8 @@ static void wpa_driver_test_mlme(struct wpa_driver_test_data *drv,
 {
        int freq = 0, own_freq;
        union wpa_event_data event;
+       const struct ieee80211_mgmt *mgmt;
+       u16 fc;
        struct test_driver_bss *bss;
 
        bss = dl_list_first(&drv->bss, struct test_driver_bss, list);
@@ -1885,23 +1952,45 @@ static void wpa_driver_test_mlme(struct wpa_driver_test_data *drv,
        event.mlme_rx.freq = freq;
        wpa_supplicant_event(drv->ctx, EVENT_MLME_RX, &event);
 
-       if (drv->probe_req_report && data_len >= 24) {
-               const struct ieee80211_mgmt *mgmt;
-               u16 fc;
+       mgmt = (const struct ieee80211_mgmt *) data;
+       fc = le_to_host16(mgmt->frame_control);
 
-               mgmt = (const struct ieee80211_mgmt *) data;
-               fc = le_to_host16(mgmt->frame_control);
+       if (drv->probe_req_report && data_len >= 24) {
                if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
                    WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_REQ) {
                        os_memset(&event, 0, sizeof(event));
                        event.rx_probe_req.sa = mgmt->sa;
+                       event.rx_probe_req.da = mgmt->da;
+                       event.rx_probe_req.bssid = mgmt->bssid;
                        event.rx_probe_req.ie = mgmt->u.probe_req.variable;
                        event.rx_probe_req.ie_len =
                                data_len - (mgmt->u.probe_req.variable - data);
                        wpa_supplicant_event(drv->ctx, EVENT_RX_PROBE_REQ,
                                             &event);
+#ifdef CONFIG_P2P
+                       if (drv->p2p)
+                               p2p_probe_req_rx(drv->p2p, mgmt->sa,
+                                                mgmt->da, mgmt->bssid,
+                                                event.rx_probe_req.ie,
+                                                event.rx_probe_req.ie_len);
+#endif /* CONFIG_P2P */
                }
        }
+
+#ifdef CONFIG_P2P
+       if (drv->p2p &&
+           WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+           WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION) {
+               size_t hdr_len;
+               hdr_len = (const u8 *)
+                       &mgmt->u.action.u.vs_public_action.action - data;
+               p2p_rx_action(drv->p2p, mgmt->da, mgmt->sa, mgmt->bssid,
+                             mgmt->u.action.category,
+                             &mgmt->u.action.u.vs_public_action.action,
+                             data_len - hdr_len, freq);
+       }
+#endif /* CONFIG_P2P */
+
 }
 
 
@@ -1917,6 +2006,29 @@ static void wpa_driver_test_scan_cmd(struct wpa_driver_test_data *drv,
        bss = dl_list_first(&drv->bss, struct test_driver_bss, list);
 
        /* data: optional [ STA-addr | ' ' | IEs(hex) ] */
+#ifdef CONFIG_P2P
+       if (drv->probe_req_report && drv->p2p && data_len) {
+               const char *d = (const char *) data;
+               u8 sa[ETH_ALEN];
+               u8 ie[512];
+               size_t ielen;
+
+               if (hwaddr_aton(d, sa))
+                       return;
+               d += 18;
+               while (*d == ' ')
+                       d++;
+               ielen = os_strlen(d) / 2;
+               if (ielen > sizeof(ie))
+                       ielen = sizeof(ie);
+               if (hexstr2bin(d, ie, ielen) < 0)
+                       ielen = 0;
+               drv->probe_from = from;
+               drv->probe_from_len = fromlen;
+               p2p_probe_req_rx(drv->p2p, sa, NULL, NULL, ie, ielen);
+               drv->probe_from = NULL;
+       }
+#endif /* CONFIG_P2P */
 
        if (!drv->ibss)
                return;
@@ -2073,6 +2185,12 @@ static void wpa_driver_test_deinit(void *priv)
        struct test_client_socket *cli, *prev;
        int i;
 
+#ifdef CONFIG_P2P
+       if (drv->p2p)
+               p2p_deinit(drv->p2p);
+       wpabuf_free(drv->pending_action_tx);
+#endif /* CONFIG_P2P */
+
        cli = drv->cli;
        while (cli) {
                prev = cli;
@@ -2269,12 +2387,12 @@ static int wpa_driver_test_set_param(void *priv, const char *param)
                drv->use_associnfo = 1;
        }
 
-#ifdef CONFIG_CLIENT_MLME
-       if (os_strstr(param, "use_mlme=1")) {
-               wpa_printf(MSG_DEBUG, "test_driver: Use internal MLME");
-               drv->use_mlme = 1;
+       if (os_strstr(param, "p2p_mgmt=1")) {
+               wpa_printf(MSG_DEBUG, "test_driver: Use internal P2P "
+                          "management");
+               if (wpa_driver_test_init_p2p(drv) < 0)
+                       return -1;
        }
-#endif /* CONFIG_CLIENT_MLME */
 
        return 0;
 }
@@ -2382,9 +2500,12 @@ static int wpa_driver_test_get_capa(void *priv, struct wpa_driver_capa *capa)
        capa->auth = WPA_DRIVER_AUTH_OPEN |
                WPA_DRIVER_AUTH_SHARED |
                WPA_DRIVER_AUTH_LEAP;
-       if (drv->use_mlme)
-               capa->flags |= WPA_DRIVER_FLAGS_USER_SPACE_MLME;
+       if (drv->p2p)
+               capa->flags |= WPA_DRIVER_FLAGS_P2P_MGMT;
        capa->flags |= WPA_DRIVER_FLAGS_AP;
+       capa->flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT;
+       capa->flags |= WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE;
+       capa->flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE;
        capa->max_scan_ssids = 2;
        capa->max_remain_on_chan = 60000;
 
@@ -2408,50 +2529,6 @@ static int wpa_driver_test_mlme_setprotection(void *priv, const u8 *addr,
 }
 
 
-static int wpa_driver_test_set_channel(void *priv,
-                                      enum hostapd_hw_mode phymode,
-                                      int chan, int freq)
-{
-       struct test_driver_bss *dbss = priv;
-       struct wpa_driver_test_data *drv = dbss->drv;
-       wpa_printf(MSG_DEBUG, "%s: phymode=%d chan=%d freq=%d",
-                  __func__, phymode, chan, freq);
-       drv->current_freq = freq;
-       return 0;
-}
-
-
-static int wpa_driver_test_mlme_add_sta(void *priv, const u8 *addr,
-                                       const u8 *supp_rates,
-                                       size_t supp_rates_len)
-{
-       wpa_printf(MSG_DEBUG, "%s: addr=" MACSTR, __func__, MAC2STR(addr));
-       return 0;
-}
-
-
-static int wpa_driver_test_mlme_remove_sta(void *priv, const u8 *addr)
-{
-       wpa_printf(MSG_DEBUG, "%s: addr=" MACSTR, __func__, MAC2STR(addr));
-       return 0;
-}
-
-
-static int wpa_driver_test_set_ssid(void *priv, const u8 *ssid,
-                                   size_t ssid_len)
-{
-       wpa_printf(MSG_DEBUG, "%s", __func__);
-       return 0;
-}
-
-
-static int wpa_driver_test_set_bssid(void *priv, const u8 *bssid)
-{
-       wpa_printf(MSG_DEBUG, "%s: bssid=" MACSTR, __func__, MAC2STR(bssid));
-       return 0;
-}
-
-
 static void * wpa_driver_test_global_init(void)
 {
        struct wpa_driver_test_global *global;
@@ -2591,9 +2668,11 @@ static int wpa_driver_test_set_freq(void *priv,
 
 
 static int wpa_driver_test_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)
+                                      const u8 *data, size_t data_len,
+                                      int no_cck)
 {
        struct test_driver_bss *dbss = priv;
        struct wpa_driver_test_data *drv = dbss->drv;
@@ -2632,6 +2711,33 @@ static int wpa_driver_test_send_action(void *priv, unsigned int freq,
 }
 
 
+#ifdef CONFIG_P2P
+static void test_send_action_cb(void *eloop_ctx, void *timeout_ctx)
+{
+       struct wpa_driver_test_data *drv = eloop_ctx;
+
+       if (drv->pending_action_tx == NULL)
+               return;
+
+       if (drv->off_channel_freq != drv->pending_action_freq) {
+               wpa_printf(MSG_DEBUG, "P2P: Pending Action frame TX "
+                          "waiting for another freq=%u",
+                          drv->pending_action_freq);
+               return;
+       }
+       wpa_printf(MSG_DEBUG, "P2P: Sending pending Action frame to "
+                  MACSTR, MAC2STR(drv->pending_action_dst));
+       wpa_driver_test_send_action(drv, drv->pending_action_freq, 0,
+                                   drv->pending_action_dst,
+                                   drv->pending_action_src,
+                                   drv->pending_action_bssid,
+                                   wpabuf_head(drv->pending_action_tx),
+                                   wpabuf_len(drv->pending_action_tx),
+                                   drv->pending_action_no_cck);
+}
+#endif /* CONFIG_P2P */
+
+
 static void test_remain_on_channel_timeout(void *eloop_ctx, void *timeout_ctx)
 {
        struct wpa_driver_test_data *drv = eloop_ctx;
@@ -2642,9 +2748,13 @@ static void test_remain_on_channel_timeout(void *eloop_ctx, void *timeout_ctx)
        os_memset(&data, 0, sizeof(data));
        data.remain_on_channel.freq = drv->remain_on_channel_freq;
        data.remain_on_channel.duration = drv->remain_on_channel_duration;
-       wpa_supplicant_event(drv->ctx, EVENT_CANCEL_REMAIN_ON_CHANNEL, &data);
+
+       if (drv->p2p)
+               drv->off_channel_freq = 0;
 
        drv->remain_on_channel_freq = 0;
+
+       wpa_supplicant_event(drv->ctx, EVENT_CANCEL_REMAIN_ON_CHANNEL, &data);
 }
 
 
@@ -2675,6 +2785,18 @@ static int wpa_driver_test_remain_on_channel(void *priv, unsigned int freq,
        data.remain_on_channel.duration = duration;
        wpa_supplicant_event(drv->ctx, EVENT_REMAIN_ON_CHANNEL, &data);
 
+#ifdef CONFIG_P2P
+       if (drv->p2p) {
+               drv->off_channel_freq = drv->remain_on_channel_freq;
+               test_send_action_cb(drv, NULL);
+               if (drv->off_channel_freq == drv->pending_listen_freq) {
+                       p2p_listen_cb(drv->p2p, drv->pending_listen_freq,
+                                     drv->pending_listen_duration);
+                       drv->pending_listen_freq = 0;
+               }
+       }
+#endif /* CONFIG_P2P */
+
        return 0;
 }
 
@@ -2702,6 +2824,461 @@ static int wpa_driver_test_probe_req_report(void *priv, int report)
 }
 
 
+#ifdef CONFIG_P2P
+
+static int wpa_driver_test_p2p_find(void *priv, unsigned int timeout, int type)
+{
+       struct wpa_driver_test_data *drv = priv;
+       wpa_printf(MSG_DEBUG, "%s(timeout=%u)", __func__, timeout);
+       if (!drv->p2p)
+               return -1;
+       return p2p_find(drv->p2p, timeout, type, 0, NULL, NULL);
+}
+
+
+static int wpa_driver_test_p2p_stop_find(void *priv)
+{
+       struct wpa_driver_test_data *drv = priv;
+       wpa_printf(MSG_DEBUG, "%s", __func__);
+       if (!drv->p2p)
+               return -1;
+       p2p_stop_find(drv->p2p);
+       return 0;
+}
+
+
+static int wpa_driver_test_p2p_listen(void *priv, unsigned int timeout)
+{
+       struct wpa_driver_test_data *drv = priv;
+       wpa_printf(MSG_DEBUG, "%s(timeout=%u)", __func__, timeout);
+       if (!drv->p2p)
+               return -1;
+       return p2p_listen(drv->p2p, timeout);
+}
+
+
+static int wpa_driver_test_p2p_connect(void *priv, const u8 *peer_addr,
+                                      int wps_method, int go_intent,
+                                      const u8 *own_interface_addr,
+                                      unsigned int force_freq,
+                                      int persistent_group)
+{
+       struct wpa_driver_test_data *drv = priv;
+       wpa_printf(MSG_DEBUG, "%s(peer_addr=" MACSTR " wps_method=%d "
+                  "go_intent=%d "
+                  "own_interface_addr=" MACSTR " force_freq=%u "
+                  "persistent_group=%d)",
+                  __func__, MAC2STR(peer_addr), wps_method, go_intent,
+                  MAC2STR(own_interface_addr), force_freq, persistent_group);
+       if (!drv->p2p)
+               return -1;
+       return p2p_connect(drv->p2p, peer_addr, wps_method, go_intent,
+                          own_interface_addr, force_freq, persistent_group);
+}
+
+
+static int wpa_driver_test_wps_success_cb(void *priv, const u8 *peer_addr)
+{
+       struct wpa_driver_test_data *drv = priv;
+       wpa_printf(MSG_DEBUG, "%s(peer_addr=" MACSTR ")",
+                  __func__, MAC2STR(peer_addr));
+       if (!drv->p2p)
+               return -1;
+       p2p_wps_success_cb(drv->p2p, peer_addr);
+       return 0;
+}
+
+
+static int wpa_driver_test_p2p_group_formation_failed(void *priv)
+{
+       struct wpa_driver_test_data *drv = priv;
+       wpa_printf(MSG_DEBUG, "%s", __func__);
+       if (!drv->p2p)
+               return -1;
+       p2p_group_formation_failed(drv->p2p);
+       return 0;
+}
+
+
+static int wpa_driver_test_p2p_set_params(void *priv,
+                                         const struct p2p_params *params)
+{
+       struct wpa_driver_test_data *drv = priv;
+       wpa_printf(MSG_DEBUG, "%s", __func__);
+       if (!drv->p2p)
+               return -1;
+       if (p2p_set_dev_name(drv->p2p, params->dev_name) < 0 ||
+           p2p_set_pri_dev_type(drv->p2p, params->pri_dev_type) < 0 ||
+           p2p_set_sec_dev_types(drv->p2p, params->sec_dev_type,
+                                 params->num_sec_dev_types) < 0)
+               return -1;
+       return 0;
+}
+
+
+static int test_p2p_scan(void *ctx, enum p2p_scan_type type, int freq,
+                        unsigned int num_req_dev_types,
+                        const u8 *req_dev_types, const u8 *dev_id)
+{
+       struct wpa_driver_test_data *drv = ctx;
+       struct wpa_driver_scan_params params;
+       int ret;
+       struct wpabuf *wps_ie, *ies;
+       int social_channels[] = { 2412, 2437, 2462, 0, 0 };
+       size_t ielen;
+
+       wpa_printf(MSG_DEBUG, "%s(type=%d freq=%d)",
+                  __func__, type, freq);
+
+       os_memset(&params, 0, sizeof(params));
+
+       /* P2P Wildcard SSID */
+       params.num_ssids = 1;
+       params.ssids[0].ssid = (u8 *) P2P_WILDCARD_SSID;
+       params.ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN;
+
+#if 0 /* TODO: WPS IE */
+       wpa_s->wps->dev.p2p = 1;
+       wps_ie = wps_build_probe_req_ie(0, &wpa_s->wps->dev, wpa_s->wps->uuid,
+                                       WPS_REQ_ENROLLEE);
+#else
+       wps_ie = wpabuf_alloc(1);
+#endif
+       if (wps_ie == NULL)
+               return -1;
+
+       ielen = p2p_scan_ie_buf_len(drv->p2p);
+       ies = wpabuf_alloc(wpabuf_len(wps_ie) + ielen);
+       if (ies == NULL) {
+               wpabuf_free(wps_ie);
+               return -1;
+       }
+       wpabuf_put_buf(ies, wps_ie);
+       wpabuf_free(wps_ie);
+
+       p2p_scan_ie(drv->p2p, ies, dev_id);
+
+       params.extra_ies = wpabuf_head(ies);
+       params.extra_ies_len = wpabuf_len(ies);
+
+       switch (type) {
+       case P2P_SCAN_SOCIAL:
+               params.freqs = social_channels;
+               break;
+       case P2P_SCAN_FULL:
+               break;
+       case P2P_SCAN_SPECIFIC:
+               social_channels[0] = freq;
+               social_channels[1] = 0;
+               params.freqs = social_channels;
+               break;
+       case P2P_SCAN_SOCIAL_PLUS_ONE:
+               social_channels[3] = freq;
+               params.freqs = social_channels;
+               break;
+       }
+
+       drv->pending_p2p_scan = 1;
+       ret = wpa_driver_test_scan(drv, &params);
+
+       wpabuf_free(ies);
+
+       return ret;
+}
+
+
+static int test_send_action(void *ctx, unsigned int freq, const u8 *dst,
+                           const u8 *src, const u8 *bssid, const u8 *buf,
+                           size_t len, unsigned int wait_time)
+{
+       struct wpa_driver_test_data *drv = ctx;
+
+       wpa_printf(MSG_DEBUG, "%s(freq=%u dst=" MACSTR " src=" MACSTR
+                  " bssid=" MACSTR " len=%d",
+                  __func__, freq, MAC2STR(dst), MAC2STR(src), MAC2STR(bssid),
+                  (int) len);
+       if (freq <= 0) {
+               wpa_printf(MSG_WARNING, "P2P: No frequency specified for "
+                          "action frame TX");
+               return -1;
+       }
+
+       if (drv->pending_action_tx) {
+               wpa_printf(MSG_DEBUG, "P2P: Dropped pending Action frame TX "
+                          "to " MACSTR, MAC2STR(drv->pending_action_dst));
+               wpabuf_free(drv->pending_action_tx);
+       }
+       drv->pending_action_tx = wpabuf_alloc(len);
+       if (drv->pending_action_tx == NULL)
+               return -1;
+       wpabuf_put_data(drv->pending_action_tx, buf, len);
+       os_memcpy(drv->pending_action_src, src, ETH_ALEN);
+       os_memcpy(drv->pending_action_dst, dst, ETH_ALEN);
+       os_memcpy(drv->pending_action_bssid, bssid, ETH_ALEN);
+       drv->pending_action_freq = freq;
+       drv->pending_action_no_cck = 1;
+
+       if (drv->off_channel_freq == freq) {
+               /* Already on requested channel; send immediately */
+               /* TODO: Would there ever be need to extend the current
+                * duration on the channel? */
+               eloop_cancel_timeout(test_send_action_cb, drv, NULL);
+               eloop_register_timeout(0, 0, test_send_action_cb, drv, NULL);
+               return 0;
+       }
+
+       wpa_printf(MSG_DEBUG, "P2P: Schedule Action frame to be transmitted "
+                  "once the driver gets to the requested channel");
+       if (wpa_driver_test_remain_on_channel(drv, freq, wait_time) < 0) {
+               wpa_printf(MSG_DEBUG, "P2P: Failed to request driver "
+                          "to remain on channel (%u MHz) for Action "
+                          "Frame TX", freq);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static void test_send_action_done(void *ctx)
+{
+       wpa_printf(MSG_DEBUG, "%s", __func__);
+       /* TODO */
+}
+
+
+static void test_go_neg_completed(void *ctx, struct p2p_go_neg_results *res)
+{
+       struct wpa_driver_test_data *drv = ctx;
+       union wpa_event_data event;
+       wpa_printf(MSG_DEBUG, "%s", __func__);
+       os_memset(&event, 0, sizeof(event));
+       event.p2p_go_neg_completed.res = res;
+       wpa_supplicant_event(drv->ctx, EVENT_P2P_GO_NEG_COMPLETED, &event);
+}
+
+
+static void test_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id)
+{
+       struct wpa_driver_test_data *drv = ctx;
+       union wpa_event_data event;
+       wpa_printf(MSG_DEBUG, "%s(src=" MACSTR ")", __func__, MAC2STR(src));
+       os_memset(&event, 0, sizeof(event));
+       event.p2p_go_neg_req_rx.src = src;
+       event.p2p_go_neg_req_rx.dev_passwd_id = dev_passwd_id;
+       wpa_supplicant_event(drv->ctx, EVENT_P2P_GO_NEG_REQ_RX, &event);
+}
+
+
+static void test_dev_found(void *ctx, const u8 *addr,
+                          const struct p2p_peer_info *info, int new_device)
+{
+       struct wpa_driver_test_data *drv = ctx;
+       union wpa_event_data event;
+       char devtype[WPS_DEV_TYPE_BUFSIZE];
+       wpa_printf(MSG_DEBUG, "%s(" MACSTR " p2p_dev_addr=" MACSTR
+                  " pri_dev_type=%s name='%s' config_methods=0x%x "
+                  "dev_capab=0x%x group_capab=0x%x)",
+                  __func__, MAC2STR(addr), MAC2STR(info->p2p_device_addr),
+                  wps_dev_type_bin2str(info->pri_dev_type, devtype,
+                                       sizeof(devtype)),
+                  info->device_name, info->config_methods, info->dev_capab,
+                  info->group_capab);
+
+       os_memset(&event, 0, sizeof(event));
+       event.p2p_dev_found.addr = addr;
+       event.p2p_dev_found.dev_addr = info->p2p_device_addr;
+       event.p2p_dev_found.pri_dev_type = info->pri_dev_type;
+       event.p2p_dev_found.dev_name = info->device_name;
+       event.p2p_dev_found.config_methods = info->config_methods;
+       event.p2p_dev_found.dev_capab = info->dev_capab;
+       event.p2p_dev_found.group_capab = info->group_capab;
+       wpa_supplicant_event(drv->ctx, EVENT_P2P_DEV_FOUND, &event);
+}
+
+
+static int test_start_listen(void *ctx, unsigned int freq,
+                            unsigned int duration,
+                            const struct wpabuf *probe_resp_ie)
+{
+       struct wpa_driver_test_data *drv = ctx;
+
+       wpa_printf(MSG_DEBUG, "%s(freq=%u duration=%u)",
+                  __func__, freq, duration);
+
+       if (wpa_driver_test_probe_req_report(drv, 1) < 0)
+               return -1;
+
+       drv->pending_listen_freq = freq;
+       drv->pending_listen_duration = duration;
+
+       if (wpa_driver_test_remain_on_channel(drv, freq, duration) < 0) {
+               drv->pending_listen_freq = 0;
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static void test_stop_listen(void *ctx)
+{
+       wpa_printf(MSG_DEBUG, "%s", __func__);
+       /* TODO */
+}
+
+
+static int test_send_probe_resp(void *ctx, const struct wpabuf *buf)
+{
+       struct wpa_driver_test_data *drv = ctx;
+       char resp[512], *pos, *end;
+       int ret;
+       const struct ieee80211_mgmt *mgmt;
+       const u8 *ie, *ie_end;
+
+       wpa_printf(MSG_DEBUG, "%s", __func__);
+       wpa_hexdump_buf(MSG_MSGDUMP, "Probe Response", buf);
+       if (wpabuf_len(buf) < 24)
+               return -1;
+       if (!drv->probe_from) {
+               wpa_printf(MSG_DEBUG, "%s: probe_from not set", __func__);
+               return -1;
+       }
+
+       pos = resp;
+       end = resp + sizeof(resp);
+
+       mgmt = wpabuf_head(buf);
+
+       /* reply: SCANRESP BSSID SSID IEs */
+       ret = os_snprintf(pos, end - pos, "SCANRESP " MACSTR " ",
+                         MAC2STR(mgmt->bssid));
+       if (ret < 0 || ret >= end - pos)
+               return -1;
+       pos += ret;
+
+       ie = mgmt->u.probe_resp.variable;
+       ie_end = wpabuf_head_u8(buf) + wpabuf_len(buf);
+       if (ie_end - ie < 2 || ie[0] != WLAN_EID_SSID ||
+           ie + 2 + ie[1] > ie_end)
+               return -1;
+       pos += wpa_snprintf_hex(pos, end - pos, ie + 2, ie[1]);
+
+       ret = os_snprintf(pos, end - pos, " ");
+       if (ret < 0 || ret >= end - pos)
+               return -1;
+       pos += ret;
+       pos += wpa_snprintf_hex(pos, end - pos, ie, ie_end - ie);
+
+       sendto(drv->test_socket, resp, pos - resp, 0,
+              drv->probe_from, drv->probe_from_len);
+
+       return 0;
+}
+
+
+static void test_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token,
+                           u16 update_indic, const u8 *tlvs, size_t tlvs_len)
+{
+       wpa_printf(MSG_DEBUG, "%s", __func__);
+       /* TODO */
+}
+
+
+static void test_sd_response(void *ctx, const u8 *sa, u16 update_indic,
+                            const u8 *tlvs, size_t tlvs_len)
+{
+       wpa_printf(MSG_DEBUG, "%s", __func__);
+       /* TODO */
+}
+
+
+static void test_prov_disc_req(void *ctx, const u8 *peer, u16 config_methods,
+                              const u8 *dev_addr, const u8 *pri_dev_type,
+                              const char *dev_name, u16 supp_config_methods,
+                              u8 dev_capab, u8 group_capab,
+                              const u8 *group_id, size_t group_id_len)
+{
+       wpa_printf(MSG_DEBUG, "%s(peer=" MACSTR " config_methods=0x%x)",
+                  __func__, MAC2STR(peer), config_methods);
+       /* TODO */
+}
+
+
+static void test_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods)
+{
+       wpa_printf(MSG_DEBUG, "%s(peer=" MACSTR " config_methods=0x%x)",
+                  __func__, MAC2STR(peer), config_methods);
+       /* TODO */
+}
+
+#endif /* CONFIG_P2P */
+
+
+static int wpa_driver_test_init_p2p(struct wpa_driver_test_data *drv)
+{
+#ifdef CONFIG_P2P
+       struct p2p_config p2p;
+       unsigned int r;
+       int i;
+
+       os_memset(&p2p, 0, sizeof(p2p));
+       p2p.msg_ctx = drv->ctx;
+       p2p.cb_ctx = drv;
+       p2p.p2p_scan = test_p2p_scan;
+       p2p.send_action = test_send_action;
+       p2p.send_action_done = test_send_action_done;
+       p2p.go_neg_completed = test_go_neg_completed;
+       p2p.go_neg_req_rx = test_go_neg_req_rx;
+       p2p.dev_found = test_dev_found;
+       p2p.start_listen = test_start_listen;
+       p2p.stop_listen = test_stop_listen;
+       p2p.send_probe_resp = test_send_probe_resp;
+       p2p.sd_request = test_sd_request;
+       p2p.sd_response = test_sd_response;
+       p2p.prov_disc_req = test_prov_disc_req;
+       p2p.prov_disc_resp = test_prov_disc_resp;
+
+       os_memcpy(p2p.dev_addr, drv->own_addr, ETH_ALEN);
+
+       p2p.reg_class = 12; /* TODO: change depending on location */
+       /*
+        * Pick one of the social channels randomly as the listen
+        * channel.
+        */
+       os_get_random((u8 *) &r, sizeof(r));
+       p2p.channel = 1 + (r % 3) * 5;
+
+       /* TODO: change depending on location */
+       p2p.op_reg_class = 12;
+       /*
+        * For initial tests, pick the operation channel randomly.
+        * TODO: Use scan results (etc.) to select the best channel.
+        */
+       p2p.op_channel = 1 + r % 11;
+
+       os_memcpy(p2p.country, "US ", 3);
+
+       /* FIX: fetch available channels from the driver */
+       p2p.channels.reg_classes = 1;
+       p2p.channels.reg_class[0].reg_class = 12; /* US/12 = 2.4 GHz band */
+       p2p.channels.reg_class[0].channels = 11;
+       for (i = 0; i < 11; i++)
+               p2p.channels.reg_class[0].channel[i] = i + 1;
+
+       p2p.max_peers = 100;
+
+       drv->p2p = p2p_init(&p2p);
+       if (drv->p2p == NULL)
+               return -1;
+       return 0;
+#else /* CONFIG_P2P */
+       wpa_printf(MSG_INFO, "driver_test: P2P support not included");
+       return -1;
+#endif /* CONFIG_P2P */
+}
+
+
 const struct wpa_driver_ops wpa_driver_test_ops = {
        "test",
        "wpa_supplicant test driver",
@@ -2715,7 +3292,6 @@ const struct wpa_driver_ops wpa_driver_test_ops = {
        .get_hw_feature_data = wpa_driver_test_get_hw_feature_data,
        .if_add = test_driver_if_add,
        .if_remove = test_driver_if_remove,
-       .valid_bss_mask = test_driver_valid_bss_mask,
        .hapd_set_ssid = test_driver_set_ssid,
        .set_privacy = test_driver_set_privacy,
        .set_sta_vlan = test_driver_set_sta_vlan,
@@ -2734,11 +3310,6 @@ const struct wpa_driver_ops wpa_driver_test_ops = {
        .get_mac_addr = wpa_driver_test_get_mac_addr,
        .send_eapol = wpa_driver_test_send_eapol,
        .mlme_setprotection = wpa_driver_test_mlme_setprotection,
-       .set_channel = wpa_driver_test_set_channel,
-       .set_ssid = wpa_driver_test_set_ssid,
-       .set_bssid = wpa_driver_test_set_bssid,
-       .mlme_add_sta = wpa_driver_test_mlme_add_sta,
-       .mlme_remove_sta = wpa_driver_test_mlme_remove_sta,
        .get_scan_results2 = wpa_driver_test_get_scan_results2,
        .global_init = wpa_driver_test_global_init,
        .global_deinit = wpa_driver_test_global_deinit,
@@ -2750,4 +3321,14 @@ const struct wpa_driver_ops wpa_driver_test_ops = {
        .remain_on_channel = wpa_driver_test_remain_on_channel,
        .cancel_remain_on_channel = wpa_driver_test_cancel_remain_on_channel,
        .probe_req_report = wpa_driver_test_probe_req_report,
+#ifdef CONFIG_P2P
+       .p2p_find = wpa_driver_test_p2p_find,
+       .p2p_stop_find = wpa_driver_test_p2p_stop_find,
+       .p2p_listen = wpa_driver_test_p2p_listen,
+       .p2p_connect = wpa_driver_test_p2p_connect,
+       .wps_success_cb = wpa_driver_test_wps_success_cb,
+       .p2p_group_formation_failed =
+       wpa_driver_test_p2p_group_formation_failed,
+       .p2p_set_params = wpa_driver_test_p2p_set_params,
+#endif /* CONFIG_P2P */
 };
index 2614f23..b980c15 100644 (file)
@@ -20,7 +20,9 @@
 
 #include "includes.h"
 #include <sys/ioctl.h>
+#include <sys/types.h>
 #include <sys/stat.h>
+#include <fcntl.h>
 #include <net/if_arp.h>
 
 #include "wireless_copy.h"
@@ -31,6 +33,7 @@
 #include "priv_netlink.h"
 #include "netlink.h"
 #include "linux_ioctl.h"
+#include "rfkill.h"
 #include "driver.h"
 #include "driver_wext.h"
 
@@ -561,10 +564,28 @@ static void wpa_driver_wext_event_link(struct wpa_driver_wext_data *drv,
                   del ? "removed" : "added");
 
        if (os_strcmp(drv->ifname, event.interface_status.ifname) == 0) {
-               if (del)
+               if (del) {
+                       if (drv->if_removed) {
+                               wpa_printf(MSG_DEBUG, "WEXT: if_removed "
+                                          "already set - ignore event");
+                               return;
+                       }
                        drv->if_removed = 1;
-               else
+               } else {
+                       if (if_nametoindex(drv->ifname) == 0) {
+                               wpa_printf(MSG_DEBUG, "WEXT: Interface %s "
+                                          "does not exist - ignore "
+                                          "RTM_NEWLINK",
+                                          drv->ifname);
+                               return;
+                       }
+                       if (!drv->if_removed) {
+                               wpa_printf(MSG_DEBUG, "WEXT: if_removed "
+                                          "already cleared - ignore event");
+                               return;
+                       }
                        drv->if_removed = 0;
+               }
        }
 
        wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
@@ -620,6 +641,7 @@ static void wpa_driver_wext_event_rtm_newlink(void *ctx, struct ifinfomsg *ifi,
        struct wpa_driver_wext_data *drv = ctx;
        int attrlen, rta_len;
        struct rtattr *attr;
+       char namebuf[IFNAMSIZ];
 
        if (!wpa_driver_wext_own_ifindex(drv, ifi->ifi_index, buf, len)) {
                wpa_printf(MSG_DEBUG, "Ignore event for foreign ifindex %d",
@@ -634,6 +656,35 @@ static void wpa_driver_wext_event_rtm_newlink(void *ctx, struct ifinfomsg *ifi,
                   (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "",
                   (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
                   (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
+
+       if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) {
+               wpa_printf(MSG_DEBUG, "WEXT: Interface down");
+               drv->if_disabled = 1;
+               wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_DISABLED, NULL);
+       }
+
+       if (drv->if_disabled && (ifi->ifi_flags & IFF_UP)) {
+               if (if_indextoname(ifi->ifi_index, namebuf) &&
+                   linux_iface_up(drv->ioctl_sock, drv->ifname) == 0) {
+                       wpa_printf(MSG_DEBUG, "WEXT: Ignore interface up "
+                                  "event since interface %s is down",
+                                  namebuf);
+               } else if (if_nametoindex(drv->ifname) == 0) {
+                       wpa_printf(MSG_DEBUG, "WEXT: Ignore interface up "
+                                  "event since interface %s does not exist",
+                                  drv->ifname);
+               } else if (drv->if_removed) {
+                       wpa_printf(MSG_DEBUG, "WEXT: Ignore interface up "
+                                  "event since interface %s is marked "
+                                  "removed", drv->ifname);
+               } else {
+                       wpa_printf(MSG_DEBUG, "WEXT: Interface up");
+                       drv->if_disabled = 0;
+                       wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED,
+                                            NULL);
+               }
+       }
+
        /*
         * Some drivers send the association event before the operup event--in
         * this case, lifting operstate in wpa_driver_wext_set_operstate()
@@ -687,6 +738,62 @@ static void wpa_driver_wext_event_rtm_dellink(void *ctx, struct ifinfomsg *ifi,
 }
 
 
+static void wpa_driver_wext_rfkill_blocked(void *ctx)
+{
+       wpa_printf(MSG_DEBUG, "WEXT: RFKILL blocked");
+       /*
+        * This may be for any interface; use ifdown event to disable
+        * interface.
+        */
+}
+
+
+static void wpa_driver_wext_rfkill_unblocked(void *ctx)
+{
+       struct wpa_driver_wext_data *drv = ctx;
+       wpa_printf(MSG_DEBUG, "WEXT: RFKILL unblocked");
+       if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1)) {
+               wpa_printf(MSG_DEBUG, "WEXT: Could not set interface UP "
+                          "after rfkill unblock");
+               return;
+       }
+       /* rtnetlink ifup handler will report interface as enabled */
+}
+
+
+static void wext_get_phy_name(struct wpa_driver_wext_data *drv)
+{
+       /* Find phy (radio) to which this interface belongs */
+       char buf[90], *pos;
+       int f, rv;
+
+       drv->phyname[0] = '\0';
+       snprintf(buf, sizeof(buf) - 1, "/sys/class/net/%s/phy80211/name",
+                drv->ifname);
+       f = open(buf, O_RDONLY);
+       if (f < 0) {
+               wpa_printf(MSG_DEBUG, "Could not open file %s: %s",
+                          buf, strerror(errno));
+               return;
+       }
+
+       rv = read(f, drv->phyname, sizeof(drv->phyname) - 1);
+       close(f);
+       if (rv < 0) {
+               wpa_printf(MSG_DEBUG, "Could not read file %s: %s",
+                          buf, strerror(errno));
+               return;
+       }
+
+       drv->phyname[rv] = '\0';
+       pos = os_strchr(drv->phyname, '\n');
+       if (pos)
+               *pos = '\0';
+       wpa_printf(MSG_DEBUG, "wext: interface %s phy: %s",
+                  drv->ifname, drv->phyname);
+}
+
+
 /**
  * wpa_driver_wext_init - Initialize WE driver interface
  * @ctx: context to be used when calling wpa_supplicant functions,
@@ -698,6 +805,7 @@ void * wpa_driver_wext_init(void *ctx, const char *ifname)
 {
        struct wpa_driver_wext_data *drv;
        struct netlink_config *cfg;
+       struct rfkill_config *rcfg;
        char path[128];
        struct stat buf;
 
@@ -711,6 +819,7 @@ void * wpa_driver_wext_init(void *ctx, const char *ifname)
        if (stat(path, &buf) == 0) {
                wpa_printf(MSG_DEBUG, "WEXT: cfg80211-based driver detected");
                drv->cfg80211 = 1;
+               wext_get_phy_name(drv);
        }
 
        drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
@@ -731,6 +840,19 @@ void * wpa_driver_wext_init(void *ctx, const char *ifname)
                goto err2;
        }
 
+       rcfg = os_zalloc(sizeof(*rcfg));
+       if (rcfg == NULL)
+               goto err3;
+       rcfg->ctx = drv;
+       os_strlcpy(rcfg->ifname, ifname, sizeof(rcfg->ifname));
+       rcfg->blocked_cb = wpa_driver_wext_rfkill_blocked;
+       rcfg->unblocked_cb = wpa_driver_wext_rfkill_unblocked;
+       drv->rfkill = rfkill_init(rcfg);
+       if (drv->rfkill == NULL) {
+               wpa_printf(MSG_DEBUG, "WEXT: RFKILL status not available");
+               os_free(rcfg);
+       }
+
        drv->mlme_sock = -1;
 
        if (wpa_driver_wext_finish_drv_init(drv) < 0)
@@ -741,6 +863,7 @@ void * wpa_driver_wext_init(void *ctx, const char *ifname)
        return drv;
 
 err3:
+       rfkill_deinit(drv->rfkill);
        netlink_deinit(drv->netlink);
 err2:
        close(drv->ioctl_sock);
@@ -750,10 +873,29 @@ err1:
 }
 
 
+static void wpa_driver_wext_send_rfkill(void *eloop_ctx, void *timeout_ctx)
+{
+       wpa_supplicant_event(timeout_ctx, EVENT_INTERFACE_DISABLED, NULL);
+}
+
+
 static int wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv)
 {
-       if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1) < 0)
-               return -1;
+       int send_rfkill_event = 0;
+
+       if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1) < 0) {
+               if (rfkill_is_blocked(drv->rfkill)) {
+                       wpa_printf(MSG_DEBUG, "WEXT: Could not yet enable "
+                                  "interface '%s' due to rfkill",
+                                  drv->ifname);
+                       drv->if_disabled = 1;
+                       send_rfkill_event = 1;
+               } else {
+                       wpa_printf(MSG_ERROR, "WEXT: Could not set "
+                                  "interface '%s' UP", drv->ifname);
+                       return -1;
+               }
+       }
 
        /*
         * Make sure that the driver does not have any obsolete PMKID entries.
@@ -795,6 +937,11 @@ static int wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv)
        netlink_send_oper_ifla(drv->netlink, drv->ifindex,
                               1, IF_OPER_DORMANT);
 
+       if (send_rfkill_event) {
+               eloop_register_timeout(0, 0, wpa_driver_wext_send_rfkill,
+                                      drv, drv->ctx);
+       }
+
        return 0;
 }
 
@@ -822,6 +969,7 @@ void wpa_driver_wext_deinit(void *priv)
 
        netlink_send_oper_ifla(drv->netlink, drv->ifindex, 0, IF_OPER_UP);
        netlink_deinit(drv->netlink);
+       rfkill_deinit(drv->rfkill);
 
        if (drv->mlme_sock >= 0)
                eloop_unregister_read_sock(drv->mlme_sock);
@@ -894,7 +1042,7 @@ int wpa_driver_wext_scan(void *priv, struct wpa_driver_scan_params *params)
 
        /* Not all drivers generate "scan completed" wireless event, so try to
         * read results after a timeout. */
-       timeout = 5;
+       timeout = 10;
        if (drv->scan_complete_events) {
                /*
                 * The driver seems to deliver SIOCGIWSCAN events to notify
@@ -1040,7 +1188,8 @@ static void wext_get_scan_freq(struct iw_event *iwe,
 }
 
 
-static void wext_get_scan_qual(struct iw_event *iwe,
+static void wext_get_scan_qual(struct wpa_driver_wext_data *drv,
+                              struct iw_event *iwe,
                               struct wext_scan_data *res)
 {
        res->res.qual = iwe->u.qual.qual;
@@ -1054,6 +1203,14 @@ static void wext_get_scan_qual(struct iw_event *iwe,
                res->res.flags |= WPA_SCAN_NOISE_INVALID;
        if (iwe->u.qual.updated & IW_QUAL_DBM)
                res->res.flags |= WPA_SCAN_LEVEL_DBM;
+       if ((iwe->u.qual.updated & IW_QUAL_DBM) ||
+           ((iwe->u.qual.level != 0) &&
+            (iwe->u.qual.level > drv->max_level))) {
+               if (iwe->u.qual.level >= 64)
+                       res->res.level -= 0x100;
+               if (iwe->u.qual.noise >= 64)
+                       res->res.noise -= 0x100;
+       }
 }
 
 
@@ -1255,7 +1412,7 @@ static void wpa_driver_wext_add_scan_entry(struct wpa_scan_results *res,
        tmp[res->num++] = r;
        res->res = tmp;
 }
-                                     
+
 
 /**
  * wpa_driver_wext_get_scan_results - Fetch the latest scan results
@@ -1265,7 +1422,7 @@ static void wpa_driver_wext_add_scan_entry(struct wpa_scan_results *res,
 struct wpa_scan_results * wpa_driver_wext_get_scan_results(void *priv)
 {
        struct wpa_driver_wext_data *drv = priv;
-       size_t ap_num = 0, len;
+       size_t len;
        int first;
        u8 *res_buf;
        struct iw_event iwe_buf, *iwe = &iwe_buf;
@@ -1277,7 +1434,6 @@ struct wpa_scan_results * wpa_driver_wext_get_scan_results(void *priv)
        if (res_buf == NULL)
                return NULL;
 
-       ap_num = 0;
        first = 1;
 
        res = os_zalloc(sizeof(*res));
@@ -1329,7 +1485,7 @@ struct wpa_scan_results * wpa_driver_wext_get_scan_results(void *priv)
                        wext_get_scan_freq(iwe, &data);
                        break;
                case IWEVQUAL:
-                       wext_get_scan_qual(iwe, &data);
+                       wext_get_scan_qual(drv, iwe, &data);
                        break;
                case SIOCGIWENCODE:
                        wext_get_scan_encode(iwe, &data);
@@ -1427,6 +1583,8 @@ static int wpa_driver_wext_get_range(void *priv)
                           "assuming WPA is not supported");
        }
 
+       drv->max_level = range->max_qual.level;
+
        os_free(range);
        return 0;
 }
@@ -1498,8 +1656,7 @@ static int wpa_driver_wext_set_key_ext(void *priv, enum wpa_alg alg,
        iwr.u.encoding.pointer = (caddr_t) ext;
        iwr.u.encoding.length = sizeof(*ext) + key_len;
 
-       if (addr == NULL ||
-           os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0)
+       if (addr == NULL || is_broadcast_ether_addr(addr))
                ext->ext_flags |= IW_ENCODE_EXT_GROUP_KEY;
        if (set_tx)
                ext->ext_flags |= IW_ENCODE_EXT_SET_TX_KEY;
@@ -1702,8 +1859,10 @@ static void wpa_driver_wext_disconnect(struct wpa_driver_wext_data *drv)
 {
        struct iwreq iwr;
        const u8 null_bssid[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
+#ifndef ANDROID
        u8 ssid[32];
        int i;
+#endif /* ANDROID */
 
        /*
         * Only force-disconnect when the card is in infrastructure mode,
@@ -1718,32 +1877,39 @@ static void wpa_driver_wext_disconnect(struct wpa_driver_wext_data *drv)
        }
 
        if (iwr.u.mode == IW_MODE_INFRA) {
+               /* Clear the BSSID selection */
+               if (wpa_driver_wext_set_bssid(drv, null_bssid) < 0) {
+                       wpa_printf(MSG_DEBUG, "WEXT: Failed to clear BSSID "
+                                  "selection on disconnect");
+               }
+
+#ifndef ANDROID
                if (drv->cfg80211) {
                        /*
                         * cfg80211 supports SIOCSIWMLME commands, so there is
                         * no need for the random SSID hack, but clear the
-                        * BSSID and SSID.
+                        * SSID.
                         */
-                       if (wpa_driver_wext_set_bssid(drv, null_bssid) < 0 ||
-                           wpa_driver_wext_set_ssid(drv, (u8 *) "", 0) < 0) {
+                       if (wpa_driver_wext_set_ssid(drv, (u8 *) "", 0) < 0) {
                                wpa_printf(MSG_DEBUG, "WEXT: Failed to clear "
-                                          "to disconnect");
+                                          "SSID on disconnect");
                        }
                        return;
                }
+
                /*
-                * Clear the BSSID selection and set a random SSID to make sure
-                * the driver will not be trying to associate with something
-                * even if it does not understand SIOCSIWMLME commands (or
-                * tries to associate automatically after deauth/disassoc).
+                * Set a random SSID to make sure the driver will not be trying
+                * to associate with something even if it does not understand
+                * SIOCSIWMLME commands (or tries to associate automatically
+                * after deauth/disassoc).
                 */
                for (i = 0; i < 32; i++)
                        ssid[i] = rand() & 0xFF;
-               if (wpa_driver_wext_set_bssid(drv, null_bssid) < 0 ||
-                   wpa_driver_wext_set_ssid(drv, ssid, 32) < 0) {
+               if (wpa_driver_wext_set_ssid(drv, ssid, 32) < 0) {
                        wpa_printf(MSG_DEBUG, "WEXT: Failed to set bogus "
-                                  "BSSID/SSID to disconnect");
+                                  "SSID to disconnect");
                }
+#endif /* ANDROID */
        }
 }
 
@@ -2167,6 +2333,13 @@ int wpa_driver_wext_get_version(struct wpa_driver_wext_data *drv)
 }
 
 
+static const char * wext_get_radio_name(void *priv)
+{
+       struct wpa_driver_wext_data *drv = priv;
+       return drv->phyname;
+}
+
+
 const struct wpa_driver_ops wpa_driver_wext_ops = {
        .name = "wext",
        .desc = "Linux wireless extensions (generic)",
@@ -2186,4 +2359,5 @@ const struct wpa_driver_ops wpa_driver_wext_ops = {
        .flush_pmkid = wpa_driver_wext_flush_pmkid,
        .get_capa = wpa_driver_wext_get_capa,
        .set_operstate = wpa_driver_wext_set_operstate,
+       .get_radio_name = wext_get_radio_name,
 };
index 602c7e1..89c13eb 100644 (file)
@@ -23,9 +23,12 @@ struct wpa_driver_wext_data {
        int ioctl_sock;
        int mlme_sock;
        char ifname[IFNAMSIZ + 1];
+       char phyname[32];
        int ifindex;
        int ifindex2;
        int if_removed;
+       int if_disabled;
+       struct rfkill_data *rfkill;
        u8 *assoc_req_ies;
        size_t assoc_req_ies_len;
        u8 *assoc_resp_ies;
@@ -45,6 +48,8 @@ struct wpa_driver_wext_data {
        int scan_complete_events;
 
        int cfg80211; /* whether driver is using cfg80211 */
+
+       u8 max_level;
 };
 
 int wpa_driver_wext_get_bssid(void *priv, u8 *bssid);
index 2b197f0..618db26 100644 (file)
@@ -24,6 +24,9 @@
 #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
 #include <net/if_dl.h>
 #endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) */
+#ifdef __sun__
+#include <sys/sockio.h>
+#endif /* __sun__ */
 
 #include "common.h"
 #include "eloop.h"
@@ -311,7 +314,7 @@ static int wired_init_sockets(struct wpa_driver_wired_data *drv, u8 *own_addr)
 
 static int wired_send_eapol(void *priv, const u8 *addr,
                            const u8 *data, size_t data_len, int encrypt,
-                           const u8 *own_addr)
+                           const u8 *own_addr, u32 flags)
 {
        struct wpa_driver_wired_data *drv = priv;
        struct ieee8023_hdr *hdr;
@@ -462,6 +465,10 @@ static int wpa_driver_wired_multi(const char *ifname, const u8 *addr, int add)
        struct ifreq ifr;
        int s;
 
+#ifdef __sun__
+       return -1;
+#endif /* __sun__ */
+
        s = socket(PF_INET, SOCK_DGRAM, 0);
        if (s < 0) {
                perror("socket");
index bffbbde..b710778 100644 (file)
@@ -24,25 +24,12 @@ extern struct wpa_driver_ops wpa_driver_nl80211_ops; /* driver_nl80211.c */
 #ifdef CONFIG_DRIVER_HOSTAP
 extern struct wpa_driver_ops wpa_driver_hostap_ops; /* driver_hostap.c */
 #endif /* CONFIG_DRIVER_HOSTAP */
-#ifdef CONFIG_DRIVER_HERMES
-extern struct wpa_driver_ops wpa_driver_hermes_ops; /* driver_hermes.c */
-#endif /* CONFIG_DRIVER_HERMES */
 #ifdef CONFIG_DRIVER_MADWIFI
 extern struct wpa_driver_ops wpa_driver_madwifi_ops; /* driver_madwifi.c */
 #endif /* CONFIG_DRIVER_MADWIFI */
-#ifdef CONFIG_DRIVER_ATMEL
-extern struct wpa_driver_ops wpa_driver_atmel_ops; /* driver_atmel.c */
-#endif /* CONFIG_DRIVER_ATMEL */
-#ifdef CONFIG_DRIVER_NDISWRAPPER
-/* driver_ndiswrapper.c */
-extern struct wpa_driver_ops wpa_driver_ndiswrapper_ops;
-#endif /* CONFIG_DRIVER_NDISWRAPPER */
 #ifdef CONFIG_DRIVER_BROADCOM
 extern struct wpa_driver_ops wpa_driver_broadcom_ops; /* driver_broadcom.c */
 #endif /* CONFIG_DRIVER_BROADCOM */
-#ifdef CONFIG_DRIVER_IPW
-extern struct wpa_driver_ops wpa_driver_ipw_ops; /* driver_ipw.c */
-#endif /* CONFIG_DRIVER_IPW */
 #ifdef CONFIG_DRIVER_BSD
 extern struct wpa_driver_ops wpa_driver_bsd_ops; /* driver_bsd.c */
 #endif /* CONFIG_DRIVER_BSD */
@@ -87,24 +74,12 @@ struct wpa_driver_ops *wpa_drivers[] =
 #ifdef CONFIG_DRIVER_HOSTAP
        &wpa_driver_hostap_ops,
 #endif /* CONFIG_DRIVER_HOSTAP */
-#ifdef CONFIG_DRIVER_HERMES
-       &wpa_driver_hermes_ops,
-#endif /* CONFIG_DRIVER_HERMES */
 #ifdef CONFIG_DRIVER_MADWIFI
        &wpa_driver_madwifi_ops,
 #endif /* CONFIG_DRIVER_MADWIFI */
-#ifdef CONFIG_DRIVER_ATMEL
-       &wpa_driver_atmel_ops,
-#endif /* CONFIG_DRIVER_ATMEL */
-#ifdef CONFIG_DRIVER_NDISWRAPPER
-       &wpa_driver_ndiswrapper_ops,
-#endif /* CONFIG_DRIVER_NDISWRAPPER */
 #ifdef CONFIG_DRIVER_BROADCOM
        &wpa_driver_broadcom_ops,
 #endif /* CONFIG_DRIVER_BROADCOM */
-#ifdef CONFIG_DRIVER_IPW
-       &wpa_driver_ipw_ops,
-#endif /* CONFIG_DRIVER_IPW */
 #ifdef CONFIG_DRIVER_BSD
        &wpa_driver_bsd_ops,
 #endif /* CONFIG_DRIVER_BSD */
index b76b229..b1f70e0 100644 (file)
@@ -1,3 +1,15 @@
+##### CLEAR VARS
+
+DRV_CFLAGS =
+DRV_WPA_CFLAGS =
+DRV_AP_CFLAGS =
+DRV_OBJS =
+DRV_WPA_OBJS =
+DRV_AP_OBJS =
+DRV_LIBS =
+DRV_WPA_LIBS =
+DRV_AP_LIBS =
+
 ##### COMMON DRIVERS
 
 ifdef CONFIG_DRIVER_HOSTAP
@@ -31,11 +43,23 @@ NEED_SME=y
 NEED_AP_MLME=y
 NEED_NETLINK=y
 NEED_LINUX_IOCTL=y
-DRV_LIBS += -lnl
+NEED_RFKILL=y
+
+ifdef CONFIG_LIBNL32
+  DRV_LIBS += -lnl-3
+  DRV_LIBS += -lnl-genl-3
+  DRV_CFLAGS += -DCONFIG_LIBNL20
+else
+  ifdef CONFIG_LIBNL_TINY
+    DRV_LIBS += -lnl-tiny
+  else
+    DRV_LIBS += -lnl
+  endif
 
-ifdef CONFIG_LIBNL20
-DRV_LIBS += -lnl-genl
-DRV_CFLAGS += -DCONFIG_LIBNL20
+  ifdef CONFIG_LIBNL20
+    DRV_LIBS += -lnl-genl
+    DRV_CFLAGS += -DCONFIG_LIBNL20
+  endif
 endif
 endif
 
@@ -77,24 +101,7 @@ DRV_WPA_CFLAGS += -DCONFIG_DRIVER_WEXT
 CONFIG_WIRELESS_EXTENSION=y
 NEED_NETLINK=y
 NEED_LINUX_IOCTL=y
-endif
-
-ifdef CONFIG_DRIVER_HERMES
-DRV_WPA_CFLAGS += -DCONFIG_DRIVER_HERMES
-DRV_WPA_OBJS += ../src/drivers/driver_hermes.o
-CONFIG_WIRELESS_EXTENSION=y
-endif
-
-ifdef CONFIG_DRIVER_ATMEL
-DRV_WPA_CFLAGS += -DCONFIG_DRIVER_ATMEL
-DRV_WPA_OBJS += ../src/drivers/driver_atmel.o
-CONFIG_WIRELESS_EXTENSION=y
-endif
-
-ifdef CONFIG_DRIVER_NDISWRAPPER
-DRV_WPA_CFLAGS += -DCONFIG_DRIVER_NDISWRAPPER
-DRV_WPA_OBJS += ../src/drivers/driver_ndiswrapper.o
-CONFIG_WIRELESS_EXTENSION=y
+NEED_RFKILL=y
 endif
 
 ifdef CONFIG_DRIVER_RALINK
@@ -109,12 +116,6 @@ DRV_WPA_CFLAGS += -DCONFIG_DRIVER_BROADCOM
 DRV_WPA_OBJS += ../src/drivers/driver_broadcom.o
 endif
 
-ifdef CONFIG_DRIVER_IPW
-DRV_WPA_CFLAGS += -DCONFIG_DRIVER_IPW
-DRV_WPA_OBJS += ../src/drivers/driver_ipw.o
-CONFIG_WIRELESS_EXTENSION=y
-endif
-
 ifdef CONFIG_DRIVER_NDIS
 DRV_WPA_CFLAGS += -DCONFIG_DRIVER_NDIS
 DRV_WPA_OBJS += ../src/drivers/driver_ndis.o
@@ -152,6 +153,7 @@ endif
 ifdef CONFIG_WIRELESS_EXTENSION
 DRV_WPA_CFLAGS += -DCONFIG_WIRELESS_EXTENSION
 DRV_WPA_OBJS += ../src/drivers/driver_wext.o
+NEED_RFKILL=y
 endif
 
 ifdef NEED_NETLINK
@@ -162,6 +164,10 @@ ifdef NEED_LINUX_IOCTL
 DRV_OBJS += ../src/drivers/linux_ioctl.o
 endif
 
+ifdef NEED_RFKILL
+DRV_OBJS += ../src/drivers/rfkill.o
+endif
+
 
 ##### COMMON VARS
 DRV_BOTH_CFLAGS := $(DRV_CFLAGS) $(DRV_WPA_CFLAGS) $(DRV_AP_CFLAGS)
diff --git a/src/drivers/drivers.mk b/src/drivers/drivers.mk
new file mode 100644 (file)
index 0000000..2d1ad9d
--- /dev/null
@@ -0,0 +1,190 @@
+##### CLEAR VARS
+
+DRV_CFLAGS =
+DRV_WPA_CFLAGS =
+DRV_AP_CFLAGS =
+DRV_OBJS =
+DRV_WPA_OBJS =
+DRV_AP_OBJS =
+DRV_LIBS =
+DRV_WPA_LIBS =
+DRV_AP_LIBS =
+
+##### COMMON DRIVERS
+
+ifdef CONFIG_DRIVER_HOSTAP
+DRV_CFLAGS += -DCONFIG_DRIVER_HOSTAP
+DRV_OBJS += src/drivers/driver_hostap.c
+CONFIG_WIRELESS_EXTENSION=y
+NEED_AP_MLME=y
+NEED_NETLINK=y
+NEED_LINUX_IOCTL=y
+endif
+
+ifdef CONFIG_DRIVER_WIRED
+DRV_CFLAGS += -DCONFIG_DRIVER_WIRED
+DRV_OBJS += src/drivers/driver_wired.c
+endif
+
+ifdef CONFIG_DRIVER_MADWIFI
+DRV_CFLAGS += -DCONFIG_DRIVER_MADWIFI
+DRV_OBJS += src/drivers/driver_madwifi.c
+CONFIG_WIRELESS_EXTENSION=y
+CONFIG_L2_PACKET=linux
+NEED_NETLINK=y
+NEED_LINUX_IOCTL=y
+endif
+
+ifdef CONFIG_DRIVER_NL80211
+DRV_CFLAGS += -DCONFIG_DRIVER_NL80211
+DRV_OBJS += src/drivers/driver_nl80211.c
+DRV_OBJS += src/utils/radiotap.c
+NEED_SME=y
+NEED_AP_MLME=y
+NEED_NETLINK=y
+NEED_LINUX_IOCTL=y
+NEED_RFKILL=y
+
+ifdef CONFIG_LIBNL32
+  DRV_LIBS += -lnl-3
+  DRV_LIBS += -lnl-genl-3
+  DRV_CFLAGS += -DCONFIG_LIBNL20
+else
+  ifdef CONFIG_LIBNL_TINY
+    DRV_LIBS += -lnl-tiny
+  else
+    DRV_LIBS += -lnl
+  endif
+
+  ifdef CONFIG_LIBNL20
+    DRV_LIBS += -lnl-genl
+    DRV_CFLAGS += -DCONFIG_LIBNL20
+  endif
+endif
+endif
+
+ifdef CONFIG_DRIVER_BSD
+ifndef CONFIG_L2_PACKET
+CONFIG_L2_PACKET=freebsd
+endif
+DRV_CFLAGS += -DCONFIG_DRIVER_BSD
+DRV_OBJS += src/drivers/driver_bsd.c
+CONFIG_L2_FREEBSD=y
+CONFIG_DNET_PCAP=y
+endif
+
+ifdef CONFIG_DRIVER_TEST
+DRV_CFLAGS += -DCONFIG_DRIVER_TEST
+DRV_OBJS += src/drivers/driver_test.c
+NEED_AP_MLME=y
+endif
+
+ifdef CONFIG_DRIVER_NONE
+DRV_CFLAGS += -DCONFIG_DRIVER_NONE
+DRV_OBJS += src/drivers/driver_none.c
+endif
+
+##### PURE AP DRIVERS
+
+ifdef CONFIG_DRIVER_ATHEROS
+DRV_AP_CFLAGS += -DCONFIG_DRIVER_ATHEROS
+DRV_AP_OBJS += src/drivers/driver_atheros.c
+CONFIG_L2_PACKET=linux
+NEED_NETLINK=y
+NEED_LINUX_IOCTL=y
+endif
+
+##### PURE CLIENT DRIVERS
+
+ifdef CONFIG_DRIVER_WEXT
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_WEXT
+CONFIG_WIRELESS_EXTENSION=y
+NEED_NETLINK=y
+NEED_LINUX_IOCTL=y
+NEED_RFKILL=y
+endif
+
+ifdef CONFIG_DRIVER_RALINK
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_RALINK
+DRV_WPA_OBJS += src/drivers/driver_ralink.c
+NEED_NETLINK=y
+NEED_LINUX_IOCTL=y
+endif
+
+ifdef CONFIG_DRIVER_BROADCOM
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_BROADCOM
+DRV_WPA_OBJS += src/drivers/driver_broadcom.c
+endif
+
+ifdef CONFIG_DRIVER_NDIS
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_NDIS
+DRV_WPA_OBJS += src/drivers/driver_ndis.c
+ifdef CONFIG_NDIS_EVENTS_INTEGRATED
+DRV_WPA_OBJS += src/drivers/driver_ndis_.c
+endif
+ifndef CONFIG_L2_PACKET
+CONFIG_L2_PACKET=pcap
+endif
+CONFIG_WINPCAP=y
+ifdef CONFIG_USE_NDISUIO
+DRV_WPA_CFLAGS += -DCONFIG_USE_NDISUIO
+endif
+endif
+
+ifdef CONFIG_DRIVER_OSX
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_OSX
+DRV_WPA_OBJS += src/drivers/driver_osx.c
+DRV_WPA_LDFLAGS += -framework CoreFoundation
+DRV_WPA_LDFLAGS += -F/System/Library/PrivateFrameworks -framework Apple80211
+endif
+
+ifdef CONFIG_DRIVER_IPHONE
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_IPHONE
+DRV_WPA_OBJS += src/drivers/driver_iphone.c
+DRV_WPA_OBJS += src/drivers/MobileApple80211.c
+DRV_WPA_LDFLAGS += -framework CoreFoundation
+endif
+
+ifdef CONFIG_DRIVER_ROBOSWITCH
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_ROBOSWITCH
+DRV_WPA_OBJS += src/drivers/driver_roboswitch.c
+endif
+
+ifdef CONFIG_WIRELESS_EXTENSION
+DRV_WPA_CFLAGS += -DCONFIG_WIRELESS_EXTENSION
+DRV_WPA_OBJS += src/drivers/driver_wext.c
+NEED_RFKILL=y
+endif
+
+ifdef NEED_NETLINK
+DRV_OBJS += src/drivers/netlink.c
+endif
+
+ifdef NEED_LINUX_IOCTL
+DRV_OBJS += src/drivers/linux_ioctl.c
+endif
+
+ifdef NEED_RFKILL
+DRV_OBJS += src/drivers/rfkill.c
+endif
+
+ifdef CONFIG_DRIVER_CUSTOM
+DRV_CFLAGS += -DCONFIG_DRIVER_CUSTOM
+endif
+
+##### COMMON VARS
+DRV_BOTH_CFLAGS := $(DRV_CFLAGS) $(DRV_WPA_CFLAGS) $(DRV_AP_CFLAGS)
+DRV_WPA_CFLAGS += $(DRV_CFLAGS)
+DRV_AP_CFLAGS += $(DRV_CFLAGS)
+
+DRV_BOTH_LIBS := $(DRV_LIBS) $(DRV_WPA_LIBS) $(DRV_AP_LIBS)
+DRV_WPA_LIBS += $(DRV_LIBS)
+DRV_AP_LIBS += $(DRV_LIBS)
+
+DRV_BOTH_OBJS := $(DRV_OBJS) $(DRV_WPA_OBJS) $(DRV_AP_OBJS)
+DRV_WPA_OBJS += $(DRV_OBJS)
+DRV_AP_OBJS += $(DRV_OBJS)
+
+DRV_BOTH_LDFLAGS := $(DRV_LDFLAGS) $(DRV_WPA_LDFLAGS) $(DRV_AP_LDFLAGS)
+DRV_WPA_LDFLAGS += $(DRV_LDFLAGS)
+DRV_AP_LDFLAGS += $(DRV_LDFLAGS)
index 0d6cf54..d7501cf 100644 (file)
@@ -24,6 +24,7 @@
 int linux_set_iface_flags(int sock, const char *ifname, int dev_up)
 {
        struct ifreq ifr;
+       int ret;
 
        if (sock < 0)
                return -1;
@@ -32,9 +33,10 @@ int linux_set_iface_flags(int sock, const char *ifname, int dev_up)
        os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
 
        if (ioctl(sock, SIOCGIFFLAGS, &ifr) != 0) {
+               ret = errno ? -errno : -999;
                wpa_printf(MSG_ERROR, "Could not read interface %s flags: %s",
                           ifname, strerror(errno));
-               return -1;
+               return ret;
        }
 
        if (dev_up) {
@@ -48,15 +50,38 @@ int linux_set_iface_flags(int sock, const char *ifname, int dev_up)
        }
 
        if (ioctl(sock, SIOCSIFFLAGS, &ifr) != 0) {
+               ret = errno ? -errno : -999;
                wpa_printf(MSG_ERROR, "Could not set interface %s flags: %s",
                           ifname, strerror(errno));
-               return -1;
+               return ret;
        }
 
        return 0;
 }
 
 
+int linux_iface_up(int sock, const char *ifname)
+{
+       struct ifreq ifr;
+       int ret;
+
+       if (sock < 0)
+               return -1;
+
+       os_memset(&ifr, 0, sizeof(ifr));
+       os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+
+       if (ioctl(sock, SIOCGIFFLAGS, &ifr) != 0) {
+               ret = errno ? -errno : -999;
+               wpa_printf(MSG_ERROR, "Could not read interface %s flags: %s",
+                          ifname, strerror(errno));
+               return ret;
+       }
+
+       return !!(ifr.ifr_flags & IFF_UP);
+}
+
+
 int linux_get_ifhwaddr(int sock, const char *ifname, u8 *addr)
 {
        struct ifreq ifr;
index a555738..e0bf673 100644 (file)
@@ -16,6 +16,7 @@
 #define LINUX_IOCTL_H
 
 int linux_set_iface_flags(int sock, const char *ifname, int dev_up);
+int linux_iface_up(int sock, const char *ifname);
 int linux_get_ifhwaddr(int sock, const char *ifname, u8 *addr);
 int linux_set_ifhwaddr(int sock, const char *ifname, const u8 *addr);
 int linux_br_add(int sock, const char *brname);
index ad15b1d..6778907 100644 (file)
@@ -34,7 +34,7 @@ static void netlink_receive_link(struct netlink_data *netlink,
        if (cb == NULL || NLMSG_PAYLOAD(h, 0) < sizeof(struct ifinfomsg))
                return;
        cb(netlink->cfg->ctx, NLMSG_DATA(h),
-          NLMSG_DATA(h) + NLMSG_ALIGN(sizeof(struct ifinfomsg)),
+          (u8 *) NLMSG_DATA(h) + NLMSG_ALIGN(sizeof(struct ifinfomsg)),
           NLMSG_PAYLOAD(h, sizeof(struct ifinfomsg)));
 }
 
index bcbfbb5..ccf12a5 100644 (file)
@@ -16,6 +16,7 @@
 #define NETLINK_H
 
 struct netlink_data;
+struct ifinfomsg;
 
 struct netlink_config {
        void *ctx;
index 2ea3ede..acd6cfa 100644 (file)
@@ -6,7 +6,7 @@
  * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
  * Copyright 2008 Michael Wu <flamingice@sourmilk.net>
  * Copyright 2008 Luis Carlos Cobo <luisca@cozybit.com>
- * Copyright 2008 Michael Buesch <mb@bu3sch.de>
+ * Copyright 2008 Michael Buesch <m@bues.ch>
  * Copyright 2008, 2009 Luis R. Rodriguez <lrodriguez@atheros.com>
  * Copyright 2008 Jouni Malinen <jouni.malinen@atheros.com>
  * Copyright 2008 Colin McCabe <colin@cozybit.com>
  */
 
 /**
+ * DOC: Frame transmission/registration support
+ *
+ * Frame transmission and registration support exists to allow userspace
+ * management entities such as wpa_supplicant react to management frames
+ * that are not being handled by the kernel. This includes, for example,
+ * certain classes of action frames that cannot be handled in the kernel
+ * for various reasons.
+ *
+ * Frame registration is done on a per-interface basis and registrations
+ * cannot be removed other than by closing the socket. It is possible to
+ * specify a registration filter to register, for example, only for a
+ * certain type of action frame. In particular with action frames, those
+ * that userspace registers for will not be returned as unhandled by the
+ * driver, so that the registered application has to take responsibility
+ * for doing that.
+ *
+ * The type of frame that can be registered for is also dependent on the
+ * driver and interface type. The frame types are advertised in wiphy
+ * attributes so applications know what to expect.
+ *
+ * NOTE: When an interface changes type while registrations are active,
+ *       these registrations are ignored until the interface type is
+ *       changed again. This means that changing the interface type can
+ *       lead to a situation that couldn't otherwise be produced, but
+ *       any such registrations will be dormant in the sense that they
+ *       will not be serviced, i.e. they will not receive any frames.
+ *
+ * Frame transmission allows userspace to send for example the required
+ * responses to action frames. It is subject to some sanity checking,
+ * but many frames can be transmitted. When a frame was transmitted, its
+ * status is indicated to the sending socket.
+ *
+ * For more technical details, see the corresponding command descriptions
+ * below.
+ */
+
+/**
+ * DOC: Virtual interface / concurrency capabilities
+ *
+ * Some devices are able to operate with virtual MACs, they can have
+ * more than one virtual interface. The capability handling for this
+ * is a bit complex though, as there may be a number of restrictions
+ * on the types of concurrency that are supported.
+ *
+ * To start with, each device supports the interface types listed in
+ * the %NL80211_ATTR_SUPPORTED_IFTYPES attribute, but by listing the
+ * types there no concurrency is implied.
+ *
+ * Once concurrency is desired, more attributes must be observed:
+ * To start with, since some interface types are purely managed in
+ * software, like the AP-VLAN type in mac80211 for example, there's
+ * an additional list of these, they can be added at any time and
+ * are only restricted by some semantic restrictions (e.g. AP-VLAN
+ * cannot be added without a corresponding AP interface). This list
+ * is exported in the %NL80211_ATTR_SOFTWARE_IFTYPES attribute.
+ *
+ * Further, the list of supported combinations is exported. This is
+ * in the %NL80211_ATTR_INTERFACE_COMBINATIONS attribute. Basically,
+ * it exports a list of "groups", and at any point in time the
+ * interfaces that are currently active must fall into any one of
+ * the advertised groups. Within each group, there are restrictions
+ * on the number of interfaces of different types that are supported
+ * and also the number of different channels, along with potentially
+ * some other restrictions. See &enum nl80211_if_combination_attrs.
+ *
+ * All together, these attributes define the concurrency of virtual
+ * interfaces that a given device supports.
+ */
+
+/**
  * enum nl80211_commands - supported nl80211 commands
  *
  * @NL80211_CMD_UNSPEC: unspecified command to catch errors
  *     %NL80211_ATTR_WIPHY_CHANNEL_TYPE, %NL80211_ATTR_WIPHY_RETRY_SHORT,
  *     %NL80211_ATTR_WIPHY_RETRY_LONG, %NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
  *     and/or %NL80211_ATTR_WIPHY_RTS_THRESHOLD.
+ *     However, for setting the channel, see %NL80211_CMD_SET_CHANNEL
+ *     instead, the support here is for backward compatibility only.
  * @NL80211_CMD_NEW_WIPHY: Newly created wiphy, response to get request
  *     or rename notification. Has attributes %NL80211_ATTR_WIPHY and
  *     %NL80211_ATTR_WIPHY_NAME.
  * @NL80211_CMD_DEL_KEY: delete a key identified by %NL80211_ATTR_KEY_IDX
  *     or %NL80211_ATTR_MAC.
  *
- * @NL80211_CMD_GET_BEACON: retrieve beacon information (returned in a
- *     %NL80222_CMD_NEW_BEACON message)
- * @NL80211_CMD_SET_BEACON: set the beacon on an access point interface
- *     using the %NL80211_ATTR_BEACON_INTERVAL, %NL80211_ATTR_DTIM_PERIOD,
- *     %NL80211_ATTR_BEACON_HEAD and %NL80211_ATTR_BEACON_TAIL attributes.
- * @NL80211_CMD_NEW_BEACON: add a new beacon to an access point interface,
- *     parameters are like for %NL80211_CMD_SET_BEACON.
- * @NL80211_CMD_DEL_BEACON: remove the beacon, stop sending it
+ * @NL80211_CMD_GET_BEACON: (not used)
+ * @NL80211_CMD_SET_BEACON: change the beacon on an access point interface
+ *     using the %NL80211_ATTR_BEACON_HEAD and %NL80211_ATTR_BEACON_TAIL
+ *     attributes. For drivers that generate the beacon and probe responses
+ *     internally, the following attributes must be provided: %NL80211_ATTR_IE,
+ *     %NL80211_ATTR_IE_PROBE_RESP and %NL80211_ATTR_IE_ASSOC_RESP.
+ * @NL80211_CMD_START_AP: Start AP operation on an AP interface, parameters
+ *     are like for %NL80211_CMD_SET_BEACON, and additionally parameters that
+ *     do not change are used, these include %NL80211_ATTR_BEACON_INTERVAL,
+ *     %NL80211_ATTR_DTIM_PERIOD, %NL80211_ATTR_SSID,
+ *     %NL80211_ATTR_HIDDEN_SSID, %NL80211_ATTR_CIPHERS_PAIRWISE,
+ *     %NL80211_ATTR_CIPHER_GROUP, %NL80211_ATTR_WPA_VERSIONS,
+ *     %NL80211_ATTR_AKM_SUITES, %NL80211_ATTR_PRIVACY,
+ *     %NL80211_ATTR_AUTH_TYPE and %NL80211_ATTR_INACTIVITY_TIMEOUT.
+ * @NL80211_CMD_NEW_BEACON: old alias for %NL80211_CMD_START_AP
+ * @NL80211_CMD_STOP_AP: Stop AP operation on the given interface
+ * @NL80211_CMD_DEL_BEACON: old alias for %NL80211_CMD_STOP_AP
  *
  * @NL80211_CMD_GET_STATION: Get station attributes for station identified by
  *     %NL80211_ATTR_MAC on the interface identified by %NL80211_ATTR_IFINDEX.
  * @NL80211_CMD_SET_MPATH:  Set mesh path attributes for mesh path to
  *     destination %NL80211_ATTR_MAC on the interface identified by
  *     %NL80211_ATTR_IFINDEX.
+ * @NL80211_CMD_NEW_MPATH: Create a new mesh path for the destination given by
+ *     %NL80211_ATTR_MAC via %NL80211_ATTR_MPATH_NEXT_HOP.
+ * @NL80211_CMD_DEL_MPATH: Delete a mesh path to the destination given by
+ *     %NL80211_ATTR_MAC.
  * @NL80211_CMD_NEW_PATH: Add a mesh path with given attributes to the
  *     the interface identified by %NL80211_ATTR_IFINDEX.
  * @NL80211_CMD_DEL_PATH: Remove a mesh path identified by %NL80211_ATTR_MAC
  *     %NL80211_ATTR_REG_RULE_POWER_MAX_ANT_GAIN and
  *     %NL80211_ATTR_REG_RULE_POWER_MAX_EIRP.
  * @NL80211_CMD_REQ_SET_REG: ask the wireless core to set the regulatory domain
- *     to the the specified ISO/IEC 3166-1 alpha2 country code. The core will
+ *     to the specified ISO/IEC 3166-1 alpha2 country code. The core will
  *     store this as a valid request and then query userspace for it.
  *
- * @NL80211_CMD_GET_MESH_PARAMS: Get mesh networking properties for the
+ * @NL80211_CMD_GET_MESH_CONFIG: Get mesh networking properties for the
  *     interface identified by %NL80211_ATTR_IFINDEX
  *
- * @NL80211_CMD_SET_MESH_PARAMS: Set mesh networking properties for the
+ * @NL80211_CMD_SET_MESH_CONFIG: Set mesh networking properties for the
  *      interface identified by %NL80211_ATTR_IFINDEX
  *
  * @NL80211_CMD_SET_MGMT_EXTRA_IE: Set extra IEs for management frames. The
  *
  * @NL80211_CMD_GET_SCAN: get scan results
  * @NL80211_CMD_TRIGGER_SCAN: trigger a new scan with the given parameters
+ *     %NL80211_ATTR_TX_NO_CCK_RATE is used to decide whether to send the
+ *     probe requests at CCK rate or not.
  * @NL80211_CMD_NEW_SCAN_RESULTS: scan notification (as a reply to
  *     NL80211_CMD_GET_SCAN and on the "scan" multicast group)
  * @NL80211_CMD_SCAN_ABORTED: scan was aborted, for unspecified reasons,
  *     partial scan results may be available
  *
+ * @NL80211_CMD_START_SCHED_SCAN: start a scheduled scan at certain
+ *     intervals, as specified by %NL80211_ATTR_SCHED_SCAN_INTERVAL.
+ *     Like with normal scans, if SSIDs (%NL80211_ATTR_SCAN_SSIDS)
+ *     are passed, they are used in the probe requests.  For
+ *     broadcast, a broadcast SSID must be passed (ie. an empty
+ *     string).  If no SSID is passed, no probe requests are sent and
+ *     a passive scan is performed.  %NL80211_ATTR_SCAN_FREQUENCIES,
+ *     if passed, define which channels should be scanned; if not
+ *     passed, all channels allowed for the current regulatory domain
+ *     are used.  Extra IEs can also be passed from the userspace by
+ *     using the %NL80211_ATTR_IE attribute.
+ * @NL80211_CMD_STOP_SCHED_SCAN: stop a scheduled scan.  Returns -ENOENT
+ *     if scheduled scan is not running.
+ * @NL80211_CMD_SCHED_SCAN_RESULTS: indicates that there are scheduled scan
+ *     results available.
+ * @NL80211_CMD_SCHED_SCAN_STOPPED: indicates that the scheduled scan has
+ *     stopped.  The driver may issue this event at any time during a
+ *     scheduled scan.  One reason for stopping the scan is if the hardware
+ *     does not support starting an association or a normal scan while running
+ *     a scheduled scan.  This event is also sent when the
+ *     %NL80211_CMD_STOP_SCHED_SCAN command is received or when the interface
+ *     is brought down while a scheduled scan was running.
+ *
  * @NL80211_CMD_GET_SURVEY: get survey resuls, e.g. channel occupation
  *      or noise level
  * @NL80211_CMD_NEW_SURVEY_RESULTS: survey data notification (as a reply to
  *     auth and assoc steps. For this, you need to specify the SSID in a
  *     %NL80211_ATTR_SSID attribute, and can optionally specify the association
  *     IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_MAC,
- *     %NL80211_ATTR_WIPHY_FREQ and %NL80211_ATTR_CONTROL_PORT.
+ *     %NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT,
+ *     %NL80211_ATTR_CONTROL_PORT_ETHERTYPE and
+ *     %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT.
+ *     Background scan period can optionally be
+ *     specified in %NL80211_ATTR_BG_SCAN_PERIOD,
+ *     if not specified default background scan configuration
+ *     in driver is used and if period value is 0, bg scan will be disabled.
+ *     This attribute is ignored if driver does not support roam scan.
  *     It is also sent as an event, with the BSSID and response IEs when the
  *     connection is established or failed to be established. This can be
  *     determined by the STATUS_CODE attribute.
  *     channel for the specified amount of time. This can be used to do
  *     off-channel operations like transmit a Public Action frame and wait for
  *     a response while being associated to an AP on another channel.
- *     %NL80211_ATTR_WIPHY or %NL80211_ATTR_IFINDEX is used to specify which
- *     radio is used. %NL80211_ATTR_WIPHY_FREQ is used to specify the
+ *     %NL80211_ATTR_IFINDEX is used to specify which interface (and thus
+ *     radio) is used. %NL80211_ATTR_WIPHY_FREQ is used to specify the
  *     frequency for the operation and %NL80211_ATTR_WIPHY_CHANNEL_TYPE may be
  *     optionally used to specify additional channel parameters.
  *     %NL80211_ATTR_DURATION is used to specify the duration in milliseconds
  *     rate selection. %NL80211_ATTR_IFINDEX is used to specify the interface
  *     and @NL80211_ATTR_TX_RATES the set of allowed rates.
  *
- * @NL80211_CMD_REGISTER_ACTION: Register for receiving certain action frames
- *     (via @NL80211_CMD_ACTION) for processing in userspace. This command
- *     requires an interface index and a match attribute containing the first
- *     few bytes of the frame that should match, e.g. a single byte for only
- *     a category match or four bytes for vendor frames including the OUI.
- *     The registration cannot be dropped, but is removed automatically
- *     when the netlink socket is closed. Multiple registrations can be made.
- * @NL80211_CMD_ACTION: Action frame TX request and RX notification. This
- *     command is used both as a request to transmit an Action frame and as an
- *     event indicating reception of an Action frame that was not processed in
+ * @NL80211_CMD_REGISTER_FRAME: Register for receiving certain mgmt frames
+ *     (via @NL80211_CMD_FRAME) for processing in userspace. This command
+ *     requires an interface index, a frame type attribute (optional for
+ *     backward compatibility reasons, if not given assumes action frames)
+ *     and a match attribute containing the first few bytes of the frame
+ *     that should match, e.g. a single byte for only a category match or
+ *     four bytes for vendor frames including the OUI. The registration
+ *     cannot be dropped, but is removed automatically when the netlink
+ *     socket is closed. Multiple registrations can be made.
+ * @NL80211_CMD_REGISTER_ACTION: Alias for @NL80211_CMD_REGISTER_FRAME for
+ *     backward compatibility
+ * @NL80211_CMD_FRAME: Management frame TX request and RX notification. This
+ *     command is used both as a request to transmit a management frame and
+ *     as an event indicating reception of a frame that was not processed in
  *     kernel code, but is for us (i.e., which may need to be processed in a
  *     user space application). %NL80211_ATTR_FRAME is used to specify the
  *     frame contents (including header). %NL80211_ATTR_WIPHY_FREQ (and
  *     optionally %NL80211_ATTR_WIPHY_CHANNEL_TYPE) is used to indicate on
- *     which channel the frame is to be transmitted or was received. This
- *     channel has to be the current channel (remain-on-channel or the
- *     operational channel). When called, this operation returns a cookie
- *     (%NL80211_ATTR_COOKIE) that will be included with the TX status event
- *     pertaining to the TX request.
- * @NL80211_CMD_ACTION_TX_STATUS: Report TX status of an Action frame
- *     transmitted with %NL80211_CMD_ACTION. %NL80211_ATTR_COOKIE identifies
+ *     which channel the frame is to be transmitted or was received. If this
+ *     channel is not the current channel (remain-on-channel or the
+ *     operational channel) the device will switch to the given channel and
+ *     transmit the frame, optionally waiting for a response for the time
+ *     specified using %NL80211_ATTR_DURATION. When called, this operation
+ *     returns a cookie (%NL80211_ATTR_COOKIE) that will be included with the
+ *     TX status event pertaining to the TX request.
+ *     %NL80211_ATTR_TX_NO_CCK_RATE is used to decide whether to send the
+ *     management frames at CCK rate or not in 2GHz band.
+ * @NL80211_CMD_FRAME_WAIT_CANCEL: When an off-channel TX was requested, this
+ *     command may be used with the corresponding cookie to cancel the wait
+ *     time if it is known that it is no longer necessary.
+ * @NL80211_CMD_ACTION: Alias for @NL80211_CMD_FRAME for backward compatibility.
+ * @NL80211_CMD_FRAME_TX_STATUS: Report TX status of a management frame
+ *     transmitted with %NL80211_CMD_FRAME. %NL80211_ATTR_COOKIE identifies
  *     the TX command and %NL80211_ATTR_FRAME includes the contents of the
  *     frame. %NL80211_ATTR_ACK flag is included if the recipient acknowledged
  *     the frame.
+ * @NL80211_CMD_ACTION_TX_STATUS: Alias for @NL80211_CMD_FRAME_TX_STATUS for
+ *     backward compatibility.
  * @NL80211_CMD_SET_CQM: Connection quality monitor configuration. This command
  *     is used to configure connection quality monitoring notification trigger
  *     levels.
  * @NL80211_CMD_NOTIFY_CQM: Connection quality monitor notification. This
  *     command is used as an event to indicate the that a trigger level was
  *     reached.
+ * @NL80211_CMD_SET_CHANNEL: Set the channel (using %NL80211_ATTR_WIPHY_FREQ
+ *     and %NL80211_ATTR_WIPHY_CHANNEL_TYPE) the given interface (identifed
+ *     by %NL80211_ATTR_IFINDEX) shall operate on.
+ *     In case multiple channels are supported by the device, the mechanism
+ *     with which it switches channels is implementation-defined.
+ *     When a monitor interface is given, it can only switch channel while
+ *     no other interfaces are operating to avoid disturbing the operation
+ *     of any other interfaces, and other interfaces will again take
+ *     precedence when they are used.
+ *
+ * @NL80211_CMD_SET_WDS_PEER: Set the MAC address of the peer on a WDS interface.
+ *
+ * @NL80211_CMD_JOIN_MESH: Join a mesh. The mesh ID must be given, and initial
+ *     mesh config parameters may be given.
+ * @NL80211_CMD_LEAVE_MESH: Leave the mesh network -- no special arguments, the
+ *     network is determined by the network interface.
+ *
+ * @NL80211_CMD_UNPROT_DEAUTHENTICATE: Unprotected deauthentication frame
+ *     notification. This event is used to indicate that an unprotected
+ *     deauthentication frame was dropped when MFP is in use.
+ * @NL80211_CMD_UNPROT_DISASSOCIATE: Unprotected disassociation frame
+ *     notification. This event is used to indicate that an unprotected
+ *     disassociation frame was dropped when MFP is in use.
+ *
+ * @NL80211_CMD_NEW_PEER_CANDIDATE: Notification on the reception of a
+ *      beacon or probe response from a compatible mesh peer.  This is only
+ *      sent while no station information (sta_info) exists for the new peer
+ *      candidate and when @NL80211_MESH_SETUP_USERSPACE_AUTH is set.  On
+ *      reception of this notification, userspace may decide to create a new
+ *      station (@NL80211_CMD_NEW_STATION).  To stop this notification from
+ *      reoccurring, the userspace authentication daemon may want to create the
+ *      new station with the AUTHENTICATED flag unset and maybe change it later
+ *      depending on the authentication result.
+ *
+ * @NL80211_CMD_GET_WOWLAN: get Wake-on-Wireless-LAN (WoWLAN) settings.
+ * @NL80211_CMD_SET_WOWLAN: set Wake-on-Wireless-LAN (WoWLAN) settings.
+ *     Since wireless is more complex than wired ethernet, it supports
+ *     various triggers. These triggers can be configured through this
+ *     command with the %NL80211_ATTR_WOWLAN_TRIGGERS attribute. For
+ *     more background information, see
+ *     http://wireless.kernel.org/en/users/Documentation/WoWLAN.
+ *
+ * @NL80211_CMD_SET_REKEY_OFFLOAD: This command is used give the driver
+ *     the necessary information for supporting GTK rekey offload. This
+ *     feature is typically used during WoWLAN. The configuration data
+ *     is contained in %NL80211_ATTR_REKEY_DATA (which is nested and
+ *     contains the data in sub-attributes). After rekeying happened,
+ *     this command may also be sent by the driver as an MLME event to
+ *     inform userspace of the new replay counter.
+ *
+ * @NL80211_CMD_PMKSA_CANDIDATE: This is used as an event to inform userspace
+ *     of PMKSA caching dandidates.
+ *
+ * @NL80211_CMD_TDLS_OPER: Perform a high-level TDLS command (e.g. link setup).
+ * @NL80211_CMD_TDLS_MGMT: Send a TDLS management frame.
  *
  * @NL80211_CMD_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  */
 enum nl80211_commands {
-/* don't change the order or add anything inbetween, this is ABI! */
+/* don't change the order or add anything between, this is ABI! */
        NL80211_CMD_UNSPEC,
 
        NL80211_CMD_GET_WIPHY,          /* can dump */
@@ -354,8 +540,10 @@ enum nl80211_commands {
 
        NL80211_CMD_GET_BEACON,
        NL80211_CMD_SET_BEACON,
-       NL80211_CMD_NEW_BEACON,
-       NL80211_CMD_DEL_BEACON,
+       NL80211_CMD_START_AP,
+       NL80211_CMD_NEW_BEACON = NL80211_CMD_START_AP,
+       NL80211_CMD_STOP_AP,
+       NL80211_CMD_DEL_BEACON = NL80211_CMD_STOP_AP,
 
        NL80211_CMD_GET_STATION,
        NL80211_CMD_SET_STATION,
@@ -372,8 +560,8 @@ enum nl80211_commands {
        NL80211_CMD_SET_REG,
        NL80211_CMD_REQ_SET_REG,
 
-       NL80211_CMD_GET_MESH_PARAMS,
-       NL80211_CMD_SET_MESH_PARAMS,
+       NL80211_CMD_GET_MESH_CONFIG,
+       NL80211_CMD_SET_MESH_CONFIG,
 
        NL80211_CMD_SET_MGMT_EXTRA_IE /* reserved; not used */,
 
@@ -418,9 +606,12 @@ enum nl80211_commands {
 
        NL80211_CMD_SET_TX_BITRATE_MASK,
 
-       NL80211_CMD_REGISTER_ACTION,
-       NL80211_CMD_ACTION,
-       NL80211_CMD_ACTION_TX_STATUS,
+       NL80211_CMD_REGISTER_FRAME,
+       NL80211_CMD_REGISTER_ACTION = NL80211_CMD_REGISTER_FRAME,
+       NL80211_CMD_FRAME,
+       NL80211_CMD_ACTION = NL80211_CMD_FRAME,
+       NL80211_CMD_FRAME_TX_STATUS,
+       NL80211_CMD_ACTION_TX_STATUS = NL80211_CMD_FRAME_TX_STATUS,
 
        NL80211_CMD_SET_POWER_SAVE,
        NL80211_CMD_GET_POWER_SAVE,
@@ -428,6 +619,34 @@ enum nl80211_commands {
        NL80211_CMD_SET_CQM,
        NL80211_CMD_NOTIFY_CQM,
 
+       NL80211_CMD_SET_CHANNEL,
+       NL80211_CMD_SET_WDS_PEER,
+
+       NL80211_CMD_FRAME_WAIT_CANCEL,
+
+       NL80211_CMD_JOIN_MESH,
+       NL80211_CMD_LEAVE_MESH,
+
+       NL80211_CMD_UNPROT_DEAUTHENTICATE,
+       NL80211_CMD_UNPROT_DISASSOCIATE,
+
+       NL80211_CMD_NEW_PEER_CANDIDATE,
+
+       NL80211_CMD_GET_WOWLAN,
+       NL80211_CMD_SET_WOWLAN,
+
+       NL80211_CMD_START_SCHED_SCAN,
+       NL80211_CMD_STOP_SCHED_SCAN,
+       NL80211_CMD_SCHED_SCAN_RESULTS,
+       NL80211_CMD_SCHED_SCAN_STOPPED,
+
+       NL80211_CMD_SET_REKEY_OFFLOAD,
+
+       NL80211_CMD_PMKSA_CANDIDATE,
+
+       NL80211_CMD_TDLS_OPER,
+       NL80211_CMD_TDLS_MGMT,
+
        /* add new commands above here */
 
        /* used to define NL80211_CMD_MAX below */
@@ -448,6 +667,11 @@ enum nl80211_commands {
 #define NL80211_CMD_DISASSOCIATE NL80211_CMD_DISASSOCIATE
 #define NL80211_CMD_REG_BEACON_HINT NL80211_CMD_REG_BEACON_HINT
 
+/* source-level API compatibility */
+#define NL80211_CMD_GET_MESH_PARAMS NL80211_CMD_GET_MESH_CONFIG
+#define NL80211_CMD_SET_MESH_PARAMS NL80211_CMD_SET_MESH_CONFIG
+#define NL80211_MESH_SETUP_VENDOR_PATH_SEL_IE NL80211_MESH_SETUP_IE
+
 /**
  * enum nl80211_attrs - nl80211 netlink attributes
  *
@@ -518,7 +742,7 @@ enum nl80211_commands {
  *     consisting of a nested array.
  *
  * @NL80211_ATTR_MESH_ID: mesh id (1-32 bytes).
- * @NL80211_ATTR_PLINK_ACTION: action to perform on the mesh peer link.
+ * @NL80211_ATTR_STA_PLINK_ACTION: action to perform on the mesh peer link.
  * @NL80211_ATTR_MPATH_NEXT_HOP: MAC address of the next hop for a mesh path.
  * @NL80211_ATTR_MPATH_INFO: information about a mesh_path, part of mesh path
  *     info given for %NL80211_CMD_GET_MPATH, nested attribute described at
@@ -563,8 +787,14 @@ enum nl80211_commands {
  *
  * @NL80211_ATTR_MAX_NUM_SCAN_SSIDS: number of SSIDs you can scan with
  *     a single scan request, a wiphy attribute.
+ * @NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS: number of SSIDs you can
+ *     scan with a single scheduled scan request, a wiphy attribute.
  * @NL80211_ATTR_MAX_SCAN_IE_LEN: maximum length of information elements
  *     that can be added to a scan request
+ * @NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN: maximum length of information
+ *     elements that can be added to a scheduled scan request
+ * @NL80211_ATTR_MAX_MATCH_SETS: maximum number of sets that can be
+ *     used with @NL80211_ATTR_SCHED_SCAN_MATCH, a wiphy attribute.
  *
  * @NL80211_ATTR_SCAN_FREQUENCIES: nested attribute with frequencies (in MHz)
  * @NL80211_ATTR_SCAN_SSIDS: nested attribute with SSIDs, leave out for passive
@@ -626,6 +856,15 @@ enum nl80211_commands {
  *     request, the driver will assume that the port is unauthorized until
  *     authorized by user space. Otherwise, port is marked authorized by
  *     default in station mode.
+ * @NL80211_ATTR_CONTROL_PORT_ETHERTYPE: A 16-bit value indicating the
+ *     ethertype that will be used for key negotiation. It can be
+ *     specified with the associate and connect commands. If it is not
+ *     specified, the value defaults to 0x888E (PAE, 802.1X). This
+ *     attribute is also used as a flag in the wiphy information to
+ *     indicate that protocols other than PAE are supported.
+ * @NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT: When included along with
+ *     %NL80211_ATTR_CONTROL_PORT_ETHERTYPE, indicates that the custom
+ *     ethertype frames used for key negotiation must not be encrypted.
  *
  * @NL80211_ATTR_TESTDATA: Testmode data blob, passed through to the driver.
  *     We recommend using nested, driver-specific attributes within this.
@@ -636,18 +875,20 @@ enum nl80211_commands {
  * @NL80211_ATTR_STATUS_CODE: StatusCode for the %NL80211_CMD_CONNECT
  *     event (u16)
  * @NL80211_ATTR_PRIVACY: Flag attribute, used with connect(), indicating
- *     that protected APs should be used.
+ *     that protected APs should be used. This is also used with NEW_BEACON to
+ *     indicate that the BSS is to use protection.
  *
- * @NL80211_ATTR_CIPHERS_PAIRWISE: Used with CONNECT and ASSOCIATE to
- *     indicate which unicast key ciphers will be used with the connection
+ * @NL80211_ATTR_CIPHERS_PAIRWISE: Used with CONNECT, ASSOCIATE, and NEW_BEACON
+ *     to indicate which unicast key ciphers will be used with the connection
  *     (an array of u32).
- * @NL80211_ATTR_CIPHER_GROUP: Used with CONNECT and ASSOCIATE to indicate
- *     which group key cipher will be used with the connection (a u32).
- * @NL80211_ATTR_WPA_VERSIONS: Used with CONNECT and ASSOCIATE to indicate
- *     which WPA version(s) the AP we want to associate with is using
+ * @NL80211_ATTR_CIPHER_GROUP: Used with CONNECT, ASSOCIATE, and NEW_BEACON to
+ *     indicate which group key cipher will be used with the connection (a
+ *     u32).
+ * @NL80211_ATTR_WPA_VERSIONS: Used with CONNECT, ASSOCIATE, and NEW_BEACON to
+ *     indicate which WPA version(s) the AP we want to associate with is using
  *     (a u32 with flags from &enum nl80211_wpa_versions).
- * @NL80211_ATTR_AKM_SUITES: Used with CONNECT and ASSOCIATE to indicate
- *     which key management algorithm(s) to use (an array of u32).
+ * @NL80211_ATTR_AKM_SUITES: Used with CONNECT, ASSOCIATE, and NEW_BEACON to
+ *     indicate which key management algorithm(s) to use (an array of u32).
  *
  * @NL80211_ATTR_REQ_IE: (Re)association request information elements as
  *     sent out by the card, for ROAM and successful CONNECT events.
@@ -684,6 +925,9 @@ enum nl80211_commands {
  *     cache, a wiphy attribute.
  *
  * @NL80211_ATTR_DURATION: Duration of an operation in milliseconds, u32.
+ * @NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION: Device attribute that
+ *     specifies the maximum duration that can be requested with the
+ *     remain-on-channel operation, in milliseconds, u32.
  *
  * @NL80211_ATTR_COOKIE: Generic 64-bit cookie to identify objects.
  *
@@ -695,7 +939,16 @@ enum nl80211_commands {
  *     is used with %NL80211_CMD_SET_TX_BITRATE_MASK.
  *
  * @NL80211_ATTR_FRAME_MATCH: A binary attribute which typically must contain
- *     at least one byte, currently used with @NL80211_CMD_REGISTER_ACTION.
+ *     at least one byte, currently used with @NL80211_CMD_REGISTER_FRAME.
+ * @NL80211_ATTR_FRAME_TYPE: A u16 indicating the frame type/subtype for the
+ *     @NL80211_CMD_REGISTER_FRAME command.
+ * @NL80211_ATTR_TX_FRAME_TYPES: wiphy capability attribute, which is a
+ *     nested attribute of %NL80211_ATTR_FRAME_TYPE attributes, containing
+ *     information about which frame types can be transmitted with
+ *     %NL80211_CMD_FRAME.
+ * @NL80211_ATTR_RX_FRAME_TYPES: wiphy capability attribute, which is a
+ *     nested attribute of %NL80211_ATTR_FRAME_TYPE attributes, containing
+ *     information about which frame types can be registered for RX.
  *
  * @NL80211_ATTR_ACK: Flag attribute indicating that the frame was
  *     acknowledged by the recipient.
@@ -709,11 +962,220 @@ enum nl80211_commands {
  *     NL80211_CMD_AUTHENTICATE, NL80211_CMD_DEAUTHENTICATE,
  *     NL80211_CMD_DISASSOCIATE.
  *
+ * @NL80211_ATTR_AP_ISOLATE: (AP mode) Do not forward traffic between stations
+ *     connected to this BSS.
+ *
+ * @NL80211_ATTR_WIPHY_TX_POWER_SETTING: Transmit power setting type. See
+ *      &enum nl80211_tx_power_setting for possible values.
+ * @NL80211_ATTR_WIPHY_TX_POWER_LEVEL: Transmit power level in signed mBm units.
+ *      This is used in association with @NL80211_ATTR_WIPHY_TX_POWER_SETTING
+ *      for non-automatic settings.
+ *
+ * @NL80211_ATTR_SUPPORT_IBSS_RSN: The device supports IBSS RSN, which mostly
+ *     means support for per-station GTKs.
+ *
+ * @NL80211_ATTR_WIPHY_ANTENNA_TX: Bitmap of allowed antennas for transmitting.
+ *     This can be used to mask out antennas which are not attached or should
+ *     not be used for transmitting. If an antenna is not selected in this
+ *     bitmap the hardware is not allowed to transmit on this antenna.
+ *
+ *     Each bit represents one antenna, starting with antenna 1 at the first
+ *     bit. Depending on which antennas are selected in the bitmap, 802.11n
+ *     drivers can derive which chainmasks to use (if all antennas belonging to
+ *     a particular chain are disabled this chain should be disabled) and if
+ *     a chain has diversity antennas wether diversity should be used or not.
+ *     HT capabilities (STBC, TX Beamforming, Antenna selection) can be
+ *     derived from the available chains after applying the antenna mask.
+ *     Non-802.11n drivers can derive wether to use diversity or not.
+ *     Drivers may reject configurations or RX/TX mask combinations they cannot
+ *     support by returning -EINVAL.
+ *
+ * @NL80211_ATTR_WIPHY_ANTENNA_RX: Bitmap of allowed antennas for receiving.
+ *     This can be used to mask out antennas which are not attached or should
+ *     not be used for receiving. If an antenna is not selected in this bitmap
+ *     the hardware should not be configured to receive on this antenna.
+ *     For a more detailed description see @NL80211_ATTR_WIPHY_ANTENNA_TX.
+ *
+ * @NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX: Bitmap of antennas which are available
+ *     for configuration as TX antennas via the above parameters.
+ *
+ * @NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX: Bitmap of antennas which are available
+ *     for configuration as RX antennas via the above parameters.
+ *
+ * @NL80211_ATTR_MCAST_RATE: Multicast tx rate (in 100 kbps) for IBSS
+ *
+ * @NL80211_ATTR_OFFCHANNEL_TX_OK: For management frame TX, the frame may be
+ *     transmitted on another channel when the channel given doesn't match
+ *     the current channel. If the current channel doesn't match and this
+ *     flag isn't set, the frame will be rejected. This is also used as an
+ *     nl80211 capability flag.
+ *
+ * @NL80211_ATTR_BSS_HTOPMODE: HT operation mode (u16)
+ *
+ * @NL80211_ATTR_KEY_DEFAULT_TYPES: A nested attribute containing flags
+ *     attributes, specifying what a key should be set as default as.
+ *     See &enum nl80211_key_default_types.
+ *
+ * @NL80211_ATTR_MESH_SETUP: Optional mesh setup parameters.  These cannot be
+ *     changed once the mesh is active.
+ * @NL80211_ATTR_MESH_CONFIG: Mesh configuration parameters, a nested attribute
+ *     containing attributes from &enum nl80211_meshconf_params.
+ * @NL80211_ATTR_SUPPORT_MESH_AUTH: Currently, this means the underlying driver
+ *     allows auth frames in a mesh to be passed to userspace for processing via
+ *     the @NL80211_MESH_SETUP_USERSPACE_AUTH flag.
+ * @NL80211_ATTR_STA_PLINK_STATE: The state of a mesh peer link as
+ *     defined in &enum nl80211_plink_state. Used when userspace is
+ *     driving the peer link management state machine.
+ *     @NL80211_MESH_SETUP_USERSPACE_AMPE must be enabled.
+ *
+ * @NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED: indicates, as part of the wiphy
+ *     capabilities, the supported WoWLAN triggers
+ * @NL80211_ATTR_WOWLAN_TRIGGERS: used by %NL80211_CMD_SET_WOWLAN to
+ *     indicate which WoW triggers should be enabled. This is also
+ *     used by %NL80211_CMD_GET_WOWLAN to get the currently enabled WoWLAN
+ *     triggers.
+
+ * @NL80211_ATTR_SCHED_SCAN_INTERVAL: Interval between scheduled scan
+ *     cycles, in msecs.
+
+ * @NL80211_ATTR_SCHED_SCAN_MATCH: Nested attribute with one or more
+ *     sets of attributes to match during scheduled scans.  Only BSSs
+ *     that match any of the sets will be reported.  These are
+ *     pass-thru filter rules.
+ *     For a match to succeed, the BSS must match all attributes of a
+ *     set.  Since not every hardware supports matching all types of
+ *     attributes, there is no guarantee that the reported BSSs are
+ *     fully complying with the match sets and userspace needs to be
+ *     able to ignore them by itself.
+ *     Thus, the implementation is somewhat hardware-dependent, but
+ *     this is only an optimization and the userspace application
+ *     needs to handle all the non-filtered results anyway.
+ *     If the match attributes don't make sense when combined with
+ *     the values passed in @NL80211_ATTR_SCAN_SSIDS (eg. if an SSID
+ *     is included in the probe request, but the match attributes
+ *     will never let it go through), -EINVAL may be returned.
+ *     If ommited, no filtering is done.
+ *
+ * @NL80211_ATTR_INTERFACE_COMBINATIONS: Nested attribute listing the supported
+ *     interface combinations. In each nested item, it contains attributes
+ *     defined in &enum nl80211_if_combination_attrs.
+ * @NL80211_ATTR_SOFTWARE_IFTYPES: Nested attribute (just like
+ *     %NL80211_ATTR_SUPPORTED_IFTYPES) containing the interface types that
+ *     are managed in software: interfaces of these types aren't subject to
+ *     any restrictions in their number or combinations.
+ *
+ * @%NL80211_ATTR_REKEY_DATA: nested attribute containing the information
+ *     necessary for GTK rekeying in the device, see &enum nl80211_rekey_data.
+ *
+ * @NL80211_ATTR_SCAN_SUPP_RATES: rates per to be advertised as supported in scan,
+ *     nested array attribute containing an entry for each band, with the entry
+ *     being a list of supported rates as defined by IEEE 802.11 7.3.2.2 but
+ *     without the length restriction (at most %NL80211_MAX_SUPP_RATES).
+ *
+ * @NL80211_ATTR_HIDDEN_SSID: indicates whether SSID is to be hidden from Beacon
+ *     and Probe Response (when response to wildcard Probe Request); see
+ *     &enum nl80211_hidden_ssid, represented as a u32
+ *
+ * @NL80211_ATTR_IE_PROBE_RESP: Information element(s) for Probe Response frame.
+ *     This is used with %NL80211_CMD_NEW_BEACON and %NL80211_CMD_SET_BEACON to
+ *     provide extra IEs (e.g., WPS/P2P IE) into Probe Response frames when the
+ *     driver (or firmware) replies to Probe Request frames.
+ * @NL80211_ATTR_IE_ASSOC_RESP: Information element(s) for (Re)Association
+ *     Response frames. This is used with %NL80211_CMD_NEW_BEACON and
+ *     %NL80211_CMD_SET_BEACON to provide extra IEs (e.g., WPS/P2P IE) into
+ *     (Re)Association Response frames when the driver (or firmware) replies to
+ *     (Re)Association Request frames.
+ *
+ * @NL80211_ATTR_STA_WME: Nested attribute containing the wme configuration
+ *     of the station, see &enum nl80211_sta_wme_attr.
+ * @NL80211_ATTR_SUPPORT_AP_UAPSD: the device supports uapsd when working
+ *     as AP.
+ *
+ * @NL80211_ATTR_ROAM_SUPPORT: Indicates whether the firmware is capable of
+ *     roaming to another AP in the same ESS if the signal lever is low.
+ *
+ * @NL80211_ATTR_PMKSA_CANDIDATE: Nested attribute containing the PMKSA caching
+ *     candidate information, see &enum nl80211_pmksa_candidate_attr.
+ *
+ * @NL80211_ATTR_TX_NO_CCK_RATE: Indicates whether to use CCK rate or not
+ *     for management frames transmission. In order to avoid p2p probe/action
+ *     frames are being transmitted at CCK rate in 2GHz band, the user space
+ *     applications use this attribute.
+ *     This attribute is used with %NL80211_CMD_TRIGGER_SCAN and
+ *     %NL80211_CMD_FRAME commands.
+ *
+ * @NL80211_ATTR_TDLS_ACTION: Low level TDLS action code (e.g. link setup
+ *     request, link setup confirm, link teardown, etc.). Values are
+ *     described in the TDLS (802.11z) specification.
+ * @NL80211_ATTR_TDLS_DIALOG_TOKEN: Non-zero token for uniquely identifying a
+ *     TDLS conversation between two devices.
+ * @NL80211_ATTR_TDLS_OPERATION: High level TDLS operation; see
+ *     &enum nl80211_tdls_operation, represented as a u8.
+ * @NL80211_ATTR_TDLS_SUPPORT: A flag indicating the device can operate
+ *     as a TDLS peer sta.
+ * @NL80211_ATTR_TDLS_EXTERNAL_SETUP: The TDLS discovery/setup and teardown
+ *     procedures should be performed by sending TDLS packets via
+ *     %NL80211_CMD_TDLS_MGMT. Otherwise %NL80211_CMD_TDLS_OPER should be
+ *     used for asking the driver to perform a TDLS operation.
+ *
+ * @NL80211_ATTR_DEVICE_AP_SME: This u32 attribute may be listed for devices
+ *     that have AP support to indicate that they have the AP SME integrated
+ *     with support for the features listed in this attribute, see
+ *     &enum nl80211_ap_sme_features.
+ *
+ * @NL80211_ATTR_DONT_WAIT_FOR_ACK: Used with %NL80211_CMD_FRAME, this tells
+ *     the driver to not wait for an acknowledgement. Note that due to this,
+ *     it will also not give a status callback nor return a cookie. This is
+ *     mostly useful for probe responses to save airtime.
+ *
+ * @NL80211_ATTR_FEATURE_FLAGS: This u32 attribute contains flags from
+ *     &enum nl80211_feature_flags and is advertised in wiphy information.
+ * @NL80211_ATTR_PROBE_RESP_OFFLOAD: Indicates that the HW responds to probe
+ *
+ *     requests while operating in AP-mode.
+ *     This attribute holds a bitmap of the supported protocols for
+ *     offloading (see &enum nl80211_probe_resp_offload_support_attr).
+ *
+ * @NL80211_ATTR_PROBE_RESP: Probe Response template data. Contains the entire
+ *     probe-response frame. The DA field in the 802.11 header is zero-ed out,
+ *     to be filled by the FW.
+ * @NL80211_ATTR_DISABLE_HT:  Force HT capable interfaces to disable
+ *      this feature.  Currently, only supported in mac80211 drivers.
+ * @NL80211_ATTR_HT_CAPABILITY_MASK: Specify which bits of the
+ *      ATTR_HT_CAPABILITY to which attention should be paid.
+ *      Currently, only mac80211 NICs support this feature.
+ *      The values that may be configured are:
+ *       MCS rates, MAX-AMSDU, HT-20-40 and HT_CAP_SGI_40
+ *       AMPDU density and AMPDU factor.
+ *      All values are treated as suggestions and may be ignored
+ *      by the driver as required.  The actual values may be seen in
+ *      the station debugfs ht_caps file.
+ *
+ * @NL80211_ATTR_DFS_REGION: region for regulatory rules which this country
+ *    abides to when initiating radiation on DFS channels. A country maps
+ *    to one DFS region.
+ *
+ * @NL80211_ATTR_NOACK_MAP: This u16 bitmap contains the No Ack Policy of
+ *      up to 16 TIDs.
+ *
+ * @NL80211_ATTR_INACTIVITY_TIMEOUT: timeout value in seconds, this can be
+ *     used by the drivers which has MLME in firmware and does not have support
+ *     to report per station tx/rx activity to free up the staion entry from
+ *     the list. This needs to be used when the driver advertises the
+ *     capability to timeout the stations.
+ *
+ * @NL80211_ATTR_RX_SIGNAL_DBM: signal strength in dBm (as a 32-bit int);
+ *     this attribute is (depending on the driver capabilities) added to
+ *     received frames indicated with %NL80211_CMD_FRAME.
+ *
+ * @NL80211_ATTR_BG_SCAN_PERIOD: Background scan period in seconds
+ *      or 0 to disable background scan.
+ *
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
 enum nl80211_attrs {
-/* don't change the order or add anything inbetween, this is ABI! */
+/* don't change the order or add anything between, this is ABI! */
        NL80211_ATTR_UNSPEC,
 
        NL80211_ATTR_WIPHY,
@@ -763,7 +1225,7 @@ enum nl80211_attrs {
        NL80211_ATTR_REG_ALPHA2,
        NL80211_ATTR_REG_RULES,
 
-       NL80211_ATTR_MESH_PARAMS,
+       NL80211_ATTR_MESH_CONFIG,
 
        NL80211_ATTR_BSS_BASIC_RATES,
 
@@ -864,6 +1326,102 @@ enum nl80211_attrs {
 
        NL80211_ATTR_LOCAL_STATE_CHANGE,
 
+       NL80211_ATTR_AP_ISOLATE,
+
+       NL80211_ATTR_WIPHY_TX_POWER_SETTING,
+       NL80211_ATTR_WIPHY_TX_POWER_LEVEL,
+
+       NL80211_ATTR_TX_FRAME_TYPES,
+       NL80211_ATTR_RX_FRAME_TYPES,
+       NL80211_ATTR_FRAME_TYPE,
+
+       NL80211_ATTR_CONTROL_PORT_ETHERTYPE,
+       NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT,
+
+       NL80211_ATTR_SUPPORT_IBSS_RSN,
+
+       NL80211_ATTR_WIPHY_ANTENNA_TX,
+       NL80211_ATTR_WIPHY_ANTENNA_RX,
+
+       NL80211_ATTR_MCAST_RATE,
+
+       NL80211_ATTR_OFFCHANNEL_TX_OK,
+
+       NL80211_ATTR_BSS_HT_OPMODE,
+
+       NL80211_ATTR_KEY_DEFAULT_TYPES,
+
+       NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION,
+
+       NL80211_ATTR_MESH_SETUP,
+
+       NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX,
+       NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX,
+
+       NL80211_ATTR_SUPPORT_MESH_AUTH,
+       NL80211_ATTR_STA_PLINK_STATE,
+
+       NL80211_ATTR_WOWLAN_TRIGGERS,
+       NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED,
+
+       NL80211_ATTR_SCHED_SCAN_INTERVAL,
+
+       NL80211_ATTR_INTERFACE_COMBINATIONS,
+       NL80211_ATTR_SOFTWARE_IFTYPES,
+
+       NL80211_ATTR_REKEY_DATA,
+
+       NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS,
+       NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN,
+
+       NL80211_ATTR_SCAN_SUPP_RATES,
+
+       NL80211_ATTR_HIDDEN_SSID,
+
+       NL80211_ATTR_IE_PROBE_RESP,
+       NL80211_ATTR_IE_ASSOC_RESP,
+
+       NL80211_ATTR_STA_WME,
+       NL80211_ATTR_SUPPORT_AP_UAPSD,
+
+       NL80211_ATTR_ROAM_SUPPORT,
+
+       NL80211_ATTR_SCHED_SCAN_MATCH,
+       NL80211_ATTR_MAX_MATCH_SETS,
+
+       NL80211_ATTR_PMKSA_CANDIDATE,
+
+       NL80211_ATTR_TX_NO_CCK_RATE,
+
+       NL80211_ATTR_TDLS_ACTION,
+       NL80211_ATTR_TDLS_DIALOG_TOKEN,
+       NL80211_ATTR_TDLS_OPERATION,
+       NL80211_ATTR_TDLS_SUPPORT,
+       NL80211_ATTR_TDLS_EXTERNAL_SETUP,
+
+       NL80211_ATTR_DEVICE_AP_SME,
+
+       NL80211_ATTR_DONT_WAIT_FOR_ACK,
+
+       NL80211_ATTR_FEATURE_FLAGS,
+
+       NL80211_ATTR_PROBE_RESP_OFFLOAD,
+
+       NL80211_ATTR_PROBE_RESP,
+
+       NL80211_ATTR_DFS_REGION,
+
+       NL80211_ATTR_DISABLE_HT,
+       NL80211_ATTR_HT_CAPABILITY_MASK,
+
+       NL80211_ATTR_NOACK_MAP,
+
+       NL80211_ATTR_INACTIVITY_TIMEOUT,
+
+       NL80211_ATTR_RX_SIGNAL_DBM,
+
+       NL80211_ATTR_BG_SCAN_PERIOD,
+
        /* add attributes here, update the policy in nl80211.c */
 
        __NL80211_ATTR_AFTER_LAST,
@@ -872,6 +1430,7 @@ enum nl80211_attrs {
 
 /* source-level API compatibility */
 #define NL80211_ATTR_SCAN_GENERATION NL80211_ATTR_GENERATION
+#define        NL80211_ATTR_MESH_PARAMS NL80211_ATTR_MESH_CONFIG
 
 /*
  * Allow user space programs to use #ifdef on new attributes by defining them
@@ -899,6 +1458,7 @@ enum nl80211_attrs {
 #define NL80211_ATTR_KEYS NL80211_ATTR_KEYS
 
 #define NL80211_MAX_SUPP_RATES                 32
+#define NL80211_MAX_SUPP_HT_RATES              77
 #define NL80211_MAX_SUPP_REG_RULES             32
 #define NL80211_TKIP_DATA_OFFSET_ENCR_KEY      0
 #define NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY    16
@@ -915,12 +1475,16 @@ enum nl80211_attrs {
  * @NL80211_IFTYPE_ADHOC: independent BSS member
  * @NL80211_IFTYPE_STATION: managed BSS member
  * @NL80211_IFTYPE_AP: access point
- * @NL80211_IFTYPE_AP_VLAN: VLAN interface for access points
+ * @NL80211_IFTYPE_AP_VLAN: VLAN interface for access points; VLAN interfaces
+ *     are a bit special in that they must always be tied to a pre-existing
+ *     AP type interface.
  * @NL80211_IFTYPE_WDS: wireless distribution interface
  * @NL80211_IFTYPE_MONITOR: monitor interface receiving all frames
  * @NL80211_IFTYPE_MESH_POINT: mesh point
+ * @NL80211_IFTYPE_P2P_CLIENT: P2P client
+ * @NL80211_IFTYPE_P2P_GO: P2P group owner
  * @NL80211_IFTYPE_MAX: highest interface type number currently defined
- * @__NL80211_IFTYPE_AFTER_LAST: internal use
+ * @NUM_NL80211_IFTYPES: number of defined interface types
  *
  * These values are used with the %NL80211_ATTR_IFTYPE
  * to set the type of an interface.
@@ -935,10 +1499,12 @@ enum nl80211_iftype {
        NL80211_IFTYPE_WDS,
        NL80211_IFTYPE_MONITOR,
        NL80211_IFTYPE_MESH_POINT,
+       NL80211_IFTYPE_P2P_CLIENT,
+       NL80211_IFTYPE_P2P_GO,
 
        /* keep last */
-       __NL80211_IFTYPE_AFTER_LAST,
-       NL80211_IFTYPE_MAX = __NL80211_IFTYPE_AFTER_LAST - 1
+       NUM_NL80211_IFTYPES,
+       NL80211_IFTYPE_MAX = NUM_NL80211_IFTYPES - 1
 };
 
 /**
@@ -947,11 +1513,16 @@ enum nl80211_iftype {
  * Station flags. When a station is added to an AP interface, it is
  * assumed to be already associated (and hence authenticated.)
  *
+ * @__NL80211_STA_FLAG_INVALID: attribute number 0 is reserved
  * @NL80211_STA_FLAG_AUTHORIZED: station is authorized (802.1X)
  * @NL80211_STA_FLAG_SHORT_PREAMBLE: station is capable of receiving frames
  *     with short barker preamble
  * @NL80211_STA_FLAG_WME: station is WME/QoS capable
  * @NL80211_STA_FLAG_MFP: station uses management frame protection
+ * @NL80211_STA_FLAG_AUTHENTICATED: station is authenticated
+ * @NL80211_STA_FLAG_TDLS_PEER: station is a TDLS peer
+ * @NL80211_STA_FLAG_MAX: highest station flag number currently defined
+ * @__NL80211_STA_FLAG_AFTER_LAST: internal use
  */
 enum nl80211_sta_flags {
        __NL80211_STA_FLAG_INVALID,
@@ -959,6 +1530,8 @@ enum nl80211_sta_flags {
        NL80211_STA_FLAG_SHORT_PREAMBLE,
        NL80211_STA_FLAG_WME,
        NL80211_STA_FLAG_MFP,
+       NL80211_STA_FLAG_AUTHENTICATED,
+       NL80211_STA_FLAG_TDLS_PEER,
 
        /* keep last */
        __NL80211_STA_FLAG_AFTER_LAST,
@@ -1004,6 +1577,36 @@ enum nl80211_rate_info {
 };
 
 /**
+ * enum nl80211_sta_bss_param - BSS information collected by STA
+ *
+ * These attribute types are used with %NL80211_STA_INFO_BSS_PARAM
+ * when getting information about the bitrate of a station.
+ *
+ * @__NL80211_STA_BSS_PARAM_INVALID: attribute number 0 is reserved
+ * @NL80211_STA_BSS_PARAM_CTS_PROT: whether CTS protection is enabled (flag)
+ * @NL80211_STA_BSS_PARAM_SHORT_PREAMBLE:  whether short preamble is enabled
+ *     (flag)
+ * @NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME:  whether short slot time is enabled
+ *     (flag)
+ * @NL80211_STA_BSS_PARAM_DTIM_PERIOD: DTIM period for beaconing (u8)
+ * @NL80211_STA_BSS_PARAM_BEACON_INTERVAL: Beacon interval (u16)
+ * @NL80211_STA_BSS_PARAM_MAX: highest sta_bss_param number currently defined
+ * @__NL80211_STA_BSS_PARAM_AFTER_LAST: internal use
+ */
+enum nl80211_sta_bss_param {
+       __NL80211_STA_BSS_PARAM_INVALID,
+       NL80211_STA_BSS_PARAM_CTS_PROT,
+       NL80211_STA_BSS_PARAM_SHORT_PREAMBLE,
+       NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME,
+       NL80211_STA_BSS_PARAM_DTIM_PERIOD,
+       NL80211_STA_BSS_PARAM_BEACON_INTERVAL,
+
+       /* keep last */
+       __NL80211_STA_BSS_PARAM_AFTER_LAST,
+       NL80211_STA_BSS_PARAM_MAX = __NL80211_STA_BSS_PARAM_AFTER_LAST - 1
+};
+
+/**
  * enum nl80211_sta_info - station information
  *
  * These attribute types are used with %NL80211_ATTR_STA_INFO
@@ -1013,14 +1616,26 @@ enum nl80211_rate_info {
  * @NL80211_STA_INFO_INACTIVE_TIME: time since last activity (u32, msecs)
  * @NL80211_STA_INFO_RX_BYTES: total received bytes (u32, from this station)
  * @NL80211_STA_INFO_TX_BYTES: total transmitted bytes (u32, to this station)
- * @__NL80211_STA_INFO_AFTER_LAST: internal
- * @NL80211_STA_INFO_MAX: highest possible station info attribute
  * @NL80211_STA_INFO_SIGNAL: signal strength of last received PPDU (u8, dBm)
  * @NL80211_STA_INFO_TX_BITRATE: current unicast tx rate, nested attribute
- *     containing info as possible, see &enum nl80211_sta_info_txrate.
+ *     containing info as possible, see &enum nl80211_rate_info
  * @NL80211_STA_INFO_RX_PACKETS: total received packet (u32, from this station)
  * @NL80211_STA_INFO_TX_PACKETS: total transmitted packets (u32, to this
  *     station)
+ * @NL80211_STA_INFO_TX_RETRIES: total retries (u32, to this station)
+ * @NL80211_STA_INFO_TX_FAILED: total failed packets (u32, to this station)
+ * @NL80211_STA_INFO_SIGNAL_AVG: signal strength average (u8, dBm)
+ * @NL80211_STA_INFO_LLID: the station's mesh LLID
+ * @NL80211_STA_INFO_PLID: the station's mesh PLID
+ * @NL80211_STA_INFO_PLINK_STATE: peer link state for the station
+ *     (see %enum nl80211_plink_state)
+ * @NL80211_STA_INFO_RX_BITRATE: last unicast data frame rx rate, nested
+ *     attribute, like NL80211_STA_INFO_TX_BITRATE.
+ * @NL80211_STA_INFO_BSS_PARAM: current station's view of BSS, nested attribute
+ *     containing info as possible, see &enum nl80211_sta_bss_param
+ * @NL80211_STA_INFO_CONNECTED_TIME: time since the station is last connected
+ * @__NL80211_STA_INFO_AFTER_LAST: internal
+ * @NL80211_STA_INFO_MAX: highest possible station info attribute
  */
 enum nl80211_sta_info {
        __NL80211_STA_INFO_INVALID,
@@ -1034,6 +1649,12 @@ enum nl80211_sta_info {
        NL80211_STA_INFO_TX_BITRATE,
        NL80211_STA_INFO_RX_PACKETS,
        NL80211_STA_INFO_TX_PACKETS,
+       NL80211_STA_INFO_TX_RETRIES,
+       NL80211_STA_INFO_TX_FAILED,
+       NL80211_STA_INFO_SIGNAL_AVG,
+       NL80211_STA_INFO_RX_BITRATE,
+       NL80211_STA_INFO_BSS_PARAM,
+       NL80211_STA_INFO_CONNECTED_TIME,
 
        /* keep last */
        __NL80211_STA_INFO_AFTER_LAST,
@@ -1064,14 +1685,17 @@ enum nl80211_mpath_flags {
  * information about a mesh path.
  *
  * @__NL80211_MPATH_INFO_INVALID: attribute number 0 is reserved
- * @NL80211_ATTR_MPATH_FRAME_QLEN: number of queued frames for this destination
- * @NL80211_ATTR_MPATH_SN: destination sequence number
- * @NL80211_ATTR_MPATH_METRIC: metric (cost) of this mesh path
- * @NL80211_ATTR_MPATH_EXPTIME: expiration time for the path, in msec from now
- * @NL80211_ATTR_MPATH_FLAGS: mesh path flags, enumerated in
+ * @NL80211_MPATH_INFO_FRAME_QLEN: number of queued frames for this destination
+ * @NL80211_MPATH_INFO_SN: destination sequence number
+ * @NL80211_MPATH_INFO_METRIC: metric (cost) of this mesh path
+ * @NL80211_MPATH_INFO_EXPTIME: expiration time for the path, in msec from now
+ * @NL80211_MPATH_INFO_FLAGS: mesh path flags, enumerated in
  *     &enum nl80211_mpath_flags;
- * @NL80211_ATTR_MPATH_DISCOVERY_TIMEOUT: total path discovery timeout, in msec
- * @NL80211_ATTR_MPATH_DISCOVERY_RETRIES: mesh path discovery retries
+ * @NL80211_MPATH_INFO_DISCOVERY_TIMEOUT: total path discovery timeout, in msec
+ * @NL80211_MPATH_INFO_DISCOVERY_RETRIES: mesh path discovery retries
+ * @NL80211_MPATH_INFO_MAX: highest mesh path information attribute number
+ *     currently defind
+ * @__NL80211_MPATH_INFO_AFTER_LAST: internal use
  */
 enum nl80211_mpath_info {
        __NL80211_MPATH_INFO_INVALID,
@@ -1100,6 +1724,8 @@ enum nl80211_mpath_info {
  * @NL80211_BAND_ATTR_HT_CAPA: HT capabilities, as in the HT information IE
  * @NL80211_BAND_ATTR_HT_AMPDU_FACTOR: A-MPDU factor, as in 11n
  * @NL80211_BAND_ATTR_HT_AMPDU_DENSITY: A-MPDU density, as in 11n
+ * @NL80211_BAND_ATTR_MAX: highest band attribute currently defined
+ * @__NL80211_BAND_ATTR_AFTER_LAST: internal use
  */
 enum nl80211_band_attr {
        __NL80211_BAND_ATTR_INVALID,
@@ -1120,6 +1746,7 @@ enum nl80211_band_attr {
 
 /**
  * enum nl80211_frequency_attr - frequency attributes
+ * @__NL80211_FREQUENCY_ATTR_INVALID: attribute number 0 is reserved
  * @NL80211_FREQUENCY_ATTR_FREQ: Frequency in MHz
  * @NL80211_FREQUENCY_ATTR_DISABLED: Channel is disabled in current
  *     regulatory domain.
@@ -1131,6 +1758,9 @@ enum nl80211_band_attr {
  *     on this channel in current regulatory domain.
  * @NL80211_FREQUENCY_ATTR_MAX_TX_POWER: Maximum transmission power in mBm
  *     (100 * dBm).
+ * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number
+ *     currently defined
+ * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use
  */
 enum nl80211_frequency_attr {
        __NL80211_FREQUENCY_ATTR_INVALID,
@@ -1150,9 +1780,13 @@ enum nl80211_frequency_attr {
 
 /**
  * enum nl80211_bitrate_attr - bitrate attributes
+ * @__NL80211_BITRATE_ATTR_INVALID: attribute number 0 is reserved
  * @NL80211_BITRATE_ATTR_RATE: Bitrate in units of 100 kbps
  * @NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE: Short preamble supported
  *     in 2.4 GHz band.
+ * @NL80211_BITRATE_ATTR_MAX: highest bitrate attribute number
+ *     currently defined
+ * @__NL80211_BITRATE_ATTR_AFTER_LAST: internal use
  */
 enum nl80211_bitrate_attr {
        __NL80211_BITRATE_ATTR_INVALID,
@@ -1174,7 +1808,11 @@ enum nl80211_bitrate_attr {
  *     wireless core it thinks its knows the regulatory domain we should be in.
  * @NL80211_REGDOM_SET_BY_COUNTRY_IE: the wireless core has received an
  *     802.11 country information element with regulatory information it
- *     thinks we should consider.
+ *     thinks we should consider. cfg80211 only processes the country
+ *     code from the IE, and relies on the regulatory domain information
+ *     structure passed by userspace (CRDA) from our wireless-regdb.
+ *     If a channel is enabled but the country code indicates it should
+ *     be disabled we disable the channel and re-enable it upon disassociation.
  */
 enum nl80211_reg_initiator {
        NL80211_REGDOM_SET_BY_CORE,
@@ -1208,6 +1846,7 @@ enum nl80211_reg_type {
 
 /**
  * enum nl80211_reg_rule_attr - regulatory rule attributes
+ * @__NL80211_REG_RULE_ATTR_INVALID: attribute number 0 is reserved
  * @NL80211_ATTR_REG_RULE_FLAGS: a set of flags which specify additional
  *     considerations for a given frequency range. These are the
  *     &enum nl80211_reg_rule_flags.
@@ -1224,6 +1863,9 @@ enum nl80211_reg_type {
  *     If you don't have one then don't send this.
  * @NL80211_ATTR_POWER_RULE_MAX_EIRP: the maximum allowed EIRP for
  *     a given frequency range. The value is in mBm (100 * dBm).
+ * @NL80211_REG_RULE_ATTR_MAX: highest regulatory rule attribute number
+ *     currently defined
+ * @__NL80211_REG_RULE_ATTR_AFTER_LAST: internal use
  */
 enum nl80211_reg_rule_attr {
        __NL80211_REG_RULE_ATTR_INVALID,
@@ -1242,6 +1884,26 @@ enum nl80211_reg_rule_attr {
 };
 
 /**
+ * enum nl80211_sched_scan_match_attr - scheduled scan match attributes
+ * @__NL80211_SCHED_SCAN_MATCH_ATTR_INVALID: attribute number 0 is reserved
+ * @NL80211_SCHED_SCAN_MATCH_ATTR_SSID: SSID to be used for matching,
+ * only report BSS with matching SSID.
+ * @NL80211_SCHED_SCAN_MATCH_ATTR_MAX: highest scheduled scan filter
+ *     attribute number currently defined
+ * @__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: internal use
+ */
+enum nl80211_sched_scan_match_attr {
+       __NL80211_SCHED_SCAN_MATCH_ATTR_INVALID,
+
+       NL80211_ATTR_SCHED_SCAN_MATCH_SSID,
+
+       /* keep last */
+       __NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST,
+       NL80211_SCHED_SCAN_MATCH_ATTR_MAX =
+               __NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST - 1
+};
+
+/**
  * enum nl80211_reg_rule_flags - regulatory rule flags
  *
  * @NL80211_RRF_NO_OFDM: OFDM modulation not allowed
@@ -1275,11 +1937,31 @@ enum nl80211_reg_rule_flags {
  * @__NL80211_SURVEY_INFO_INVALID: attribute number 0 is reserved
  * @NL80211_SURVEY_INFO_FREQUENCY: center frequency of channel
  * @NL80211_SURVEY_INFO_NOISE: noise level of channel (u8, dBm)
+ * @NL80211_SURVEY_INFO_IN_USE: channel is currently being used
+ * @NL80211_SURVEY_INFO_CHANNEL_TIME: amount of time (in ms) that the radio
+ *     spent on this channel
+ * @NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY: amount of the time the primary
+ *     channel was sensed busy (either due to activity or energy detect)
+ * @NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY: amount of time the extension
+ *     channel was sensed busy
+ * @NL80211_SURVEY_INFO_CHANNEL_TIME_RX: amount of time the radio spent
+ *     receiving data
+ * @NL80211_SURVEY_INFO_CHANNEL_TIME_TX: amount of time the radio spent
+ *     transmitting data
+ * @NL80211_SURVEY_INFO_MAX: highest survey info attribute number
+ *     currently defined
+ * @__NL80211_SURVEY_INFO_AFTER_LAST: internal use
  */
 enum nl80211_survey_info {
        __NL80211_SURVEY_INFO_INVALID,
        NL80211_SURVEY_INFO_FREQUENCY,
        NL80211_SURVEY_INFO_NOISE,
+       NL80211_SURVEY_INFO_IN_USE,
+       NL80211_SURVEY_INFO_CHANNEL_TIME,
+       NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY,
+       NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY,
+       NL80211_SURVEY_INFO_CHANNEL_TIME_RX,
+       NL80211_SURVEY_INFO_CHANNEL_TIME_TX,
 
        /* keep last */
        __NL80211_SURVEY_INFO_AFTER_LAST,
@@ -1319,14 +2001,15 @@ enum nl80211_mntr_flags {
 /**
  * enum nl80211_meshconf_params - mesh configuration parameters
  *
- * Mesh configuration parameters
+ * Mesh configuration parameters. These can be changed while the mesh is
+ * active.
  *
  * @__NL80211_MESHCONF_INVALID: internal use
  *
  * @NL80211_MESHCONF_RETRY_TIMEOUT: specifies the initial retry timeout in
  * millisecond units, used by the Peer Link Open message
  *
- * @NL80211_MESHCONF_CONFIRM_TIMEOUT: specifies the inital confirm timeout, in
+ * @NL80211_MESHCONF_CONFIRM_TIMEOUT: specifies the initial confirm timeout, in
  * millisecond units, used by the peer link management to close a peer link
  *
  * @NL80211_MESHCONF_HOLDING_TIMEOUT: specifies the holding timeout, in
@@ -1366,7 +2049,28 @@ enum nl80211_mntr_flags {
  * @NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME: The interval of time (in TUs)
  * that it takes for an HWMP information element to propagate across the mesh
  *
- * @NL80211_MESHCONF_ROOTMODE: whether root mode is enabled or not
+ * @NL80211_MESHCONF_HWMP_ROOTMODE: whether root mode is enabled or not
+ *
+ * @NL80211_MESHCONF_ELEMENT_TTL: specifies the value of TTL field set at a
+ * source mesh point for path selection elements.
+ *
+ * @NL80211_MESHCONF_HWMP_RANN_INTERVAL:  The interval of time (in TUs) between
+ * root announcements are transmitted.
+ *
+ * @NL80211_MESHCONF_GATE_ANNOUNCEMENTS: Advertise that this mesh station has
+ * access to a broader network beyond the MBSS.  This is done via Root
+ * Announcement frames.
+ *
+ * @NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL: The minimum interval of time (in
+ * TUs) during which a mesh STA can send only one Action frame containing a
+ * PERR element.
+ *
+ * @NL80211_MESHCONF_FORWARDING: set Mesh STA as forwarding or non-forwarding
+ * or forwarding entity (default is TRUE - forwarding entity)
+ *
+ * @NL80211_MESHCONF_RSSI_THRESHOLD: RSSI threshold in dBm. This specifies the
+ * threshold for average signal strength of candidate station to establish
+ * a peer link.
  *
  * @NL80211_MESHCONF_ATTR_MAX: highest possible mesh configuration attribute
  *
@@ -1388,6 +2092,12 @@ enum nl80211_meshconf_params {
        NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
        NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
        NL80211_MESHCONF_HWMP_ROOTMODE,
+       NL80211_MESHCONF_ELEMENT_TTL,
+       NL80211_MESHCONF_HWMP_RANN_INTERVAL,
+       NL80211_MESHCONF_GATE_ANNOUNCEMENTS,
+       NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL,
+       NL80211_MESHCONF_FORWARDING,
+       NL80211_MESHCONF_RSSI_THRESHOLD,
 
        /* keep last */
        __NL80211_MESHCONF_ATTR_AFTER_LAST,
@@ -1395,6 +2105,54 @@ enum nl80211_meshconf_params {
 };
 
 /**
+ * enum nl80211_mesh_setup_params - mesh setup parameters
+ *
+ * Mesh setup parameters.  These are used to start/join a mesh and cannot be
+ * changed while the mesh is active.
+ *
+ * @__NL80211_MESH_SETUP_INVALID: Internal use
+ *
+ * @NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL: Enable this option to use a
+ * vendor specific path selection algorithm or disable it to use the default
+ * HWMP.
+ *
+ * @NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC: Enable this option to use a
+ * vendor specific path metric or disable it to use the default Airtime
+ * metric.
+ *
+ * @NL80211_MESH_SETUP_IE: Information elements for this mesh, for instance, a
+ * robust security network ie, or a vendor specific information element that
+ * vendors will use to identify the path selection methods and metrics in use.
+ *
+ * @NL80211_MESH_SETUP_USERSPACE_AUTH: Enable this option if an authentication
+ * daemon will be authenticating mesh candidates.
+ *
+ * @NL80211_MESH_SETUP_USERSPACE_AMPE: Enable this option if an authentication
+ * daemon will be securing peer link frames.  AMPE is a secured version of Mesh
+ * Peering Management (MPM) and is implemented with the assistance of a
+ * userspace daemon.  When this flag is set, the kernel will send peer
+ * management frames to a userspace daemon that will implement AMPE
+ * functionality (security capabilities selection, key confirmation, and key
+ * management).  When the flag is unset (default), the kernel can autonomously
+ * complete (unsecured) mesh peering without the need of a userspace daemon.
+ *
+ * @NL80211_MESH_SETUP_ATTR_MAX: highest possible mesh setup attribute number
+ * @__NL80211_MESH_SETUP_ATTR_AFTER_LAST: Internal use
+ */
+enum nl80211_mesh_setup_params {
+       __NL80211_MESH_SETUP_INVALID,
+       NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL,
+       NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC,
+       NL80211_MESH_SETUP_IE,
+       NL80211_MESH_SETUP_USERSPACE_AUTH,
+       NL80211_MESH_SETUP_USERSPACE_AMPE,
+
+       /* keep last */
+       __NL80211_MESH_SETUP_ATTR_AFTER_LAST,
+       NL80211_MESH_SETUP_ATTR_MAX = __NL80211_MESH_SETUP_ATTR_AFTER_LAST - 1
+};
+
+/**
  * enum nl80211_txq_attr - TX queue parameter attributes
  * @__NL80211_TXQ_ATTR_INVALID: Attribute number 0 is reserved
  * @NL80211_TXQ_ATTR_QUEUE: TX queue identifier (NL80211_TXQ_Q_*)
@@ -1439,6 +2197,7 @@ enum nl80211_channel_type {
  * enum nl80211_bss - netlink attributes for a BSS
  *
  * @__NL80211_BSS_INVALID: invalid
+ * @NL80211_BSS_BSSID: BSSID of the BSS (6 octets)
  * @NL80211_BSS_FREQUENCY: frequency in MHz (u32)
  * @NL80211_BSS_TSF: TSF of the received probe response/beacon (u64)
  * @NL80211_BSS_BEACON_INTERVAL: beacon interval of the (I)BSS (u16)
@@ -1482,6 +2241,12 @@ enum nl80211_bss {
 
 /**
  * enum nl80211_bss_status - BSS "status"
+ * @NL80211_BSS_STATUS_AUTHENTICATED: Authenticated with this BSS.
+ * @NL80211_BSS_STATUS_ASSOCIATED: Associated with this BSS.
+ * @NL80211_BSS_STATUS_IBSS_JOINED: Joined to this IBSS.
+ *
+ * The BSS status is a BSS attribute in scan dumps, which
+ * indicates the status the interface has wrt. this BSS.
  */
 enum nl80211_bss_status {
        NL80211_BSS_STATUS_AUTHENTICATED,
@@ -1519,11 +2284,14 @@ enum nl80211_auth_type {
  * @NL80211_KEYTYPE_GROUP: Group (broadcast/multicast) key
  * @NL80211_KEYTYPE_PAIRWISE: Pairwise (unicast/individual) key
  * @NL80211_KEYTYPE_PEERKEY: PeerKey (DLS)
+ * @NUM_NL80211_KEYTYPES: number of defined key types
  */
 enum nl80211_key_type {
        NL80211_KEYTYPE_GROUP,
        NL80211_KEYTYPE_PAIRWISE,
        NL80211_KEYTYPE_PEERKEY,
+
+       NUM_NL80211_KEYTYPES
 };
 
 /**
@@ -1542,6 +2310,23 @@ enum nl80211_wpa_versions {
 };
 
 /**
+ * enum nl80211_key_default_types - key default types
+ * @__NL80211_KEY_DEFAULT_TYPE_INVALID: invalid
+ * @NL80211_KEY_DEFAULT_TYPE_UNICAST: key should be used as default
+ *     unicast key
+ * @NL80211_KEY_DEFAULT_TYPE_MULTICAST: key should be used as default
+ *     multicast key
+ * @NUM_NL80211_KEY_DEFAULT_TYPES: number of default types
+ */
+enum nl80211_key_default_types {
+       __NL80211_KEY_DEFAULT_TYPE_INVALID,
+       NL80211_KEY_DEFAULT_TYPE_UNICAST,
+       NL80211_KEY_DEFAULT_TYPE_MULTICAST,
+
+       NUM_NL80211_KEY_DEFAULT_TYPES
+};
+
+/**
  * enum nl80211_key_attributes - key attributes
  * @__NL80211_KEY_INVALID: invalid
  * @NL80211_KEY_DATA: (temporal) key data; for TKIP this consists of
@@ -1554,6 +2339,12 @@ enum nl80211_wpa_versions {
  *     CCMP keys, each six bytes in little endian
  * @NL80211_KEY_DEFAULT: flag indicating default key
  * @NL80211_KEY_DEFAULT_MGMT: flag indicating default management key
+ * @NL80211_KEY_TYPE: the key type from enum nl80211_key_type, if not
+ *     specified the default depends on whether a MAC address was
+ *     given with the command using the key or not (u32)
+ * @NL80211_KEY_DEFAULT_TYPES: A nested attribute containing flags
+ *     attributes, specifying what a key should be set as default as.
+ *     See &enum nl80211_key_default_types.
  * @__NL80211_KEY_AFTER_LAST: internal
  * @NL80211_KEY_MAX: highest key attribute
  */
@@ -1565,6 +2356,8 @@ enum nl80211_key_attributes {
        NL80211_KEY_SEQ,
        NL80211_KEY_DEFAULT,
        NL80211_KEY_DEFAULT_MGMT,
+       NL80211_KEY_TYPE,
+       NL80211_KEY_DEFAULT_TYPES,
 
        /* keep last */
        __NL80211_KEY_AFTER_LAST,
@@ -1578,12 +2371,15 @@ enum nl80211_key_attributes {
  *     in an array of rates as defined in IEEE 802.11 7.3.2.2 (u8 values with
  *     1 = 500 kbps) but without the IE length restriction (at most
  *     %NL80211_MAX_SUPP_RATES in a single array).
+ * @NL80211_TXRATE_MCS: HT (MCS) rates allowed for TX rate selection
+ *     in an array of MCS numbers.
  * @__NL80211_TXRATE_AFTER_LAST: internal
  * @NL80211_TXRATE_MAX: highest TX rate attribute
  */
 enum nl80211_tx_rate_attributes {
        __NL80211_TXRATE_INVALID,
        NL80211_TXRATE_LEGACY,
+       NL80211_TXRATE_MCS,
 
        /* keep last */
        __NL80211_TXRATE_AFTER_LAST,
@@ -1592,8 +2388,8 @@ enum nl80211_tx_rate_attributes {
 
 /**
  * enum nl80211_band - Frequency band
- * @NL80211_BAND_2GHZ - 2.4 GHz ISM band
- * @NL80211_BAND_5GHZ - around 5 GHz band (4.9 - 5.7 GHz)
+ * @NL80211_BAND_2GHZ: 2.4 GHz ISM band
+ * @NL80211_BAND_5GHZ: around 5 GHz band (4.9 - 5.7 GHz)
  */
 enum nl80211_band {
        NL80211_BAND_2GHZ,
@@ -1615,6 +2411,8 @@ enum nl80211_ps_state {
  *     the minimum amount the RSSI level must change after an event before a
  *     new event may be issued (to reduce effects of RSSI oscillation).
  * @NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT: RSSI threshold event
+ * @NL80211_ATTR_CQM_PKT_LOSS_EVENT: a u32 value indicating that this many
+ *     consecutive packets were not acknowledged by the peer
  * @__NL80211_ATTR_CQM_AFTER_LAST: internal
  * @NL80211_ATTR_CQM_MAX: highest key attribute
  */
@@ -1623,6 +2421,7 @@ enum nl80211_attr_cqm {
        NL80211_ATTR_CQM_RSSI_THOLD,
        NL80211_ATTR_CQM_RSSI_HYST,
        NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
+       NL80211_ATTR_CQM_PKT_LOSS_EVENT,
 
        /* keep last */
        __NL80211_ATTR_CQM_AFTER_LAST,
@@ -1631,9 +2430,9 @@ enum nl80211_attr_cqm {
 
 /**
  * enum nl80211_cqm_rssi_threshold_event - RSSI threshold event
- * @NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW - The RSSI level is lower than the
+ * @NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW: The RSSI level is lower than the
  *      configured threshold
- * @NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH - The RSSI is higher than the
+ * @NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH: The RSSI is higher than the
  *      configured threshold
  */
 enum nl80211_cqm_rssi_threshold_event {
@@ -1641,4 +2440,359 @@ enum nl80211_cqm_rssi_threshold_event {
        NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
 };
 
+
+/**
+ * enum nl80211_tx_power_setting - TX power adjustment
+ * @NL80211_TX_POWER_AUTOMATIC: automatically determine transmit power
+ * @NL80211_TX_POWER_LIMITED: limit TX power by the mBm parameter
+ * @NL80211_TX_POWER_FIXED: fix TX power to the mBm parameter
+ */
+enum nl80211_tx_power_setting {
+       NL80211_TX_POWER_AUTOMATIC,
+       NL80211_TX_POWER_LIMITED,
+       NL80211_TX_POWER_FIXED,
+};
+
+/**
+ * enum nl80211_wowlan_packet_pattern_attr - WoWLAN packet pattern attribute
+ * @__NL80211_WOWLAN_PKTPAT_INVALID: invalid number for nested attribute
+ * @NL80211_WOWLAN_PKTPAT_PATTERN: the pattern, values where the mask has
+ *     a zero bit are ignored
+ * @NL80211_WOWLAN_PKTPAT_MASK: pattern mask, must be long enough to have
+ *     a bit for each byte in the pattern. The lowest-order bit corresponds
+ *     to the first byte of the pattern, but the bytes of the pattern are
+ *     in a little-endian-like format, i.e. the 9th byte of the pattern
+ *     corresponds to the lowest-order bit in the second byte of the mask.
+ *     For example: The match 00:xx:00:00:xx:00:00:00:00:xx:xx:xx (where
+ *     xx indicates "don't care") would be represented by a pattern of
+ *     twelve zero bytes, and a mask of "0xed,0x07".
+ *     Note that the pattern matching is done as though frames were not
+ *     802.11 frames but 802.3 frames, i.e. the frame is fully unpacked
+ *     first (including SNAP header unpacking) and then matched.
+ * @NUM_NL80211_WOWLAN_PKTPAT: number of attributes
+ * @MAX_NL80211_WOWLAN_PKTPAT: max attribute number
+ */
+enum nl80211_wowlan_packet_pattern_attr {
+       __NL80211_WOWLAN_PKTPAT_INVALID,
+       NL80211_WOWLAN_PKTPAT_MASK,
+       NL80211_WOWLAN_PKTPAT_PATTERN,
+
+       NUM_NL80211_WOWLAN_PKTPAT,
+       MAX_NL80211_WOWLAN_PKTPAT = NUM_NL80211_WOWLAN_PKTPAT - 1,
+};
+
+/**
+ * struct nl80211_wowlan_pattern_support - pattern support information
+ * @max_patterns: maximum number of patterns supported
+ * @min_pattern_len: minimum length of each pattern
+ * @max_pattern_len: maximum length of each pattern
+ *
+ * This struct is carried in %NL80211_WOWLAN_TRIG_PKT_PATTERN when
+ * that is part of %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED in the
+ * capability information given by the kernel to userspace.
+ */
+struct nl80211_wowlan_pattern_support {
+       __u32 max_patterns;
+       __u32 min_pattern_len;
+       __u32 max_pattern_len;
+} __attribute__((packed));
+
+/**
+ * enum nl80211_wowlan_triggers - WoWLAN trigger definitions
+ * @__NL80211_WOWLAN_TRIG_INVALID: invalid number for nested attributes
+ * @NL80211_WOWLAN_TRIG_ANY: wake up on any activity, do not really put
+ *     the chip into a special state -- works best with chips that have
+ *     support for low-power operation already (flag)
+ * @NL80211_WOWLAN_TRIG_DISCONNECT: wake up on disconnect, the way disconnect
+ *     is detected is implementation-specific (flag)
+ * @NL80211_WOWLAN_TRIG_MAGIC_PKT: wake up on magic packet (6x 0xff, followed
+ *     by 16 repetitions of MAC addr, anywhere in payload) (flag)
+ * @NL80211_WOWLAN_TRIG_PKT_PATTERN: wake up on the specified packet patterns
+ *     which are passed in an array of nested attributes, each nested attribute
+ *     defining a with attributes from &struct nl80211_wowlan_trig_pkt_pattern.
+ *     Each pattern defines a wakeup packet. The matching is done on the MSDU,
+ *     i.e. as though the packet was an 802.3 packet, so the pattern matching
+ *     is done after the packet is converted to the MSDU.
+ *
+ *     In %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED, it is a binary attribute
+ *     carrying a &struct nl80211_wowlan_pattern_support.
+ * @NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED: Not a real trigger, and cannot be
+ *     used when setting, used only to indicate that GTK rekeying is supported
+ *     by the device (flag)
+ * @NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE: wake up on GTK rekey failure (if
+ *     done by the device) (flag)
+ * @NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST: wake up on EAP Identity Request
+ *     packet (flag)
+ * @NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE: wake up on 4-way handshake (flag)
+ * @NL80211_WOWLAN_TRIG_RFKILL_RELEASE: wake up when rfkill is released
+ *     (on devices that have rfkill in the device) (flag)
+ * @NUM_NL80211_WOWLAN_TRIG: number of wake on wireless triggers
+ * @MAX_NL80211_WOWLAN_TRIG: highest wowlan trigger attribute number
+ */
+enum nl80211_wowlan_triggers {
+       __NL80211_WOWLAN_TRIG_INVALID,
+       NL80211_WOWLAN_TRIG_ANY,
+       NL80211_WOWLAN_TRIG_DISCONNECT,
+       NL80211_WOWLAN_TRIG_MAGIC_PKT,
+       NL80211_WOWLAN_TRIG_PKT_PATTERN,
+       NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED,
+       NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE,
+       NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST,
+       NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE,
+       NL80211_WOWLAN_TRIG_RFKILL_RELEASE,
+
+       /* keep last */
+       NUM_NL80211_WOWLAN_TRIG,
+       MAX_NL80211_WOWLAN_TRIG = NUM_NL80211_WOWLAN_TRIG - 1
+};
+
+/**
+ * enum nl80211_iface_limit_attrs - limit attributes
+ * @NL80211_IFACE_LIMIT_UNSPEC: (reserved)
+ * @NL80211_IFACE_LIMIT_MAX: maximum number of interfaces that
+ *     can be chosen from this set of interface types (u32)
+ * @NL80211_IFACE_LIMIT_TYPES: nested attribute containing a
+ *     flag attribute for each interface type in this set
+ * @NUM_NL80211_IFACE_LIMIT: number of attributes
+ * @MAX_NL80211_IFACE_LIMIT: highest attribute number
+ */
+enum nl80211_iface_limit_attrs {
+       NL80211_IFACE_LIMIT_UNSPEC,
+       NL80211_IFACE_LIMIT_MAX,
+       NL80211_IFACE_LIMIT_TYPES,
+
+       /* keep last */
+       NUM_NL80211_IFACE_LIMIT,
+       MAX_NL80211_IFACE_LIMIT = NUM_NL80211_IFACE_LIMIT - 1
+};
+
+/**
+ * enum nl80211_if_combination_attrs -- interface combination attributes
+ *
+ * @NL80211_IFACE_COMB_UNSPEC: (reserved)
+ * @NL80211_IFACE_COMB_LIMITS: Nested attributes containing the limits
+ *     for given interface types, see &enum nl80211_iface_limit_attrs.
+ * @NL80211_IFACE_COMB_MAXNUM: u32 attribute giving the total number of
+ *     interfaces that can be created in this group. This number doesn't
+ *     apply to interfaces purely managed in software, which are listed
+ *     in a separate attribute %NL80211_ATTR_INTERFACES_SOFTWARE.
+ * @NL80211_IFACE_COMB_STA_AP_BI_MATCH: flag attribute specifying that
+ *     beacon intervals within this group must be all the same even for
+ *     infrastructure and AP/GO combinations, i.e. the GO(s) must adopt
+ *     the infrastructure network's beacon interval.
+ * @NL80211_IFACE_COMB_NUM_CHANNELS: u32 attribute specifying how many
+ *     different channels may be used within this group.
+ * @NUM_NL80211_IFACE_COMB: number of attributes
+ * @MAX_NL80211_IFACE_COMB: highest attribute number
+ *
+ * Examples:
+ *     limits = [ #{STA} <= 1, #{AP} <= 1 ], matching BI, channels = 1, max = 2
+ *     => allows an AP and a STA that must match BIs
+ *
+ *     numbers = [ #{AP, P2P-GO} <= 8 ], channels = 1, max = 8
+ *     => allows 8 of AP/GO
+ *
+ *     numbers = [ #{STA} <= 2 ], channels = 2, max = 2
+ *     => allows two STAs on different channels
+ *
+ *     numbers = [ #{STA} <= 1, #{P2P-client,P2P-GO} <= 3 ], max = 4
+ *     => allows a STA plus three P2P interfaces
+ *
+ * The list of these four possiblities could completely be contained
+ * within the %NL80211_ATTR_INTERFACE_COMBINATIONS attribute to indicate
+ * that any of these groups must match.
+ *
+ * "Combinations" of just a single interface will not be listed here,
+ * a single interface of any valid interface type is assumed to always
+ * be possible by itself. This means that implicitly, for each valid
+ * interface type, the following group always exists:
+ *     numbers = [ #{<type>} <= 1 ], channels = 1, max = 1
+ */
+enum nl80211_if_combination_attrs {
+       NL80211_IFACE_COMB_UNSPEC,
+       NL80211_IFACE_COMB_LIMITS,
+       NL80211_IFACE_COMB_MAXNUM,
+       NL80211_IFACE_COMB_STA_AP_BI_MATCH,
+       NL80211_IFACE_COMB_NUM_CHANNELS,
+
+       /* keep last */
+       NUM_NL80211_IFACE_COMB,
+       MAX_NL80211_IFACE_COMB = NUM_NL80211_IFACE_COMB - 1
+};
+
+
+/**
+ * enum nl80211_plink_state - state of a mesh peer link finite state machine
+ *
+ * @NL80211_PLINK_LISTEN: initial state, considered the implicit
+ *     state of non existant mesh peer links
+ * @NL80211_PLINK_OPN_SNT: mesh plink open frame has been sent to
+ *     this mesh peer
+ * @NL80211_PLINK_OPN_RCVD: mesh plink open frame has been received
+ *     from this mesh peer
+ * @NL80211_PLINK_CNF_RCVD: mesh plink confirm frame has been
+ *     received from this mesh peer
+ * @NL80211_PLINK_ESTAB: mesh peer link is established
+ * @NL80211_PLINK_HOLDING: mesh peer link is being closed or cancelled
+ * @NL80211_PLINK_BLOCKED: all frames transmitted from this mesh
+ *     plink are discarded
+ * @NUM_NL80211_PLINK_STATES: number of peer link states
+ * @MAX_NL80211_PLINK_STATES: highest numerical value of plink states
+ */
+enum nl80211_plink_state {
+       NL80211_PLINK_LISTEN,
+       NL80211_PLINK_OPN_SNT,
+       NL80211_PLINK_OPN_RCVD,
+       NL80211_PLINK_CNF_RCVD,
+       NL80211_PLINK_ESTAB,
+       NL80211_PLINK_HOLDING,
+       NL80211_PLINK_BLOCKED,
+
+       /* keep last */
+       NUM_NL80211_PLINK_STATES,
+       MAX_NL80211_PLINK_STATES = NUM_NL80211_PLINK_STATES - 1
+};
+
+#define NL80211_KCK_LEN                        16
+#define NL80211_KEK_LEN                        16
+#define NL80211_REPLAY_CTR_LEN         8
+
+/**
+ * enum nl80211_rekey_data - attributes for GTK rekey offload
+ * @__NL80211_REKEY_DATA_INVALID: invalid number for nested attributes
+ * @NL80211_REKEY_DATA_KEK: key encryption key (binary)
+ * @NL80211_REKEY_DATA_KCK: key confirmation key (binary)
+ * @NL80211_REKEY_DATA_REPLAY_CTR: replay counter (binary)
+ * @NUM_NL80211_REKEY_DATA: number of rekey attributes (internal)
+ * @MAX_NL80211_REKEY_DATA: highest rekey attribute (internal)
+ */
+enum nl80211_rekey_data {
+       __NL80211_REKEY_DATA_INVALID,
+       NL80211_REKEY_DATA_KEK,
+       NL80211_REKEY_DATA_KCK,
+       NL80211_REKEY_DATA_REPLAY_CTR,
+
+       /* keep last */
+       NUM_NL80211_REKEY_DATA,
+       MAX_NL80211_REKEY_DATA = NUM_NL80211_REKEY_DATA - 1
+};
+
+/**
+ * enum nl80211_hidden_ssid - values for %NL80211_ATTR_HIDDEN_SSID
+ * @NL80211_HIDDEN_SSID_NOT_IN_USE: do not hide SSID (i.e., broadcast it in
+ *     Beacon frames)
+ * @NL80211_HIDDEN_SSID_ZERO_LEN: hide SSID by using zero-length SSID element
+ *     in Beacon frames
+ * @NL80211_HIDDEN_SSID_ZERO_CONTENTS: hide SSID by using correct length of SSID
+ *     element in Beacon frames but zero out each byte in the SSID
+ */
+enum nl80211_hidden_ssid {
+       NL80211_HIDDEN_SSID_NOT_IN_USE,
+       NL80211_HIDDEN_SSID_ZERO_LEN,
+       NL80211_HIDDEN_SSID_ZERO_CONTENTS
+};
+
+/**
+ * enum nl80211_sta_wme_attr - station WME attributes
+ * @__NL80211_STA_WME_INVALID: invalid number for nested attribute
+ * @NL80211_STA_WME_UAPSD_QUEUES: bitmap of uapsd queues. the format
+ *     is the same as the AC bitmap in the QoS info field.
+ * @NL80211_STA_WME_MAX_SP: max service period. the format is the same
+ *     as the MAX_SP field in the QoS info field (but already shifted down).
+ * @__NL80211_STA_WME_AFTER_LAST: internal
+ * @NL80211_STA_WME_MAX: highest station WME attribute
+ */
+enum nl80211_sta_wme_attr {
+       __NL80211_STA_WME_INVALID,
+       NL80211_STA_WME_UAPSD_QUEUES,
+       NL80211_STA_WME_MAX_SP,
+
+       /* keep last */
+       __NL80211_STA_WME_AFTER_LAST,
+       NL80211_STA_WME_MAX = __NL80211_STA_WME_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_pmksa_candidate_attr - attributes for PMKSA caching candidates
+ * @__NL80211_PMKSA_CANDIDATE_INVALID: invalid number for nested attributes
+ * @NL80211_PMKSA_CANDIDATE_INDEX: candidate index (u32; the smaller, the higher
+ *     priority)
+ * @NL80211_PMKSA_CANDIDATE_BSSID: candidate BSSID (6 octets)
+ * @NL80211_PMKSA_CANDIDATE_PREAUTH: RSN pre-authentication supported (flag)
+ * @NUM_NL80211_PMKSA_CANDIDATE: number of PMKSA caching candidate attributes
+ *     (internal)
+ * @MAX_NL80211_PMKSA_CANDIDATE: highest PMKSA caching candidate attribute
+ *     (internal)
+ */
+enum nl80211_pmksa_candidate_attr {
+       __NL80211_PMKSA_CANDIDATE_INVALID,
+       NL80211_PMKSA_CANDIDATE_INDEX,
+       NL80211_PMKSA_CANDIDATE_BSSID,
+       NL80211_PMKSA_CANDIDATE_PREAUTH,
+
+       /* keep last */
+       NUM_NL80211_PMKSA_CANDIDATE,
+       MAX_NL80211_PMKSA_CANDIDATE = NUM_NL80211_PMKSA_CANDIDATE - 1
+};
+
+/**
+ * enum nl80211_tdls_operation - values for %NL80211_ATTR_TDLS_OPERATION
+ * @NL80211_TDLS_DISCOVERY_REQ: Send a TDLS discovery request
+ * @NL80211_TDLS_SETUP: Setup TDLS link
+ * @NL80211_TDLS_TEARDOWN: Teardown a TDLS link which is already established
+ * @NL80211_TDLS_ENABLE_LINK: Enable TDLS link
+ * @NL80211_TDLS_DISABLE_LINK: Disable TDLS link
+ */
+enum nl80211_tdls_operation {
+       NL80211_TDLS_DISCOVERY_REQ,
+       NL80211_TDLS_SETUP,
+       NL80211_TDLS_TEARDOWN,
+       NL80211_TDLS_ENABLE_LINK,
+       NL80211_TDLS_DISABLE_LINK,
+};
+
+/*
+ * enum nl80211_ap_sme_features - device-integrated AP features
+ * Reserved for future use, no bits are defined in
+ * NL80211_ATTR_DEVICE_AP_SME yet.
+enum nl80211_ap_sme_features {
+};
+ */
+
+/**
+ * enum nl80211_feature_flags - device/driver features
+ * @NL80211_FEATURE_SK_TX_STATUS: This driver supports reflecting back
+ *     TX status to the socket error queue when requested with the
+ *     socket option.
+ * @NL80211_FEATURE_HT_IBSS: This driver supports IBSS with HT datarates.
+ * @NL80211_FEATURE_INACTIVITY_TIMER: This driver takes care of freeing up
+ *     the connected inactive stations in AP mode.
+ */
+enum nl80211_feature_flags {
+       NL80211_FEATURE_SK_TX_STATUS    = 1 << 0,
+       NL80211_FEATURE_HT_IBSS         = 1 << 1,
+       NL80211_FEATURE_INACTIVITY_TIMER = 1 << 2,
+};
+
+/**
+ * enum nl80211_probe_resp_offload_support_attr - optional supported
+ *     protocols for probe-response offloading by the driver/FW.
+ *     To be used with the %NL80211_ATTR_PROBE_RESP_OFFLOAD attribute.
+ *     Each enum value represents a bit in the bitmap of supported
+ *     protocols. Typically a subset of probe-requests belonging to a
+ *     supported protocol will be excluded from offload and uploaded
+ *     to the host.
+ *
+ * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS: Support for WPS ver. 1
+ * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2: Support for WPS ver. 2
+ * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P: Support for P2P
+ * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U: Support for 802.11u
+ */
+enum nl80211_probe_resp_offload_support_attr {
+       NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS =        1<<0,
+       NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 =       1<<1,
+       NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P =        1<<2,
+       NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U =     1<<3,
+};
+
 #endif /* __LINUX_NL80211_H */
diff --git a/src/drivers/rfkill.c b/src/drivers/rfkill.c
new file mode 100644 (file)
index 0000000..8818311
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * Linux rfkill helper functions for driver wrappers
+ * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include <fcntl.h>
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "rfkill.h"
+
+#define RFKILL_EVENT_SIZE_V1 8
+
+struct rfkill_event {
+       u32 idx;
+       u8 type;
+       u8 op;
+       u8 soft;
+       u8 hard;
+} STRUCT_PACKED;
+
+enum rfkill_operation {
+       RFKILL_OP_ADD = 0,
+       RFKILL_OP_DEL,
+       RFKILL_OP_CHANGE,
+       RFKILL_OP_CHANGE_ALL,
+};
+
+enum rfkill_type {
+       RFKILL_TYPE_ALL = 0,
+       RFKILL_TYPE_WLAN,
+       RFKILL_TYPE_BLUETOOTH,
+       RFKILL_TYPE_UWB,
+       RFKILL_TYPE_WIMAX,
+       RFKILL_TYPE_WWAN,
+       RFKILL_TYPE_GPS,
+       RFKILL_TYPE_FM,
+       NUM_RFKILL_TYPES,
+};
+
+
+struct rfkill_data {
+       struct rfkill_config *cfg;
+       int fd;
+       int blocked;
+};
+
+
+static void rfkill_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+       struct rfkill_data *rfkill = eloop_ctx;
+       struct rfkill_event event;
+       ssize_t len;
+       int new_blocked;
+
+       len = read(rfkill->fd, &event, sizeof(event));
+       if (len < 0) {
+               wpa_printf(MSG_ERROR, "rfkill: Event read failed: %s",
+                          strerror(errno));
+               return;
+       }
+       if (len != RFKILL_EVENT_SIZE_V1) {
+               wpa_printf(MSG_DEBUG, "rfkill: Unexpected event size "
+                          "%d (expected %d)",
+                          (int) len, RFKILL_EVENT_SIZE_V1);
+               return;
+       }
+       wpa_printf(MSG_DEBUG, "rfkill: event: idx=%u type=%d "
+                  "op=%u soft=%u hard=%u",
+                  event.idx, event.type, event.op, event.soft,
+                  event.hard);
+       if (event.op != RFKILL_OP_CHANGE || event.type != RFKILL_TYPE_WLAN)
+               return;
+
+       if (event.hard) {
+               wpa_printf(MSG_INFO, "rfkill: WLAN hard blocked");
+               new_blocked = 1;
+       } else if (event.soft) {
+               wpa_printf(MSG_INFO, "rfkill: WLAN soft blocked");
+               new_blocked = 1;
+       } else {
+               wpa_printf(MSG_INFO, "rfkill: WLAN unblocked");
+               new_blocked = 0;
+       }
+
+       if (new_blocked != rfkill->blocked) {
+               rfkill->blocked = new_blocked;
+               if (new_blocked)
+                       rfkill->cfg->blocked_cb(rfkill->cfg->ctx);
+               else
+                       rfkill->cfg->unblocked_cb(rfkill->cfg->ctx);
+       }
+}
+
+
+struct rfkill_data * rfkill_init(struct rfkill_config *cfg)
+{
+       struct rfkill_data *rfkill;
+       struct rfkill_event event;
+       ssize_t len;
+
+       rfkill = os_zalloc(sizeof(*rfkill));
+       if (rfkill == NULL)
+               return NULL;
+
+       rfkill->cfg = cfg;
+       rfkill->fd = open("/dev/rfkill", O_RDONLY);
+       if (rfkill->fd < 0) {
+               wpa_printf(MSG_INFO, "rfkill: Cannot open RFKILL control "
+                          "device");
+               goto fail;
+       }
+
+       if (fcntl(rfkill->fd, F_SETFL, O_NONBLOCK) < 0) {
+               wpa_printf(MSG_ERROR, "rfkill: Cannot set non-blocking mode: "
+                          "%s", strerror(errno));
+               goto fail2;
+       }
+
+       for (;;) {
+               len = read(rfkill->fd, &event, sizeof(event));
+               if (len < 0) {
+                       if (errno == EAGAIN)
+                               break; /* No more entries */
+                       wpa_printf(MSG_ERROR, "rfkill: Event read failed: %s",
+                                  strerror(errno));
+                       break;
+               }
+               if (len != RFKILL_EVENT_SIZE_V1) {
+                       wpa_printf(MSG_DEBUG, "rfkill: Unexpected event size "
+                                  "%d (expected %d)",
+                                  (int) len, RFKILL_EVENT_SIZE_V1);
+                       continue;
+               }
+               wpa_printf(MSG_DEBUG, "rfkill: initial event: idx=%u type=%d "
+                          "op=%u soft=%u hard=%u",
+                          event.idx, event.type, event.op, event.soft,
+                          event.hard);
+               if (event.op != RFKILL_OP_ADD ||
+                   event.type != RFKILL_TYPE_WLAN)
+                       continue;
+               if (event.hard) {
+                       wpa_printf(MSG_INFO, "rfkill: WLAN hard blocked");
+                       rfkill->blocked = 1;
+               } else if (event.soft) {
+                       wpa_printf(MSG_INFO, "rfkill: WLAN soft blocked");
+                       rfkill->blocked = 1;
+               }
+       }
+
+       eloop_register_read_sock(rfkill->fd, rfkill_receive, rfkill, NULL);
+
+       return rfkill;
+
+fail2:
+       close(rfkill->fd);
+fail:
+       os_free(rfkill);
+       return NULL;
+}
+
+
+void rfkill_deinit(struct rfkill_data *rfkill)
+{
+       if (rfkill == NULL)
+               return;
+
+       if (rfkill->fd >= 0) {
+               eloop_unregister_read_sock(rfkill->fd);
+               close(rfkill->fd);
+       }
+
+       os_free(rfkill->cfg);
+       os_free(rfkill);
+}
+
+
+int rfkill_is_blocked(struct rfkill_data *rfkill)
+{
+       if (rfkill == NULL)
+               return 0;
+
+       return rfkill->blocked;
+}
diff --git a/src/drivers/rfkill.h b/src/drivers/rfkill.h
new file mode 100644 (file)
index 0000000..7a984a6
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Linux rfkill helper functions for driver wrappers
+ * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef RFKILL_H
+#define RFKILL_H
+
+struct rfkill_data;
+
+struct rfkill_config {
+       void *ctx;
+       char ifname[IFNAMSIZ];
+       void (*blocked_cb)(void *ctx);
+       void (*unblocked_cb)(void *ctx);
+};
+
+struct rfkill_data * rfkill_init(struct rfkill_config *cfg);
+void rfkill_deinit(struct rfkill_data *rfkill);
+int rfkill_is_blocked(struct rfkill_data *rfkill);
+
+#endif /* RFKILL_H */
index ad76466..d01f4cb 100644 (file)
@@ -1,4 +1,4 @@
-/* This is based on Linux Wireless Extensions header file from WIRELESS_EXT 18.
+/* This is based on Linux Wireless Extensions header file from WIRELESS_EXT 22.
  * I have just removed kernel related headers and added some typedefs etc. to
  * make this easier to include into user space programs.
  * Jouni Malinen, 2005-03-12.
@@ -8,10 +8,10 @@
 /*
  * This file define a set of standard wireless extensions
  *
- * Version :   19      18.3.05
+ * Version :   22      16.3.07
  *
  * Authors :   Jean Tourrilhes - HPL - <jt@hpl.hp.com>
- * Copyright (c) 1997-2005 Jean Tourrilhes, All Rights Reserved.
+ * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved.
  */
 
 #ifndef _LINUX_WIRELESS_H
 /***************************** INCLUDES *****************************/
 
  /* jkm - replaced linux headers with C library headers, added typedefs */
-#if 0
-/* To minimise problems in user space, I might remove those headers
- * at some point. Jean II */
-#include <linux/types.h>               /* for "caddr_t" et al          */
+#ifdef ANDROID
+#include <linux/types.h>               /* for __u* and __s* typedefs */
 #include <linux/socket.h>              /* for "struct sockaddr" et al  */
 #include <linux/if.h>                  /* for IFNAMSIZ and co... */
-#else
+#else /* ANDROID */
 #include <sys/types.h>
 #include <net/if.h>
 typedef __uint32_t __u32;
@@ -94,7 +92,7 @@ typedef __uint8_t __u8;
 #ifndef __user
 #define __user
 #endif /* __user */
-#endif
+#endif /* ANDROID */
 
 /***************************** VERSION *****************************/
 /*
@@ -103,7 +101,7 @@ typedef __uint8_t __u8;
  * (there is some stuff that will be added in the future...)
  * I just plan to increment with each new version.
  */
-#define WIRELESS_EXT   19
+#define WIRELESS_EXT   22
 
 /*
  * Changes :
@@ -227,6 +225,22 @@ typedef __uint8_t __u8;
  *     - Add IW_QUAL_ALL_UPDATED and IW_QUAL_ALL_INVALID macros
  *     - Add explicit flag to tell stats are in dBm : IW_QUAL_DBM
  *     - Add IW_IOCTL_IDX() and IW_EVENT_IDX() macros
+ *
+ * V19 to V20
+ * ----------
+ *     - RtNetlink requests support (SET/GET)
+ *
+ * V20 to V21
+ * ----------
+ *     - Remove (struct net_device *)->get_wireless_stats()
+ *     - Change length in ESSID and NICK to strlen() instead of strlen()+1
+ *     - Add IW_RETRY_SHORT/IW_RETRY_LONG retry modifiers
+ *     - Power/Retry relative values no longer * 100000
+ *     - Add explicit flag to tell stats are in 802.11k RCPI : IW_QUAL_RCPI
+ *
+ * V21 to V22
+ * ----------
+ *     - Prevent leaking of kernel space in stream on 64 bits.
  */
 
 /**************************** CONSTANTS ****************************/
@@ -340,7 +354,7 @@ typedef __uint8_t __u8;
  * separate range because of collisions with other tools such as
  * 'mii-tool'.
  * We now have 32 commands, so a bit more space ;-).
- * Also, all 'odd' commands are only usable by root and don't return the
+ * Also, all 'even' commands are only usable by root and don't return the
  * content of ifr/iwr to user (but you are not obliged to use the set/get
  * convention, just use every other two command). More details in iwpriv.c.
  * And I repeat : you are not forced to use them with iwpriv, but you
@@ -353,8 +367,10 @@ typedef __uint8_t __u8;
 #define SIOCIWFIRST    0x8B00
 #define SIOCIWLAST     SIOCIWLASTPRIV          /* 0x8BFF */
 #define IW_IOCTL_IDX(cmd)      ((cmd) - SIOCIWFIRST)
+#define IW_HANDLER(id, func)                   \
+       [IW_IOCTL_IDX(id)] = func
 
-/* Even : get (world access), odd : set (root access) */
+/* Odd : get (world access), even : set (root access) */
 #define IW_IS_SET(cmd) (!((cmd) & 0x1))
 #define IW_IS_GET(cmd) ((cmd) & 0x1)
 
@@ -457,6 +473,7 @@ typedef __uint8_t __u8;
 #define IW_MODE_REPEAT 4       /* Wireless Repeater (forwarder) */
 #define IW_MODE_SECOND 5       /* Secondary master/repeater (backup) */
 #define IW_MODE_MONITOR        6       /* Passive monitor (listen only) */
+#define IW_MODE_MESH   7       /* Mesh (IEEE 802.11s) network */
 
 /* Statistics flags (bitmask in updated) */
 #define IW_QUAL_QUAL_UPDATED   0x01    /* Value was updated since last read */
@@ -467,6 +484,7 @@ typedef __uint8_t __u8;
 #define IW_QUAL_QUAL_INVALID   0x10    /* Driver doesn't provide value */
 #define IW_QUAL_LEVEL_INVALID  0x20
 #define IW_QUAL_NOISE_INVALID  0x40
+#define IW_QUAL_RCPI           0x80    /* Level + Noise are 802.11k RCPI */
 #define IW_QUAL_ALL_INVALID    0x70
 
 /* Frequency flags */
@@ -519,10 +537,12 @@ typedef __uint8_t __u8;
 #define IW_RETRY_TYPE          0xF000  /* Type of parameter */
 #define IW_RETRY_LIMIT         0x1000  /* Maximum number of retries*/
 #define IW_RETRY_LIFETIME      0x2000  /* Maximum duration of retries in us */
-#define IW_RETRY_MODIFIER      0x000F  /* Modify a parameter */
+#define IW_RETRY_MODIFIER      0x00FF  /* Modify a parameter */
 #define IW_RETRY_MIN           0x0001  /* Value is a minimum  */
 #define IW_RETRY_MAX           0x0002  /* Value is a maximum */
 #define IW_RETRY_RELATIVE      0x0004  /* Value is not in seconds/ms/us */
+#define IW_RETRY_SHORT         0x0010  /* Value is for short packets  */
+#define IW_RETRY_LONG          0x0020  /* Value is for long packets */
 
 /* Scanning request flags */
 #define IW_SCAN_DEFAULT                0x0000  /* Default scan of the driver */
@@ -540,6 +560,16 @@ typedef __uint8_t __u8;
 /* Maximum size of returned data */
 #define IW_SCAN_MAX_DATA       4096    /* In bytes */
 
+/* Scan capability flags - in (struct iw_range *)->scan_capa */
+#define IW_SCAN_CAPA_NONE              0x00
+#define IW_SCAN_CAPA_ESSID             0x01
+#define IW_SCAN_CAPA_BSSID             0x02
+#define IW_SCAN_CAPA_CHANNEL   0x04
+#define IW_SCAN_CAPA_MODE              0x08
+#define IW_SCAN_CAPA_RATE              0x10
+#define IW_SCAN_CAPA_TYPE              0x20
+#define IW_SCAN_CAPA_TIME              0x40
+
 /* Max number of char in custom event - use multiple of them if needed */
 #define IW_CUSTOM_MAX          256     /* In bytes */
 
@@ -549,6 +579,8 @@ typedef __uint8_t __u8;
 /* MLME requests (SIOCSIWMLME / struct iw_mlme) */
 #define IW_MLME_DEAUTH         0
 #define IW_MLME_DISASSOC       1
+#define IW_MLME_AUTH           2
+#define IW_MLME_ASSOC          3
 
 /* SIOCSIWAUTH/SIOCGIWAUTH struct iw_param flags */
 #define IW_AUTH_INDEX          0x0FFF
@@ -576,12 +608,14 @@ typedef __uint8_t __u8;
 #define IW_AUTH_WPA_VERSION_WPA                0x00000002
 #define IW_AUTH_WPA_VERSION_WPA2       0x00000004
 
-/* IW_AUTH_PAIRWISE_CIPHER and IW_AUTH_GROUP_CIPHER values (bit field) */
+/* IW_AUTH_PAIRWISE_CIPHER, IW_AUTH_GROUP_CIPHER, and IW_AUTH_CIPHER_GROUP_MGMT
+ * values (bit field) */
 #define IW_AUTH_CIPHER_NONE    0x00000001
 #define IW_AUTH_CIPHER_WEP40   0x00000002
 #define IW_AUTH_CIPHER_TKIP    0x00000004
 #define IW_AUTH_CIPHER_CCMP    0x00000008
 #define IW_AUTH_CIPHER_WEP104  0x00000010
+#define IW_AUTH_CIPHER_AES_CMAC        0x00000020
 
 /* IW_AUTH_KEY_MGMT values (bit field) */
 #define IW_AUTH_KEY_MGMT_802_1X        1
@@ -637,7 +671,7 @@ typedef __uint8_t __u8;
  * 32 bit bitmasks. Note : 32 bits = 0x20 = 2^5. */
 #define IW_EVENT_CAPA_BASE(cmd)                ((cmd >= SIOCIWFIRSTPRIV) ? \
                                         (cmd - SIOCIWFIRSTPRIV + 0x60) : \
-                                        (cmd - SIOCSIWCOMMIT))
+                                        (cmd - SIOCIWFIRST))
 #define IW_EVENT_CAPA_INDEX(cmd)       (IW_EVENT_CAPA_BASE(cmd) >> 5)
 #define IW_EVENT_CAPA_MASK(cmd)                (1 << (IW_EVENT_CAPA_BASE(cmd) & 0x1F))
 /* Event capability constants - event autogenerated by the kernel
@@ -676,6 +710,19 @@ struct     iw_point
   __u16                flags;          /* Optional params */
 };
 
+#ifdef __KERNEL__
+#ifdef CONFIG_COMPAT
+
+#include <linux/compat.h>
+
+struct compat_iw_point {
+       compat_caddr_t pointer;
+       __u16 length;
+       __u16 flags;
+};
+#endif
+#endif
+
 /*
  *     A frequency
  *     For numbers lower than 10^9, we encode the number in 'm' and
@@ -970,6 +1017,9 @@ struct     iw_range
        __u16           old_num_channels;
        __u8            old_num_frequency;
 
+       /* Scan capabilities */
+       __u8            scan_capa;      /* IW_SCAN_CAPA_* bit field */
+
        /* Wireless event capability bitmasks */
        __u32           event_capa[6];
 
@@ -1046,7 +1096,7 @@ struct    iw_range
        /* Note : this frequency list doesn't need to fit channel numbers,
         * because each entry contain its channel index */
 
-       __u32           enc_capa; /* IW_ENC_CAPA_* bit field */
+       __u32           enc_capa;       /* IW_ENC_CAPA_* bit field */
 };
 
 /*
@@ -1073,7 +1123,7 @@ struct    iw_priv_args
  */
 struct iw_event
 {
-       __u16           len;                    /* Real lenght of this stuff */
+       __u16           len;                    /* Real length of this stuff */
        __u16           cmd;                    /* Wireless IOCTL */
        union iwreq_data        u;              /* IOCTL fixed payload */
 };
@@ -1096,4 +1146,38 @@ struct iw_event
 #define IW_EV_POINT_LEN        (IW_EV_LCP_LEN + sizeof(struct iw_point) - \
                         IW_EV_POINT_OFF)
 
+#ifdef __KERNEL__
+#ifdef CONFIG_COMPAT
+struct __compat_iw_event {
+       __u16           len;                    /* Real length of this stuff */
+       __u16           cmd;                    /* Wireless IOCTL */
+       compat_caddr_t  pointer;
+};
+#define IW_EV_COMPAT_LCP_LEN offsetof(struct __compat_iw_event, pointer)
+#define IW_EV_COMPAT_POINT_OFF offsetof(struct compat_iw_point, length)
+
+/* Size of the various events for compat */
+#define IW_EV_COMPAT_CHAR_LEN  (IW_EV_COMPAT_LCP_LEN + IFNAMSIZ)
+#define IW_EV_COMPAT_UINT_LEN  (IW_EV_COMPAT_LCP_LEN + sizeof(__u32))
+#define IW_EV_COMPAT_FREQ_LEN  (IW_EV_COMPAT_LCP_LEN + sizeof(struct iw_freq))
+#define IW_EV_COMPAT_PARAM_LEN (IW_EV_COMPAT_LCP_LEN + sizeof(struct iw_param))
+#define IW_EV_COMPAT_ADDR_LEN  (IW_EV_COMPAT_LCP_LEN + sizeof(struct sockaddr))
+#define IW_EV_COMPAT_QUAL_LEN  (IW_EV_COMPAT_LCP_LEN + sizeof(struct iw_quality))
+#define IW_EV_COMPAT_POINT_LEN \
+       (IW_EV_COMPAT_LCP_LEN + sizeof(struct compat_iw_point) - \
+        IW_EV_COMPAT_POINT_OFF)
+#endif
+#endif
+
+/* Size of the Event prefix when packed in stream */
+#define IW_EV_LCP_PK_LEN       (4)
+/* Size of the various events when packed in stream */
+#define IW_EV_CHAR_PK_LEN      (IW_EV_LCP_PK_LEN + IFNAMSIZ)
+#define IW_EV_UINT_PK_LEN      (IW_EV_LCP_PK_LEN + sizeof(__u32))
+#define IW_EV_FREQ_PK_LEN      (IW_EV_LCP_PK_LEN + sizeof(struct iw_freq))
+#define IW_EV_PARAM_PK_LEN     (IW_EV_LCP_PK_LEN + sizeof(struct iw_param))
+#define IW_EV_ADDR_PK_LEN      (IW_EV_LCP_PK_LEN + sizeof(struct sockaddr))
+#define IW_EV_QUAL_PK_LEN      (IW_EV_LCP_PK_LEN + sizeof(struct iw_quality))
+#define IW_EV_POINT_PK_LEN     (IW_EV_LCP_PK_LEN + 4)
+
 #endif /* _LINUX_WIRELESS_H */
index 0efe7ab..3035301 100644 (file)
@@ -68,6 +68,7 @@ typedef enum {
        EAP_TYPE_IKEV2 = 49 /* RFC 5106 */,
        EAP_TYPE_AKA_PRIME = 50 /* draft-arkko-eap-aka-kdf-10.txt */,
        EAP_TYPE_GPSK = 51 /* RFC 5433 */,
+       EAP_TYPE_PWD = 52 /* RFC 5931 */,
        EAP_TYPE_EXPANDED = 254 /* RFC 3748 */
 } EapType;
 
index 3a64b8e..8a701d2 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * EAP-PEAP common routines
- * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008-2011, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -18,9 +18,9 @@
 #include "crypto/sha1.h"
 #include "eap_peap_common.h"
 
-void peap_prfplus(int version, const u8 *key, size_t key_len,
-                 const char *label, const u8 *seed, size_t seed_len,
-                 u8 *buf, size_t buf_len)
+int peap_prfplus(int version, 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;
@@ -75,7 +75,8 @@ void peap_prfplus(int version, const u8 *key, size_t key_len,
        while (pos < buf_len) {
                counter++;
                plen = buf_len - pos;
-               hmac_sha1_vector(key, key_len, 5, addr, len, hash);
+               if (hmac_sha1_vector(key, key_len, 5, addr, len, hash) < 0)
+                       return -1;
                if (plen >= SHA1_MAC_LEN) {
                        os_memcpy(&buf[pos], hash, SHA1_MAC_LEN);
                        pos += SHA1_MAC_LEN;
@@ -85,4 +86,6 @@ void peap_prfplus(int version, const u8 *key, size_t key_len,
                }
                len[0] = SHA1_MAC_LEN;
        }
+
+       return 0;
 }
index f59afb0..f182078 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * EAP-PEAP common routines
- * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008-2011, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -15,8 +15,8 @@
 #ifndef EAP_PEAP_COMMON_H
 #define EAP_PEAP_COMMON_H
 
-void peap_prfplus(int version, const u8 *key, size_t key_len,
-                 const char *label, const u8 *seed, size_t seed_len,
-                 u8 *buf, size_t buf_len);
+int peap_prfplus(int version, const u8 *key, size_t key_len,
+                const char *label, const u8 *seed, size_t seed_len,
+                u8 *buf, size_t buf_len);
 
 #endif /* EAP_PEAP_COMMON_H */
diff --git a/src/eap_common/eap_pwd_common.c b/src/eap_common/eap_pwd_common.c
new file mode 100644 (file)
index 0000000..0dbdff2
--- /dev/null
@@ -0,0 +1,330 @@
+/*
+ * EAP server/peer: EAP-pwd shared routines
+ * Copyright (c) 2010, Dan Harkins <dharkins@lounge.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the BSD license.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include "common.h"
+#include "eap_defs.h"
+#include "eap_pwd_common.h"
+
+/* The random function H(x) = HMAC-SHA256(0^32, x) */
+void H_Init(HMAC_CTX *ctx)
+{
+       u8 allzero[SHA256_DIGEST_LENGTH];
+
+       os_memset(allzero, 0, SHA256_DIGEST_LENGTH);
+       HMAC_Init(ctx, allzero, SHA256_DIGEST_LENGTH, EVP_sha256());
+}
+
+
+void H_Update(HMAC_CTX *ctx, const u8 *data, int len)
+{
+       HMAC_Update(ctx, data, len);
+}
+
+
+void H_Final(HMAC_CTX *ctx, u8 *digest)
+{
+       unsigned int mdlen = SHA256_DIGEST_LENGTH;
+
+       HMAC_Final(ctx, digest, &mdlen);
+       HMAC_CTX_cleanup(ctx);
+}
+
+
+/* a counter-based KDF based on NIST SP800-108 */
+void eap_pwd_kdf(u8 *key, int keylen, u8 *label, int labellen,
+                u8 *result, int resultbitlen)
+{
+       HMAC_CTX hctx;
+       unsigned char digest[SHA256_DIGEST_LENGTH];
+       u16 i, ctr, L;
+       int resultbytelen, len = 0;
+       unsigned int mdlen = SHA256_DIGEST_LENGTH;
+       unsigned char mask = 0xff;
+
+       resultbytelen = (resultbitlen + 7)/8;
+       ctr = 0;
+       L = htons(resultbitlen);
+       while (len < resultbytelen) {
+               ctr++; i = htons(ctr);
+               HMAC_Init(&hctx, key, keylen, EVP_sha256());
+               if (ctr > 1)
+                       HMAC_Update(&hctx, digest, mdlen);
+               HMAC_Update(&hctx, (u8 *) &i, sizeof(u16));
+               HMAC_Update(&hctx, label, labellen);
+               HMAC_Update(&hctx, (u8 *) &L, sizeof(u16));
+               HMAC_Final(&hctx, digest, &mdlen);
+               if ((len + (int) mdlen) > resultbytelen)
+                       os_memcpy(result + len, digest, resultbytelen - len);
+               else
+                       os_memcpy(result + len, digest, mdlen);
+               len += mdlen;
+               HMAC_CTX_cleanup(&hctx);
+       }
+
+       /* since we're expanding to a bit length, mask off the excess */
+       if (resultbitlen % 8) {
+               mask <<= (8 - (resultbitlen % 8));
+               result[resultbytelen - 1] &= mask;
+       }
+}
+
+
+/*
+ * 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,
+                            u8 *password, int password_len,
+                            u8 *id_server, int id_server_len,
+                            u8 *id_peer, int id_peer_len, u8 *token)
+{
+       BIGNUM *x_candidate = NULL, *rnd = NULL, *cofactor = NULL;
+       HMAC_CTX ctx;
+       unsigned char pwe_digest[SHA256_DIGEST_LENGTH], *prfbuf = NULL, ctr;
+       int nid, is_odd, primebitlen, primebytelen, ret = 0;
+
+       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;
+        case 25:
+               nid = NID_X9_62_prime192v1;
+               break;
+        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 > 10) {
+                       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)
+                */
+               H_Init(&ctx);
+               H_Update(&ctx, token, sizeof(u32));
+               H_Update(&ctx, id_peer, id_peer_len);
+               H_Update(&ctx, id_server, id_server_len);
+               H_Update(&ctx, password, password_len);
+               H_Update(&ctx, &ctr, sizeof(ctr));
+               H_Final(&ctx, pwe_digest);
+
+               BN_bin2bn(pwe_digest, SHA256_DIGEST_LENGTH, rnd);
+
+               eap_pwd_kdf(pwe_digest, SHA256_DIGEST_LENGTH,
+                           (unsigned char *) "EAP-pwd Hunting And Pecking",
+                           os_strlen("EAP-pwd Hunting And Pecking"),
+                           prfbuf, primebitlen);
+
+               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);
+               EC_POINT_free(grp->pwe);
+               BN_free(grp->order);
+               BN_free(grp->prime);
+               os_free(grp);
+               grp = NULL;
+               ret = 1;
+       }
+       /* cleanliness and order.... */
+       BN_free(cofactor);
+       BN_free(x_candidate);
+       BN_free(rnd);
+       os_free(prfbuf);
+
+       return ret;
+}
+
+
+int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, BIGNUM *k,
+                BIGNUM *peer_scalar, BIGNUM *server_scalar,
+                u8 *commit_peer, u8 *commit_server,
+                u32 *ciphersuite, u8 *msk, u8 *emsk)
+{
+       HMAC_CTX ctx;
+       u8 mk[SHA256_DIGEST_LENGTH], *cruft;
+       u8 session_id[SHA256_DIGEST_LENGTH + 1];
+       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;
+       H_Init(&ctx);
+       H_Update(&ctx, (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);
+       H_Update(&ctx, 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);
+       H_Update(&ctx, cruft, BN_num_bytes(grp->order));
+       H_Final(&ctx, &session_id[1]);
+
+       /* then compute MK = H(k | commit-peer | commit-server) */
+       H_Init(&ctx);
+       offset = BN_num_bytes(grp->prime) - BN_num_bytes(k);
+       os_memset(cruft, 0, BN_num_bytes(grp->prime));
+       BN_bn2bin(k, cruft + offset);
+       H_Update(&ctx, cruft, BN_num_bytes(grp->prime));
+       H_Update(&ctx, commit_peer, SHA256_DIGEST_LENGTH);
+       H_Update(&ctx, commit_server, SHA256_DIGEST_LENGTH);
+       H_Final(&ctx, mk);
+
+       /* stretch the mk with the session-id to get MSK | EMSK */
+       eap_pwd_kdf(mk, SHA256_DIGEST_LENGTH,
+                   session_id, SHA256_DIGEST_LENGTH+1,
+                   msk_emsk, (EAP_MSK_LEN + EAP_EMSK_LEN) * 8);
+
+       os_memcpy(msk, msk_emsk, EAP_MSK_LEN);
+       os_memcpy(emsk, msk_emsk + EAP_MSK_LEN, EAP_EMSK_LEN);
+
+       os_free(cruft);
+
+       return 1;
+}
diff --git a/src/eap_common/eap_pwd_common.h b/src/eap_common/eap_pwd_common.h
new file mode 100644 (file)
index 0000000..971386d
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * EAP server/peer: EAP-pwd shared definitions
+ * Copyright (c) 2009, Dan Harkins <dharkins@lounge.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the BSD license.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef EAP_PWD_COMMON_H
+#define EAP_PWD_COMMON_H
+
+#include <openssl/bn.h>
+#include <openssl/sha.h>
+#include <openssl/ec.h>
+#include <openssl/evp.h>
+#include <openssl/hmac.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
+ */
+struct eap_pwd_hdr {
+       u8 l_bit:1;
+       u8 m_bit:1;
+       u8 exch:6;
+       u8 total_length[0];         /* included when l_bit is set */
+} STRUCT_PACKED;
+
+#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)->lm_exch & 0x80)
+#define EAP_PWD_SET_LENGTH_BIT(x)       ((x)->lm_exch |= 0x80)
+#define EAP_PWD_GET_MORE_BIT(x)         ((x)->lm_exch & 0x40)
+#define EAP_PWD_SET_MORE_BIT(x)         ((x)->lm_exch |= 0x40)
+#define EAP_PWD_GET_EXCHANGE(x)         ((x)->lm_exch & 0x3f)
+#define EAP_PWD_SET_EXCHANGE(x,y)       ((x)->lm_exch |= (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 *, u16, u8 *, int, u8 *, int, u8 *,
+                            int, u8 *);
+int compute_keys(EAP_PWD_group *, BN_CTX *, BIGNUM *, BIGNUM *, BIGNUM *,
+                u8 *, u8 *, u32 *, u8 *, u8 *);
+void H_Init(HMAC_CTX *);
+void H_Update(HMAC_CTX *, const u8 *, int);
+void H_Final(HMAC_CTX *, u8 *);
+
+#endif  /* EAP_PWD_COMMON_H */
index 56b4ded..0b37b0b 100644 (file)
@@ -20,6 +20,7 @@
 #include "crypto/crypto.h"
 #include "crypto/sha1.h"
 #include "crypto/sha256.h"
+#include "crypto/random.h"
 #include "eap_common/eap_defs.h"
 #include "eap_common/eap_sim_common.h"
 
@@ -1121,8 +1122,8 @@ int eap_sim_msg_add_encr_start(struct eap_sim_msg *msg, u8 attr_iv,
        if (pos == NULL)
                return -1;
        msg->iv = (pos - wpabuf_head_u8(msg->buf)) + 4;
-       if (os_get_random(wpabuf_mhead_u8(msg->buf) + msg->iv,
-                         EAP_SIM_IV_LEN)) {
+       if (random_get_bytes(wpabuf_mhead_u8(msg->buf) + msg->iv,
+                            EAP_SIM_IV_LEN)) {
                msg->iv = 0;
                return -1;
        }
index 67754d8..003c288 100644 (file)
@@ -18,6 +18,7 @@
 #include "crypto/crypto.h"
 #include "crypto/md5.h"
 #include "crypto/sha1.h"
+#include "crypto/random.h"
 #include "ikev2_common.h"
 
 
@@ -639,7 +640,7 @@ int ikev2_build_encrypted(int encr_id, int integ_id, struct ikev2_keys *keys,
        phdr->flags = 0;
 
        iv = wpabuf_put(msg, iv_len);
-       if (os_get_random(iv, iv_len)) {
+       if (random_get_bytes(iv, iv_len)) {
                wpa_printf(MSG_INFO, "IKEV2: Could not generate IV");
                return -1;
        }
index c96a070..31a2b0d 100644 (file)
@@ -139,7 +139,7 @@ enum {
        IKEV2_TRANSFORM_ESN = 5
 };
 
-/* IKEv2 Tranform Type 1 (Encryption Algorithm) */
+/* IKEv2 Transform Type 1 (Encryption Algorithm) */
 enum {
        ENCR_DES_IV64 = 1,
        ENCR_DES = 2,
index b9f186b..cac85db 100644 (file)
@@ -37,6 +37,7 @@
 #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,
@@ -146,6 +147,7 @@ SM_STATE(EAP, INITIALIZE)
        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);
@@ -268,6 +270,8 @@ SM_STATE(EAP, GET_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);
@@ -891,6 +895,11 @@ static int eap_sm_imsi_identity(struct eap_sm *sm,
 
        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;
+       }
+
        for (i = 0; m && (m[i].vendor != EAP_VENDOR_IETF ||
                          m[i].method != EAP_TYPE_NONE); i++) {
                if (m[i].vendor == EAP_VENDOR_IETF &&
@@ -1165,7 +1174,6 @@ static void eap_peer_sm_tls_event(void *ctx, enum tls_event ev,
 {
        struct eap_sm *sm = ctx;
        char *hash_hex = NULL;
-       char *cert_hex = NULL;
 
        switch (ev) {
        case TLS_CERT_CHAIN_FAILURE:
@@ -1177,6 +1185,9 @@ static void eap_peer_sm_tls_event(void *ctx, enum tls_event ev,
                        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);
@@ -1186,31 +1197,15 @@ static void eap_peer_sm_tls_event(void *ctx, enum tls_event ev,
                                                 data->peer_cert.hash_len);
                        }
                }
-               wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PEER_CERT
-                       "depth=%d subject='%s'%s%s",
-                       data->peer_cert.depth, data->peer_cert.subject,
-                       hash_hex ? " hash=" : "", hash_hex ? hash_hex : "");
-
-               if (data->peer_cert.cert) {
-                       size_t len = wpabuf_len(data->peer_cert.cert) * 2 + 1;
-                       cert_hex = os_malloc(len);
-                       if (cert_hex == NULL)
-                               break;
-                       wpa_snprintf_hex(cert_hex, len,
-                                        wpabuf_head(data->peer_cert.cert),
-                                        wpabuf_len(data->peer_cert.cert));
-                       wpa_msg_ctrl(sm->msg_ctx, MSG_INFO,
-                                    WPA_EVENT_EAP_PEER_CERT
-                                    "depth=%d subject='%s' cert=%s",
-                                    data->peer_cert.depth,
-                                    data->peer_cert.subject,
-                                    cert_hex);
-               }
+
+               sm->eapol_cb->notify_cert(sm->eapol_ctx,
+                                         data->peer_cert.depth,
+                                         data->peer_cert.subject,
+                                         hash_hex, data->peer_cert.cert);
                break;
        }
 
        os_free(hash_hex);
-       os_free(cert_hex);
 }
 
 
@@ -1241,7 +1236,7 @@ struct eap_sm * eap_peer_sm_init(void *eapol_ctx,
        sm->eapol_ctx = eapol_ctx;
        sm->eapol_cb = eapol_cb;
        sm->msg_ctx = msg_ctx;
-       sm->ClientTimeout = 60;
+       sm->ClientTimeout = EAP_CLIENT_TIMEOUT_DEFAULT;
        sm->wps = conf->wps;
 
        os_memset(&tlsconf, 0, sizeof(tlsconf));
@@ -1253,6 +1248,7 @@ struct eap_sm * eap_peer_sm_init(void *eapol_ctx,
 #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 "
@@ -1477,16 +1473,11 @@ int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen, int verbose)
 
 
 #if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
-typedef enum {
-       TYPE_IDENTITY, TYPE_PASSWORD, TYPE_OTP, TYPE_PIN, TYPE_NEW_PASSWORD,
-       TYPE_PASSPHRASE
-} eap_ctrl_req_type;
-
-static void eap_sm_request(struct eap_sm *sm, eap_ctrl_req_type type,
+static void eap_sm_request(struct eap_sm *sm, enum wpa_ctrl_req_type field,
                           const char *msg, size_t msglen)
 {
        struct eap_peer_config *config;
-       char *field, *txt, *tmp;
+       char *txt = NULL, *tmp;
 
        if (sm == NULL)
                return;
@@ -1494,29 +1485,20 @@ static void eap_sm_request(struct eap_sm *sm, eap_ctrl_req_type type,
        if (config == NULL)
                return;
 
-       switch (type) {
-       case TYPE_IDENTITY:
-               field = "IDENTITY";
-               txt = "Identity";
+       switch (field) {
+       case WPA_CTRL_REQ_EAP_IDENTITY:
                config->pending_req_identity++;
                break;
-       case TYPE_PASSWORD:
-               field = "PASSWORD";
-               txt = "Password";
+       case WPA_CTRL_REQ_EAP_PASSWORD:
                config->pending_req_password++;
                break;
-       case TYPE_NEW_PASSWORD:
-               field = "NEW_PASSWORD";
-               txt = "New Password";
+       case WPA_CTRL_REQ_EAP_NEW_PASSWORD:
                config->pending_req_new_password++;
                break;
-       case TYPE_PIN:
-               field = "PIN";
-               txt = "PIN";
+       case WPA_CTRL_REQ_EAP_PIN:
                config->pending_req_pin++;
                break;
-       case TYPE_OTP:
-               field = "OTP";
+       case WPA_CTRL_REQ_EAP_OTP:
                if (msg) {
                        tmp = os_malloc(msglen + 3);
                        if (tmp == NULL)
@@ -1535,9 +1517,7 @@ static void eap_sm_request(struct eap_sm *sm, eap_ctrl_req_type type,
                        txt = config->pending_req_otp;
                }
                break;
-       case TYPE_PASSPHRASE:
-               field = "PASSPHRASE";
-               txt = "Private key passphrase";
+       case WPA_CTRL_REQ_EAP_PASSPHRASE:
                config->pending_req_passphrase++;
                break;
        default:
@@ -1551,6 +1531,13 @@ static void eap_sm_request(struct eap_sm *sm, eap_ctrl_req_type type,
 #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)
@@ -1563,7 +1550,7 @@ static void eap_sm_request(struct eap_sm *sm, eap_ctrl_req_type type,
  */
 void eap_sm_request_identity(struct eap_sm *sm)
 {
-       eap_sm_request(sm, TYPE_IDENTITY, NULL, 0);
+       eap_sm_request(sm, WPA_CTRL_REQ_EAP_IDENTITY, NULL, 0);
 }
 
 
@@ -1578,7 +1565,7 @@ void eap_sm_request_identity(struct eap_sm *sm)
  */
 void eap_sm_request_password(struct eap_sm *sm)
 {
-       eap_sm_request(sm, TYPE_PASSWORD, NULL, 0);
+       eap_sm_request(sm, WPA_CTRL_REQ_EAP_PASSWORD, NULL, 0);
 }
 
 
@@ -1593,7 +1580,7 @@ void eap_sm_request_password(struct eap_sm *sm)
  */
 void eap_sm_request_new_password(struct eap_sm *sm)
 {
-       eap_sm_request(sm, TYPE_NEW_PASSWORD, NULL, 0);
+       eap_sm_request(sm, WPA_CTRL_REQ_EAP_NEW_PASSWORD, NULL, 0);
 }
 
 
@@ -1608,7 +1595,7 @@ void eap_sm_request_new_password(struct eap_sm *sm)
  */
 void eap_sm_request_pin(struct eap_sm *sm)
 {
-       eap_sm_request(sm, TYPE_PIN, NULL, 0);
+       eap_sm_request(sm, WPA_CTRL_REQ_EAP_PIN, NULL, 0);
 }
 
 
@@ -1624,7 +1611,7 @@ 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)
 {
-       eap_sm_request(sm, TYPE_OTP, msg, msg_len);
+       eap_sm_request(sm, WPA_CTRL_REQ_EAP_OTP, msg, msg_len);
 }
 
 
@@ -1639,7 +1626,7 @@ 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)
 {
-       eap_sm_request(sm, TYPE_PASSPHRASE, NULL, 0);
+       eap_sm_request(sm, WPA_CTRL_REQ_EAP_PASSPHRASE, NULL, 0);
 }
 
 
@@ -1923,6 +1910,15 @@ const char * eap_get_config_phase2(struct eap_sm *sm)
 }
 
 
+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()
index 40d0b69..f35197f 100644 (file)
@@ -216,11 +216,22 @@ struct eapol_callbacks {
        /**
         * eap_param_needed - Notify that EAP parameter is needed
         * @ctx: eapol_ctx from eap_peer_sm_init() call
-        * @field: Field name (e.g., "IDENTITY")
+        * @field: Field indicator (e.g., WPA_CTRL_REQ_EAP_IDENTITY)
         * @txt: User readable text describing the required parameter
         */
-       void (*eap_param_needed)(void *ctx, const char *field,
+       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
+        * @cert_hash: SHA-256 hash of the certificate
+        * @cert: Peer certificate
+        */
+       void (*notify_cert)(void *ctx, int depth, const char *subject,
+                           const char *cert_hash, const struct wpabuf *cert);
 };
 
 /**
@@ -251,6 +262,11 @@ struct eap_config {
         * 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,
@@ -261,6 +277,7 @@ 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);
index 182f01a..6ac6b64 100644 (file)
@@ -235,21 +235,20 @@ static int eap_aka_umts_auth(struct eap_sm *sm, struct eap_aka_data *data)
 
 static void eap_aka_clear_identities(struct eap_aka_data *data, int id)
 {
-       wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old%s%s%s",
-                  id & CLEAR_PSEUDONYM ? " pseudonym" : "",
-                  id & CLEAR_REAUTH_ID ? " reauth_id" : "",
-                  id & CLEAR_EAP_ID ? " eap_id" : "");
-       if (id & CLEAR_PSEUDONYM) {
+       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;
        }
-       if (id & CLEAR_REAUTH_ID) {
+       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) {
+       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;
@@ -257,24 +256,44 @@ static void eap_aka_clear_identities(struct eap_aka_data *data, int id)
 }
 
 
-static int eap_aka_learn_ids(struct eap_aka_data *data,
+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);
-               data->pseudonym = os_malloc(attr->next_pseudonym_len);
+               /* 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);
-               data->pseudonym_len = attr->next_pseudonym_len;
-               wpa_hexdump_ascii(MSG_DEBUG,
-                                 "EAP-AKA: (encr) AT_NEXT_PSEUDONYM",
-                                 data->pseudonym,
-                                 data->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;
        }
 
        if (attr->next_reauth_id) {
@@ -283,6 +302,7 @@ static int eap_aka_learn_ids(struct eap_aka_data *data,
                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,
@@ -880,11 +900,11 @@ static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm,
                                            EAP_AKA_UNABLE_TO_PROCESS_PACKET);
        }
 
-       /* Old reauthentication and pseudonym identities must not be used
-        * anymore. In other words, if no new identities are received, full
-        * authentication will be used on next reauthentication. */
-       eap_aka_clear_identities(data, CLEAR_PSEUDONYM | CLEAR_REAUTH_ID |
-                                CLEAR_EAP_ID);
+       /* 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(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
 
        if (attr->encr_data) {
                u8 *decrypted;
@@ -895,7 +915,7 @@ static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm,
                        return eap_aka_client_error(
                                data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET);
                }
-               eap_aka_learn_ids(data, &eattr);
+               eap_aka_learn_ids(sm, data, &eattr);
                os_free(decrypted);
        }
 
@@ -1113,7 +1133,7 @@ static struct wpabuf * eap_aka_process_reauthentication(
                                           data->msk, data->emsk);
        }
        eap_aka_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
-       eap_aka_learn_ids(data, &eattr);
+       eap_aka_learn_ids(sm, data, &eattr);
 
        if (data->result_ind && attr->result_ind)
                data->use_result_ind = 1;
index 5d3e69d..3cfb41a 100644 (file)
@@ -444,8 +444,9 @@ static int eap_fast_phase2_request(struct eap_sm *sm,
                return 0;
        }
 
-       if (data->phase2_priv == NULL &&
-           eap_fast_init_phase2_method(sm, data) < 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;
@@ -542,7 +543,7 @@ static struct wpabuf * eap_fast_tlv_pac_ack(void)
 
 static struct wpabuf * eap_fast_process_eap_payload_tlv(
        struct eap_sm *sm, struct eap_fast_data *data,
-       struct eap_method_ret *ret, const struct eap_hdr *req,
+       struct eap_method_ret *ret,
        u8 *eap_payload_tlv, size_t eap_payload_tlv_len)
 {
        struct eap_hdr *hdr;
@@ -1037,11 +1038,15 @@ static struct wpabuf * eap_fast_process_pac(struct eap_sm *sm,
        } else {
                /*
                 * This is PAC refreshing, i.e., normal authentication that is
-                * expected to be completed with an EAP-Success.
+                * 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_UNCOND_SUCC;
+               ret->decision = DECISION_COND_SUCC;
        }
        ret->methodState = METHOD_DONE;
        return eap_fast_tlv_pac_ack();
@@ -1184,7 +1189,7 @@ static int eap_fast_process_decrypted(struct eap_sm *sm,
 
        if (tlv.eap_payload_tlv) {
                tmp = eap_fast_process_eap_payload_tlv(
-                       sm, data, ret, req, tlv.eap_payload_tlv,
+                       sm, data, ret, tlv.eap_payload_tlv,
                        tlv.eap_payload_tlv_len);
                resp = wpabuf_concat(resp, tmp);
        }
index 541cce5..4037288 100644 (file)
@@ -497,6 +497,7 @@ static void eap_fast_write(char **buf, char **pos, size_t *buf_len,
                        *buf = NULL;
                        return;
                }
+               *pos = nbuf + (*pos - *buf);
                *buf = nbuf;
                *buf_len += need;
        }
index f6a1955..5037c60 100644 (file)
@@ -15,6 +15,7 @@
 #include "includes.h"
 
 #include "common.h"
+#include "crypto/random.h"
 #include "eap_peer/eap_i.h"
 #include "eap_common/eap_gpsk_common.h"
 
@@ -326,7 +327,7 @@ static struct wpabuf * eap_gpsk_send_gpsk_2(struct eap_gpsk_data *data,
        wpabuf_put_be16(resp, data->id_server_len);
        wpabuf_put_data(resp, data->id_server, data->id_server_len);
 
-       if (os_get_random(data->rand_peer, EAP_GPSK_RAND_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);
index e7c826e..afca611 100644 (file)
@@ -345,6 +345,7 @@ 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 *
index a7c94a4..6a8efcd 100644 (file)
@@ -17,6 +17,7 @@
 #include "common.h"
 #include "crypto/ms_funcs.h"
 #include "crypto/crypto.h"
+#include "crypto/random.h"
 #include "eap_i.h"
 
 #define LEAP_VERSION 1
@@ -167,7 +168,7 @@ static struct wpabuf * eap_leap_process_success(struct eap_sm *sm, void *priv,
        wpabuf_put_u8(resp, 0); /* unused */
        wpabuf_put_u8(resp, LEAP_CHALLENGE_LEN);
        pos = wpabuf_put(resp, LEAP_CHALLENGE_LEN);
-       if (os_get_random(pos, LEAP_CHALLENGE_LEN)) {
+       if (random_get_bytes(pos, LEAP_CHALLENGE_LEN)) {
                wpa_printf(MSG_WARNING, "EAP-LEAP: Failed to read random data "
                           "for challenge");
                wpabuf_free(resp);
index 3b0af05..937fd45 100644 (file)
@@ -77,6 +77,8 @@ EapType eap_peer_get_type(const char *name, int *vendor)
 const char * eap_get_name(int vendor, EapType type)
 {
        struct eap_method *m;
+       if (vendor == EAP_VENDOR_IETF && type == EAP_TYPE_EXPANDED)
+               return "expanded";
        for (m = eap_methods; m; m = m->next) {
                if (m->vendor == vendor && m->method == type)
                        return m->name;
index 384c61b..4330b57 100644 (file)
@@ -109,5 +109,6 @@ int eap_peer_wsc_register(void);
 int eap_peer_ikev2_register(void);
 int eap_peer_vendor_test_register(void);
 int eap_peer_tnc_register(void);
+int eap_peer_pwd_register(void);
 
 #endif /* EAP_METHODS_H */
index cd410d9..321e9f7 100644 (file)
@@ -23,6 +23,7 @@
 
 #include "common.h"
 #include "crypto/ms_funcs.h"
+#include "crypto/random.h"
 #include "common/wpa_ctrl.h"
 #include "mschapv2.h"
 #include "eap_i.h"
@@ -199,7 +200,7 @@ static struct wpabuf * eap_mschapv2_challenge_reply(
                           "in Phase 1");
                peer_challenge = data->peer_challenge;
                os_memset(r->peer_challenge, 0, MSCHAPV2_CHAL_LEN);
-       } else if (os_get_random(peer_challenge, MSCHAPV2_CHAL_LEN)) {
+       } else if (random_get_bytes(peer_challenge, MSCHAPV2_CHAL_LEN)) {
                wpabuf_free(resp);
                return NULL;
        }
@@ -564,7 +565,7 @@ static struct wpabuf * eap_mschapv2_change_password(
        }
 
        /* Peer-Challenge */
-       if (os_get_random(cp->peer_challenge, MSCHAPV2_CHAL_LEN))
+       if (random_get_bytes(cp->peer_challenge, MSCHAPV2_CHAL_LEN))
                goto fail;
 
        /* Reserved, must be zero */
index 2e04831..d42a7f8 100644 (file)
@@ -15,6 +15,7 @@
 #include "includes.h"
 
 #include "common.h"
+#include "crypto/random.h"
 #include "eap_common/eap_pax_common.h"
 #include "eap_i.h"
 
@@ -174,7 +175,7 @@ static struct wpabuf * eap_pax_process_std_1(struct eap_pax_data *data,
                            pos, left);
        }
 
-       if (os_get_random(data->rand.r.y, EAP_PAX_RAND_LEN)) {
+       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;
index 2b72084..7cb8213 100644 (file)
@@ -196,7 +196,7 @@ static void eap_peap_deinit(struct eap_sm *sm, void *priv)
  * @nak_type: TLV type (EAP_TLV_*)
  * Returns: Buffer to the allocated EAP-TLV NAK message or %NULL on failure
  *
- * This funtion builds an EAP-TLV NAK message. The caller is responsible for
+ * 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)
@@ -285,8 +285,10 @@ static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data)
         * in the end of the label just before ISK; is that just a typo?)
         */
        wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TempKey", tk, 40);
-       peap_prfplus(data->peap_version, tk, 40, "Inner Methods Compound Keys",
-                    isk, sizeof(isk), imck, sizeof(imck));
+       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));
 
@@ -346,8 +348,8 @@ static int eap_tlv_add_cryptobinding(struct eap_sm *sm,
  * @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 funtion builds an EAP-TLV Result message. The caller is responsible for
- * freeing the returned buffer.
+ * 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,
@@ -1247,9 +1249,12 @@ static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len)
                 * termination for this label while the one used for deriving
                 * IPMK|CMK did not use null termination.
                 */
-               peap_prfplus(data->peap_version, data->ipmk, 40,
-                            "Session Key Generating Function",
-                            (u8 *) "\00", 1, csk, sizeof(csk));
+               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",
index ccf871e..592ef13 100644 (file)
@@ -19,6 +19,7 @@
 
 #include "common.h"
 #include "crypto/aes_wrap.h"
+#include "crypto/random.h"
 #include "eap_common/eap_psk_common.h"
 #include "eap_i.h"
 
@@ -130,7 +131,7 @@ static struct wpabuf * eap_psk_process_1(struct eap_psk_data *data,
        wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: ID_S",
                          data->id_s, data->id_s_len);
 
-       if (os_get_random(data->rand_p, EAP_PSK_RAND_LEN)) {
+       if (random_get_bytes(data->rand_p, EAP_PSK_RAND_LEN)) {
                wpa_printf(MSG_ERROR, "EAP-PSK: Failed to get random data");
                ret->ignore = TRUE;
                return NULL;
diff --git a/src/eap_peer/eap_pwd.c b/src/eap_peer/eap_pwd.c
new file mode 100644 (file)
index 0000000..6511a66
--- /dev/null
@@ -0,0 +1,763 @@
+/*
+ * EAP peer method: EAP-pwd (RFC 5931)
+ * Copyright (c) 2010, Dan Harkins <dharkins@lounge.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the BSD license.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.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, FAILURE
+       } state;
+       u8 *id_peer;
+       size_t id_peer_len;
+       u8 *id_server;
+       size_t id_server_len;
+       u8 *password;
+       size_t password_len;
+       u16 group_num;
+       EAP_PWD_group *grp;
+
+       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];
+
+       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:
+               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_INFO, "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;
+
+       password = eap_get_config_password(sm, &password_len);
+       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);
+               os_free(data->id_peer);
+               os_free(data);
+               return NULL;
+       }
+       os_memcpy(data->password, password, password_len);
+       data->password_len = password_len;
+
+       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_free(data->private_value);
+       BN_free(data->server_scalar);
+       BN_free(data->my_scalar);
+       BN_free(data->k);
+       BN_CTX_free(data->bnctx);
+       EC_POINT_free(data->my_element);
+       EC_POINT_free(data->server_element);
+       os_free(data->id_peer);
+       os_free(data->id_server);
+       os_free(data->password);
+       if (data->grp) {
+               EC_GROUP_free(data->grp->group);
+               EC_POINT_free(data->grp->pwe);
+               BN_free(data->grp->order);
+               BN_free(data->grp->prime);
+               os_free(data->grp);
+       }
+       os_free(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 struct wpabuf *
+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;
+       struct wpabuf *resp;
+
+       if (data->state != PWD_ID_Req) {
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       if (payload_len < sizeof(struct eap_pwd_id)) {
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       id = (struct eap_pwd_id *) payload;
+       data->group_num = be_to_host16(id->group_num);
+       if ((id->random_function != EAP_PWD_DEFAULT_RAND_FUNC) ||
+           (id->prf != EAP_PWD_DEFAULT_PRF)) {
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       wpa_printf(MSG_DEBUG, "EAP-PWD (peer): server said 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");
+               return NULL;
+       }
+       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);
+
+       if ((data->grp = (EAP_PWD_group *) os_malloc(sizeof(EAP_PWD_group))) ==
+           NULL) {
+               wpa_printf(MSG_INFO, "EAP-PWD: failed to allocate memory for "
+                          "group");
+               return NULL;
+       }
+
+       /* compute PWE */
+       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)) {
+               wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute PWE");
+               return NULL;
+       }
+
+       wpa_printf(MSG_INFO, "EAP-PWD (peer): computed %d bit PWE...",
+                  BN_num_bits(data->grp->prime));
+
+       resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
+                            1 + sizeof(struct eap_pwd_id) + data->id_peer_len,
+                            EAP_CODE_RESPONSE, eap_get_id(reqData));
+       if (resp == NULL)
+               return NULL;
+
+       wpabuf_put_u8(resp, EAP_PWD_OPCODE_ID_EXCH);
+       wpabuf_put_be16(resp, data->group_num);
+       wpabuf_put_u8(resp, EAP_PWD_DEFAULT_RAND_FUNC);
+       wpabuf_put_u8(resp, EAP_PWD_DEFAULT_PRF);
+       wpabuf_put_data(resp, id->token, sizeof(id->token));
+       wpabuf_put_u8(resp, EAP_PWD_PREP_NONE);
+       wpabuf_put_data(resp, data->id_peer, data->id_peer_len);
+
+       eap_pwd_state(data, PWD_Commit_Req);
+
+       return resp;
+}
+
+
+static struct wpabuf *
+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)
+{
+       struct wpabuf *resp = NULL;
+       EC_POINT *K = NULL, *point = NULL;
+       BIGNUM *mask = NULL, *x = NULL, *y = NULL, *cofactor = NULL;
+       u16 offset;
+       u8 *ptr, *scalar = NULL, *element = NULL;
+
+       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;
+       }
+
+       BN_rand_range(data->private_value, data->grp->order);
+       BN_rand_range(mask, data->grp->order);
+       BN_add(data->my_scalar, data->private_value, mask);
+       BN_mod(data->my_scalar, data->my_scalar, data->grp->order,
+              data->bnctx);
+
+       if (!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_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);
+
+       resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
+                            sizeof(struct eap_pwd_hdr) +
+                            BN_num_bytes(data->grp->order) +
+                            (2 * BN_num_bytes(data->grp->prime)),
+                            EAP_CODE_RESPONSE, eap_get_id(reqData));
+       if (resp == NULL)
+               goto fin;
+
+       wpabuf_put_u8(resp, EAP_PWD_OPCODE_COMMIT_EXCH);
+
+       /* we send the element as (x,y) follwed by the scalar */
+       wpabuf_put_data(resp, element, (2 * BN_num_bytes(data->grp->prime)));
+       wpabuf_put_data(resp, scalar, BN_num_bytes(data->grp->order));
+
+fin:
+       os_free(scalar);
+       os_free(element);
+       BN_free(x);
+       BN_free(y);
+       BN_free(cofactor);
+       EC_POINT_free(K);
+       EC_POINT_free(point);
+       if (resp == NULL)
+               eap_pwd_state(data, FAILURE);
+       else
+               eap_pwd_state(data, PWD_Confirm_Req);
+
+       return resp;
+}
+
+
+static struct wpabuf *
+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)
+{
+       struct wpabuf *resp = NULL;
+       BIGNUM *x = NULL, *y = NULL;
+       HMAC_CTX ctx;
+       u32 cs;
+       u16 grp;
+       u8 conf[SHA256_DIGEST_LENGTH], *cruft = NULL, *ptr;
+       int offset;
+
+       /*
+        * 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): debug allocation "
+                          "fail");
+               goto fin;
+       }
+
+       /*
+        * server's commit is H(k | server_element | server_scalar |
+        *                      peer_element | peer_scalar | ciphersuite)
+        */
+       H_Init(&ctx);
+
+       /*
+        * 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);
+       H_Update(&ctx, 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);
+       H_Update(&ctx, 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);
+       H_Update(&ctx, 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);
+       H_Update(&ctx, 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);
+       H_Update(&ctx, 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);
+       H_Update(&ctx, 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);
+       H_Update(&ctx, cruft, BN_num_bytes(data->grp->order));
+
+       /* the ciphersuite */
+       H_Update(&ctx, (u8 *) &cs, sizeof(u32));
+
+       /* random function fin */
+       H_Final(&ctx, conf);
+
+       ptr = (u8 *) payload;
+       if (os_memcmp(conf, ptr, SHA256_DIGEST_LENGTH)) {
+               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)
+        */
+       H_Init(&ctx);
+
+       /* 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);
+       H_Update(&ctx, 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);
+       H_Update(&ctx, 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);
+       H_Update(&ctx, 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);
+       H_Update(&ctx, 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);
+       H_Update(&ctx, 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);
+       H_Update(&ctx, 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);
+       H_Update(&ctx, cruft, BN_num_bytes(data->grp->order));
+
+       /* the ciphersuite */
+       H_Update(&ctx, (u8 *) &cs, sizeof(u32));
+
+       /* all done */
+       H_Final(&ctx, conf);
+
+       resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
+                            sizeof(struct eap_pwd_hdr) + SHA256_DIGEST_LENGTH,
+                            EAP_CODE_RESPONSE, eap_get_id(reqData));
+       if (resp == NULL)
+               goto fin;
+
+       wpabuf_put_u8(resp, EAP_PWD_OPCODE_CONFIRM_EXCH);
+       wpabuf_put_data(resp, conf, SHA256_DIGEST_LENGTH);
+
+       if (compute_keys(data->grp, data->bnctx, data->k,
+                        data->my_scalar, data->server_scalar, conf, ptr,
+                        &cs, data->msk, data->emsk) < 0) {
+               wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute MSK | "
+                          "EMSK");
+               goto fin;
+       }
+
+fin:
+       os_free(cruft);
+       BN_free(x);
+       BN_free(y);
+       ret->methodState = METHOD_DONE;
+       if (resp == NULL) {
+               ret->decision = DECISION_FAIL;
+               eap_pwd_state(data, FAILURE);
+       } else {
+               ret->decision = DECISION_UNCOND_SUCC;
+               eap_pwd_state(data, SUCCESS);
+       }
+
+       return resp;
+}
+
+
+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;
+       size_t len;
+       u8 exch;
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PWD, reqData, &len);
+       if ((pos == NULL) || (len < 1)) {
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       wpa_printf(MSG_INFO, "EAP-pwd: Received frame: opcode %d", *pos);
+
+       ret->ignore = FALSE;
+       ret->methodState = METHOD_MAY_CONT;
+       ret->decision = DECISION_FAIL;
+       ret->allowNotifications = FALSE;
+
+       exch = *pos & 0x3f;
+       switch (exch) {
+        case EAP_PWD_OPCODE_ID_EXCH:
+               resp = eap_pwd_perform_id_exchange(sm, data, ret, reqData,
+                                                  pos + 1, len - 1);
+               break;
+        case EAP_PWD_OPCODE_COMMIT_EXCH:
+               resp = eap_pwd_perform_commit_exchange(sm, data, ret, reqData,
+                                                      pos + 1, len - 1);
+               break;
+        case EAP_PWD_OPCODE_CONFIRM_EXCH:
+               resp = eap_pwd_perform_confirm_exchange(sm, data, ret, reqData,
+                                                       pos + 1, len - 1);
+               break;
+        default:
+               wpa_printf(MSG_INFO, "EAP-pwd: Ignoring message with unknown "
+                          "opcode %d", exch);
+               break;
+       }
+
+       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;
+
+       EVP_add_digest(EVP_sha256());
+       eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+                                   EAP_VENDOR_IETF, EAP_TYPE_PWD, "PWD");
+       if (eap == NULL)
+               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->get_emsk = eap_pwd_get_emsk;
+
+       ret = eap_peer_method_register(eap);
+       if (ret)
+               eap_peer_method_free(eap);
+       return ret;
+}
index bb06bb2..1474b7f 100644 (file)
@@ -15,6 +15,7 @@
 #include "includes.h"
 
 #include "common.h"
+#include "crypto/random.h"
 #include "eap_peer/eap_i.h"
 #include "eap_common/eap_sake_common.h"
 
@@ -223,7 +224,7 @@ static struct wpabuf * eap_sake_process_challenge(struct eap_sm *sm,
        wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_S (server rand)",
                    data->rand_s, EAP_SAKE_RAND_LEN);
 
-       if (os_get_random(data->rand_p, 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;
        }
index 3d8afb2..2e81850 100644 (file)
@@ -17,6 +17,7 @@
 #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"
@@ -93,7 +94,7 @@ static void * eap_sim_init(struct eap_sm *sm)
        if (data == NULL)
                return NULL;
 
-       if (os_get_random(data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) {
+       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);
@@ -265,21 +266,20 @@ static int eap_sim_supported_ver(int version)
 
 static void eap_sim_clear_identities(struct eap_sim_data *data, int id)
 {
-       wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old%s%s%s",
-                  id & CLEAR_PSEUDONYM ? " pseudonym" : "",
-                  id & CLEAR_REAUTH_ID ? " reauth_id" : "",
-                  id & CLEAR_EAP_ID ? " eap_id" : "");
-       if (id & CLEAR_PSEUDONYM) {
+       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;
        }
-       if (id & CLEAR_REAUTH_ID) {
+       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) {
+       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;
@@ -287,24 +287,44 @@ static void eap_sim_clear_identities(struct eap_sim_data *data, int id)
 }
 
 
-static int eap_sim_learn_ids(struct eap_sim_data *data,
+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);
-               data->pseudonym = os_malloc(attr->next_pseudonym_len);
+               /* 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);
-               data->pseudonym_len = attr->next_pseudonym_len;
-               wpa_hexdump_ascii(MSG_DEBUG,
-                                 "EAP-SIM: (encr) AT_NEXT_PSEUDONYM",
-                                 data->pseudonym,
-                                 data->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;
        }
 
        if (attr->next_reauth_id) {
@@ -313,6 +333,7 @@ static int eap_sim_learn_ids(struct eap_sim_data *data,
                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,
@@ -648,11 +669,11 @@ static struct wpabuf * eap_sim_process_challenge(struct eap_sm *sm,
                                            EAP_SIM_UNABLE_TO_PROCESS_PACKET);
        }
 
-       /* Old reauthentication and pseudonym identities must not be used
-        * anymore. In other words, if no new identities are received, full
-        * authentication will be used on next reauthentication. */
-       eap_sim_clear_identities(data, CLEAR_PSEUDONYM | CLEAR_REAUTH_ID |
-                                CLEAR_EAP_ID);
+       /* 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(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
 
        if (attr->encr_data) {
                u8 *decrypted;
@@ -663,7 +684,7 @@ static struct wpabuf * eap_sim_process_challenge(struct eap_sm *sm,
                        return eap_sim_client_error(
                                data, id, EAP_SIM_UNABLE_TO_PROCESS_PACKET);
                }
-               eap_sim_learn_ids(data, &eattr);
+               eap_sim_learn_ids(sm, data, &eattr);
                os_free(decrypted);
        }
 
@@ -861,7 +882,7 @@ static struct wpabuf * eap_sim_process_reauthentication(
                                   data->nonce_s, data->mk, data->msk,
                                   data->emsk);
        eap_sim_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
-       eap_sim_learn_ids(data, &eattr);
+       eap_sim_learn_ids(sm, data, &eattr);
 
        if (data->result_ind && attr->result_ind)
                data->use_result_ind = 1;
@@ -995,7 +1016,7 @@ static void eap_sim_deinit_for_reauth(struct eap_sm *sm, void *priv)
 static void * eap_sim_init_for_reauth(struct eap_sm *sm, void *priv)
 {
        struct eap_sim_data *data = priv;
-       if (os_get_random(data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) {
+       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);
index 7bd50f6..93df756 100644 (file)
@@ -112,7 +112,6 @@ static int eap_tls_params_from_conf(struct eap_sm *sm,
                wpa_printf(MSG_DEBUG, "TLS: using phase1 config options");
                eap_tls_params_from_conf1(params, config);
        }
-       params->tls_ia = data->tls_ia;
 
        /*
         * Use blob data, if available. Otherwise, leave reference to external
@@ -361,7 +360,8 @@ static int eap_peer_tls_reassemble_fragment(struct eap_ssl_data *data,
                eap_peer_tls_reset_input(data);
                return -1;
        }
-       wpabuf_put_buf(data->tls_in, in_data);
+       if (in_data)
+               wpabuf_put_buf(data->tls_in, in_data);
        data->tls_in_left -= in_len;
 
        if (data->tls_in_left > 0) {
index e9e0998..e9a07b8 100644 (file)
@@ -66,11 +66,6 @@ struct eap_ssl_data {
        int include_tls_length;
 
        /**
-        * tls_ia - Whether TLS/IA is enabled for this TLS connection
-        */
-       int tls_ia;
-
-       /**
         * eap - EAP state machine allocated with eap_peer_sm_init()
         */
        struct eap_sm *eap;
index 2573780..2c939f9 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * EAP peer method: EAP-TTLS (RFC 5281)
- * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
 #include "eap_config.h"
 
 
-/* Maximum supported TTLS version
- * 0 = RFC 5281
- * 1 = draft-funk-eap-ttls-v1-00.txt
- */
-#ifndef EAP_TTLS_VERSION
-#define EAP_TTLS_VERSION 0 /* TTLSv1 implementation is not yet complete */
-#endif /* EAP_TTLS_VERSION */
-
-
-#define MSCHAPV2_KEY_LEN 16
-#define MSCHAPV2_NT_RESPONSE_LEN 24
+#define EAP_TTLS_VERSION 0
 
 
 static void eap_ttls_deinit(struct eap_sm *sm, void *priv);
@@ -44,9 +34,8 @@ static void eap_ttls_deinit(struct eap_sm *sm, void *priv);
 
 struct eap_ttls_data {
        struct eap_ssl_data ssl;
-       int ssl_initialized;
 
-       int ttls_version, force_ttls_version;
+       int ttls_version;
 
        const struct eap_method *phase2_method;
        void *phase2_priv;
@@ -91,22 +80,9 @@ static void * eap_ttls_init(struct eap_sm *sm)
        if (data == NULL)
                return NULL;
        data->ttls_version = EAP_TTLS_VERSION;
-       data->force_ttls_version = -1;
        selected = "EAP";
        data->phase2_type = EAP_TTLS_PHASE2_EAP;
 
-#if EAP_TTLS_VERSION > 0
-       if (config && config->phase1) {
-               const char *pos = os_strstr(config->phase1, "ttlsver=");
-               if (pos) {
-                       data->force_ttls_version = atoi(pos + 8);
-                       data->ttls_version = data->force_ttls_version;
-                       wpa_printf(MSG_DEBUG, "EAP-TTLS: Forced TTLS version "
-                                  "%d", data->force_ttls_version);
-               }
-       }
-#endif /* EAP_TTLS_VERSION */
-
        if (config && config->phase2) {
                if (os_strstr(config->phase2, "autheap=")) {
                        selected = "EAP";
@@ -140,19 +116,11 @@ static void * eap_ttls_init(struct eap_sm *sm)
                data->phase2_eap_type.method = EAP_TYPE_NONE;
        }
 
-#if EAP_TTLS_VERSION > 0
-       if (!(tls_capabilities(sm->ssl_ctx) & TLS_CAPABILITY_IA) &&
-           data->ttls_version > 0) {
-               if (data->force_ttls_version > 0) {
-                       wpa_printf(MSG_INFO, "EAP-TTLS: Forced TTLSv%d and "
-                                  "TLS library does not support TLS/IA.",
-                                  data->force_ttls_version);
-                       eap_ttls_deinit(sm, data);
-                       return NULL;
-               }
-               data->ttls_version = 0;
+       if (eap_peer_tls_ssl_init(sm, &data->ssl, config)) {
+               wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL.");
+               eap_ttls_deinit(sm, data);
+               return NULL;
        }
-#endif /* EAP_TTLS_VERSION */
 
        return data;
 }
@@ -176,8 +144,7 @@ static void eap_ttls_deinit(struct eap_sm *sm, void *priv)
                return;
        eap_ttls_phase2_eap_deinit(sm, data);
        os_free(data->phase2_eap_types);
-       if (data->ssl_initialized)
-               eap_peer_tls_ssl_deinit(sm, &data->ssl);
+       eap_peer_tls_ssl_deinit(sm, &data->ssl);
        os_free(data->key_data);
        wpabuf_free(data->pending_phase2_req);
        os_free(data);
@@ -202,7 +169,7 @@ static u8 * eap_ttls_avp_hdr(u8 *avphdr, u32 avp_code, u32 vendor_id,
        }
 
        avp->avp_code = host_to_be32(avp_code);
-       avp->avp_length = host_to_be32((flags << 24) | (hdrlen + len));
+       avp->avp_length = host_to_be32((flags << 24) | (u32) (hdrlen + len));
 
        return avphdr + hdrlen;
 }
@@ -246,39 +213,6 @@ static int eap_ttls_avp_encapsulate(struct wpabuf **resp, u32 avp_code,
 }
 
 
-#if EAP_TTLS_VERSION > 0
-static int eap_ttls_ia_permute_inner_secret(struct eap_sm *sm,
-                                           struct eap_ttls_data *data,
-                                           const u8 *key, size_t key_len)
-{
-       u8 *buf;
-       size_t buf_len;
-       int ret;
-
-       if (key) {
-               buf_len = 2 + key_len;
-               buf = os_malloc(buf_len);
-               if (buf == NULL)
-                       return -1;
-               WPA_PUT_BE16(buf, key_len);
-               os_memcpy(buf + 2, key, key_len);
-       } else {
-               buf = NULL;
-               buf_len = 0;
-       }
-
-       wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Session keys for TLS/IA inner "
-                       "secret permutation", buf, buf_len);
-       ret = tls_connection_ia_permute_inner_secret(sm->ssl_ctx,
-                                                    data->ssl.conn,
-                                                    buf, buf_len);
-       os_free(buf);
-
-       return ret;
-}
-#endif /* EAP_TTLS_VERSION */
-
-
 static int eap_ttls_v0_derive_key(struct eap_sm *sm,
                                  struct eap_ttls_data *data)
 {
@@ -298,156 +232,10 @@ static int eap_ttls_v0_derive_key(struct eap_sm *sm,
 }
 
 
-#if EAP_TTLS_VERSION > 0
-static int eap_ttls_v1_derive_key(struct eap_sm *sm,
-                                 struct eap_ttls_data *data)
-{
-       struct tls_keys keys;
-       u8 *rnd;
-
-       os_free(data->key_data);
-       data->key_data = NULL;
-
-       os_memset(&keys, 0, sizeof(keys));
-       if (tls_connection_get_keys(sm->ssl_ctx, data->ssl.conn, &keys) ||
-           keys.client_random == NULL || keys.server_random == NULL ||
-           keys.inner_secret == NULL) {
-               wpa_printf(MSG_INFO, "EAP-TTLS: Could not get inner secret, "
-                          "client random, or server random to derive keying "
-                          "material");
-               return -1;
-       }
-
-       rnd = os_malloc(keys.client_random_len + keys.server_random_len);
-       data->key_data = os_malloc(EAP_TLS_KEY_LEN);
-       if (rnd == NULL || data->key_data == NULL) {
-               wpa_printf(MSG_INFO, "EAP-TTLS: No memory for key derivation");
-               os_free(rnd);
-               os_free(data->key_data);
-               data->key_data = NULL;
-               return -1;
-       }
-       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(keys.inner_secret, keys.inner_secret_len,
-                   "ttls v1 keying material", rnd, keys.client_random_len +
-                   keys.server_random_len, data->key_data, EAP_TLS_KEY_LEN)) {
-               wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive key");
-               os_free(rnd);
-               os_free(data->key_data);
-               data->key_data = NULL;
-               return -1;
-       }
-
-       wpa_hexdump(MSG_DEBUG, "EAP-TTLS: client/server random",
-                   rnd, keys.client_random_len + keys.server_random_len);
-       wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: TLS/IA inner secret",
-                       keys.inner_secret, keys.inner_secret_len);
-
-       os_free(rnd);
-
-       wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key",
-                       data->key_data, EAP_TLS_KEY_LEN);
-
-       return 0;
-}
-#endif /* EAP_TTLS_VERSION */
-
-
 static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm,
                                        struct eap_ttls_data *data, size_t len)
 {
-#if EAP_TTLS_VERSION > 0
-       struct tls_keys keys;
-       u8 *challenge, *rnd;
-#endif /* EAP_TTLS_VERSION */
-
-       if (data->ttls_version == 0) {
-               return eap_peer_tls_derive_key(sm, &data->ssl,
-                                              "ttls challenge", len);
-       }
-
-#if EAP_TTLS_VERSION > 0
-
-       os_memset(&keys, 0, sizeof(keys));
-       if (tls_connection_get_keys(sm->ssl_ctx, data->ssl.conn, &keys) ||
-           keys.client_random == NULL || keys.server_random == NULL ||
-           keys.inner_secret == NULL) {
-               wpa_printf(MSG_INFO, "EAP-TTLS: Could not get inner secret, "
-                          "client random, or server random to derive "
-                          "implicit challenge");
-               return NULL;
-       }
-
-       rnd = os_malloc(keys.client_random_len + keys.server_random_len);
-       challenge = os_malloc(len);
-       if (rnd == NULL || challenge == NULL) {
-               wpa_printf(MSG_INFO, "EAP-TTLS: No memory for implicit "
-                          "challenge derivation");
-               os_free(rnd);
-               os_free(challenge);
-               return NULL;
-       }
-       os_memcpy(rnd, keys.server_random, keys.server_random_len);
-       os_memcpy(rnd + keys.server_random_len, keys.client_random,
-                 keys.client_random_len);
-
-       if (tls_prf(keys.inner_secret, keys.inner_secret_len,
-                   "inner application challenge", rnd,
-                   keys.client_random_len + keys.server_random_len,
-                   challenge, len)) {
-               wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive implicit "
-                          "challenge");
-               os_free(rnd);
-               os_free(challenge);
-               return NULL;
-       }
-
-       os_free(rnd);
-
-       wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived implicit challenge",
-                       challenge, len);
-
-       return challenge;
-
-#else /* EAP_TTLS_VERSION */
-
-       return NULL;
-
-#endif /* EAP_TTLS_VERSION */
-}
-
-
-static void eap_ttlsv1_phase2_eap_finish(struct eap_sm *sm,
-                                        struct eap_ttls_data *data,
-                                        struct eap_method_ret *ret)
-{
-#if EAP_TTLS_VERSION > 0
-       if (data->ttls_version > 0) {
-               const struct eap_method *m = data->phase2_method;
-               void *priv = data->phase2_priv;
-
-               /* TTLSv1 requires TLS/IA FinalPhaseFinished */
-               if (ret->decision == DECISION_UNCOND_SUCC)
-                       ret->decision = DECISION_COND_SUCC;
-               ret->methodState = METHOD_CONT;
-
-               if (ret->decision == DECISION_COND_SUCC &&
-                   m->isKeyAvailable && m->getKey &&
-                   m->isKeyAvailable(sm, priv)) {
-                       u8 *key;
-                       size_t key_len;
-                       key = m->getKey(sm, priv, &key_len);
-                       if (key) {
-                               eap_ttls_ia_permute_inner_secret(
-                                       sm, data, key, key_len);
-                               os_free(key);
-                       }
-               }
-       }
-#endif /* EAP_TTLS_VERSION */
+       return eap_peer_tls_derive_key(sm, &data->ssl, "ttls challenge", len);
 }
 
 
@@ -494,7 +282,6 @@ static int eap_ttls_phase2_eap_process(struct eap_sm *sm,
                ret->methodState = iret.methodState;
                ret->decision = iret.decision;
        }
-       eap_ttlsv1_phase2_eap_finish(sm, data, ret);
 
        return 0;
 }
@@ -615,26 +402,6 @@ static int eap_ttls_phase2_request_eap(struct eap_sm *sm,
 }
 
 
-static void eap_ttlsv1_permute_inner(struct eap_sm *sm,
-                                    struct eap_ttls_data *data)
-{
-#if EAP_TTLS_VERSION > 0
-       u8 session_key[2 * MSCHAPV2_KEY_LEN];
-
-       if (data->ttls_version == 0)
-               return;
-
-       get_asymetric_start_key(data->master_key, session_key,
-                               MSCHAPV2_KEY_LEN, 0, 0);
-       get_asymetric_start_key(data->master_key,
-                               session_key + MSCHAPV2_KEY_LEN,
-                               MSCHAPV2_KEY_LEN, 1, 0);
-       eap_ttls_ia_permute_inner_secret(sm, data, session_key,
-                                        sizeof(session_key));
-#endif /* EAP_TTLS_VERSION */
-}
-
-
 static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm,
                                            struct eap_ttls_data *data,
                                            struct eap_method_ret *ret,
@@ -674,7 +441,6 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm,
                           "implicit challenge");
                return -1;
        }
-       peer_challenge = challenge + 1 + EAP_TTLS_MSCHAPV2_CHALLENGE_LEN;
 
        pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE,
                               RADIUS_VENDOR_ID_MICROSOFT, 1,
@@ -687,7 +453,14 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm,
        data->ident = challenge[EAP_TTLS_MSCHAPV2_CHALLENGE_LEN];
        *pos++ = data->ident;
        *pos++ = 0; /* Flags */
-       os_memcpy(pos, peer_challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN);
+       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;
@@ -695,6 +468,7 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm,
                                     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");
@@ -702,8 +476,6 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm,
        }
        data->auth_response_valid = 1;
 
-       eap_ttlsv1_permute_inner(sm, data);
-
        pos += 24;
        os_free(challenge);
        AVP_PAD(buf, pos);
@@ -711,7 +483,7 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm,
        wpabuf_put(msg, pos - buf);
        *resp = msg;
 
-       if (sm->workaround && data->ttls_version == 0) {
+       if (sm->workaround) {
                /* At least FreeRADIUS seems to be terminating
                 * EAP-TTLS/MSHCAPV2 without the expected MS-CHAP-v2 Success
                 * packet. */
@@ -798,17 +570,10 @@ static int eap_ttls_phase2_request_mschap(struct eap_sm *sm,
        wpabuf_put(msg, pos - buf);
        *resp = msg;
 
-       if (data->ttls_version > 0) {
-               /* EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report success,
-                * so do not allow connection to be terminated yet. */
-               ret->methodState = METHOD_CONT;
-               ret->decision = DECISION_COND_SUCC;
-       } else {
-               /* EAP-TTLS/MSCHAP does not provide tunneled success
-                * notification, so assume that Phase2 succeeds. */
-               ret->methodState = METHOD_DONE;
-               ret->decision = DECISION_COND_SUCC;
-       }
+       /* 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;
 }
@@ -859,17 +624,10 @@ static int eap_ttls_phase2_request_pap(struct eap_sm *sm,
        wpabuf_put(msg, pos - buf);
        *resp = msg;
 
-       if (data->ttls_version > 0) {
-               /* EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report success,
-                * so do not allow connection to be terminated yet. */
-               ret->methodState = METHOD_CONT;
-               ret->decision = DECISION_COND_SUCC;
-       } else {
-               /* EAP-TTLS/PAP does not provide tunneled success notification,
-                * so assume that Phase2 succeeds. */
-               ret->methodState = METHOD_DONE;
-               ret->decision = DECISION_COND_SUCC;
-       }
+       /* 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;
 }
@@ -942,17 +700,10 @@ static int eap_ttls_phase2_request_chap(struct eap_sm *sm,
        wpabuf_put(msg, pos - buf);
        *resp = msg;
 
-       if (data->ttls_version > 0) {
-               /* EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report success,
-                * so do not allow connection to be terminated yet. */
-               ret->methodState = METHOD_CONT;
-               ret->decision = DECISION_COND_SUCC;
-       } else {
-               /* EAP-TTLS/CHAP does not provide tunneled success
-                * notification, so assume that Phase2 succeeds. */
-               ret->methodState = METHOD_DONE;
-               ret->decision = DECISION_COND_SUCC;
-       }
+       /* 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;
 }
@@ -1027,36 +778,6 @@ static int eap_ttls_phase2_request(struct eap_sm *sm,
 }
 
 
-#if EAP_TTLS_VERSION > 0
-static struct wpabuf * eap_ttls_build_phase_finished(
-       struct eap_sm *sm, struct eap_ttls_data *data, int id, int final)
-{
-       struct wpabuf *req, *buf;
-
-       buf = tls_connection_ia_send_phase_finished(sm->ssl_ctx,
-                                                   data->ssl.conn,
-                                                   final);
-       if (buf == NULL)
-               return NULL;
-
-       req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TTLS,
-                           1 + wpabuf_len(buf),
-                           EAP_CODE_RESPONSE, id);
-       if (req == NULL) {
-               wpabuf_free(buf);
-               return NULL;
-       }
-
-       wpabuf_put_u8(req, data->ttls_version);
-       wpabuf_put_buf(req, buf);
-       wpabuf_free(buf);
-       eap_update_len(req);
-
-       return req;
-}
-#endif /* EAP_TTLS_VERSION */
-
-
 struct ttls_parse_avp {
        u8 *mschapv2;
        u8 *eapdata;
@@ -1366,19 +1087,9 @@ static int eap_ttls_process_phase2_mschapv2(struct eap_sm *sm,
 
        wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 MSCHAPV2 "
                   "authentication succeeded");
-       if (data->ttls_version > 0) {
-               /*
-                * EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report
-                * success, so do not allow connection to be terminated
-                * yet.
-                */
-               ret->methodState = METHOD_CONT;
-               ret->decision = DECISION_COND_SUCC;
-       } else {
-               ret->methodState = METHOD_DONE;
-               ret->decision = DECISION_UNCOND_SUCC;
-               data->phase2_success = 1;
-       }
+       ret->methodState = METHOD_DONE;
+       ret->decision = DECISION_UNCOND_SUCC;
+       data->phase2_success = 1;
 
        /*
         * Reply with empty data; authentication server will reply
@@ -1493,24 +1204,6 @@ static int eap_ttls_process_decrypted(struct eap_sm *sm,
 }
 
 
-#if EAP_TTLS_VERSION > 0
-static void eap_ttls_final_phase_finished(struct eap_sm *sm,
-                                         struct eap_ttls_data *data,
-                                         struct eap_method_ret *ret,
-                                         u8 identifier,
-                                         struct wpabuf **out_data)
-{
-       wpa_printf(MSG_DEBUG, "EAP-TTLS: FinalPhaseFinished received");
-       wpa_printf(MSG_INFO, "EAP-TTLS: TLS/IA authentication succeeded");
-       ret->methodState = METHOD_DONE;
-       ret->decision = DECISION_UNCOND_SUCC;
-       data->phase2_success = 1;
-       *out_data = eap_ttls_build_phase_finished(sm, data, identifier, 1);
-       eap_ttls_v1_derive_key(sm, data);
-}
-#endif /* EAP_TTLS_VERSION */
-
-
 static int eap_ttls_implicit_identity_request(struct eap_sm *sm,
                                              struct eap_ttls_data *data,
                                              struct eap_method_ret *ret,
@@ -1534,6 +1227,21 @@ static int eap_ttls_implicit_identity_request(struct eap_sm *sm,
                           "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);
        }
@@ -1627,17 +1335,6 @@ static int eap_ttls_decrypt(struct eap_sm *sm, struct eap_ttls_data *data,
        if (retval)
                goto done;
 
-#if EAP_TTLS_VERSION > 0
-       if (data->ttls_version > 0 &&
-           (in_decrypted == NULL || wpabuf_len(in_decrypted) == 0) &&
-           tls_connection_ia_final_phase_finished(sm->ssl_ctx,
-                                                  data->ssl.conn)) {
-               eap_ttls_final_phase_finished(sm, data, ret, identifier,
-                                             out_data);
-               goto done;
-       }
-#endif /* EAP_TTLS_VERSION */
-
 continue_req:
        data->phase2_start = 0;
 
@@ -1662,46 +1359,6 @@ done:
 }
 
 
-static int eap_ttls_process_start(struct eap_sm *sm,
-                                 struct eap_ttls_data *data, u8 flags,
-                                 struct eap_method_ret *ret)
-{
-       struct eap_peer_config *config = eap_get_config(sm);
-
-       wpa_printf(MSG_DEBUG, "EAP-TTLS: Start (server ver=%d, own ver=%d)",
-                  flags & EAP_TLS_VERSION_MASK, data->ttls_version);
-#if EAP_TTLS_VERSION > 0
-       if ((flags & EAP_TLS_VERSION_MASK) < data->ttls_version)
-               data->ttls_version = flags & EAP_TLS_VERSION_MASK;
-       if (data->force_ttls_version >= 0 &&
-           data->force_ttls_version != data->ttls_version) {
-               wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to select "
-                          "forced TTLS version %d",
-                          data->force_ttls_version);
-               ret->methodState = METHOD_DONE;
-               ret->decision = DECISION_FAIL;
-               ret->allowNotifications = FALSE;
-               return -1;
-       }
-       wpa_printf(MSG_DEBUG, "EAP-TTLS: Using TTLS version %d",
-                  data->ttls_version);
-
-       if (data->ttls_version > 0)
-               data->ssl.tls_ia = 1;
-#endif /* EAP_TTLS_VERSION */
-       if (!data->ssl_initialized &&
-           eap_peer_tls_ssl_init(sm, &data->ssl, config)) {
-               wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL.");
-               return -1;
-       }
-       data->ssl_initialized = 1;
-
-       wpa_printf(MSG_DEBUG, "EAP-TTLS: Start");
-
-       return 0;
-}
-
-
 static int eap_ttls_process_handshake(struct eap_sm *sm,
                                      struct eap_ttls_data *data,
                                      struct eap_method_ret *ret,
@@ -1725,8 +1382,7 @@ static int eap_ttls_process_handshake(struct eap_sm *sm,
                        ret->methodState = METHOD_MAY_CONT;
                }
                data->phase2_start = 1;
-               if (data->ttls_version == 0)
-                       eap_ttls_v0_derive_key(sm, data);
+               eap_ttls_v0_derive_key(sm, data);
 
                if (*out_data == NULL || wpabuf_len(*out_data) == 0) {
                        if (eap_ttls_decrypt(sm, data, ret, identifier,
@@ -1761,7 +1417,7 @@ static void eap_ttls_check_auth_status(struct eap_sm *sm,
                                       struct eap_ttls_data *data,
                                       struct eap_method_ret *ret)
 {
-       if (data->ttls_version == 0 && ret->methodState == METHOD_DONE) {
+       if (ret->methodState == METHOD_DONE) {
                ret->allowNotifications = FALSE;
                if (ret->decision == DECISION_UNCOND_SUCC ||
                    ret->decision == DECISION_COND_SUCC) {
@@ -1779,8 +1435,7 @@ static void eap_ttls_check_auth_status(struct eap_sm *sm,
                        }
 #endif /* EAP_TNC */
                }
-       } else if (data->ttls_version == 0 &&
-                  ret->methodState == METHOD_MAY_CONT &&
+       } else if (ret->methodState == METHOD_MAY_CONT &&
                   (ret->decision == DECISION_UNCOND_SUCC ||
                    ret->decision == DECISION_COND_SUCC)) {
                        wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication "
@@ -1808,8 +1463,9 @@ static struct wpabuf * eap_ttls_process(struct eap_sm *sm, void *priv,
        id = eap_get_id(reqData);
 
        if (flags & EAP_TLS_FLAGS_START) {
-               if (eap_ttls_process_start(sm, data, flags, ret) < 0)
-                       return NULL;
+               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
@@ -1817,13 +1473,6 @@ static struct wpabuf * eap_ttls_process(struct eap_sm *sm, void *priv,
                 * For now, ignore any potential extra data.
                 */
                left = 0;
-       } else if (!data->ssl_initialized) {
-               wpa_printf(MSG_DEBUG, "EAP-TTLS: First message did not "
-                          "include Start flag");
-               ret->methodState = METHOD_DONE;
-               ret->decision = DECISION_FAIL;
-               ret->allowNotifications = FALSE;
-               return NULL;
        }
 
        resp = NULL;
index 8317f72..09d8a1c 100644 (file)
@@ -203,6 +203,10 @@ static void * eap_wsc_init(struct eap_sm *sm)
                return NULL;
        }
 
+       pos = os_strstr(phase1, "dev_pw_id=");
+       if (pos && cfg.pin)
+               cfg.dev_pw_id = atoi(pos + 10);
+
        res = eap_wsc_new_ap_settings(&new_ap_settings, phase1);
        if (res < 0) {
                os_free(data);
@@ -219,10 +223,16 @@ static void * eap_wsc_init(struct eap_sm *sm)
                os_free(data);
                return NULL;
        }
-       data->fragment_size = WSC_FRAGMENT_SIZE;
+       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,
+               wps_registrar_add_pin(data->wps_ctx->registrar, NULL, NULL,
                                      cfg.pin, cfg.pin_len, 0);
        }
 
index 309a331..acd7611 100644 (file)
@@ -16,6 +16,7 @@
 
 #include "common.h"
 #include "crypto/dh_groups.h"
+#include "crypto/random.h"
 #include "ikev2.h"
 
 
@@ -424,7 +425,7 @@ static int ikev2_process_kei(struct ikev2_responder_data *data,
        }
 
        /* RFC 4306, Section 3.4:
-        * The length of DH public value MUST be equal to the lenght of the
+        * The length of DH public value MUST be equal to the length of the
         * prime modulus.
         */
        if (kei_len - 4 != data->dh->prime_len) {
@@ -1133,7 +1134,7 @@ static struct wpabuf * ikev2_build_sa_init(struct ikev2_responder_data *data)
                    data->r_spi, IKEV2_SPI_LEN);
 
        data->r_nonce_len = IKEV2_NONCE_MIN_LEN;
-       if (os_get_random(data->r_nonce, data->r_nonce_len))
+       if (random_get_bytes(data->r_nonce, data->r_nonce_len))
                return NULL;
 #ifdef CCNS_PL
        /* Zeros are removed incorrectly from the beginning of the nonces in
index eaaa168..a70d70c 100644 (file)
@@ -180,11 +180,11 @@ TNC_Result TNC_TNCC_ReportMessageTypes(
        imc = tnc_imc[imcID];
        os_free(imc->supported_types);
        imc->supported_types =
-               os_malloc(typeCount * sizeof(TNC_MessageTypeList));
+               os_malloc(typeCount * sizeof(TNC_MessageType));
        if (imc->supported_types == NULL)
                return TNC_RESULT_FATAL;
        os_memcpy(imc->supported_types, supportedTypes,
-                 typeCount * sizeof(TNC_MessageTypeList));
+                 typeCount * sizeof(TNC_MessageType));
        imc->num_supported_types = typeCount;
 
        return TNC_RESULT_SUCCESS;
index 92400a5..e1f500a 100644 (file)
@@ -95,6 +95,7 @@ struct eap_config {
        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;
@@ -106,7 +107,11 @@ struct eap_config {
        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;
 };
 
 
@@ -120,5 +125,6 @@ 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);
 
 #endif /* EAP_H */
index 4269a8c..f48cf71 100644 (file)
@@ -119,7 +119,7 @@ struct eap_sm {
 
        /* Full authenticator state machine local variables */
 
-       /* Long-term (maintained betwen packets) */
+       /* Long-term (maintained between packets) */
        EapType currentMethod;
        int currentId;
        enum {
@@ -181,12 +181,19 @@ struct eap_sm {
        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;
 };
 
 int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len,
index 5d4d92c..4a5296e 100644 (file)
@@ -49,5 +49,6 @@ int eap_server_fast_register(void);
 int eap_server_wsc_register(void);
 int eap_server_ikev2_register(void);
 int eap_server_tnc_register(void);
+int eap_server_pwd_register(void);
 
 #endif /* EAP_SERVER_METHODS_H */
index fdc26f9..7a5beb6 100644 (file)
@@ -136,6 +136,14 @@ 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->currentId = -1;
        sm->eap_if.eapSuccess = FALSE;
        sm->eap_if.eapFail = FALSE;
@@ -1028,9 +1036,12 @@ void eap_sm_process_nak(struct eap_sm *sm, const u8 *nak_list, size_t len)
 
        not_found:
                /* not found - remove from the list */
-               os_memmove(&sm->user->methods[i], &sm->user->methods[i + 1],
-                          (EAP_MAX_METHODS - i - 1) *
-                          sizeof(sm->user->methods[0]));
+               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;
@@ -1255,8 +1266,13 @@ struct eap_sm * eap_server_sm_init(void *eapol_ctx,
        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;
 
        wpa_printf(MSG_DEBUG, "EAP: Server state machine created");
 
@@ -1291,6 +1307,7 @@ void eap_server_sm_deinit(struct eap_sm *sm)
        os_free(sm->eap_if.aaaEapKeyData);
        eap_user_free(sm->user);
        wpabuf_free(sm->assoc_wps_ie);
+       wpabuf_free(sm->assoc_p2p_ie);
        os_free(sm);
 }
 
@@ -1362,3 +1379,18 @@ 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;
+}
index 4e7db48..6ae7a6f 100644 (file)
@@ -17,6 +17,7 @@
 #include "common.h"
 #include "crypto/sha256.h"
 #include "crypto/crypto.h"
+#include "crypto/random.h"
 #include "eap_common/eap_sim_common.h"
 #include "eap_server/eap_i.h"
 #include "eap_server/eap_sim_db.h"
@@ -132,14 +133,13 @@ static void * eap_aka_prime_init(struct eap_sm *sm)
                return NULL;
 
        data->eap_method = EAP_TYPE_AKA_PRIME;
-       data->network_name = os_malloc(os_strlen(network_name));
+       data->network_name = (u8 *) os_strdup(network_name);
        if (data->network_name == NULL) {
                os_free(data);
                return NULL;
        }
 
        data->network_name_len = os_strlen(network_name);
-       os_memcpy(data->network_name, network_name, data->network_name_len);
 
        data->state = IDENTITY;
        eap_aka_determine_identity(sm, data, 1, 0);
@@ -298,8 +298,13 @@ static int eap_aka_build_encr(struct eap_sm *sm, struct eap_aka_data *data,
                              const u8 *nonce_s)
 {
        os_free(data->next_pseudonym);
-       data->next_pseudonym =
-               eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv, 1);
+       if (nonce_s == NULL) {
+               data->next_pseudonym =
+                       eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv, 1);
+       } else {
+               /* Do not update pseudonym during re-authentication */
+               data->next_pseudonym = NULL;
+       }
        os_free(data->next_reauth_id);
        if (data->counter <= EAP_AKA_MAX_FAST_REAUTHS) {
                data->next_reauth_id =
@@ -440,7 +445,7 @@ static struct wpabuf * eap_aka_build_reauth(struct eap_sm *sm,
 
        wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Re-authentication");
 
-       if (os_get_random(data->nonce_s, EAP_SIM_NONCE_S_LEN))
+       if (random_get_bytes(data->nonce_s, EAP_SIM_NONCE_S_LEN))
                return NULL;
        wpa_hexdump_key(MSG_MSGDUMP, "EAP-AKA: NONCE_S",
                        data->nonce_s, EAP_SIM_NONCE_S_LEN);
@@ -1023,11 +1028,6 @@ static void eap_aka_process_reauth(struct eap_sm *sm,
                identity_len = id2_len;
        }
 
-       if (data->next_pseudonym) {
-               eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity,
-                                        identity_len, data->next_pseudonym);
-               data->next_pseudonym = NULL;
-       }
        if (data->next_reauth_id) {
                if (data->eap_method == EAP_TYPE_AKA_PRIME) {
 #ifdef EAP_SERVER_AKA_PRIME
index 39beb33..ba17e98 100644 (file)
@@ -18,6 +18,7 @@
 #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"
@@ -642,7 +643,7 @@ static struct wpabuf * eap_fast_build_crypto_binding(
        binding->version = EAP_FAST_VERSION;
        binding->received_version = data->peer_version;
        binding->subtype = EAP_TLV_CRYPTO_BINDING_SUBTYPE_REQUEST;
-       if (os_get_random(binding->nonce, sizeof(binding->nonce)) < 0) {
+       if (random_get_bytes(binding->nonce, sizeof(binding->nonce)) < 0) {
                wpabuf_free(buf);
                return NULL;
        }
@@ -692,7 +693,7 @@ static struct wpabuf * eap_fast_build_pac(struct eap_sm *sm,
        struct eap_tlv_result_tlv *result;
        struct os_time now;
 
-       if (os_get_random(pac_key, EAP_FAST_PAC_KEY_LEN) < 0 ||
+       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",
index d0c7559..a794806 100644 (file)
@@ -15,6 +15,7 @@
 #include "includes.h"
 
 #include "common.h"
+#include "crypto/random.h"
 #include "eap_server/eap_i.h"
 #include "eap_common/eap_gpsk_common.h"
 
@@ -120,7 +121,7 @@ static struct wpabuf * eap_gpsk_build_gpsk_1(struct eap_sm *sm,
 
        wpa_printf(MSG_DEBUG, "EAP-GPSK: Request/GPSK-1");
 
-       if (os_get_random(data->rand_server, EAP_GPSK_RAND_LEN)) {
+       if (random_get_bytes(data->rand_server, EAP_GPSK_RAND_LEN)) {
                wpa_printf(MSG_ERROR, "EAP-GPSK: Failed to get random data");
                eap_gpsk_state(data, FAILURE);
                return NULL;
index 06074ee..ec4fa87 100644 (file)
@@ -93,7 +93,8 @@ static void * eap_ikev2_init(struct eap_sm *sm)
        if (data == NULL)
                return NULL;
        data->state = MSG;
-       data->fragment_size = IKEV2_FRAGMENT_SIZE;
+       data->fragment_size = sm->fragment_size > 0 ? sm->fragment_size :
+               IKEV2_FRAGMENT_SIZE;
        data->ikev2.state = SA_INIT;
        data->ikev2.peer_auth = PEER_AUTH_SECRET;
        data->ikev2.key_pad = (u8 *) os_strdup("Key Pad for EAP-IKEv2");
index dee2dc5..d03ec53 100644 (file)
@@ -15,6 +15,7 @@
 #include "includes.h"
 
 #include "common.h"
+#include "crypto/random.h"
 #include "eap_i.h"
 #include "eap_common/chap.h"
 
@@ -52,7 +53,7 @@ static struct wpabuf * eap_md5_buildReq(struct eap_sm *sm, void *priv, u8 id)
        struct eap_md5_data *data = priv;
        struct wpabuf *req;
 
-       if (os_get_random(data->challenge, CHALLENGE_LEN)) {
+       if (random_get_bytes(data->challenge, CHALLENGE_LEN)) {
                wpa_printf(MSG_ERROR, "EAP-MD5: Failed to get random data");
                data->state = FAILURE;
                return NULL;
index 900a5dd..4d241a4 100644 (file)
@@ -167,6 +167,8 @@ void eap_server_unregister_methods(void)
 const char * eap_server_get_name(int vendor, EapType type)
 {
        struct eap_method *m;
+       if (vendor == EAP_VENDOR_IETF && type == EAP_TYPE_EXPANDED)
+               return "expanded";
        for (m = eap_methods; m; m = m->next) {
                if (m->vendor == vendor && m->method == type)
                        return m->name;
index 39d1c6e..64120a4 100644 (file)
@@ -16,6 +16,7 @@
 
 #include "common.h"
 #include "crypto/ms_funcs.h"
+#include "crypto/random.h"
 #include "eap_i.h"
 
 
@@ -109,7 +110,7 @@ static struct wpabuf * eap_mschapv2_build_challenge(
        size_t ms_len;
 
        if (!data->auth_challenge_from_tls &&
-           os_get_random(data->auth_challenge, CHALLENGE_LEN)) {
+           random_get_bytes(data->auth_challenge, CHALLENGE_LEN)) {
                wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to get random "
                           "data");
                data->state = FAILURE;
index 1dc023b..4d64269 100644 (file)
@@ -15,6 +15,7 @@
 #include "includes.h"
 
 #include "common.h"
+#include "crypto/random.h"
 #include "eap_server/eap_i.h"
 #include "eap_common/eap_pax_common.h"
 
@@ -82,7 +83,7 @@ static struct wpabuf * eap_pax_build_std_1(struct eap_sm *sm,
 
        wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-1 (sending)");
 
-       if (os_get_random(data->rand.r.x, EAP_PAX_RAND_LEN)) {
+       if (random_get_bytes(data->rand.r.x, EAP_PAX_RAND_LEN)) {
                wpa_printf(MSG_ERROR, "EAP-PAX: Failed to get random data");
                data->state = FAILURE;
                return NULL;
index 674ecd2..381c44a 100644 (file)
@@ -17,6 +17,7 @@
 #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"
@@ -350,8 +351,12 @@ static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data)
         * in the end of the label just before ISK; is that just a typo?)
         */
        wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TempKey", tk, 40);
-       peap_prfplus(data->peap_version, tk, 40, "Inner Methods Compound Keys",
-                    isk, sizeof(isk), imck, sizeof(imck));
+       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));
 
@@ -414,7 +419,7 @@ static struct wpabuf * eap_peap_build_phase2_tlv(struct eap_sm *sm,
 #endif /* EAP_SERVER_TNC */
 
                if (eap_peap_derive_cmk(sm, data) < 0 ||
-                   os_get_random(data->binding_nonce, 32)) {
+                   random_get_bytes(data->binding_nonce, 32)) {
                        wpabuf_free(buf);
                        return NULL;
                }
@@ -1319,9 +1324,10 @@ static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len)
                 * termination for this label while the one used for deriving
                 * IPMK|CMK did not use null termination.
                 */
-               peap_prfplus(data->peap_version, data->ipmk, 40,
-                            "Session Key Generating Function",
-                            (u8 *) "\00", 1, csk, sizeof(csk));
+               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) {
index 4c30346..fb299ae 100644 (file)
@@ -19,6 +19,7 @@
 
 #include "common.h"
 #include "crypto/aes_wrap.h"
+#include "crypto/random.h"
 #include "eap_common/eap_psk_common.h"
 #include "eap_server/eap_i.h"
 
@@ -66,7 +67,7 @@ static struct wpabuf * eap_psk_build_1(struct eap_sm *sm,
 
        wpa_printf(MSG_DEBUG, "EAP-PSK: PSK-1 (sending)");
 
-       if (os_get_random(data->rand_s, EAP_PSK_RAND_LEN)) {
+       if (random_get_bytes(data->rand_s, EAP_PSK_RAND_LEN)) {
                wpa_printf(MSG_ERROR, "EAP-PSK: Failed to get random data");
                data->state = FAILURE;
                return NULL;
@@ -124,8 +125,10 @@ static struct wpabuf * eap_psk_build_3(struct eap_sm *sm,
 
        os_memcpy(buf, data->id_s, data->id_s_len);
        os_memcpy(buf + data->id_s_len, data->rand_p, EAP_PSK_RAND_LEN);
-       if (omac1_aes_128(data->ak, buf, buflen, psk->mac_s))
+       if (omac1_aes_128(data->ak, buf, buflen, psk->mac_s)) {
+               os_free(buf);
                goto fail;
+       }
        os_free(buf);
 
        if (eap_psk_derive_keys(data->kdk, data->rand_p, data->tek, data->msk,
diff --git a/src/eap_server/eap_server_pwd.c b/src/eap_server/eap_server_pwd.c
new file mode 100644 (file)
index 0000000..cf714c5
--- /dev/null
@@ -0,0 +1,864 @@
+/*
+ * hostapd / EAP-pwd (RFC 5931) server
+ * Copyright (c) 2010, Dan Harkins <dharkins@lounge.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the BSD license.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.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;
+       u32 token;
+       u16 group_num;
+       EAP_PWD_group *grp;
+
+       BIGNUM *k;
+       BIGNUM *private_value;
+       BIGNUM *peer_scalar;
+       BIGNUM *my_scalar;
+       EC_POINT *my_element;
+       EC_POINT *peer_element;
+
+       u8 my_confirm[SHA256_DIGEST_LENGTH];
+
+       u8 msk[EAP_MSK_LEN];
+       u8 emsk[EAP_EMSK_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");
+               os_free(data->id_server);
+               os_free(data);
+               return NULL;
+       }
+       data->password_len = sm->user->password_len;
+       os_memcpy(data->password, sm->user->password, data->password_len);
+
+       data->bnctx = BN_CTX_new();
+       if (data->bnctx == NULL) {
+               wpa_printf(MSG_INFO, "EAP-PWD: bn context allocation fail");
+               os_free(data->password);
+               os_free(data->id_server);
+               os_free(data);
+               return NULL;
+       }
+
+       return data;
+}
+
+
+static void eap_pwd_reset(struct eap_sm *sm, void *priv)
+{
+       struct eap_pwd_data *data = priv;
+
+       BN_free(data->private_value);
+       BN_free(data->peer_scalar);
+       BN_free(data->my_scalar);
+       BN_free(data->k);
+       BN_CTX_free(data->bnctx);
+       EC_POINT_free(data->my_element);
+       EC_POINT_free(data->peer_element);
+       os_free(data->id_peer);
+       os_free(data->id_server);
+       os_free(data->password);
+       if (data->grp) {
+               EC_GROUP_free(data->grp->group);
+               EC_POINT_free(data->grp->pwe);
+               BN_free(data->grp->order);
+               BN_free(data->grp->prime);
+               os_free(data->grp);
+       }
+       os_free(data);
+}
+
+
+static struct wpabuf *
+eap_pwd_build_id_req(struct eap_sm *sm, struct eap_pwd_data *data, u8 id)
+{
+       struct wpabuf *req;
+
+       wpa_printf(MSG_DEBUG, "EAP-pwd: ID/Request");
+       req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
+                           sizeof(struct eap_pwd_hdr) +
+                           sizeof(struct eap_pwd_id) + data->id_server_len,
+                           EAP_CODE_REQUEST, id);
+       if (req == NULL) {
+               eap_pwd_state(data, FAILURE);
+               return NULL;
+       }
+
+       /* an lfsr is good enough to generate unpredictable tokens */
+       data->token = os_random();
+       wpabuf_put_u8(req, EAP_PWD_OPCODE_ID_EXCH);
+       wpabuf_put_be16(req, data->group_num);
+       wpabuf_put_u8(req, EAP_PWD_DEFAULT_RAND_FUNC);
+       wpabuf_put_u8(req, EAP_PWD_DEFAULT_PRF);
+       wpabuf_put_data(req, &data->token, sizeof(data->token));
+       wpabuf_put_u8(req, EAP_PWD_PREP_NONE);
+       wpabuf_put_data(req, data->id_server, data->id_server_len);
+
+       return req;
+}
+
+
+static struct wpabuf *
+eap_pwd_build_commit_req(struct eap_sm *sm, struct eap_pwd_data *data, u8 id)
+{
+       struct wpabuf *req = NULL;
+       BIGNUM *mask = NULL, *x = NULL, *y = NULL;
+       u8 *scalar = NULL, *element = NULL;
+       u16 offset;
+
+       wpa_printf(MSG_DEBUG, "EAP-pwd: Commit/Request");
+
+       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;
+       }
+
+       BN_rand_range(data->private_value, data->grp->order);
+       BN_rand_range(mask, data->grp->order);
+       BN_add(data->my_scalar, data->private_value, mask);
+       BN_mod(data->my_scalar, data->my_scalar, data->grp->order,
+              data->bnctx);
+
+       if (!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_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);
+
+       req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
+                           sizeof(struct eap_pwd_hdr) +
+                           (2 * BN_num_bytes(data->grp->prime)) +
+                           BN_num_bytes(data->grp->order),
+                           EAP_CODE_REQUEST, id);
+       if (req == NULL)
+               goto fin;
+       wpabuf_put_u8(req, EAP_PWD_OPCODE_COMMIT_EXCH);
+
+       /* We send the element as (x,y) followed by the scalar */
+       wpabuf_put_data(req, element, (2 * BN_num_bytes(data->grp->prime)));
+       wpabuf_put_data(req, scalar, BN_num_bytes(data->grp->order));
+
+fin:
+       os_free(scalar);
+       os_free(element);
+       BN_free(x);
+       BN_free(y);
+       if (req == NULL)
+               eap_pwd_state(data, FAILURE);
+
+       return req;
+}
+
+
+static struct wpabuf *
+eap_pwd_build_confirm_req(struct eap_sm *sm, struct eap_pwd_data *data, u8 id)
+{
+       struct wpabuf *req = NULL;
+       BIGNUM *x = NULL, *y = NULL;
+       HMAC_CTX ctx;
+       u8 conf[SHA256_DIGEST_LENGTH], *cruft = NULL, *ptr;
+       u16 grp;
+       int offset;
+
+       wpa_printf(MSG_DEBUG, "EAP-pwd: Confirm/Request");
+
+       /* 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)
+        */
+       H_Init(&ctx);
+
+       /*
+        * 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);
+       H_Update(&ctx, 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);
+       H_Update(&ctx, 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);
+       H_Update(&ctx, 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);
+       H_Update(&ctx, 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);
+       H_Update(&ctx, 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);
+       H_Update(&ctx, 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);
+       H_Update(&ctx, 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);
+       H_Update(&ctx, cruft, ptr-cruft);
+
+       /* all done with the random function */
+       H_Final(&ctx, conf);
+       os_memcpy(data->my_confirm, conf, SHA256_DIGEST_LENGTH);
+
+       req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
+                           sizeof(struct eap_pwd_hdr) + SHA256_DIGEST_LENGTH,
+                           EAP_CODE_REQUEST, id);
+       if (req == NULL)
+               goto fin;
+
+       wpabuf_put_u8(req, EAP_PWD_OPCODE_CONFIRM_EXCH);
+       wpabuf_put_data(req, conf, SHA256_DIGEST_LENGTH);
+
+fin:
+       os_free(cruft);
+       BN_free(x);
+       BN_free(y);
+       if (req == NULL)
+               eap_pwd_state(data, FAILURE);
+
+       return req;
+}
+
+
+static struct wpabuf *
+eap_pwd_build_req(struct eap_sm *sm, void *priv, u8 id)
+{
+       struct eap_pwd_data *data = priv;
+
+       switch (data->state) {
+        case PWD_ID_Req:
+               return eap_pwd_build_id_req(sm, data, id);
+        case PWD_Commit_Req:
+               return eap_pwd_build_commit_req(sm, data, id);
+        case PWD_Confirm_Req:
+               return eap_pwd_build_confirm_req(sm, data, id);
+        default:
+               wpa_printf(MSG_INFO, "EAP-pwd: Unknown state %d in build_req",
+                          data->state);
+               break;
+       }
+
+       return NULL;
+}
+
+
+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: opcode=%d", *pos);
+
+       if (data->state == PWD_ID_Req && *pos == EAP_PWD_OPCODE_ID_EXCH)
+               return FALSE;
+
+       if (data->state == PWD_Commit_Req &&
+           *pos == EAP_PWD_OPCODE_COMMIT_EXCH)
+               return FALSE;
+
+       if (data->state == PWD_Confirm_Req &&
+           *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;
+
+       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);
+
+       if ((data->grp = os_malloc(sizeof(EAP_PWD_group))) == NULL) {
+               wpa_printf(MSG_INFO, "EAP-PWD: failed to allocate memory for "
+                          "group");
+               return;
+       }
+       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)) {
+               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;
+
+       wpa_printf(MSG_DEBUG, "EAP-pwd: Received commit response");
+
+       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_free(K);
+       EC_POINT_free(point);
+       BN_free(cofactor);
+       BN_free(x);
+       BN_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;
+       HMAC_CTX ctx;
+       u32 cs;
+       u16 grp;
+       u8 conf[SHA256_DIGEST_LENGTH], *cruft = NULL, *ptr;
+       int offset;
+
+       /* 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)
+        */
+       H_Init(&ctx);
+
+       /* 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);
+       H_Update(&ctx, 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);
+       H_Update(&ctx, 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);
+       H_Update(&ctx, 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);
+       H_Update(&ctx, 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);
+       H_Update(&ctx, 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);
+       H_Update(&ctx, 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);
+       H_Update(&ctx, cruft, BN_num_bytes(data->grp->order));
+
+       /* ciphersuite */
+       os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+       H_Update(&ctx, (u8 *)&cs, sizeof(u32));
+
+       /* all done */
+       H_Final(&ctx, conf);
+
+       ptr = (u8 *) payload;
+       if (os_memcmp(conf, ptr, SHA256_DIGEST_LENGTH)) {
+               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) < 0)
+               eap_pwd_state(data, FAILURE);
+       else
+               eap_pwd_state(data, SUCCESS);
+
+fin:
+       os_free(cruft);
+       BN_free(x);
+       BN_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 exch;
+
+       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;
+       }
+
+       exch = *pos & 0x3f;
+       switch (exch) {
+       case EAP_PWD_OPCODE_ID_EXCH:
+               eap_pwd_process_id_resp(sm, data, pos + 1, len - 1);
+               break;
+       case EAP_PWD_OPCODE_COMMIT_EXCH:
+               eap_pwd_process_commit_resp(sm, data, pos + 1, len - 1);
+               break;
+        case EAP_PWD_OPCODE_CONFIRM_EXCH:
+               eap_pwd_process_confirm_resp(sm, data, pos + 1, len - 1);
+               break;
+       }
+}
+
+
+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);
+}
+
+
+int eap_server_pwd_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+       struct timeval tp;
+       struct timezone tz;
+       u32 sr;
+
+       EVP_add_digest(EVP_sha256());
+
+       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;
+
+       ret = eap_server_method_register(eap);
+       if (ret)
+               eap_server_method_free(eap);
+       return ret;
+}
+
index ce4848f..a9b515f 100644 (file)
@@ -15,6 +15,7 @@
 #include "includes.h"
 
 #include "common.h"
+#include "crypto/random.h"
 #include "eap_server/eap_i.h"
 #include "eap_common/eap_sake_common.h"
 
@@ -166,7 +167,7 @@ static struct wpabuf * eap_sake_build_challenge(struct eap_sm *sm,
 
        wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Challenge");
 
-       if (os_get_random(data->rand_s, EAP_SAKE_RAND_LEN)) {
+       if (random_get_bytes(data->rand_s, EAP_SAKE_RAND_LEN)) {
                wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to get random data");
                data->state = FAILURE;
                return NULL;
index 436c655..6de2e70 100644 (file)
@@ -15,6 +15,7 @@
 #include "includes.h"
 
 #include "common.h"
+#include "crypto/random.h"
 #include "eap_server/eap_i.h"
 #include "eap_common/eap_sim_common.h"
 #include "eap_server/eap_sim_db.h"
@@ -136,8 +137,13 @@ static int eap_sim_build_encr(struct eap_sm *sm, struct eap_sim_data *data,
                              const u8 *nonce_s)
 {
        os_free(data->next_pseudonym);
-       data->next_pseudonym =
-               eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv, 0);
+       if (nonce_s == NULL) {
+               data->next_pseudonym =
+                       eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv, 0);
+       } else {
+               /* Do not update pseudonym during re-authentication */
+               data->next_pseudonym = NULL;
+       }
        os_free(data->next_reauth_id);
        if (data->counter <= EAP_SIM_MAX_FAST_REAUTHS) {
                data->next_reauth_id =
@@ -232,7 +238,7 @@ static struct wpabuf * eap_sim_build_reauth(struct eap_sm *sm,
 
        wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Re-authentication");
 
-       if (os_get_random(data->nonce_s, EAP_SIM_NONCE_S_LEN))
+       if (random_get_bytes(data->nonce_s, EAP_SIM_NONCE_S_LEN))
                return NULL;
        wpa_hexdump_key(MSG_MSGDUMP, "EAP-SIM: NONCE_S",
                        data->nonce_s, EAP_SIM_NONCE_S_LEN);
@@ -621,11 +627,6 @@ static void eap_sim_process_reauth(struct eap_sm *sm,
                identity_len = id2_len;
        }
 
-       if (data->next_pseudonym) {
-               eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity,
-                                        identity_len, data->next_pseudonym);
-               data->next_pseudonym = NULL;
-       }
        if (data->next_reauth_id) {
                eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity,
                                      identity_len, data->next_reauth_id,
index 25ae683..e149ee3 100644 (file)
@@ -45,8 +45,7 @@ int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
                return -1;
        }
 
-       /* TODO: make this configurable */
-       data->tls_out_limit = 1398;
+       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
index f3b70ed..a2d6f17 100644 (file)
@@ -91,7 +91,8 @@ static void * eap_tnc_init(struct eap_sm *sm)
                return NULL;
        }
 
-       data->fragment_size = 1300;
+       data->fragment_size = sm->fragment_size > 100 ?
+               sm->fragment_size - 98 : 1300;
 
        return data;
 }
index 702c50c..398d0f1 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * hostapd / EAP-TTLS (RFC 5281)
- * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
 #include "eap_common/eap_ttls.h"
 
 
-/* Maximum supported TTLS version
- * 0 = RFC 5281
- * 1 = draft-funk-eap-ttls-v1-00.txt
- */
-#ifndef EAP_TTLS_VERSION
-#define EAP_TTLS_VERSION 0 /* TTLSv1 implementation is not yet complete */
-#endif /* EAP_TTLS_VERSION */
-
-
-#define MSCHAPV2_KEY_LEN 16
+#define EAP_TTLS_VERSION 0
 
 
 static void eap_ttls_reset(struct eap_sm *sm, void *priv);
@@ -43,17 +34,15 @@ struct eap_ttls_data {
        struct eap_ssl_data ssl;
        enum {
                START, PHASE1, PHASE2_START, PHASE2_METHOD,
-               PHASE2_MSCHAPV2_RESP, PHASE_FINISHED, SUCCESS, FAILURE
+               PHASE2_MSCHAPV2_RESP, SUCCESS, FAILURE
        } state;
 
        int ttls_version;
-       int force_version;
        const struct eap_method *phase2_method;
        void *phase2_priv;
        int mschapv2_resp_ok;
        u8 mschapv2_auth_response[20];
        u8 mschapv2_ident;
-       int tls_ia_configured;
        struct wpabuf *pending_phase2_eap_resp;
        int tnc_started;
 };
@@ -72,8 +61,6 @@ static const char * eap_ttls_state_txt(int state)
                return "PHASE2_METHOD";
        case PHASE2_MSCHAPV2_RESP:
                return "PHASE2_MSCHAPV2_RESP";
-       case PHASE_FINISHED:
-               return "PHASE_FINISHED";
        case SUCCESS:
                return "SUCCESS";
        case FAILURE:
@@ -111,7 +98,8 @@ static u8 * eap_ttls_avp_hdr(u8 *avphdr, u32 avp_code, u32 vendor_id,
        }
 
        avp->avp_code = host_to_be32(avp_code);
-       avp->avp_length = host_to_be32((flags << 24) | (hdrlen + len));
+       avp->avp_length = host_to_be32(((u32) flags << 24) |
+                                      ((u32) (hdrlen + len)));
 
        return avphdr + hdrlen;
 }
@@ -320,54 +308,8 @@ fail:
 static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm,
                                        struct eap_ttls_data *data, size_t len)
 {
-       struct tls_keys keys;
-       u8 *challenge, *rnd;
-
-       if (data->ttls_version == 0) {
-               return eap_server_tls_derive_key(sm, &data->ssl,
-                                                "ttls challenge", len);
-       }
-
-       os_memset(&keys, 0, sizeof(keys));
-       if (tls_connection_get_keys(sm->ssl_ctx, data->ssl.conn, &keys) ||
-           keys.client_random == NULL || keys.server_random == NULL ||
-           keys.inner_secret == NULL) {
-               wpa_printf(MSG_INFO, "EAP-TTLS: Could not get inner secret, "
-                          "client random, or server random to derive "
-                          "implicit challenge");
-               return NULL;
-       }
-
-       rnd = os_malloc(keys.client_random_len + keys.server_random_len);
-       challenge = os_malloc(len);
-       if (rnd == NULL || challenge == NULL) {
-               wpa_printf(MSG_INFO, "EAP-TTLS: No memory for implicit "
-                          "challenge derivation");
-               os_free(rnd);
-               os_free(challenge);
-               return NULL;
-       }
-       os_memcpy(rnd, keys.server_random, keys.server_random_len);
-       os_memcpy(rnd + keys.server_random_len, keys.client_random,
-                 keys.client_random_len);
-
-       if (tls_prf(keys.inner_secret, keys.inner_secret_len,
-                   "inner application challenge", rnd,
-                   keys.client_random_len + keys.server_random_len,
-                   challenge, len)) {
-               wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive implicit "
-                          "challenge");
-               os_free(rnd);
-               os_free(challenge);
-               return NULL;
-       }
-
-       os_free(rnd);
-
-       wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived implicit challenge",
-                       challenge, len);
-
-       return challenge;
+       return eap_server_tls_derive_key(sm, &data->ssl, "ttls challenge",
+                                        len);
 }
 
 
@@ -379,27 +321,8 @@ static void * eap_ttls_init(struct eap_sm *sm)
        if (data == NULL)
                return NULL;
        data->ttls_version = EAP_TTLS_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-TTLS: forcing version %d",
-                          data->force_version);
-               data->ttls_version = data->force_version;
-       }
        data->state = START;
 
-       if (!(tls_capabilities(sm->ssl_ctx) & TLS_CAPABILITY_IA) &&
-           data->ttls_version > 0) {
-               if (data->force_version > 0) {
-                       wpa_printf(MSG_INFO, "EAP-TTLS: Forced TTLSv%d and "
-                                  "TLS library does not support TLS/IA.",
-                                  data->force_version);
-                       eap_ttls_reset(sm, data);
-                       return NULL;
-               }
-               data->ttls_version = 0;
-       }
-
        if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) {
                wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL.");
                eap_ttls_reset(sm, data);
@@ -516,14 +439,6 @@ static struct wpabuf * eap_ttls_build_phase2_mschapv2(
 }
 
 
-static struct wpabuf * eap_ttls_build_phase_finished(
-       struct eap_sm *sm, struct eap_ttls_data *data, int final)
-{
-       return tls_connection_ia_send_phase_finished(sm->ssl_ctx,
-                                                    data->ssl.conn, final);
-}
-
-
 static struct wpabuf * eap_ttls_buildReq(struct eap_sm *sm, void *priv, u8 id)
 {
        struct eap_ttls_data *data = priv;
@@ -559,11 +474,6 @@ static struct wpabuf * eap_ttls_buildReq(struct eap_sm *sm, void *priv, u8 id)
                data->ssl.tls_out_pos = 0;
                data->ssl.tls_out = eap_ttls_build_phase2_mschapv2(sm, data);
                break;
-       case PHASE_FINISHED:
-               wpabuf_free(data->ssl.tls_out);
-               data->ssl.tls_out_pos = 0;
-               data->ssl.tls_out = eap_ttls_build_phase_finished(sm, data, 1);
-               break;
        default:
                wpa_printf(MSG_DEBUG, "EAP-TTLS: %s - unexpected state %d",
                           __func__, data->state);
@@ -591,37 +501,6 @@ static Boolean eap_ttls_check(struct eap_sm *sm, void *priv,
 }
 
 
-static int eap_ttls_ia_permute_inner_secret(struct eap_sm *sm,
-                                           struct eap_ttls_data *data,
-                                           const u8 *key, size_t key_len)
-{
-       u8 *buf;
-       size_t buf_len;
-       int ret;
-
-       if (key) {
-               buf_len = 2 + key_len;
-               buf = os_malloc(buf_len);
-               if (buf == NULL)
-                       return -1;
-               WPA_PUT_BE16(buf, key_len);
-               os_memcpy(buf + 2, key, key_len);
-       } else {
-               buf = NULL;
-               buf_len = 0;
-       }
-
-       wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Session keys for TLS/IA inner "
-                       "secret permutation", buf, buf_len);
-       ret = tls_connection_ia_permute_inner_secret(sm->ssl_ctx,
-                                                    data->ssl.conn,
-                                                    buf, buf_len);
-       os_free(buf);
-
-       return ret;
-}
-
-
 static void eap_ttls_process_phase2_pap(struct eap_sm *sm,
                                        struct eap_ttls_data *data,
                                        const u8 *user_password,
@@ -644,8 +523,7 @@ static void eap_ttls_process_phase2_pap(struct eap_sm *sm,
        }
 
        wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: Correct user password");
-       eap_ttls_state(data, data->ttls_version > 0 ? PHASE_FINISHED :
-                      SUCCESS);
+       eap_ttls_state(data, SUCCESS);
 }
 
 
@@ -701,8 +579,7 @@ static void eap_ttls_process_phase2_chap(struct eap_sm *sm,
 
        if (os_memcmp(hash, password + 1, EAP_TTLS_CHAP_PASSWORD_LEN) == 0) {
                wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Correct user password");
-               eap_ttls_state(data, data->ttls_version > 0 ? PHASE_FINISHED :
-                              SUCCESS);
+               eap_ttls_state(data, SUCCESS);
        } else {
                wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Invalid user password");
                eap_ttls_state(data, FAILURE);
@@ -762,8 +639,7 @@ static void eap_ttls_process_phase2_mschap(struct eap_sm *sm,
 
        if (os_memcmp(nt_response, response + 2 + 24, 24) == 0) {
                wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Correct response");
-               eap_ttls_state(data, data->ttls_version > 0 ? PHASE_FINISHED :
-                              SUCCESS);
+               eap_ttls_state(data, SUCCESS);
        } else {
                wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Invalid NT-Response");
                wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAP: Received",
@@ -863,30 +739,6 @@ static void eap_ttls_process_phase2_mschapv2(struct eap_sm *sm,
                wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Correct "
                           "NT-Response");
                data->mschapv2_resp_ok = 1;
-               if (data->ttls_version > 0) {
-                       const u8 *pw_hash;
-                       u8 pw_hash_buf[16], pw_hash_hash[16], master_key[16];
-                       u8 session_key[2 * MSCHAPV2_KEY_LEN];
-
-                       if (sm->user->password_hash)
-                               pw_hash = sm->user->password;
-                       else {
-                               nt_password_hash(sm->user->password,
-                                                sm->user->password_len,
-                                                pw_hash_buf);
-                               pw_hash = pw_hash_buf;
-                       }
-                       hash_nt_password_hash(pw_hash, pw_hash_hash);
-                       get_master_key(pw_hash_hash, nt_response, master_key);
-                       get_asymetric_start_key(master_key, session_key,
-                                               MSCHAPV2_KEY_LEN, 0, 0);
-                       get_asymetric_start_key(master_key,
-                                               session_key + MSCHAPV2_KEY_LEN,
-                                               MSCHAPV2_KEY_LEN, 1, 0);
-                       eap_ttls_ia_permute_inner_secret(sm, data,
-                                                        session_key,
-                                                        sizeof(session_key));
-               }
 
                if (sm->user->password_hash) {
                        generate_authenticator_response_pwhash(
@@ -1030,17 +882,7 @@ static void eap_ttls_process_phase2_eap_response(struct eap_sm *sm,
                }
                break;
        case PHASE2_METHOD:
-               if (data->ttls_version > 0) {
-                       if (m->getKey) {
-                               u8 *key;
-                               size_t key_len;
-                               key = m->getKey(sm, priv, &key_len);
-                               eap_ttls_ia_permute_inner_secret(sm, data,
-                                                                key, key_len);
-                       }
-                       eap_ttls_state(data, PHASE_FINISHED);
-               } else
-                       eap_ttls_state(data, SUCCESS);
+               eap_ttls_state(data, SUCCESS);
                break;
        case FAILURE:
                break;
@@ -1130,23 +972,6 @@ static void eap_ttls_process_phase2(struct eap_sm *sm,
                return;
        }
 
-       if (data->state == PHASE_FINISHED) {
-               if (wpabuf_len(in_decrypted) == 0 &&
-                   tls_connection_ia_final_phase_finished(sm->ssl_ctx,
-                                                          data->ssl.conn)) {
-                       wpa_printf(MSG_DEBUG, "EAP-TTLS: FinalPhaseFinished "
-                                  "received");
-                       eap_ttls_state(data, SUCCESS);
-               } else {
-                       wpa_printf(MSG_INFO, "EAP-TTLS: Did not receive valid "
-                                  "FinalPhaseFinished");
-                       eap_ttls_state(data, FAILURE);
-               }
-
-               wpabuf_free(in_decrypted);
-               return;
-       }
-
        wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TTLS: Decrypted Phase 2 EAP",
                            in_decrypted);
 
@@ -1245,15 +1070,6 @@ static int eap_ttls_process_version(struct eap_sm *sm, void *priv,
                data->ttls_version = peer_version;
        }
 
-       if (data->ttls_version > 0 && !data->tls_ia_configured) {
-               if (tls_connection_set_ia(sm->ssl_ctx, data->ssl.conn, 1)) {
-                       wpa_printf(MSG_INFO, "EAP-TTLS: Failed to enable "
-                                  "TLS/IA");
-                       return -1;
-               }
-               data->tls_ia_configured = 1;
-       }
-
        return 0;
 }
 
@@ -1270,7 +1086,6 @@ static void eap_ttls_process_msg(struct eap_sm *sm, void *priv,
                break;
        case PHASE2_START:
        case PHASE2_METHOD:
-       case PHASE_FINISHED:
                eap_ttls_process_phase2(sm, data, data->ssl.tls_in);
                eap_ttls_start_tnc(sm, data);
                break;
@@ -1279,8 +1094,7 @@ static void eap_ttls_process_msg(struct eap_sm *sm, void *priv,
                    0) {
                        wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer "
                                   "acknowledged response");
-                       eap_ttls_state(data, data->ttls_version > 0 ?
-                                      PHASE_FINISHED : SUCCESS);
+                       eap_ttls_state(data, SUCCESS);
                } else if (!data->mschapv2_resp_ok) {
                        wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer "
                                   "acknowledged error");
@@ -1321,54 +1135,6 @@ static Boolean eap_ttls_isDone(struct eap_sm *sm, void *priv)
 }
 
 
-static u8 * eap_ttls_v1_derive_key(struct eap_sm *sm,
-                                  struct eap_ttls_data *data)
-{
-       struct tls_keys keys;
-       u8 *rnd, *key;
-
-       os_memset(&keys, 0, sizeof(keys));
-       if (tls_connection_get_keys(sm->ssl_ctx, data->ssl.conn, &keys) ||
-           keys.client_random == NULL || keys.server_random == NULL ||
-           keys.inner_secret == NULL) {
-               wpa_printf(MSG_INFO, "EAP-TTLS: Could not get inner secret, "
-                          "client random, or server random to derive keying "
-                          "material");
-               return NULL;
-       }
-
-       rnd = os_malloc(keys.client_random_len + keys.server_random_len);
-       key = os_malloc(EAP_TLS_KEY_LEN);
-       if (rnd == NULL || key == NULL) {
-               wpa_printf(MSG_INFO, "EAP-TTLS: No memory for key derivation");
-               os_free(rnd);
-               os_free(key);
-               return NULL;
-       }
-       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(keys.inner_secret, keys.inner_secret_len,
-                   "ttls v1 keying material", rnd, keys.client_random_len +
-                   keys.server_random_len, key, EAP_TLS_KEY_LEN)) {
-               wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive key");
-               os_free(rnd);
-               os_free(key);
-               return NULL;
-       }
-
-       wpa_hexdump(MSG_DEBUG, "EAP-TTLS: client/server random",
-                   rnd, keys.client_random_len + keys.server_random_len);
-       wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: TLS/IA inner secret",
-                       keys.inner_secret, keys.inner_secret_len);
-
-       os_free(rnd);
-
-       return key;
-}
-
-
 static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len)
 {
        struct eap_ttls_data *data = priv;
@@ -1377,14 +1143,9 @@ static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len)
        if (data->state != SUCCESS)
                return NULL;
 
-       if (data->ttls_version == 0) {
-               eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
-                                                      "ttls keying material",
-                                                      EAP_TLS_KEY_LEN);
-       } else {
-               eapKeyData = eap_ttls_v1_derive_key(sm, data);
-       }
-
+       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",
index 77cf9e2..556882d 100644 (file)
@@ -18,6 +18,7 @@
 #include "eloop.h"
 #include "eap_i.h"
 #include "eap_common/eap_wsc_common.h"
+#include "p2p/p2p.h"
 #include "wps/wps.h"
 
 
@@ -135,14 +136,22 @@ static void * eap_wsc_init(struct eap_sm *sm)
        }
        cfg.assoc_wps_ie = sm->assoc_wps_ie;
        cfg.peer_addr = sm->peer_addr;
-       if (0 /* TODO: could provide option for forcing PSK format */)
-                cfg.use_psk_key = 1;
+#ifdef CONFIG_P2P
+       if (sm->assoc_p2p_ie) {
+               wpa_printf(MSG_DEBUG, "EAP-WSC: Prefer PSK format for P2P "
+                          "client");
+               cfg.use_psk_key = 1;
+               cfg.p2p_dev_addr = p2p_get_go_dev_addr(sm->assoc_p2p_ie);
+       }
+#endif /* CONFIG_P2P */
+       cfg.pbc_in_m1 = sm->pbc_in_m1;
        data->wps = wps_init(&cfg);
        if (data->wps == NULL) {
                os_free(data);
                return NULL;
        }
-       data->fragment_size = WSC_FRAGMENT_SIZE;
+       data->fragment_size = sm->fragment_size > 0 ? sm->fragment_size :
+               WSC_FRAGMENT_SIZE;
 
        return data;
 }
index aba919a..248b216 100644 (file)
@@ -25,6 +25,7 @@
 #include <sys/un.h>
 
 #include "common.h"
+#include "crypto/random.h"
 #include "eap_common/eap_sim_common.h"
 #include "eap_server/eap_sim_db.h"
 #include "eloop.h"
@@ -830,7 +831,7 @@ static char * eap_sim_db_get_next(struct eap_sim_db_data *data, char prefix)
        char *id, *pos, *end;
        u8 buf[10];
 
-       if (os_get_random(buf, sizeof(buf)))
+       if (random_get_bytes(buf, sizeof(buf)))
                return NULL;
        id = os_malloc(sizeof(buf) * 2 + 2);
        if (id == NULL)
index 435ba26..f5bbb14 100644 (file)
@@ -16,6 +16,7 @@
 
 #include "common.h"
 #include "crypto/dh_groups.h"
+#include "crypto/random.h"
 #include "ikev2.h"
 
 
@@ -403,7 +404,7 @@ static int ikev2_process_ker(struct ikev2_initiator_data *data,
        }
 
        /* RFC 4306, Section 3.4:
-        * The length of DH public value MUST be equal to the lenght of the
+        * The length of DH public value MUST be equal to the length of the
         * prime modulus.
         */
        if (ker_len - 4 != data->dh->prime_len) {
@@ -1100,7 +1101,7 @@ static struct wpabuf * ikev2_build_sa_init(struct ikev2_initiator_data *data)
                    data->i_spi, IKEV2_SPI_LEN);
 
        data->i_nonce_len = IKEV2_NONCE_MIN_LEN;
-       if (os_get_random(data->i_nonce, data->i_nonce_len))
+       if (random_get_bytes(data->i_nonce, data->i_nonce_len))
                return NULL;
        wpa_hexdump(MSG_DEBUG, "IKEV2: Ni", data->i_nonce, data->i_nonce_len);
 
@@ -1148,7 +1149,7 @@ static struct wpabuf * ikev2_build_sa_auth(struct ikev2_initiator_data *data)
                if (data->shared_secret == NULL)
                        return NULL;
                data->shared_secret_len = 16;
-               if (os_get_random(data->shared_secret, 16))
+               if (random_get_bytes(data->shared_secret, 16))
                        return NULL;
        } else {
                os_free(data->shared_secret);
index 497b51a..637b6f8 100644 (file)
@@ -234,11 +234,11 @@ TNC_Result TNC_TNCS_ReportMessageTypes(
                return TNC_RESULT_INVALID_PARAMETER;
        os_free(imv->supported_types);
        imv->supported_types =
-               os_malloc(typeCount * sizeof(TNC_MessageTypeList));
+               os_malloc(typeCount * sizeof(TNC_MessageType));
        if (imv->supported_types == NULL)
                return TNC_RESULT_FATAL;
        os_memcpy(imv->supported_types, supportedTypes,
-                 typeCount * sizeof(TNC_MessageTypeList));
+                 typeCount * sizeof(TNC_MessageType));
        imv->num_supported_types = typeCount;
 
        return TNC_RESULT_SUCCESS;
index a1976e8..e600954 100644 (file)
@@ -762,7 +762,8 @@ SM_STEP(CTRL_DIR)
 
 struct eapol_state_machine *
 eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr,
-                int flags, const struct wpabuf *assoc_wps_ie, void *sta_ctx)
+                int flags, const struct wpabuf *assoc_wps_ie,
+                const struct wpabuf *assoc_p2p_ie, void *sta_ctx)
 {
        struct eapol_state_machine *sm;
        struct eap_config eap_conf;
@@ -829,7 +830,11 @@ eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr,
        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;
        sm->eap = eap_server_sm_init(sm, &eapol_cb, &eap_conf);
        if (sm->eap == NULL) {
                eapol_auth_free(sm);
@@ -1012,7 +1017,7 @@ static struct eapol_callbacks eapol_cb =
 
 int eapol_auth_eap_pending_cb(struct eapol_state_machine *sm, void *ctx)
 {
-       if (sm == NULL || ctx != sm->eap)
+       if (sm == NULL || ctx == NULL || ctx != sm->eap)
                return -1;
 
        eap_sm_pending_cb(sm->eap);
@@ -1034,6 +1039,8 @@ static int eapol_auth_conf_clone(struct eapol_auth_config *dst,
        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;
        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)
@@ -1077,6 +1084,7 @@ static int eapol_auth_conf_clone(struct eapol_auth_config *dst,
        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;
        return 0;
 }
 
index ef943ad..724bf8b 100644 (file)
@@ -40,6 +40,9 @@ struct eapol_auth_config {
        int eap_sim_aka_result_ind;
        int tnc;
        struct wps_context *wps;
+       int fragment_size;
+       u16 pwd_group;
+       int pbc_in_m1;
 
        /* Opaque context pointer to owner data for callback functions */
        void *ctx;
@@ -79,7 +82,8 @@ struct eapol_authenticator * eapol_auth_init(struct eapol_auth_config *conf,
 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, void *sta_ctx);
+                int flags, const struct wpabuf *assoc_wps_ie,
+                const struct wpabuf *assoc_p2p_ie, void *sta_ctx);
 void eapol_auth_free(struct eapol_state_machine *sm);
 void eapol_auth_step(struct eapol_state_machine *sm);
 void eapol_auth_dump_state(FILE *f, const char *prefix,
index 77cd564..ffc6619 100644 (file)
@@ -561,7 +561,7 @@ SM_STEP(SUPP_BE)
                 * 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 conjuction with eapSuccess and
+                * 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
@@ -1029,6 +1029,21 @@ void eapol_sm_configure(struct eapol_sm *sm, int heldPeriod, int authPeriod,
 }
 
 
+/**
+ * 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
@@ -1798,7 +1813,7 @@ static void eapol_sm_notify_pending(void *ctx)
 
 
 #if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
-static void eapol_sm_eap_param_needed(void *ctx, const char *field,
+static void eapol_sm_eap_param_needed(void *ctx, enum wpa_ctrl_req_type field,
                                      const char *txt)
 {
        struct eapol_sm *sm = ctx;
@@ -1810,6 +1825,15 @@ static void eapol_sm_eap_param_needed(void *ctx, const char *field,
 #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 *cert_hash,
+                                const struct wpabuf *cert)
+{
+       struct eapol_sm *sm = ctx;
+       if (sm->ctx->cert_cb)
+               sm->ctx->cert_cb(sm->ctx->ctx, depth, subject,
+                                cert_hash, cert);
+}
 
 static struct eapol_callbacks eapol_cb =
 {
@@ -1822,7 +1846,8 @@ static struct eapol_callbacks eapol_cb =
        eapol_sm_set_config_blob,
        eapol_sm_get_config_blob,
        eapol_sm_notify_pending,
-       eapol_sm_eap_param_needed
+       eapol_sm_eap_param_needed,
+       eapol_sm_notify_cert
 };
 
 
@@ -1858,6 +1883,7 @@ struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx)
        conf.pkcs11_engine_path = ctx->pkcs11_engine_path;
        conf.pkcs11_module_path = ctx->pkcs11_module_path;
        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) {
index 1d2a32b..bcb00b5 100644 (file)
@@ -208,10 +208,10 @@ struct eapol_ctx {
        /**
         * eap_param_needed - Notify that EAP parameter is needed
         * @ctx: Callback context (ctx)
-        * @field: Field name (e.g., "IDENTITY")
+        * @field: Field indicator (e.g., WPA_CTRL_REQ_EAP_IDENTITY)
         * @txt: User readable text describing the required parameter
         */
-       void (*eap_param_needed)(void *ctx, const char *field,
+       void (*eap_param_needed)(void *ctx, enum wpa_ctrl_req_type field,
                                 const char *txt);
 
        /**
@@ -220,6 +220,22 @@ struct eapol_ctx {
         * @authorized: Whether the supplicant port is now in authorized state
         */
        void (*port_cb)(void *ctx, int authorized);
+
+       /**
+        * cert_cb - Notification of a peer certificate
+        * @ctx: Callback context (ctx)
+        * @depth: Depth in certificate chain (0 = server)
+        * @subject: Subject of the peer certificate
+        * @cert_hash: SHA-256 hash of the certificate
+        * @cert: Peer certificate
+        */
+       void (*cert_cb)(void *ctx, int depth, const char *subject,
+                       const char *cert_hash, const struct wpabuf *cert);
+
+       /**
+        * cert_in_cb - Include server certificates in callback
+        */
+       int cert_in_cb;
 };
 
 
@@ -255,6 +271,7 @@ void eapol_sm_notify_ctrl_response(struct eapol_sm *sm);
 void eapol_sm_request_reauth(struct eapol_sm *sm);
 void eapol_sm_notify_lower_layer_success(struct eapol_sm *sm, int in_eapol_sm);
 void eapol_sm_invalidate_cached_session(struct eapol_sm *sm);
+const char * eapol_sm_get_method_name(struct eapol_sm *sm);
 #else /* IEEE8021X_EAPOL */
 static inline struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx)
 {
@@ -342,6 +359,10 @@ static inline void eapol_sm_notify_lower_layer_success(struct eapol_sm *sm,
 static inline void eapol_sm_invalidate_cached_session(struct eapol_sm *sm)
 {
 }
+static inline const char * eapol_sm_get_method_name(struct eapol_sm *sm)
+{
+       return NULL;
+}
 #endif /* IEEE8021X_EAPOL */
 
 #endif /* EAPOL_SUPP_SM_H */
index 009e02c..e24277c 100644 (file)
 #include <pcap.h>
 
 #include <sys/ioctl.h>
+#ifdef __sun__
+#include <libdlpi.h>
+#else /* __sun__ */
 #include <sys/sysctl.h>
+#endif /* __sun__ */
 
 #include <net/if.h>
 #include <net/if_dl.h>
@@ -139,6 +143,7 @@ static int l2_packet_init_libpcap(struct l2_packet_data *l2,
        }
 
        pcap_freecode(&pcap_fp);
+#ifndef __sun__
        /*
         * When libpcap uses BPF we must enable "immediate mode" to
         * receive frames right away; otherwise the system may
@@ -153,6 +158,7 @@ static int l2_packet_init_libpcap(struct l2_packet_data *l2,
                        /* XXX should we fail? */
                }
        }
+#endif /* __sun__ */
 
        eloop_register_read_sock(pcap_get_selectable_fd(l2->pcap),
                                 l2_packet_receive, l2, l2->pcap);
@@ -163,6 +169,30 @@ static int l2_packet_init_libpcap(struct l2_packet_data *l2,
 
 static int eth_get(const char *device, u8 ea[ETH_ALEN])
 {
+#ifdef __sun__
+       dlpi_handle_t dh;
+       u32 physaddrlen = DLPI_PHYSADDR_MAX;
+       u8 physaddr[DLPI_PHYSADDR_MAX];
+       int retval;
+
+       retval = dlpi_open(device, &dh, 0);
+       if (retval != DLPI_SUCCESS) {
+               wpa_printf(MSG_ERROR, "dlpi_open error: %s",
+                          dlpi_strerror(retval));
+               return -1;
+       }
+
+       retval = dlpi_get_physaddr(dh, DL_CURR_PHYS_ADDR, physaddr,
+                                  &physaddrlen);
+       if (retval != DLPI_SUCCESS) {
+               wpa_printf(MSG_ERROR, "dlpi_get_physaddr error: %s",
+                          dlpi_strerror(retval));
+               dlpi_close(dh);
+               return -1;
+       }
+       os_memcpy(ea, physaddr, ETH_ALEN);
+       dlpi_close(dh);
+#else /* __sun__ */
        struct if_msghdr *ifm;
        struct sockaddr_dl *sdl;
        u_char *p, *buf;
@@ -195,6 +225,7 @@ static int eth_get(const char *device, u8 ea[ETH_ALEN])
                errno = ESRCH;
                return -1;
        }
+#endif /* __sun__ */
        return 0;
 }
 
index 48d1bde..93e15eb 100644 (file)
@@ -51,7 +51,8 @@ int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
        if (l2->l2_hdr) {
                ret = send(l2->fd, buf, len, 0);
                if (ret < 0)
-                       perror("l2_packet_send - send");
+                       wpa_printf(MSG_ERROR, "l2_packet_send - send: %s",
+                                  strerror(errno));
        } else {
                struct sockaddr_ll ll;
                os_memset(&ll, 0, sizeof(ll));
@@ -62,8 +63,10 @@ int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
                os_memcpy(ll.sll_addr, dst_addr, ETH_ALEN);
                ret = sendto(l2->fd, buf, len, 0, (struct sockaddr *) &ll,
                             sizeof(ll));
-               if (ret < 0)
-                       perror("l2_packet_send - sendto");
+               if (ret < 0) {
+                       wpa_printf(MSG_ERROR, "l2_packet_send - sendto: %s",
+                                  strerror(errno));
+               }
        }
        return ret;
 }
@@ -82,7 +85,8 @@ static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx)
        res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &ll,
                       &fromlen);
        if (res < 0) {
-               perror("l2_packet_receive - recvfrom");
+               wpa_printf(MSG_DEBUG, "l2_packet_receive - recvfrom: %s",
+                          strerror(errno));
                return;
        }
 
@@ -111,14 +115,16 @@ struct l2_packet_data * l2_packet_init(
        l2->fd = socket(PF_PACKET, l2_hdr ? SOCK_RAW : SOCK_DGRAM,
                        htons(protocol));
        if (l2->fd < 0) {
-               perror("socket(PF_PACKET)");
+               wpa_printf(MSG_ERROR, "%s: socket(PF_PACKET): %s",
+                          __func__, strerror(errno));
                os_free(l2);
                return NULL;
        }
        os_memset(&ifr, 0, sizeof(ifr));
        os_strlcpy(ifr.ifr_name, l2->ifname, sizeof(ifr.ifr_name));
        if (ioctl(l2->fd, SIOCGIFINDEX, &ifr) < 0) {
-               perror("ioctl[SIOCGIFINDEX]");
+               wpa_printf(MSG_ERROR, "%s: ioctl[SIOCGIFINDEX]: %s",
+                          __func__, strerror(errno));
                close(l2->fd);
                os_free(l2);
                return NULL;
@@ -130,14 +136,16 @@ struct l2_packet_data * l2_packet_init(
        ll.sll_ifindex = ifr.ifr_ifindex;
        ll.sll_protocol = htons(protocol);
        if (bind(l2->fd, (struct sockaddr *) &ll, sizeof(ll)) < 0) {
-               perror("bind[PF_PACKET]");
+               wpa_printf(MSG_ERROR, "%s: bind[PF_PACKET]: %s",
+                          __func__, strerror(errno));
                close(l2->fd);
                os_free(l2);
                return NULL;
        }
 
        if (ioctl(l2->fd, SIOCGIFHWADDR, &ifr) < 0) {
-               perror("ioctl[SIOCGIFHWADDR]");
+               wpa_printf(MSG_ERROR, "%s: ioctl[SIOCGIFHWADDR]: %s",
+                          __func__, strerror(errno));
                close(l2->fd);
                os_free(l2);
                return NULL;
@@ -173,14 +181,16 @@ int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
 
        s = socket(PF_INET, SOCK_DGRAM, 0);
        if (s < 0) {
-               perror("socket");
+               wpa_printf(MSG_ERROR, "%s: socket: %s",
+                          __func__, strerror(errno));
                return -1;
        }
        os_memset(&ifr, 0, sizeof(ifr));
        os_strlcpy(ifr.ifr_name, l2->ifname, sizeof(ifr.ifr_name));
        if (ioctl(s, SIOCGIFADDR, &ifr) < 0) {
                if (errno != EADDRNOTAVAIL)
-                       perror("ioctl[SIOCGIFADDR]");
+                       wpa_printf(MSG_ERROR, "%s: ioctl[SIOCGIFADDR]: %s",
+                                  __func__, strerror(errno));
                close(s);
                return -1;
        }
diff --git a/src/p2p/Makefile b/src/p2p/Makefile
new file mode 100644 (file)
index 0000000..cffba62
--- /dev/null
@@ -0,0 +1,9 @@
+all:
+       @echo Nothing to be made.
+
+clean:
+       for d in $(SUBDIRS); do make -C $$d clean; done
+       rm -f *~ *.o *.d
+
+install:
+       @echo Nothing to be made.
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
new file mode 100644 (file)
index 0000000..3bcbea6
--- /dev/null
@@ -0,0 +1,3901 @@
+/*
+ * Wi-Fi Direct - P2P module
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#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 "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
+ */
+#define P2P_PEER_EXPIRATION_AGE 300
+
+#define P2P_PEER_EXPIRATION_INTERVAL (P2P_PEER_EXPIRATION_AGE / 2)
+
+static void p2p_expire_peers(struct p2p_data *p2p)
+{
+       struct p2p_device *dev, *n;
+       struct os_time now;
+       size_t i;
+
+       os_get_time(&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 (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_time(&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_time(&dev->last_seen);
+                       continue;
+               }
+
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Expiring old peer "
+                       "entry " MACSTR, MAC2STR(dev->info.p2p_device_addr));
+               dl_list_del(&dev->list);
+               p2p_device_free(p2p, dev);
+       }
+}
+
+
+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);
+}
+
+
+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";
+       case P2P_SEARCH_WHEN_READY:
+               return "SEARCH_WHEN_READY";
+       default:
+               return "?";
+       }
+}
+
+
+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)
+{
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: State %s -> %s",
+               p2p_state_txt(p2p->state), p2p_state_txt(new_state));
+       p2p->state = new_state;
+}
+
+
+void p2p_set_timeout(struct p2p_data *p2p, unsigned int sec, unsigned int usec)
+{
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "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)
+{
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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, struct p2p_device *peer,
+                      int status)
+{
+       struct p2p_go_neg_results res;
+       p2p_clear_timeout(p2p);
+       p2p_set_state(p2p, P2P_IDLE);
+       if (p2p->go_neg_peer)
+               p2p->go_neg_peer->wps_method = WPS_NOT_READY;
+       p2p->go_neg_peer = NULL;
+
+       os_memset(&res, 0, sizeof(res));
+       res.status = status;
+       if (peer) {
+               os_memcpy(res.peer_device_addr, peer->info.p2p_device_addr,
+                         ETH_ALEN);
+               os_memcpy(res.peer_interface_addr, peer->intended_addr,
+                         ETH_ALEN);
+       }
+       p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res);
+}
+
+
+static void p2p_listen_in_find(struct p2p_data *p2p)
+{
+       unsigned int r, tu;
+       int freq;
+       struct wpabuf *ies;
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "P2P: Starting short listen state (state=%s)",
+               p2p_state_txt(p2p->state));
+
+       freq = p2p_channel_to_freq(p2p->cfg->country, p2p->cfg->reg_class,
+                                  p2p->cfg->channel);
+       if (freq < 0) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Unknown regulatory class/channel");
+               return;
+       }
+
+       os_get_random((u8 *) &r, sizeof(r));
+       tu = (r % ((p2p->max_disc_int - p2p->min_disc_int) + 1) +
+             p2p->min_disc_int) * 100;
+
+       p2p->pending_listen_freq = freq;
+       p2p->pending_listen_sec = 0;
+       p2p->pending_listen_usec = 1024 * tu;
+
+       ies = p2p_build_probe_resp_ies(p2p);
+       if (ies == NULL)
+               return;
+
+       if (p2p->cfg->start_listen(p2p->cfg->cb_ctx, freq, 1024 * tu / 1000,
+                   ies) < 0) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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;
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "P2P: Going to listen(only) state");
+
+       freq = p2p_channel_to_freq(p2p->cfg->country, p2p->cfg->reg_class,
+                                  p2p->cfg->channel);
+       if (freq < 0) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Unknown regulatory class/channel");
+               return -1;
+       }
+
+       p2p->pending_listen_freq = freq;
+       p2p->pending_listen_sec = timeout / 1000;
+       p2p->pending_listen_usec = (timeout % 1000) * 1000;
+
+       if (p2p->p2p_scan_running) {
+               if (p2p->start_after_scan == P2P_AFTER_SCAN_NOTHING) {
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                               "P2P: p2p_scan running - connect is already "
+                               "pending - skip listen");
+                       return 0;
+               }
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: p2p_scan running - delay start of listen state");
+               p2p->start_after_scan = P2P_AFTER_SCAN_LISTEN;
+               return 0;
+       }
+
+       ies = p2p_build_probe_resp_ies(p2p);
+       if (ies == NULL)
+               return -1;
+
+       if (p2p->cfg->start_listen(p2p->cfg->cb_ctx, freq, timeout, ies) < 0) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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;
+}
+
+
+/**
+ * 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_time_before(&dev->last_seen, &oldest->last_seen))
+                       oldest = dev;
+       }
+       if (count + 1 > p2p->cfg->max_peers && oldest) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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,
+                                const u8 *gi, size_t gi_len)
+{
+       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) {
+                       /*
+                        * Update information only if we have not received this
+                        * directly from the client.
+                        */
+                       if (dev->flags & (P2P_DEV_GROUP_CLIENT_ONLY |
+                                         P2P_DEV_PROBE_REQ_ONLY))
+                               p2p_copy_client_info(dev, cli);
+                       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);
+               os_get_time(&dev->last_seen);
+               os_memcpy(dev->member_in_go_dev, go_dev_addr, ETH_ALEN);
+               os_memcpy(dev->member_in_go_iface, go_interface_addr,
+                         ETH_ALEN);
+       }
+
+       return 0;
+}
+
+
+static void p2p_copy_wps_info(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) {
+               dev->info.dev_capab = msg->capability[0];
+               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) {
+               dev->info.config_methods = msg->config_methods ?
+                       msg->config_methods : msg->wps_config_methods;
+       }
+}
+
+
+/**
+ * p2p_add_device - Add peer entries based on scan results
+ * @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
+ * @ies: IEs from the Beacon or Probe Response frame
+ * @ies_len: Length of ies buffer in octets
+ * 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, int level,
+                  const u8 *ies, size_t ies_len)
+{
+       struct p2p_device *dev;
+       struct p2p_message msg;
+       const u8 *p2p_dev_addr;
+       int i;
+
+       os_memset(&msg, 0, sizeof(msg));
+       if (p2p_parse_ies(ies, ies_len, &msg)) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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 {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Do not add peer "
+                       "filter for " MACSTR " due to peer filter",
+                       MAC2STR(p2p_dev_addr));
+               return 0;
+       }
+
+       dev = p2p_create_device(p2p, p2p_dev_addr);
+       if (dev == NULL) {
+               p2p_parse_free(&msg);
+               return -1;
+       }
+       os_get_time(&dev->last_seen);
+       dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY);
+
+       if (os_memcmp(addr, p2p_dev_addr, ETH_ALEN) != 0)
+               os_memcpy(dev->interface_addr, addr, ETH_ALEN);
+       if (msg.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 (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) {
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                               "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) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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);
+       }
+       dev->listen_freq = freq;
+       if (msg.group_info)
+               dev->oper_freq = freq;
+       dev->info.level = level;
+
+       p2p_copy_wps_info(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;
+       }
+
+       p2p_add_group_clients(p2p, p2p_dev_addr, addr, freq, msg.group_info,
+                             msg.group_info_len);
+
+       p2p_parse_free(&msg);
+
+       if (p2p_pending_sd_req(p2p, dev))
+               dev->flags |= P2P_DEV_SD_SCHEDULE;
+
+       if (dev->flags & P2P_DEV_REPORTED)
+               return 0;
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "P2P: Peer found with Listen frequency %d MHz", freq);
+       if (dev->flags & P2P_DEV_USER_REJECTED) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Do not report rejected device");
+               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;
+
+       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, dev, -1);
+               p2p->go_neg_peer = NULL;
+       }
+       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;
+       }
+
+       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(p2p->cfg->country, reg_class, channel);
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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;
+
+       if (p2p->drv_in_listen) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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->go_neg_peer) {
+               /*
+                * Only scan the known listen frequency of the peer
+                * during GO Negotiation start.
+                */
+               freq = p2p->go_neg_peer->listen_freq;
+               if (freq <= 0)
+                       freq = p2p->go_neg_peer->oper_freq;
+               type = P2P_SCAN_SPECIFIC;
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search "
+                       "for freq %u (GO Neg)", freq);
+       } else if (p2p->invite_peer) {
+               /*
+                * Only scan the known listen frequency of the peer
+                * during Invite start.
+                */
+               freq = p2p->invite_peer->listen_freq;
+               if (freq <= 0)
+                       freq = p2p->invite_peer->oper_freq;
+               type = P2P_SCAN_SPECIFIC;
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search "
+                       "for freq %u (Invite)", freq);
+       } else if (p2p->find_type == P2P_FIND_PROGRESSIVE &&
+                  (freq = p2p_get_next_prog_freq(p2p)) > 0) {
+               type = P2P_SCAN_SOCIAL_PLUS_ONE;
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search "
+                       "(+ freq %u)", freq);
+       } else {
+               type = P2P_SCAN_SOCIAL;
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search");
+       }
+
+       if (p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, type, freq,
+                              p2p->num_req_dev_types, p2p->req_dev_types,
+                              p2p->find_dev_id)) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Scan request failed");
+               p2p_continue_find(p2p);
+       } else {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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 void p2p_find_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+       struct p2p_data *p2p = eloop_ctx;
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Find timeout -> stop");
+       p2p_stop_find(p2p);
+}
+
+
+static int p2p_run_after_scan(struct p2p_data *p2p)
+{
+       struct p2p_device *dev;
+       enum p2p_after_scan op;
+
+       if (p2p->after_scan_tx) {
+               /* TODO: schedule p2p_run_after_scan to be called from TX
+                * status callback(?) */
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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:
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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:
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Start previously "
+                       "requested connect with " MACSTR,
+                       MAC2STR(p2p->after_scan_peer));
+               dev = p2p_get_device(p2p, p2p->after_scan_peer);
+               if (dev == NULL) {
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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;
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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;
+}
+
+
+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)
+{
+       int res;
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting find (type=%d)",
+               type);
+       if (p2p->p2p_scan_running) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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;
+
+       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);
+       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:
+       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);
+               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);
+               break;
+       default:
+               return -1;
+       }
+
+       if (res == 0) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Running p2p_scan");
+               p2p->p2p_scan_running = 1;
+               eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
+               eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout,
+                                      p2p, NULL);
+       } else if (res == 1) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Could not start "
+                       "p2p_scan at this point - will try again after "
+                       "previous scan completes");
+               res = 0;
+               p2p_set_state(p2p, P2P_SEARCH_WHEN_READY);
+               eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
+       } else {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Failed to start "
+                       "p2p_scan");
+               p2p_set_state(p2p, P2P_IDLE);
+               eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
+       }
+
+       return res;
+}
+
+
+int p2p_other_scan_completed(struct p2p_data *p2p)
+{
+       if (p2p->state != P2P_SEARCH_WHEN_READY)
+               return 0;
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting pending P2P find "
+               "now that previous scan was completed");
+       if (p2p_find(p2p, p2p->last_p2p_find_timeout, p2p->find_type,
+                    p2p->num_req_dev_types, p2p->req_dev_types,
+                    p2p->find_dev_id) < 0)
+               return 0;
+       return 1;
+}
+
+
+void p2p_stop_find_for_freq(struct p2p_data *p2p, int freq)
+{
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Stopping find");
+       eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
+       p2p_clear_timeout(p2p);
+       if (p2p->state == P2P_SEARCH)
+               wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, P2P_EVENT_FIND_STOPPED);
+       p2p_set_state(p2p, P2P_IDLE);
+       p2p_free_req_dev_types(p2p);
+       p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
+       p2p->go_neg_peer = NULL;
+       p2p->sd_peer = NULL;
+       p2p->invite_peer = NULL;
+       p2p_stop_listen_for_freq(p2p, freq);
+}
+
+
+void p2p_stop_listen_for_freq(struct p2p_data *p2p, int freq)
+{
+       if (freq > 0 && p2p->drv_in_listen == freq && p2p->in_listen) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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.
+                */
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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_find(struct p2p_data *p2p)
+{
+       p2p_stop_find_for_freq(p2p, 0);
+}
+
+
+static int p2p_prepare_channel(struct p2p_data *p2p, unsigned int force_freq)
+{
+       if (force_freq) {
+               u8 op_reg_class, op_channel;
+               if (p2p_freq_to_channel(p2p->cfg->country, force_freq,
+                                       &op_reg_class, &op_channel) < 0) {
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                               "P2P: Unsupported frequency %u MHz",
+                               force_freq);
+                       return -1;
+               }
+               if (!p2p_channels_includes(&p2p->cfg->channels, op_reg_class,
+                                          op_channel)) {
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                               "P2P: Frequency %u MHz (oper_class %u "
+                               "channel %u) not allowed for P2P",
+                               force_freq, op_reg_class, op_channel);
+                       return -1;
+               }
+               p2p->op_reg_class = op_reg_class;
+               p2p->op_channel = op_channel;
+               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 {
+               u8 op_reg_class, op_channel;
+
+               if (!p2p->cfg->cfg_op_channel && p2p->best_freq_overall > 0 &&
+                   p2p_supported_freq(p2p, p2p->best_freq_overall) &&
+                   p2p_freq_to_channel(p2p->cfg->country,
+                                       p2p->best_freq_overall,
+                                       &op_reg_class, &op_channel) == 0) {
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                               "P2P: Select best overall channel as "
+                               "operating channel preference");
+                       p2p->op_reg_class = op_reg_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->cfg->country,
+                                              p2p->best_freq_5,
+                                              &op_reg_class, &op_channel) ==
+                          0) {
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                               "P2P: Select best 5 GHz channel as "
+                               "operating channel preference");
+                       p2p->op_reg_class = op_reg_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->cfg->country,
+                                              p2p->best_freq_24,
+                                              &op_reg_class, &op_channel) ==
+                          0) {
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                               "P2P: Select best 2.4 GHz channel as "
+                               "operating channel preference");
+                       p2p->op_reg_class = op_reg_class;
+                       p2p->op_channel = op_channel;
+               } else {
+                       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));
+       }
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "P2P: Own preference for operation channel: "
+               "Operating Class %u Channel %u%s",
+               p2p->op_reg_class, p2p->op_channel,
+               force_freq ? " (forced)" : "");
+
+       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)
+{
+       struct p2p_device *dev;
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "P2P: Request to start group negotiation - peer=" MACSTR
+               "  GO Intent=%d  Intended Interface Address=" MACSTR
+               " wps_method=%d persistent_group=%d",
+               MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr),
+               wps_method, persistent_group);
+
+       if (p2p_prepare_channel(p2p, force_freq) < 0)
+               return -1;
+
+       p2p->ssid_set = 0;
+       dev = p2p_get_device(p2p, peer_addr);
+       if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Cannot connect to unknown P2P Device " MACSTR,
+                       MAC2STR(peer_addr));
+               return -1;
+       }
+
+       if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) {
+               if (!(dev->info.dev_capab &
+                     P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) {
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                               "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) {
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                               "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.
+                */
+       }
+
+       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;
+       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.
+                */
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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->status = P2P_SC_SUCCESS;
+
+       if (force_freq)
+               dev->flags |= P2P_DEV_FORCE_FREQ;
+       else
+               dev->flags &= ~P2P_DEV_FORCE_FREQ;
+
+       if (p2p->p2p_scan_running) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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)
+{
+       struct p2p_device *dev;
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "P2P: Request to authorize group negotiation - peer=" MACSTR
+               "  GO Intent=%d  Intended Interface Address=" MACSTR
+               " wps_method=%d  persistent_group=%d",
+               MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr),
+               wps_method, persistent_group);
+
+       if (p2p_prepare_channel(p2p, force_freq) < 0)
+               return -1;
+
+       dev = p2p_get_device(p2p, peer_addr);
+       if (dev == NULL) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Cannot authorize unknown P2P Device " MACSTR,
+                       MAC2STR(peer_addr));
+               return -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->status = P2P_SC_SUCCESS;
+
+       if (force_freq)
+               dev->flags |= P2P_DEV_FORCE_FREQ;
+       else
+               dev->flags &= ~P2P_DEV_FORCE_FREQ;
+
+       return 0;
+}
+
+
+void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr,
+                     struct p2p_device *dev, struct p2p_message *msg)
+{
+       os_get_time(&dev->last_seen);
+
+       p2p_copy_wps_info(dev, 0, msg);
+
+       if (msg->listen_channel) {
+               int freq;
+               freq = p2p_channel_to_freq((char *) msg->listen_channel,
+                                          msg->listen_channel[3],
+                                          msg->listen_channel[4]);
+               if (freq < 0) {
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                               "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 {
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Update "
+                               "peer " MACSTR " Listen channel: %u -> %u MHz",
+                               MAC2STR(dev->info.p2p_device_addr),
+                               dev->listen_freq, freq);
+                       dev->listen_freq = freq;
+               }
+       }
+
+       if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) {
+               dev->flags &= ~P2P_DEV_PROBE_REQ_ONLY;
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Completed device entry based on data from "
+                       "GO Negotiation Request");
+       } else {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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)
+{
+       p2p_build_ssid(p2p, params->ssid, &params->ssid_len);
+       p2p_random(params->passphrase, 8);
+       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;
+       int freqs;
+       size_t i, j;
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "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->cfg->country,
+                                              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, 8);
+       } 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_intersect(&p2p->channels, &peer->channels,
+                              &intersection);
+       freqs = 0;
+       for (i = 0; i < intersection.reg_classes; i++) {
+               struct p2p_reg_class *c = &intersection.reg_class[i];
+               if (freqs + 1 == P2P_MAX_CHANNELS)
+                       break;
+               for (j = 0; j < c->channels; j++) {
+                       int freq;
+                       if (freqs + 1 == P2P_MAX_CHANNELS)
+                               break;
+                       freq = p2p_channel_to_freq(peer->country, c->reg_class,
+                                                  c->channel[j]);
+                       if (freq < 0)
+                               continue;
+                       res.freq_list[freqs++] = freq;
+               }
+       }
+
+       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;
+
+       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)
+{
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "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:
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Unsupported P2P Public Action frame type %d",
+                       data[0]);
+               break;
+       }
+}
+
+
+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 < 3)
+                       return;
+               if (WPA_GET_BE24(data) != OUI_WFA)
+                       return;
+
+               data += 3;
+               len -= 3;
+               if (len < 1)
+                       return;
+
+               if (*data != P2P_OUI_TYPE)
+                       return;
+
+               p2p_rx_p2p_action(p2p, sa, data + 1, len - 1, 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_BE24(data) != OUI_WFA)
+               return;
+       data += 3;
+       len -= 3;
+
+       if (*data != P2P_OUI_TYPE)
+               return;
+       data++;
+       len--;
+
+       /* P2P action frame */
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "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:
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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:
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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;
+       p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
+       p2p->go_neg_peer->status = P2P_SC_SUCCESS;
+       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;
+       p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
+       p2p_invite_send(p2p, p2p->invite_peer, p2p->invite_go_dev_addr);
+}
+
+
+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_time(&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_time(&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(dev->country,
+                                                      msg.listen_channel[3],
+                                                      msg.listen_channel[4]);
+       }
+
+       p2p_copy_wps_info(dev, 1, &msg);
+
+       p2p_parse_free(&msg);
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "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_time(&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;
+}
+
+
+struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p)
+{
+       struct wpabuf *buf;
+       u8 *len;
+
+       buf = wpabuf_alloc(1000);
+       if (buf == NULL)
+               return NULL;
+
+       p2p_build_wps_ie(p2p, buf, DEV_PW_DEFAULT, 1);
+
+       /* P2P IE */
+       len = p2p_buf_add_ie_hdr(buf);
+       p2p_buf_add_capability(buf, p2p->dev_capab, 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);
+
+       return buf;
+}
+
+
+static int is_11b(u8 rate)
+{
+       return rate == 0x02 || rate == 0x04 || rate == 0x0b || rate == 0x16;
+}
+
+
+static 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;
+}
+
+
+static void p2p_reply_probe(struct p2p_data *p2p, const u8 *addr,
+                           const u8 *dst, const u8 *bssid, const u8 *ie,
+                           size_t ie_len)
+{
+       struct ieee802_11_elems elems;
+       struct wpabuf *buf;
+       struct ieee80211_mgmt *resp;
+       struct p2p_message msg;
+       struct wpabuf *ies;
+
+       if (!p2p->in_listen || !p2p->drv_in_listen) {
+               /* not in Listen state - ignore Probe Request */
+               return;
+       }
+
+       if (ieee802_11_parse_elems((u8 *) ie, ie_len, &elems, 0) ==
+           ParseFailed) {
+               /* Ignore invalid Probe Request frames */
+               return;
+       }
+
+       if (elems.p2p == NULL) {
+               /* not a P2P probe - ignore it */
+               return;
+       }
+
+       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
+                */
+               return;
+       }
+
+       if (bssid && !is_broadcast_ether_addr(bssid)) {
+               /* Not sent to the Wildcard BSSID */
+               return;
+       }
+
+       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 */
+               return;
+       }
+
+       if (supp_rates_11b_only(&elems)) {
+               /* Indicates support for 11b rates only */
+               return;
+       }
+
+       os_memset(&msg, 0, sizeof(msg));
+       if (p2p_parse_ies(ie, ie_len, &msg) < 0) {
+               /* Could not parse P2P attributes */
+               return;
+       }
+
+       if (msg.device_id &&
+           os_memcmp(msg.device_id, p2p->cfg->dev_addr, ETH_ALEN != 0)) {
+               /* Device ID did not match */
+               p2p_parse_free(&msg);
+               return;
+       }
+
+       /* Check Requested Device Type match */
+       if (msg.wps_attributes &&
+           !p2p_match_dev_type(p2p, msg.wps_attributes)) {
+               /* No match with Requested Device Type */
+               p2p_parse_free(&msg);
+               return;
+       }
+       p2p_parse_free(&msg);
+
+       if (!p2p->cfg->send_probe_resp)
+               return; /* Response generated elsewhere */
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "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.
+        */
+       ies = p2p_build_probe_resp_ies(p2p);
+       if (ies == NULL)
+               return;
+
+       buf = wpabuf_alloc(200 + wpabuf_len(ies));
+       if (buf == NULL) {
+               wpabuf_free(ies);
+               return;
+       }
+
+       resp = NULL;
+       resp = wpabuf_put(buf, resp->u.probe_resp.variable - (u8 *) resp);
+
+       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);
+
+       wpabuf_put_u8(buf, WLAN_EID_DS_PARAMS);
+       wpabuf_put_u8(buf, 1);
+       wpabuf_put_u8(buf, p2p->cfg->channel);
+
+       wpabuf_put_buf(buf, ies);
+       wpabuf_free(ies);
+
+       p2p->cfg->send_probe_resp(p2p->cfg->cb_ctx, buf);
+
+       wpabuf_free(buf);
+}
+
+
+int p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
+                    const u8 *bssid, const u8 *ie, size_t ie_len)
+{
+       p2p_add_dev_from_probe_req(p2p, addr, ie, ie_len);
+
+       p2p_reply_probe(p2p, addr, dst, bssid, ie, ie_len);
+
+       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) {
+               /* Received a Probe Request from GO Negotiation peer */
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Found GO Negotiation peer - try to start GO "
+                       "negotiation from timeout");
+               eloop_register_timeout(0, 0, p2p_go_neg_start, p2p, NULL);
+               return 1;
+       }
+
+       if ((p2p->state == P2P_INVITE || p2p->state == P2P_INVITE_LISTEN) &&
+           p2p->invite_peer &&
+           os_memcmp(addr, p2p->invite_peer->info.p2p_device_addr, ETH_ALEN)
+           == 0) {
+               /* Received a Probe Request from Invite peer */
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Found Invite peer - try to start Invite from "
+                       "timeout");
+               eloop_register_timeout(0, 0, p2p_invite_start, p2p, NULL);
+               return 1;
+       }
+
+       return 0;
+}
+
+
+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;
+
+       if (p2p_ie == NULL)
+               return 0; /* WLAN AP is not a P2P manager */
+
+       /*
+        * (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;
+
+       if (!p2p_group)
+               return p2p_assoc_req_ie_wlan_ap(p2p, bssid, buf, len, p2p_ie);
+
+       /*
+        * (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);
+       if (tmp == NULL)
+               return -1;
+
+       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;
+}
+
+
+int p2p_parse_dev_addr(const u8 *ies, size_t ies_len, u8 *dev_addr)
+{
+       struct wpabuf *p2p_ie;
+       struct p2p_message msg;
+       int ret = -1;
+
+       p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len,
+                                            P2P_IE_VENDOR_TYPE);
+       if (p2p_ie == NULL)
+               return -1;
+       os_memset(&msg, 0, sizeof(msg));
+       if (p2p_parse_p2p_ie(p2p_ie, &msg)) {
+               wpabuf_free(p2p_ie);
+               return -1;
+       }
+
+       if (msg.p2p_device_addr) {
+               os_memcpy(dev_addr, msg.p2p_device_addr, ETH_ALEN);
+               ret = 0;
+       } else if (msg.device_id) {
+               os_memcpy(dev_addr, msg.device_id, ETH_ALEN);
+               ret = 0;
+       }
+
+       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) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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 */
+       }
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "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) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: No pending Group Formation - "
+                       "ignore group formation failure notification");
+               return; /* No pending Group Formation */
+       }
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "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)
+               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;
+       }
+
+       p2p->min_disc_int = 1;
+       p2p->max_disc_int = 3;
+
+       os_get_random(&p2p->next_tie_breaker, 1);
+       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);
+
+       eloop_register_timeout(P2P_PEER_EXPIRATION_INTERVAL, 0,
+                              p2p_expiration_timeout, p2p, NULL);
+
+       return p2p;
+}
+
+
+void p2p_deinit(struct p2p_data *p2p)
+{
+       eloop_cancel_timeout(p2p_expiration_timeout, p2p, NULL);
+       eloop_cancel_timeout(p2p_ext_listen_timeout, p2p, NULL);
+       eloop_cancel_timeout(p2p_scan_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);
+       wpabuf_free(p2p->sd_resp);
+       os_free(p2p->after_scan_tx);
+       p2p_remove_wps_vendor_extensions(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;
+}
+
+
+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;
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Unauthorizing " MACSTR,
+               MAC2STR(addr));
+
+       if (p2p->go_neg_peer == dev)
+               p2p->go_neg_peer = NULL;
+
+       dev->wps_method = WPS_NOT_READY;
+       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;
+}
+
+
+void p2p_continue_find(struct p2p_data *p2p)
+{
+       struct p2p_device *dev;
+       p2p_set_state(p2p, P2P_SEARCH);
+       dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
+               if (dev->flags & P2P_DEV_SD_SCHEDULE) {
+                       if (p2p_start_sd(p2p, dev) == 0)
+                               return;
+                       else
+                               break;
+               } else if (dev->req_config_methods &&
+                          !(dev->flags & P2P_DEV_PD_FOR_JOIN)) {
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send "
+                               "pending Provisioning 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;
+               }
+       }
+
+       p2p_listen_in_find(p2p);
+}
+
+
+static void p2p_sd_cb(struct p2p_data *p2p, int success)
+{
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "P2P: Service Discovery Query TX callback: success=%d",
+               success);
+       p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+
+       if (!success) {
+               if (p2p->sd_peer) {
+                       p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE;
+                       p2p->sd_peer = NULL;
+               }
+               p2p_continue_find(p2p);
+               return;
+       }
+
+       if (p2p->sd_peer == NULL) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: No SD peer entry known");
+               p2p_continue_find(p2p);
+               return;
+       }
+
+       /* 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()
+ */
+void p2p_retry_pd(struct p2p_data *p2p)
+{
+       struct p2p_device *dev;
+
+       if (p2p->state != P2P_IDLE)
+               return;
+
+       /*
+        * Retry the prov disc req attempt only for the peer that the user had
+        * requested for and provided a join has not been initiated on it
+        * in the meantime.
+        */
+
+       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;
+               if (dev->flags & P2P_DEV_PD_FOR_JOIN)
+                       continue;
+
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send "
+                       "pending Provisioning 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, 0, 0);
+               return;
+       }
+}
+
+
+static void p2p_prov_disc_cb(struct p2p_data *p2p, int success)
+{
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "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->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);
+}
+
+
+int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq,
+                        int level, const u8 *ies, size_t ies_len)
+{
+       p2p_add_device(p2p, bssid, freq, level, ies, ies_len);
+
+       if (p2p->go_neg_peer && p2p->state == P2P_SEARCH &&
+           os_memcmp(p2p->go_neg_peer->info.p2p_device_addr, bssid, ETH_ALEN)
+           == 0) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Found GO Negotiation peer - try to start GO "
+                       "negotiation");
+               p2p_connect_send(p2p, p2p->go_neg_peer);
+               return 1;
+       }
+
+       return 0;
+}
+
+
+void p2p_scan_res_handled(struct p2p_data *p2p)
+{
+       if (!p2p->p2p_scan_running) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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 *len = p2p_buf_add_ie_hdr(ies);
+       p2p_buf_add_capability(ies, p2p->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);
+       /* 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)
+{
+       return 100;
+}
+
+
+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;
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "P2P: GO Negotiation Request TX callback: success=%d",
+               success);
+
+       if (dev == NULL) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: No pending GO Negotiation");
+               return;
+       }
+
+       if (success) {
+               dev->go_neg_req_sent++;
+               if (dev->flags & P2P_DEV_USER_REJECTED) {
+                       p2p_set_state(p2p, P2P_IDLE);
+                       return;
+               }
+       }
+
+       if (!success &&
+           (dev->info.dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY) &&
+           !is_zero_ether_addr(dev->member_in_go_dev)) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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);
+       p2p_set_timeout(p2p, 0, 100000);
+}
+
+
+static void p2p_go_neg_resp_cb(struct p2p_data *p2p, int success)
+{
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "P2P: GO Negotiation Response TX callback: success=%d",
+               success);
+       if (!p2p->go_neg_peer && p2p->state == P2P_PROVISIONING) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Ignore TX callback event - GO Negotiation is "
+                       "not running anymore");
+               return;
+       }
+       p2p_set_state(p2p, P2P_CONNECT);
+       p2p_set_timeout(p2p, 0, 100000);
+}
+
+
+static void p2p_go_neg_resp_failure_cb(struct p2p_data *p2p, int success)
+{
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "P2P: GO Negotiation Response (failure) TX callback: "
+               "success=%d", success);
+       if (p2p->go_neg_peer && p2p->go_neg_peer->status != P2P_SC_SUCCESS) {
+               p2p_go_neg_failed(p2p, p2p->go_neg_peer,
+                                 p2p->go_neg_peer->status);
+       }
+}
+
+
+static void p2p_go_neg_conf_cb(struct p2p_data *p2p,
+                              enum p2p_send_action_result result)
+{
+       struct p2p_device *dev;
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "P2P: GO Negotiation Confirm TX callback: result=%d",
+               result);
+       p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+       if (result == P2P_SEND_ACTION_FAILED) {
+               p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1);
+               return;
+       }
+       if (result == P2P_SEND_ACTION_NO_ACK) {
+               /*
+                * 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.
+                */
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Assume GO Negotiation Confirm TX was actually "
+                       "received by the peer even though Ack was not "
+                       "reported");
+       }
+
+       dev = p2p->go_neg_peer;
+       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;
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "P2P: Action frame TX callback (state=%d freq=%u dst=" MACSTR
+               " src=" MACSTR " bssid=" MACSTR " result=%d",
+               p2p->pending_action_state, freq, MAC2STR(dst), MAC2STR(src),
+               MAC2STR(bssid), result);
+       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:
+               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);
+               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_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;
+       }
+}
+
+
+void p2p_listen_cb(struct p2p_data *p2p, unsigned int freq,
+                  unsigned int duration)
+{
+       if (freq == p2p->pending_client_disc_freq) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Client discoverability remain-awake completed");
+               p2p->pending_client_disc_freq = 0;
+               return;
+       }
+
+       if (freq != p2p->pending_listen_freq) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Unexpected listen callback for freq=%u "
+                       "duration=%u (pending_listen_freq=%u)",
+                       freq, duration, p2p->pending_listen_freq);
+               return;
+       }
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "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)
+{
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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) {
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                               "P2P: Timeout on sending GO Negotiation "
+                               "Request without getting response");
+                       p2p_go_neg_failed(p2p, p2p->go_neg_peer, -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.
+                         */
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: p2p_scan "
+                               "already in progress - do not try to start a "
+                               "new one");
+                       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);
+       p2p_set_state(p2p, P2P_CONNECT_LISTEN);
+       p2p_listen_in_find(p2p);
+}
+
+
+static void p2p_timeout_connect_listen(struct p2p_data *p2p)
+{
+       if (p2p->go_neg_peer) {
+               if (p2p->drv_in_listen) {
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Driver is "
+                               "still in Listen state; wait for it to "
+                               "complete");
+                       return;
+               }
+
+               if (p2p->go_neg_peer->connect_reqs >= 120) {
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                               "P2P: Timeout on sending GO Negotiation "
+                               "Request without getting response");
+                       p2p_go_neg_failed(p2p, p2p->go_neg_peer, -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)
+{
+       /*
+        * TODO: could remain constantly in Listen state for some time if there
+        * are no other concurrent uses for the radio. For now, go to listen
+        * state once per second to give other uses a chance to use the radio.
+        */
+       p2p_set_state(p2p, P2P_WAIT_PEER_IDLE);
+       p2p_set_timeout(p2p, 0, 500000);
+}
+
+
+static void p2p_timeout_wait_peer_idle(struct p2p_data *p2p)
+{
+       struct p2p_device *dev = p2p->go_neg_peer;
+
+       if (dev == NULL) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Unknown GO Neg peer - stop GO Neg wait");
+               return;
+       }
+
+       dev->wait_count++;
+       if (dev->wait_count >= 120) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Timeout on waiting peer to become ready for GO "
+                       "Negotiation");
+               p2p_go_neg_failed(p2p, dev, -1);
+               return;
+       }
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "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);
+}
+
+
+static void p2p_timeout_sd_during_find(struct p2p_data *p2p)
+{
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "P2P: Service Discovery Query timeout");
+       if (p2p->sd_peer) {
+               p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+               p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE;
+               p2p->sd_peer = NULL;
+       }
+       p2p_continue_find(p2p);
+}
+
+
+static void p2p_timeout_prov_disc_during_find(struct p2p_data *p2p)
+{
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "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)
+{
+       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;
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "P2P: User initiated Provision Discovery Request timeout");
+
+       if (p2p->pd_retries) {
+               p2p->pd_retries--;
+               p2p_retry_pd(p2p);
+       } else {
+               if (p2p->cfg->prov_disc_fail)
+                       p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx,
+                                                p2p->pending_pd_devaddr,
+                                                P2P_PROV_DISC_TIMEOUT);
+               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.
+                */
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Inviting in "
+                       "active GO role - wait on operating channel");
+               p2p_set_timeout(p2p, 0, 100000);
+               return;
+       }
+       p2p_listen_in_find(p2p);
+}
+
+
+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);
+       } else {
+               if (p2p->invite_peer) {
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                               "P2P: Invitation Request retry limit reached");
+                       if (p2p->cfg->invitation_result)
+                               p2p->cfg->invitation_result(
+                                       p2p->cfg->cb_ctx, -1, NULL);
+               }
+               p2p_set_state(p2p, P2P_IDLE);
+       }
+}
+
+
+static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+       struct p2p_data *p2p = eloop_ctx;
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Timeout (state=%s)",
+               p2p_state_txt(p2p->state));
+
+       p2p->in_listen = 0;
+
+       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);
+               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) {
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                               "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;
+       case P2P_SEARCH_WHEN_READY:
+               break;
+       }
+}
+
+
+int p2p_reject(struct p2p_data *p2p, const u8 *peer_addr)
+{
+       struct p2p_device *dev;
+
+       dev = p2p_get_device(p2p, peer_addr);
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Local request to reject "
+               "connection attempts by peer " MACSTR, MAC2STR(peer_addr));
+       if (dev == NULL) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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";
+       }
+
+       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_time now;
+
+       if (info == NULL)
+               return -1;
+
+       dev = (struct p2p_device *) (((u8 *) info) -
+                                    offsetof(struct p2p_device, info));
+
+       pos = buf;
+       end = buf + buflen;
+
+       os_get_time(&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"
+                         "wait_count=%u\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_SD_INFO ? "[SD_INFO]" : "",
+                         dev->flags & P2P_DEV_SD_SCHEDULE ? "[SD_SCHEDULE]" :
+                         "",
+                         dev->flags & P2P_DEV_PD_PEER_DISPLAY ?
+                         "[PD_PEER_DISPLAY]" : "",
+                         dev->flags & P2P_DEV_PD_PEER_KEYPAD ?
+                         "[PD_PEER_KEYPAD]" : "",
+                         dev->flags & P2P_DEV_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->status,
+                         dev->wait_count,
+                         dev->invitation_reqs);
+       if (res < 0 || res >= end - pos)
+               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 (res < 0 || res >= end - pos)
+                       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 (res < 0 || res >= end - pos)
+                       return pos - buf;
+               pos += res;
+       }
+
+       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) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Client "
+                       "discoverability enabled");
+               p2p->dev_capab |= P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
+       } else {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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;
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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;
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "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) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Ignore P2P Presence Request for unknown group "
+                       MACSTR, MAC2STR(da));
+               return;
+       }
+
+       if (p2p_parse(data, len, &msg) < 0) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Failed to parse P2P Presence Request");
+               status = P2P_SC_FAIL_INVALID_PARAMS;
+               goto fail;
+       }
+       parsed = 1;
+
+       if (msg.noa == NULL) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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;
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "P2P: Received P2P Action - P2P Presence Response");
+
+       if (p2p_parse(data, len, &msg) < 0) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Failed to parse P2P Presence Response");
+               return;
+       }
+
+       if (msg.status == NULL || msg.noa == NULL) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: No Status or NoA attribute in P2P Presence "
+                       "Response");
+               p2p_parse_free(&msg);
+               return;
+       }
+
+       if (*msg.status) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: P2P Presence Request was rejected: status %u",
+                       *msg.status);
+               p2p_parse_free(&msg);
+               return;
+       }
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "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->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.
+                */
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Skip Extended "
+                       "Listen timeout in active state (%s)",
+                       p2p_state_txt(p2p->state));
+               return;
+       }
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Extended Listen timeout");
+       p2p->ext_listen_only = 1;
+       if (p2p_listen(p2p, p2p->ext_listen_period) < 0) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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)) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Disabling Extended Listen Timing");
+               p2p->ext_listen_period = 0;
+               p2p->ext_listen_interval = 0;
+               return 0;
+       }
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "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)
+               return;
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_INFO,
+               "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)
+               return;
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_INFO,
+               "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) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Managed P2P "
+                       "Device operations enabled");
+               p2p->dev_capab |= P2P_DEV_CAPAB_INFRA_MANAGED;
+       } else {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Managed P2P "
+                       "Device operations disabled");
+               p2p->dev_capab &= ~P2P_DEV_CAPAB_INFRA_MANAGED;
+       }
+}
+
+
+int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel)
+{
+       if (p2p_channel_to_freq(p2p->cfg->country, reg_class, channel) < 0)
+               return -1;
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Set Listen channel: "
+               "reg_class %u channel %u", reg_class, channel);
+       p2p->cfg->reg_class = reg_class;
+       p2p->cfg->channel = channel;
+
+       return 0;
+}
+
+
+int p2p_set_ssid_postfix(struct p2p_data *p2p, const u8 *postfix, size_t len)
+{
+       wpa_hexdump_ascii(MSG_DEBUG, "P2P: New SSID postfix", 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(p2p->cfg->country, op_reg_class, op_channel)
+           < 0)
+               return -1;
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, "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_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))
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Disable peer "
+                       "filter");
+       else
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Enable peer "
+                       "filter for " MACSTR, MAC2STR(p2p->peer_filter));
+}
+
+
+void p2p_set_cross_connect(struct p2p_data *p2p, int enabled)
+{
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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)
+{
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Intra BSS distribution %s",
+               enabled ? "enabled" : "disabled");
+       p2p->cfg->p2p_intra_bss = enabled;
+}
+
+
+void p2p_update_channel_list(struct p2p_data *p2p, struct p2p_channels *chan)
+{
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Update channel list");
+       os_memcpy(&p2p->cfg->channels, chan, sizeof(struct p2p_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) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Delay Action "
+                       "frame TX until p2p_scan completes");
+               if (p2p->after_scan_tx) {
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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)
+{
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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;
+}
+
+
+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->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->list == &p2p->devices)
+                               return NULL;
+               }
+       }
+
+       return &dev->info;
+}
+
+
+int p2p_in_progress(struct p2p_data *p2p)
+{
+       if (p2p == NULL)
+               return 0;
+       return p2p->state != P2P_IDLE && p2p->state != P2P_PROVISIONING;
+}
diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h
new file mode 100644 (file)
index 0000000..75d4739
--- /dev/null
@@ -0,0 +1,1657 @@
+/*
+ * Wi-Fi Direct - P2P module
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef P2P_H
+#define P2P_H
+
+/**
+ * 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
+};
+
+/**
+ * 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;
+
+       /**
+        * ssid - SSID of the group
+        */
+       u8 ssid[32];
+
+       /**
+        * ssid_len - Length of SSID in octets
+        */
+       size_t ssid_len;
+
+       /**
+        * 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 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 device_name[33];
+
+       /**
+        * manufacturer - Manufacturer (0..64 octets encoded in UTF-8)
+        */
+       char manufacturer[65];
+
+       /**
+        * model_name - Model Name (0..32 octets encoded in UTF-8)
+        */
+       char model_name[33];
+
+       /**
+        * model_number - Model Number (0..32 octets encoded in UTF-8)
+        */
+       char model_number[33];
+
+       /**
+        * serial_number - Serial Number (0..32 octets encoded in UTF-8)
+        */
+       char serial_number[33];
+
+       /**
+        * 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 wps_sec_dev_type_list[128];
+
+       /**
+        * 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];
+};
+
+enum p2p_prov_disc_status {
+       P2P_PROV_DISC_SUCCESS,
+       P2P_PROV_DISC_TIMEOUT,
+       P2P_PROV_DISC_REJECTED,
+};
+
+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;
+
+       /**
+        * 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;
+
+       /**
+        * 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.
+        */
+       u8 ssid_postfix[32 - 9];
+
+       /**
+        * ssid_postfix_len - Length of the ssid_postfix data
+        */
+       size_t ssid_postfix_len;
+
+       /**
+        * msg_ctx - Context to use with wpa_msg() calls
+        */
+       void *msg_ctx;
+
+       /**
+        * cb_ctx - Context to use with callback functions
+        */
+       void *cb_ctx;
+
+
+       /* 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
+        * 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);
+
+       /**
+        * send_probe_resp - Transmit a Probe Response frame
+        * @ctx: Callback context from cb_ctx
+        * @buf: Probe Response frame (including the header and body)
+        * 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
+        * sent on the same channel or to be dropped if the driver is not
+        * anymore listening to Probe Request frames.
+        *
+        * 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.
+        */
+       int (*send_probe_resp)(void *ctx, const struct wpabuf *buf);
+
+       /**
+        * 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);
+
+       /**
+        * 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
+        *
+        * 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().
+        */
+       void (*go_neg_req_rx)(void *ctx, const u8 *src, u16 dev_passwd_id);
+
+       /**
+        * 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
+        *
+        * 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);
+
+       /**
+        * 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)
+        * 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);
+
+       /**
+        * 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
+        *
+        * 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);
+
+       /**
+        * 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);
+};
+
+
+/* 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
+ * 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);
+
+/**
+ * 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_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)
+ * 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);
+
+/**
+ * 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)
+ * 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);
+
+/**
+ * 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
+ * @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)
+ * 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,
+                     u16 config_methods, int join, int force_freq);
+
+/**
+ * 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);
+
+/**
+ * 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
+ * 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);
+
+/**
+ * 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 */
+
+/**
+ * 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
+ * Returns: 0 to indicate the frame was not processed or 1 if it was
+ */
+int p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
+                    const u8 *bssid, const u8 *ie, size_t ie_len);
+
+/**
+ * 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
+ * @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,
+                        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;
+
+       /**
+        * 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 - 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);
+
+int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel);
+
+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);
+
+/**
+ * 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);
+
+void p2p_update_channel_list(struct p2p_data *p2p, struct p2p_channels *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);
+
+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_iterate_group_members - Iterate group members
+ * @group: P2P group context from p2p_group_init()
+ * @next: iteration pointer, must be a pointer to a void * that is set to %NULL
+ *     on the first call and not modified later
+ * Returns: A P2P Interface Address for each call and %NULL for no more members
+ */
+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_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_in_progress - Check whether a P2P operation is progress
+ * @p2p: P2P module context from p2p_init()
+ * Returns: 0 if P2P module is idle or 1 if an operation is in progress
+ */
+int p2p_in_progress(struct p2p_data *p2p);
+
+/**
+ * p2p_other_scan_completed - Notify completion of non-P2P scan
+ * @p2p: P2P module context from p2p_init()
+ * Returns: 0 if P2P module is idle or 1 if an operation was started
+ */
+int p2p_other_scan_completed(struct p2p_data *p2p);
+
+const char * p2p_wps_method_text(enum p2p_wps_method method);
+
+#endif /* P2P_H */
diff --git a/src/p2p/p2p_build.c b/src/p2p/p2p_build.c
new file mode 100644 (file)
index 0000000..a82e16d
--- /dev/null
@@ -0,0 +1,433 @@
+/*
+ * P2P - IE builder
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.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_be24(buf, OUI_WFA);
+       wpabuf_put_u8(buf, P2P_OUI_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_be24(buf, OUI_WFA);
+       wpabuf_put_u8(buf, P2P_OUI_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_be24(buf, OUI_WFA);
+       wpabuf_put_u8(buf, P2P_OUI_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_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_printf(MSG_DEBUG, "P2P: * Channel List");
+}
+
+
+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;
+       } else if (p2p->cfg->config_methods) {
+               methods |= p2p->cfg->config_methods &
+                       (WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_DISPLAY |
+                        WPS_CONFIG_KEYPAD);
+       } else {
+               methods |= WPS_CONFIG_PUSHBUTTON;
+               methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
+       }
+       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));
+}
+
+
+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);
+}
+
+
+static void p2p_add_wps_string(struct wpabuf *buf, enum wps_attribute attr,
+                              const char *val)
+{
+       size_t len;
+
+       wpabuf_put_be16(buf, attr);
+       len = val ? os_strlen(val) : 0;
+#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.
+                */
+               wpabuf_put_be16(buf, 1);
+               wpabuf_put_u8(buf, ' ');
+               return;
+       }
+#endif /* CONFIG_WPS_STRICT */
+       wpabuf_put_be16(buf, len);
+       if (val)
+               wpabuf_put_data(buf, val, len);
+}
+
+
+void p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, u16 pw_id,
+                     int all_attr)
+{
+       u8 *len;
+       int i;
+
+       wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+       len = wpabuf_put(buf, 1);
+       wpabuf_put_be32(buf, WPS_DEV_OUI_WFA);
+
+       wps_build_version(buf);
+
+       if (all_attr) {
+               wpabuf_put_be16(buf, ATTR_WPS_STATE);
+               wpabuf_put_be16(buf, 1);
+               wpabuf_put_u8(buf, WPS_STATE_NOT_CONFIGURED);
+       }
+
+       /* 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) {
+               wpabuf_put_be16(buf, ATTR_RESPONSE_TYPE);
+               wpabuf_put_be16(buf, 1);
+               wpabuf_put_u8(buf, WPS_RESP_ENROLLEE_INFO);
+
+               wps_build_uuid_e(buf, p2p->cfg->uuid);
+               p2p_add_wps_string(buf, ATTR_MANUFACTURER,
+                                  p2p->cfg->manufacturer);
+               p2p_add_wps_string(buf, ATTR_MODEL_NAME, p2p->cfg->model_name);
+               p2p_add_wps_string(buf, ATTR_MODEL_NUMBER,
+                                  p2p->cfg->model_number);
+               p2p_add_wps_string(buf, ATTR_SERIAL_NUMBER,
+                                  p2p->cfg->serial_number);
+
+               wpabuf_put_be16(buf, ATTR_PRIMARY_DEV_TYPE);
+               wpabuf_put_be16(buf, WPS_DEV_TYPE_LEN);
+               wpabuf_put_data(buf, p2p->cfg->pri_dev_type, WPS_DEV_TYPE_LEN);
+
+               p2p_add_wps_string(buf, ATTR_DEV_NAME, p2p->cfg->dev_name);
+
+               wpabuf_put_be16(buf, ATTR_CONFIG_METHODS);
+               wpabuf_put_be16(buf, 2);
+               wpabuf_put_be16(buf, p2p->cfg->config_methods);
+       }
+
+       wps_build_wfa_ext(buf, 0, NULL, 0);
+
+       if (all_attr && p2p->cfg->num_sec_dev_types) {
+               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);
+}
diff --git a/src/p2p/p2p_dev_disc.c b/src/p2p/p2p_dev_disc.c
new file mode 100644 (file)
index 0000000..47cc0fd
--- /dev/null
@@ -0,0 +1,365 @@
+/*
+ * Wi-Fi Direct - P2P Device Discoverability procedure
+ * Copyright (c) 2010, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#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)
+{
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "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;
+       }
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "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;
+
+       go = p2p_get_device(p2p, dev->member_in_go_dev);
+       if (go == NULL || dev->oper_freq <= 0) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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;
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "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;
+       if (p2p_send_action(p2p, dev->oper_freq, go->info.p2p_device_addr,
+                           p2p->cfg->dev_addr, go->info.p2p_device_addr,
+                           wpabuf_head(req), wpabuf_len(req), 1000) < 0) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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)
+{
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "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;
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "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) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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;
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "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) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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) {
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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;
+               }
+       }
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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;
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "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) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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);
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "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) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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.
+                */
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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.
+                */
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Client discoverability request failed");
+               p2p_set_timeout(p2p, 0, 500000);
+       }
+
+}
+
+
+void p2p_go_disc_req_cb(struct p2p_data *p2p, int success)
+{
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "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) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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;
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "P2P: Received GO Discoverability Request - remain awake for "
+               "100 TU");
+
+       ies = p2p_build_probe_resp_ies(p2p);
+       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) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Failed to start listen mode for client "
+                       "discoverability");
+       }
+       wpabuf_free(ies);
+}
diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c
new file mode 100644 (file)
index 0000000..f83d3de
--- /dev/null
@@ -0,0 +1,1130 @@
+/*
+ * Wi-Fi Direct - P2P Group Owner Negotiation
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.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) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_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) {
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_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);
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_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);
+}
+
+
+static 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;
+       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";
+       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;
+
+       buf = wpabuf_alloc(1000);
+       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_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, group_capab);
+       p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) |
+                             p2p->next_tie_breaker);
+       p2p->next_tie_breaker = !p2p->next_tie_breaker;
+       p2p_buf_add_config_timeout(buf, 100, 20);
+       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);
+
+       /* WPS IE with Device Password ID attribute */
+       p2p_build_wps_ie(p2p, buf, p2p_wps_method_pw_id(peer->wps_method), 0);
+
+       return buf;
+}
+
+
+int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev)
+{
+       struct wpabuf *req;
+       int freq;
+
+       freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
+       if (freq <= 0) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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;
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "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;
+       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), 200) < 0) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Failed to send Action frame");
+               /* Use P2P find to recover and retry */
+               p2p_set_timeout(p2p, 0, 0);
+       }
+
+       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;
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "P2P: Building GO Negotiation Response");
+       buf = wpabuf_alloc(1000);
+       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, group_capab);
+       p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | tie_breaker);
+       p2p_buf_add_config_timeout(buf, 100, 20);
+       if (peer && peer->go_state == REMOTE_GO) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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 */
+       p2p_build_wps_ie(p2p, buf,
+                        p2p_wps_method_pw_id(peer ? peer->wps_method :
+                                             WPS_NOT_READY), 0);
+
+       return buf;
+}
+
+
+static 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;
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Selected operating "
+               "channel (reg_class %u channel %u) not acceptable to the "
+               "peer", p2p->op_reg_class, p2p->op_channel);
+
+       /* First, try to pick the best channel from another band */
+       freq = p2p_channel_to_freq(p2p->cfg->country, p2p->op_reg_class,
+                                  p2p->op_channel);
+       if (freq >= 2400 && freq < 2500 && p2p->best_freq_5 > 0 &&
+           p2p_freq_to_channel(p2p->cfg->country, p2p->best_freq_5,
+                               &op_reg_class, &op_channel) == 0 &&
+           p2p_channels_includes(intersection, op_reg_class, op_channel)) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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_freq_to_channel(p2p->cfg->country, p2p->best_freq_24,
+                               &op_reg_class, &op_channel) == 0 &&
+           p2p_channels_includes(intersection, op_reg_class, op_channel)) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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;
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Pick "
+                               "highest preferred chnnel (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];
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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];
+}
+
+
+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;
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "P2P: Received GO Negotiation Request from " MACSTR
+               "(freq=%d)", MAC2STR(sa), rx_freq);
+
+       if (p2p_parse(data, len, &msg))
+               return;
+
+       if (!msg.capability) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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 {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Mandatory GO Intent attribute missing from GO "
+                       "Negotiation Request");
+#ifdef CONFIG_P2P_STRICT
+               goto fail;
+#endif /* CONFIG_P2P_STRICT */
+       }
+
+       if (!msg.config_timeout) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Mandatory Configuration Timeout attribute "
+                       "missing from GO Negotiation Request");
+#ifdef CONFIG_P2P_STRICT
+               goto fail;
+#endif /* CONFIG_P2P_STRICT */
+       }
+
+       if (!msg.listen_channel) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: No Listen Channel attribute received");
+               goto fail;
+       }
+       if (!msg.operating_channel) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: No Operating Channel attribute received");
+               goto fail;
+       }
+       if (!msg.channel_list) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: No Channel List attribute received");
+               goto fail;
+       }
+       if (!msg.intended_addr) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: No Intended P2P Interface Address attribute "
+                       "received");
+               goto fail;
+       }
+       if (!msg.p2p_device_info) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: No P2P Device Info attribute received");
+               goto fail;
+       }
+
+       if (os_memcmp(msg.p2p_device_addr, sa, ETH_ALEN) != 0) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Unexpected Status attribute (%d) in GO "
+                       "Negotiation Request", *msg.status);
+               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)
+               p2p_add_dev_info(p2p, sa, dev, &msg);
+       if (dev && dev->flags & P2P_DEV_USER_REJECTED) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: User has rejected this peer");
+               status = P2P_SC_FAIL_REJECTED_BY_USER;
+       } else if (dev == NULL || dev->wps_method == WPS_NOT_READY) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Not ready for GO negotiation with " MACSTR,
+                       MAC2STR(sa));
+               status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
+               if (dev)
+                       dev->flags |= P2P_DEV_PEER_WAITING_RESPONSE;
+               p2p->cfg->go_neg_req_rx(p2p->cfg->cb_ctx, sa,
+                                       msg.dev_password_id);
+       } else if (p2p->go_neg_peer && p2p->go_neg_peer != dev) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Already in Group Formation with another peer");
+               status = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
+       } else {
+               int go;
+
+               if (!p2p->go_neg_peer) {
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting "
+                               "GO Negotiation with previously authorized "
+                               "peer");
+                       if (!(dev->flags & P2P_DEV_FORCE_FREQ)) {
+                               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                                       "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 {
+                               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                                       "P2P: Use previously configured "
+                                       "forced channel settings");
+                       }
+               }
+
+               dev->flags &= ~P2P_DEV_NOT_YET_READY;
+
+               if (!msg.go_intent) {
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                               "P2P: No GO Intent attribute received");
+                       goto fail;
+               }
+               if ((*msg.go_intent >> 1) > P2P_MAX_GO_INTENT) {
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                               "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) {
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                               "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) {
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                               "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) {
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                               "P2P: No common channels found");
+                       status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+                       goto fail;
+               }
+
+               switch (msg.dev_password_id) {
+               case DEV_PW_REGISTRAR_SPECIFIED:
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                               "P2P: PIN from peer Display");
+                       if (dev->wps_method != WPS_PIN_KEYPAD) {
+                               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                                       "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:
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                               "P2P: Peer entered PIN on Keypad");
+                       if (dev->wps_method != WPS_PIN_DISPLAY) {
+                               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                                       "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:
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                               "P2P: Peer using pushbutton");
+                       if (dev->wps_method != WPS_PBC) {
+                               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                                       "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:
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                               "P2P: Unsupported Device Password ID %d",
+                               msg.dev_password_id);
+                       status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+                       goto fail;
+               }
+
+               if (go) {
+                       struct p2p_channels intersection;
+                       size_t i;
+                       p2p_channels_intersect(&p2p->channels, &dev->channels,
+                                              &intersection);
+                       if (intersection.reg_classes == 0 ||
+                           intersection.reg_class[0].channels == 0) {
+                               status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+                               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                                       "P2P: No common channels found");
+                               goto fail;
+                       }
+                       for (i = 0; i < intersection.reg_classes; i++) {
+                               struct p2p_reg_class *c;
+                               c = &intersection.reg_class[i];
+                               wpa_printf(MSG_DEBUG, "P2P: reg_class %u",
+                                          c->reg_class);
+                               wpa_hexdump(MSG_DEBUG, "P2P: channels",
+                                           c->channel, c->channels);
+                       }
+                       if (!p2p_channels_includes(&intersection,
+                                                  p2p->op_reg_class,
+                                                  p2p->op_channel))
+                               p2p_reselect_channel(p2p, &intersection);
+
+                       if (!p2p->ssid_set) {
+                               p2p_build_ssid(p2p, p2p->ssid, &p2p->ssid_len);
+                               p2p->ssid_set = 1;
+                       }
+               }
+
+               dev->go_state = go ? LOCAL_GO : REMOTE_GO;
+               dev->oper_freq = p2p_channel_to_freq((const char *)
+                                                    msg.operating_channel,
+                                                    msg.operating_channel[3],
+                                                    msg.operating_channel[4]);
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer operating "
+                       "channel preference: %d MHz", dev->oper_freq);
+
+               if (msg.config_timeout) {
+                       dev->go_timeout = msg.config_timeout[0];
+                       dev->client_timeout = msg.config_timeout[1];
+               }
+
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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;
+               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;
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "P2P: Sending GO Negotiation Response");
+       if (rx_freq > 0)
+               freq = rx_freq;
+       else
+               freq = p2p_channel_to_freq(p2p->cfg->country,
+                                          p2p->cfg->reg_class,
+                                          p2p->cfg->channel);
+       if (freq < 0) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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;
+       } 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), 200) < 0) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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;
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "P2P: Building GO Negotiation Confirm");
+       buf = wpabuf_alloc(1000);
+       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, 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);
+
+       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;
+       struct wpabuf *conf;
+       int go = -1;
+       struct p2p_message msg;
+       u8 status = P2P_SC_SUCCESS;
+       int freq;
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "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) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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)) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Unexpected Dialog Token %u (expected %u)",
+                       msg.dialog_token, dev->dialog_token);
+               p2p_parse_free(&msg);
+               return;
+       }
+
+       if (!msg.status) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: No Status attribute received");
+               status = P2P_SC_FAIL_INVALID_PARAMS;
+               goto fail;
+       }
+       if (*msg.status) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: GO Negotiation rejected: status %d",
+                       *msg.status);
+               dev->go_neg_req_sent = 0;
+               if (*msg.status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                               "P2P: Wait for the peer to become ready for "
+                               "GO Negotiation");
+                       dev->flags |= P2P_DEV_NOT_YET_READY;
+                       dev->wait_count = 0;
+                       p2p_set_state(p2p, P2P_WAIT_PEER_IDLE);
+                       p2p_set_timeout(p2p, 0, 0);
+               } else {
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                               "P2P: Stop GO Negotiation attempt");
+                       p2p_go_neg_failed(p2p, dev, *msg.status);
+               }
+               p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+               p2p_parse_free(&msg);
+               return;
+       }
+
+       if (!msg.capability) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: No Intended P2P Interface Address attribute "
+                       "received");
+               status = P2P_SC_FAIL_INVALID_PARAMS;
+               goto fail;
+       }
+
+       if (!msg.go_intent) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: No GO Intent attribute received");
+               status = P2P_SC_FAIL_INVALID_PARAMS;
+               goto fail;
+       }
+       if ((*msg.go_intent >> 1) > P2P_MAX_GO_INTENT) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Mandatory P2P Group ID attribute missing from "
+                       "GO Negotiation Response");
+               p2p->ssid_len = 0;
+#ifdef CONFIG_P2P_STRICT
+               status = P2P_SC_FAIL_INVALID_PARAMS;
+               goto fail;
+#endif /* CONFIG_P2P_STRICT */
+       }
+
+       if (!msg.config_timeout) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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.
+                */
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: No Operating Channel attribute received");
+               status = P2P_SC_FAIL_INVALID_PARAMS;
+               goto fail;
+       }
+       if (!msg.channel_list) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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((const char *)
+                                                    msg.operating_channel,
+                                                    msg.operating_channel[3],
+                                                    msg.operating_channel[4]);
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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:
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: PIN from peer Display");
+               if (dev->wps_method != WPS_PIN_KEYPAD) {
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                               "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:
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Peer entered PIN on Keypad");
+               if (dev->wps_method != WPS_PIN_DISPLAY) {
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                               "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:
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Peer using pushbutton");
+               if (dev->wps_method != WPS_PBC) {
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                               "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:
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Unsupported Device Password ID %d",
+                       msg.dev_password_id);
+               status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+               goto fail;
+       }
+
+       if (go) {
+               struct p2p_channels intersection;
+               size_t i;
+               p2p_channels_intersect(&p2p->channels, &dev->channels,
+                                      &intersection);
+               if (intersection.reg_classes == 0 ||
+                   intersection.reg_class[0].channels == 0) {
+                       status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                               "P2P: No common channels found");
+                       goto fail;
+               }
+               for (i = 0; i < intersection.reg_classes; i++) {
+                       struct p2p_reg_class *c;
+                       c = &intersection.reg_class[i];
+                       wpa_printf(MSG_DEBUG, "P2P: reg_class %u",
+                                  c->reg_class);
+                       wpa_hexdump(MSG_DEBUG, "P2P: channels",
+                                   c->channel, c->channels);
+               }
+               if (!p2p_channels_includes(&intersection, p2p->op_reg_class,
+                                          p2p->op_channel))
+                       p2p_reselect_channel(p2p, &intersection);
+
+               if (!p2p->ssid_set) {
+                       p2p_build_ssid(p2p, p2p->ssid, &p2p->ssid_len);
+                       p2p->ssid_set = 1;
+               }
+       }
+
+       p2p_set_state(p2p, P2P_GO_NEG);
+       p2p_clear_timeout(p2p);
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "P2P: GO Negotiation with " MACSTR, MAC2STR(sa));
+       os_memcpy(dev->intended_addr, msg.intended_addr, ETH_ALEN);
+
+fail:
+       conf = p2p_build_go_neg_conf(p2p, dev, msg.dialog_token, status,
+                                    msg.operating_channel, go);
+       p2p_parse_free(&msg);
+       if (conf == NULL)
+               return;
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "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;
+       if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, sa,
+                           wpabuf_head(conf), wpabuf_len(conf), 200) < 0) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Failed to send Action frame");
+               p2p_go_neg_failed(p2p, dev, -1);
+       }
+       wpabuf_free(conf);
+}
+
+
+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;
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "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) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Not ready for GO negotiation with " MACSTR,
+                       MAC2STR(sa));
+               return;
+       }
+
+       if (p2p->pending_action_state == P2P_PENDING_GO_NEG_RESPONSE) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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)) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Was not expecting GO Negotiation Confirm - "
+                       "ignore");
+               return;
+       }
+       dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM;
+
+       if (msg.dialog_token != dev->dialog_token) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Unexpected Dialog Token %u (expected %u)",
+                       msg.dialog_token, dev->dialog_token);
+               p2p_parse_free(&msg);
+               return;
+       }
+
+       if (!msg.status) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: No Status attribute received");
+               p2p_parse_free(&msg);
+               return;
+       }
+       if (*msg.status) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: GO Negotiation rejected: status %d",
+                       *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) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Mandatory P2P Group ID attribute missing from "
+                       "GO Negotiation Confirmation");
+               p2p->ssid_len = 0;
+#ifdef CONFIG_P2P_STRICT
+               p2p_parse_free(&msg);
+               return;
+#endif /* CONFIG_P2P_STRICT */
+       }
+
+       if (!msg.operating_channel) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Mandatory Operating Channel attribute missing "
+                       "from GO Negotiation Confirmation");
+#ifdef CONFIG_P2P_STRICT
+               p2p_parse_free(&msg);
+               return;
+#endif /* CONFIG_P2P_STRICT */
+       }
+
+       if (!msg.channel_list) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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.
+                */
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Unexpected GO Neg state - do not know which end "
+                       "becomes GO");
+               return;
+       }
+
+       p2p_go_complete(p2p, dev);
+}
diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c
new file mode 100644 (file)
index 0000000..e25baaa
--- /dev/null
@@ -0,0 +1,739 @@
+/*
+ * Wi-Fi Direct - P2P group operations
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.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 *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;
+};
+
+
+static void p2p_group_update_ies(struct p2p_group *group);
+
+
+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(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->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);
+       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 = 0, group_capab = 0;
+
+       /* P2P Capability */
+       dev_capab |= P2P_DEV_CAPAB_SERVICE_DISCOVERY;
+       dev_capab |= P2P_DEV_CAPAB_INVITATION_PROCEDURE;
+       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;
+       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_build_beacon_ie(struct p2p_group *group)
+{
+       struct wpabuf *ie;
+       u8 *len;
+
+       ie = wpabuf_alloc(257);
+       if (ie == NULL)
+               return NULL;
+
+       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;
+}
+
+
+static struct wpabuf * p2p_group_build_probe_resp_ie(struct p2p_group *group)
+{
+       u8 *group_info;
+       struct wpabuf *ie;
+       struct p2p_group_member *m;
+       u8 *len;
+
+       ie = wpabuf_alloc(257);
+       if (ie == NULL)
+               return NULL;
+
+       len = p2p_buf_add_ie_hdr(ie);
+
+       p2p_group_add_common_ies(group, ie);
+       p2p_group_add_noa(ie, group->noa);
+
+       /* P2P Device Info */
+       p2p_buf_add_device_info(ie, group->p2p, NULL);
+
+       /* P2P Group Info */
+       group_info = wpabuf_put(ie, 0);
+       wpabuf_put_u8(ie, P2P_ATTR_GROUP_INFO);
+       wpabuf_put_le16(ie, 0); /* Length to be filled */
+       for (m = group->members; m; m = m->next)
+               p2p_client_info(ie, m);
+       WPA_PUT_LE16(group_info + 1,
+                    (u8 *) wpabuf_put(ie, 0) - group_info - 3);
+
+       p2p_buf_update_ie_hdr(ie, len);
+       return ie;
+}
+
+
+static void p2p_group_update_ies(struct p2p_group *group)
+{
+       struct wpabuf *beacon_ie;
+       struct wpabuf *probe_resp_ie;
+
+       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;
+
+       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);
+       }
+
+       p2p_group_remove_member(group, addr);
+
+       m->next = group->members;
+       group->members = m;
+       group->num_members++;
+       wpa_msg(group->p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Add client " MACSTR
+               " to group (p2p=%d client_info=%d); num_members=%u/%u",
+               MAC2STR(addr), m->p2p_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;
+
+       /*
+        * (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);
+       if (resp == NULL)
+               return NULL;
+       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)) {
+               wpa_msg(group->p2p->cfg->msg_ctx, MSG_DEBUG, "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) {
+               wpa_printf(MSG_DEBUG, "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)) {
+               wpa_printf(MSG_DEBUG, "P2P: Requested client does not support "
+                          "client discoverability");
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "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)
+       {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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) {
+               wpa_printf(MSG_DEBUG, "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)
+               wpa_printf(MSG_DEBUG, "P2P: Failed to fetch current NoA");
+       else if (curr_noa_len == 0)
+               wpa_printf(MSG_DEBUG, "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)
+{
+       return group->num_members;
+}
+
+
+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->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;
+}
diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h
new file mode 100644 (file)
index 0000000..d052a03
--- /dev/null
@@ -0,0 +1,678 @@
+/*
+ * P2P - Internal definitions for P2P module
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef P2P_I_H
+#define P2P_I_H
+
+#include "utils/list.h"
+#include "p2p.h"
+
+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_time last_seen;
+       int listen_freq;
+       enum p2p_wps_method wps_method;
+
+       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.
+        */
+       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 intended_addr[ETH_ALEN];
+
+       char country[3];
+       struct p2p_channels channels;
+       int oper_freq;
+       u8 oper_ssid[32];
+       size_t oper_ssid_len;
+
+       /**
+        * req_config_methods - Pending provisioning 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_SD_INFO BIT(3)
+#define P2P_DEV_SD_SCHEDULE BIT(4)
+#define P2P_DEV_PD_PEER_DISPLAY BIT(5)
+#define P2P_DEV_PD_PEER_KEYPAD BIT(6)
+#define P2P_DEV_USER_REJECTED BIT(7)
+#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)
+       unsigned int flags;
+
+       int status; /* enum p2p_status_code */
+       unsigned int wait_count;
+       unsigned int connect_reqs;
+       unsigned int invitation_reqs;
+
+       u16 ext_listen_period;
+       u16 ext_listen_interval;
+
+       u8 go_timeout;
+       u8 client_timeout;
+};
+
+struct p2p_sd_query {
+       struct p2p_sd_query *next;
+       u8 peer[ETH_ALEN];
+       int for_all_peers;
+       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,
+
+               /**
+                * P2P_SEARCH_WHEN_READY - Waiting to start Search
+                */
+               P2P_SEARCH_WHEN_READY,
+       } state;
+
+       /**
+        * min_disc_int - minDiscoverableInterval
+        */
+       int min_disc_int;
+
+       /**
+        * max_disc_int - maxDiscoverableInterval
+        */
+       int max_disc_int;
+
+       /**
+        * 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;
+
+       const u8 *invite_go_dev_addr;
+       u8 invite_go_dev_addr_buf[ETH_ALEN];
+
+       /**
+        * 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;
+
+       /* 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 ssid[32];
+
+       /**
+        * 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;
+
+       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_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 inv_ssid[32];
+       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;
+
+       /* 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 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;
+
+       /**
+        * 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;
+};
+
+/**
+ * struct p2p_message - Parsed P2P message (or P2P IE)
+ */
+struct p2p_message {
+       struct wpabuf *p2p_attributes;
+       struct wpabuf *wps_attributes;
+
+       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;
+
+       /* 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;
+       char device_name[33];
+       u16 config_methods;
+
+       /* WPS IE */
+       u16 dev_password_id;
+       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;
+
+       /* DS Parameter Set IE */
+       const u8 *ds_params;
+
+       /* SSID IE */
+       const u8 *ssid;
+};
+
+
+#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(const char *country, int reg_class, int channel);
+int p2p_freq_to_channel(const char *country, unsigned int freq, u8 *reg_class,
+                       u8 *channel);
+void p2p_channels_intersect(const struct p2p_channels *a,
+                           const struct p2p_channels *b,
+                           struct p2p_channels *res);
+int p2p_channels_includes(const struct p2p_channels *channels, u8 reg_class,
+                         u8 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);
+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);
+
+
+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_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, u16 pw_id,
+                     int all_attr);
+
+/* 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);
+
+/* 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);
+
+/* 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);
+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, int level,
+                  const u8 *ies, size_t ies_len);
+struct p2p_device * p2p_get_device(struct p2p_data *p2p, const u8 *addr);
+struct p2p_device * p2p_get_device_interface(struct p2p_data *p2p,
+                                            const u8 *addr);
+void p2p_go_neg_failed(struct p2p_data *p2p, struct p2p_device *peer,
+                      int status);
+void p2p_go_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);
+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);
+
+#endif /* P2P_I_H */
diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c
new file mode 100644 (file)
index 0000000..016c572
--- /dev/null
@@ -0,0 +1,491 @@
+/*
+ * Wi-Fi Direct - P2P Invitation procedure
+ * Copyright (c) 2010, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.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)
+{
+       struct wpabuf *buf;
+       u8 *len;
+       const u8 *dev_addr;
+
+       buf = wpabuf_alloc(1000);
+       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, 100, 20);
+       p2p_buf_add_invitation_flags(buf, p2p->inv_persistent ?
+                                    P2P_INVITATION_FLAGS_TYPE : 0);
+       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);
+
+       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;
+
+       buf = wpabuf_alloc(1000);
+       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);
+
+       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 intersection, *channels = NULL;
+       int persistent;
+
+       os_memset(group_bssid, 0, sizeof(group_bssid));
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "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)) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Invitation Request from unknown peer "
+                       MACSTR, MAC2STR(sa));
+
+               if (p2p_add_device(p2p, sa, rx_freq, 0, data + 1, len - 1)) {
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                               "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) {
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                               "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) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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.
+                */
+               wpa_printf(MSG_DEBUG, "P2P: Mandatory Invitation Flags "
+                          "attribute missing from Invitation Request");
+               persistent = 1;
+       }
+
+       if (p2p_peer_channels_check(p2p, &p2p->cfg->channels, dev,
+                                   msg.channel_list, msg.channel_list_len) <
+           0) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: No common channels found");
+               status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+               goto fail;
+       }
+
+       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);
+       }
+
+       if (op_freq) {
+               if (p2p_freq_to_channel(p2p->cfg->country, op_freq,
+                                       &reg_class, &channel) < 0) {
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                               "P2P: Unknown forced freq %d MHz from "
+                               "invitation_process()", op_freq);
+                       status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+                       goto fail;
+               }
+
+               p2p_channels_intersect(&p2p->cfg->channels, &dev->channels,
+                                      &intersection);
+               if (!p2p_channels_includes(&intersection, reg_class, channel))
+               {
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                               "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 {
+               op_freq = p2p_channel_to_freq(p2p->cfg->country,
+                                             p2p->cfg->op_reg_class,
+                                             p2p->cfg->op_channel);
+               if (op_freq < 0) {
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                               "P2P: Unknown operational channel "
+                               "(country=%c%c reg_class=%u channel=%u)",
+                               p2p->cfg->country[0], p2p->cfg->country[1],
+                               p2p->cfg->op_reg_class, p2p->cfg->op_channel);
+                       status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+                       goto fail;
+               }
+
+               p2p_channels_intersect(&p2p->cfg->channels, &dev->channels,
+                                      &intersection);
+               if (status == P2P_SC_SUCCESS) {
+                       reg_class = p2p->cfg->op_reg_class;
+                       channel = p2p->cfg->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->country,
+                                          p2p->cfg->reg_class,
+                                          p2p->cfg->channel);
+       if (freq < 0) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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_len - ETH_ALEN <= 32) {
+               os_memcpy(p2p->inv_ssid, msg.group_id + ETH_ALEN,
+                         msg.group_id_len - ETH_ALEN);
+               p2p->inv_ssid_len = msg.group_id_len - ETH_ALEN;
+       }
+       os_memcpy(p2p->inv_go_dev_addr, msg.group_id, ETH_ALEN);
+       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) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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;
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "P2P: Received Invitation Response from " MACSTR,
+               MAC2STR(sa));
+
+       dev = p2p_get_device(p2p, sa);
+       if (dev == NULL) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Ignore Invitation Response from unknown peer "
+                       MACSTR, MAC2STR(sa));
+               return;
+       }
+
+       if (dev != p2p->invite_peer) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Ignore unexpected Invitation Response from peer "
+                       MACSTR, MAC2STR(sa));
+               return;
+       }
+
+       if (p2p_parse(data, len, &msg))
+               return;
+
+       if (!msg.status) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Mandatory Status attribute missing in "
+                       "Invitation Response from " MACSTR, MAC2STR(sa));
+               p2p_parse_free(&msg);
+               return;
+       }
+
+       if (p2p->cfg->invitation_result)
+               p2p->cfg->invitation_result(p2p->cfg->cb_ctx, *msg.status,
+                                           msg.group_bssid);
+
+       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)
+{
+       struct wpabuf *req;
+       int freq;
+
+       freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
+       if (freq <= 0) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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);
+       if (req == NULL)
+               return -1;
+       if (p2p->state != P2P_IDLE)
+               p2p_stop_listen_for_freq(p2p, freq);
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "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), 200) < 0) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Failed to send Action frame");
+               /* Use P2P find to recover and retry */
+               p2p_set_timeout(p2p, 0, 0);
+       }
+
+       wpabuf_free(req);
+
+       return 0;
+}
+
+
+void p2p_invitation_req_cb(struct p2p_data *p2p, int success)
+{
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "P2P: Invitation Request TX callback: success=%d", success);
+
+       if (p2p->invite_peer == NULL) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: No pending Invite");
+               return;
+       }
+
+       /*
+        * 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, 100000);
+}
+
+
+void p2p_invitation_resp_cb(struct p2p_data *p2p, int success)
+{
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "P2P: Invitation Response TX callback: success=%d", success);
+       p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+
+       if (success && 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)
+{
+       struct p2p_device *dev;
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "P2P: Request to invite peer " MACSTR " role=%d persistent=%d "
+               "force_freq=%u",
+               MAC2STR(peer), role, persistent_group, force_freq);
+       if (bssid)
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Invitation for BSSID " MACSTR, MAC2STR(bssid));
+       if (go_dev_addr) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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, "P2P: Invitation for SSID",
+                         ssid, ssid_len);
+
+       dev = p2p_get_device(p2p, peer);
+       if (dev == NULL || (dev->listen_freq <= 0 && dev->oper_freq <= 0)) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Cannot invite unknown P2P Device " MACSTR,
+                       MAC2STR(peer));
+               return -1;
+       }
+
+       if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) {
+               if (!(dev->info.dev_capab &
+                     P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) {
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                               "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 (force_freq) {
+               if (p2p_freq_to_channel(p2p->cfg->country, force_freq,
+                                       &p2p->op_reg_class, &p2p->op_channel) <
+                   0) {
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                               "P2P: Unsupported frequency %u MHz",
+                               force_freq);
+                       return -1;
+               }
+               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 {
+               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));
+       }
+
+       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);
+}
diff --git a/src/p2p/p2p_parse.c b/src/p2p/p2p_parse.c
new file mode 100644 (file)
index 0000000..5c5445a
--- /dev/null
@@ -0,0 +1,718 @@
+/*
+ * P2P - IE parser
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#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 (data + len - pos < (int) nlen || nlen > 32) {
+                       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 (msg->device_name[i] > 0 &&
+                           msg->device_name[i] < 32)
+                               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 || len > ETH_ALEN + 32) {
+                       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;
+       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;
+               if (pos + 2 >= end) {
+                       wpa_printf(MSG_DEBUG, "P2P: Invalid P2P attribute");
+                       return -1;
+               }
+               attr_len = WPA_GET_LE16(pos + 1);
+               wpa_printf(MSG_DEBUG, "P2P: Attribute %d length %u",
+                          pos[0], attr_len);
+               if (pos + 3 + attr_len > end) {
+                       wpa_printf(MSG_DEBUG, "P2P: Attribute underflow "
+                                  "(len=%u left=%d)",
+                                  attr_len, (int) (end - pos - 3));
+                       wpa_hexdump(MSG_MSGDUMP, "P2P: Data", pos, end - pos);
+                       return -1;
+               }
+               if (p2p_parse_attribute(pos[0], pos + 3, attr_len, msg))
+                       return -1;
+               pos += 3 + 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);
+       }
+       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;
+
+       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 (elems.ds_params && elems.ds_params_len >= 1)
+               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;
+       }
+
+       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);
+}
+
+
+/**
+ * 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;
+}
+
+
+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 */
+               if (count >= 32)
+                       count = 32;
+               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;
+               char name[33];
+               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 (ret < 0 || ret >= end - pos)
+                       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 (ret < 0 || ret >= end - pos)
+                       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 (ret < 0 || ret >= end - pos)
+                               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 (name[count] > 0 && name[count] < 32)
+                               name[count] = '_';
+                       count--;
+               }
+
+               ret = os_snprintf(pos, end - pos, " dev_name='%s'\n", name);
+               if (ret < 0 || ret >= end - pos)
+                       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 (ret < 0 || ret >= end - pos)
+                       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 (ret < 0 || ret >= end - pos)
+                       return pos - buf;
+               pos += ret;
+       }
+
+       ret = os_snprintf(pos, end - pos, "p2p_device_name=%s\n",
+                         msg.device_name);
+       if (ret < 0 || ret >= end - pos)
+               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 (ret < 0 || ret >= end - pos)
+                       return pos - buf;
+               pos += ret;
+       }
+
+       ret = os_snprintf(pos, end - pos, "p2p_config_methods=0x%x\n",
+                         msg.config_methods);
+       if (ret < 0 || ret >= end - pos)
+               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;
+}
diff --git a/src/p2p/p2p_pd.c b/src/p2p/p2p_pd.c
new file mode 100644 (file)
index 0000000..8eced26
--- /dev/null
@@ -0,0 +1,416 @@
+/*
+ * Wi-Fi Direct - P2P provision discovery
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "wps/wps_defs.h"
+#include "p2p_i.h"
+#include "p2p.h"
+
+
+/*
+ * Number of retries to attempt for provision discovery requests during IDLE
+ * state in case the peer is not listening.
+ */
+#define MAX_PROV_DISC_REQ_RETRIES 10
+
+
+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 struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p,
+                                              u8 dialog_token,
+                                              u16 config_methods,
+                                              struct p2p_device *go)
+{
+       struct wpabuf *buf;
+       u8 *len;
+
+       buf = wpabuf_alloc(1000);
+       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);
+       p2p_buf_add_capability(buf, p2p->dev_capab, 0);
+       p2p_buf_add_device_info(buf, p2p, NULL);
+       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);
+
+       return buf;
+}
+
+
+static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p,
+                                               u8 dialog_token,
+                                               u16 config_methods)
+{
+       struct wpabuf *buf;
+
+       buf = wpabuf_alloc(100);
+       if (buf == NULL)
+               return NULL;
+
+       p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_RESP, dialog_token);
+
+       /* WPS IE with Config Methods attribute */
+       p2p_build_wps_ie_config_methods(buf, config_methods);
+
+       return buf;
+}
+
+
+void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa,
+                              const u8 *data, size_t len, int rx_freq)
+{
+       struct p2p_message msg;
+       struct p2p_device *dev;
+       int freq;
+       int reject = 1;
+       struct wpabuf *resp;
+
+       if (p2p_parse(data, len, &msg))
+               return;
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "P2P: Received Provision Discovery Request from " MACSTR
+               " with config methods 0x%x (freq=%d)",
+               MAC2STR(sa), msg.wps_config_methods, rx_freq);
+
+       dev = p2p_get_device(p2p, sa);
+       if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Provision Discovery Request from "
+                       "unknown peer " MACSTR, MAC2STR(sa));
+               if (p2p_add_device(p2p, sa, rx_freq, 0, data + 1, len - 1)) {
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                               "P2P: Provision Discovery Request add device "
+                               "failed " MACSTR, MAC2STR(sa));
+               }
+       }
+
+       if (!(msg.wps_config_methods &
+             (WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD |
+              WPS_CONFIG_PUSHBUTTON))) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Unsupported "
+                       "Config Methods in Provision Discovery Request");
+               goto out;
+       }
+
+       if (dev)
+               dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
+                               P2P_DEV_PD_PEER_KEYPAD);
+       if (msg.wps_config_methods & WPS_CONFIG_DISPLAY) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR
+                       " requested us to show a PIN on display", MAC2STR(sa));
+               if (dev)
+                       dev->flags |= P2P_DEV_PD_PEER_KEYPAD;
+       } else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR
+                       " requested us to write its PIN using keypad",
+                       MAC2STR(sa));
+               if (dev)
+                       dev->flags |= P2P_DEV_PD_PEER_DISPLAY;
+       }
+
+       reject = 0;
+
+out:
+       resp = p2p_build_prov_disc_resp(p2p, msg.dialog_token,
+                                       reject ? 0 : msg.wps_config_methods);
+       if (resp == NULL) {
+               p2p_parse_free(&msg);
+               return;
+       }
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "P2P: Sending Provision Discovery Response");
+       if (rx_freq > 0)
+               freq = rx_freq;
+       else
+               freq = p2p_channel_to_freq(p2p->cfg->country,
+                                          p2p->cfg->reg_class,
+                                          p2p->cfg->channel);
+       if (freq < 0) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Unknown regulatory class/channel");
+               wpabuf_free(resp);
+               p2p_parse_free(&msg);
+               return;
+       }
+       p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+       if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr,
+                           p2p->cfg->dev_addr,
+                           wpabuf_head(resp), wpabuf_len(resp), 200) < 0) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Failed to send Action frame");
+       }
+
+       wpabuf_free(resp);
+
+       if (!reject && p2p->cfg->prov_disc_req) {
+               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);
+       }
+       p2p_parse_free(&msg);
+}
+
+
+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;
+
+       if (p2p_parse(data, len, &msg))
+               return;
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "P2P: Received Provisioning 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) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Ignore Provisioning Discovery Response from "
+                       MACSTR " with no pending request", MAC2STR(sa));
+               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;
+       }
+
+       if (dev->dialog_token != msg.dialog_token) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Ignore Provisioning Discovery Response with "
+                       "unexpected Dialog Token %u (expected %u)",
+                       msg.dialog_token, dev->dialog_token);
+               p2p_parse_free(&msg);
+               return;
+       }
+
+       /*
+        * 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 != dev->req_config_methods) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer rejected "
+                       "our Provisioning Discovery Request");
+               if (p2p->cfg->prov_disc_fail)
+                       p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa,
+                                                P2P_PROV_DISC_REJECTED);
+               p2p_parse_free(&msg);
+               goto out;
+       }
+
+       report_config_methods = dev->req_config_methods;
+       dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
+                       P2P_DEV_PD_PEER_KEYPAD);
+       if (dev->req_config_methods & WPS_CONFIG_DISPLAY) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR
+                       " accepted to show a PIN on display", MAC2STR(sa));
+               dev->flags |= P2P_DEV_PD_PEER_DISPLAY;
+       } else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR
+                       " accepted to write our PIN using keypad",
+                       MAC2STR(sa));
+               dev->flags |= P2P_DEV_PD_PEER_KEYPAD;
+       }
+
+       /* Store the provisioning info */
+       dev->wps_prov_info = msg.wps_config_methods;
+
+       p2p_parse_free(&msg);
+
+out:
+       dev->req_config_methods = 0;
+       p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+       if (p2p->cfg->prov_disc_resp)
+               p2p->cfg->prov_disc_resp(p2p->cfg->cb_ctx, sa,
+                                        report_config_methods);
+}
+
+
+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) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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)) {
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                               "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 */
+       }
+
+       dev->dialog_token++;
+       if (dev->dialog_token == 0)
+               dev->dialog_token = 1;
+       req = p2p_build_prov_disc_req(p2p, dev->dialog_token,
+                                     dev->req_config_methods,
+                                     join ? dev : NULL);
+       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) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "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,
+                     u16 config_methods, int join, int force_freq)
+{
+       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)) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Provision "
+                       "Discovery Request destination " MACSTR
+                       " not yet known", MAC2STR(peer_addr));
+               return -1;
+       }
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Provision Discovery "
+               "Request with " MACSTR " (config methods 0x%x)",
+               MAC2STR(peer_addr), config_methods);
+       if (config_methods == 0)
+               return -1;
+
+       /* Reset provisioning info */
+       dev->wps_prov_info = 0;
+
+       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) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Busy with other "
+                       "operations; postpone Provision Discovery Request "
+                       "with " MACSTR " (config methods 0x%x)",
+                       MAC2STR(peer_addr), config_methods);
+               return 0;
+       }
+
+       /*
+        * We use the join param as a cue to differentiate between user
+        * initiated PD request and one issued during finds (internal).
+        */
+       p2p->user_initiated_pd = !join;
+
+       /* Also set some retries to attempt in case of IDLE state */
+       if (p2p->user_initiated_pd && p2p->state == P2P_IDLE)
+               p2p->pd_retries = MAX_PROV_DISC_REQ_RETRIES;
+
+       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;
+}
diff --git a/src/p2p/p2p_sd.c b/src/p2p/p2p_sd.c
new file mode 100644 (file)
index 0000000..43b3a61
--- /dev/null
@@ -0,0 +1,889 @@
+/*
+ * Wi-Fi Direct - P2P service discovery
+ * Copyright (c) 2009, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/gas.h"
+#include "p2p_i.h"
+#include "p2p.h"
+
+
+struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p,
+                                        struct p2p_device *dev)
+{
+       struct p2p_sd_query *q;
+
+       if (!(dev->info.dev_capab & P2P_DEV_CAPAB_SERVICE_DISCOVERY))
+               return 0; /* peer does not support SD */
+
+       for (q = p2p->sd_queries; q; q = q->next) {
+               if (q->for_all_peers && !(dev->flags & P2P_DEV_SD_INFO))
+                       return q;
+               if (!q->for_all_peers &&
+                   os_memcmp(q->peer, dev->info.p2p_device_addr, ETH_ALEN) ==
+                   0)
+                       return q;
+       }
+
+       return NULL;
+}
+
+
+static int p2p_unlink_sd_query(struct p2p_data *p2p,
+                              struct p2p_sd_query *query)
+{
+       struct p2p_sd_query *q, *prev;
+       q = p2p->sd_queries;
+       prev = NULL;
+       while (q) {
+               if (q == query) {
+                       if (prev)
+                               prev->next = q->next;
+                       else
+                               p2p->sd_queries = q->next;
+                       if (p2p->sd_query == query)
+                               p2p->sd_query = NULL;
+                       return 1;
+               }
+               prev = q;
+               q = q->next;
+       }
+       return 0;
+}
+
+
+static void p2p_free_sd_query(struct p2p_sd_query *q)
+{
+       if (q == NULL)
+               return;
+       wpabuf_free(q->tlvs);
+       os_free(q);
+}
+
+
+void p2p_free_sd_queries(struct p2p_data *p2p)
+{
+       struct p2p_sd_query *q, *prev;
+       q = p2p->sd_queries;
+       p2p->sd_queries = NULL;
+       while (q) {
+               prev = q;
+               q = q->next;
+               p2p_free_sd_query(prev);
+       }
+}
+
+
+static struct wpabuf * p2p_build_sd_query(u16 update_indic,
+                                         struct wpabuf *tlvs)
+{
+       struct wpabuf *buf;
+       u8 *len_pos;
+
+       buf = gas_anqp_build_initial_req(0, 100 + wpabuf_len(tlvs));
+       if (buf == NULL)
+               return NULL;
+
+       /* ANQP Query Request Frame */
+       len_pos = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+       wpabuf_put_be24(buf, OUI_WFA);
+       wpabuf_put_u8(buf, P2P_OUI_TYPE);
+       wpabuf_put_le16(buf, update_indic); /* Service Update Indicator */
+       wpabuf_put_buf(buf, tlvs);
+       gas_anqp_set_element_len(buf, len_pos);
+
+       gas_anqp_set_len(buf);
+
+       return buf;
+}
+
+
+static void p2p_send_gas_comeback_req(struct p2p_data *p2p, const u8 *dst,
+                                     u8 dialog_token, int freq)
+{
+       struct wpabuf *req;
+
+       req = gas_build_comeback_req(dialog_token);
+       if (req == NULL)
+               return;
+
+       p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+       if (p2p_send_action(p2p, freq, dst, p2p->cfg->dev_addr, dst,
+                           wpabuf_head(req), wpabuf_len(req), 200) < 0)
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Failed to send Action frame");
+
+       wpabuf_free(req);
+}
+
+
+static struct wpabuf * p2p_build_sd_response(u8 dialog_token, u16 status_code,
+                                            u16 comeback_delay,
+                                            u16 update_indic,
+                                            const struct wpabuf *tlvs)
+{
+       struct wpabuf *buf;
+       u8 *len_pos;
+
+       buf = gas_anqp_build_initial_resp(dialog_token, status_code,
+                                         comeback_delay,
+                                         100 + (tlvs ? wpabuf_len(tlvs) : 0));
+       if (buf == NULL)
+               return NULL;
+
+       if (tlvs) {
+               /* ANQP Query Response Frame */
+               len_pos = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+               wpabuf_put_be24(buf, OUI_WFA);
+               wpabuf_put_u8(buf, P2P_OUI_TYPE);
+                /* Service Update Indicator */
+               wpabuf_put_le16(buf, update_indic);
+               wpabuf_put_buf(buf, tlvs);
+               gas_anqp_set_element_len(buf, len_pos);
+       }
+
+       gas_anqp_set_len(buf);
+
+       return buf;
+}
+
+
+static struct wpabuf * p2p_build_gas_comeback_resp(u8 dialog_token,
+                                                  u16 status_code,
+                                                  u16 update_indic,
+                                                  const u8 *data, size_t len,
+                                                  u8 frag_id, u8 more,
+                                                  u16 total_len)
+{
+       struct wpabuf *buf;
+
+       buf = gas_anqp_build_comeback_resp(dialog_token, status_code, frag_id,
+                                          more, 0, 100 + len);
+       if (buf == NULL)
+               return NULL;
+
+       if (frag_id == 0) {
+               /* ANQP Query Response Frame */
+               wpabuf_put_le16(buf, ANQP_VENDOR_SPECIFIC); /* Info ID */
+               wpabuf_put_le16(buf, 3 + 1 + 2 + total_len);
+               wpabuf_put_be24(buf, OUI_WFA);
+               wpabuf_put_u8(buf, P2P_OUI_TYPE);
+               /* Service Update Indicator */
+               wpabuf_put_le16(buf, update_indic);
+       }
+
+       wpabuf_put_data(buf, data, len);
+       gas_anqp_set_len(buf);
+
+       return buf;
+}
+
+
+int p2p_start_sd(struct p2p_data *p2p, struct p2p_device *dev)
+{
+       struct wpabuf *req;
+       int ret = 0;
+       struct p2p_sd_query *query;
+       int freq;
+
+       freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
+       if (freq <= 0) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: No Listen/Operating frequency known for the "
+                       "peer " MACSTR " to send SD Request",
+                       MAC2STR(dev->info.p2p_device_addr));
+               return -1;
+       }
+
+       query = p2p_pending_sd_req(p2p, dev);
+       if (query == NULL)
+               return -1;
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "P2P: Start Service Discovery with " MACSTR,
+               MAC2STR(dev->info.p2p_device_addr));
+
+       req = p2p_build_sd_query(p2p->srv_update_indic, query->tlvs);
+       if (req == NULL)
+               return -1;
+
+       p2p->sd_peer = dev;
+       p2p->sd_query = query;
+       p2p->pending_action_state = P2P_PENDING_SD;
+
+       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), 5000) < 0) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Failed to send Action frame");
+               ret = -1;
+       }
+
+       wpabuf_free(req);
+
+       return ret;
+}
+
+
+void p2p_rx_gas_initial_req(struct p2p_data *p2p, const u8 *sa,
+                           const u8 *data, size_t len, int rx_freq)
+{
+       const u8 *pos = data;
+       const u8 *end = data + len;
+       const u8 *next;
+       u8 dialog_token;
+       u16 slen;
+       int freq;
+       u16 update_indic;
+
+
+       if (p2p->cfg->sd_request == NULL)
+               return;
+
+       if (rx_freq > 0)
+               freq = rx_freq;
+       else
+               freq = p2p_channel_to_freq(p2p->cfg->country,
+                                          p2p->cfg->reg_class,
+                                          p2p->cfg->channel);
+       if (freq < 0)
+               return;
+
+       if (len < 1 + 2)
+               return;
+
+       dialog_token = *pos++;
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "P2P: GAS Initial Request from " MACSTR " (dialog token %u, "
+               "freq %d)",
+               MAC2STR(sa), dialog_token, rx_freq);
+
+       if (*pos != WLAN_EID_ADV_PROTO) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Unexpected IE in GAS Initial Request: %u", *pos);
+               return;
+       }
+       pos++;
+
+       slen = *pos++;
+       next = pos + slen;
+       if (next > end || slen < 2) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Invalid IE in GAS Initial Request");
+               return;
+       }
+       pos++; /* skip QueryRespLenLimit and PAME-BI */
+
+       if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Unsupported GAS advertisement protocol id %u",
+                       *pos);
+               return;
+       }
+
+       pos = next;
+       /* Query Request */
+       if (pos + 2 > end)
+               return;
+       slen = WPA_GET_LE16(pos);
+       pos += 2;
+       if (pos + slen > end)
+               return;
+       end = pos + slen;
+
+       /* ANQP Query Request */
+       if (pos + 4 > end)
+               return;
+       if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Unsupported ANQP Info ID %u", WPA_GET_LE16(pos));
+               return;
+       }
+       pos += 2;
+
+       slen = WPA_GET_LE16(pos);
+       pos += 2;
+       if (pos + slen > end || slen < 3 + 1) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Invalid ANQP Query Request length");
+               return;
+       }
+
+       if (WPA_GET_BE24(pos) != OUI_WFA) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Unsupported ANQP OUI %06x", WPA_GET_BE24(pos));
+               return;
+       }
+       pos += 3;
+
+       if (*pos != P2P_OUI_TYPE) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Unsupported ANQP vendor type %u", *pos);
+               return;
+       }
+       pos++;
+
+       if (pos + 2 > end)
+               return;
+       update_indic = WPA_GET_LE16(pos);
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "P2P: Service Update Indicator: %u", update_indic);
+       pos += 2;
+
+       p2p->cfg->sd_request(p2p->cfg->cb_ctx, freq, sa, dialog_token,
+                            update_indic, pos, end - pos);
+       /* the response will be indicated with a call to p2p_sd_response() */
+}
+
+
+void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst,
+                    u8 dialog_token, const struct wpabuf *resp_tlvs)
+{
+       struct wpabuf *resp;
+
+       /* TODO: fix the length limit to match with the maximum frame length */
+       if (wpabuf_len(resp_tlvs) > 1400) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: SD response long "
+                       "enough to require fragmentation");
+               if (p2p->sd_resp) {
+                       /*
+                        * TODO: Could consider storing the fragmented response
+                        * separately for each peer to avoid having to drop old
+                        * one if there is more than one pending SD query.
+                        * Though, that would eat more memory, so there are
+                        * also benefits to just using a single buffer.
+                        */
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Drop "
+                               "previous SD response");
+                       wpabuf_free(p2p->sd_resp);
+               }
+               os_memcpy(p2p->sd_resp_addr, dst, ETH_ALEN);
+               p2p->sd_resp_dialog_token = dialog_token;
+               p2p->sd_resp = wpabuf_dup(resp_tlvs);
+               p2p->sd_resp_pos = 0;
+               p2p->sd_frag_id = 0;
+               resp = p2p_build_sd_response(dialog_token, WLAN_STATUS_SUCCESS,
+                                            1, p2p->srv_update_indic, NULL);
+       } else {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: SD response fits "
+                       "in initial response");
+               resp = p2p_build_sd_response(dialog_token,
+                                            WLAN_STATUS_SUCCESS, 0,
+                                            p2p->srv_update_indic, resp_tlvs);
+       }
+       if (resp == NULL)
+               return;
+
+       p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+       if (p2p_send_action(p2p, freq, dst, p2p->cfg->dev_addr,
+                           p2p->cfg->dev_addr,
+                           wpabuf_head(resp), wpabuf_len(resp), 200) < 0)
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Failed to send Action frame");
+
+       wpabuf_free(resp);
+}
+
+
+void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa,
+                            const u8 *data, size_t len, int rx_freq)
+{
+       const u8 *pos = data;
+       const u8 *end = data + len;
+       const u8 *next;
+       u8 dialog_token;
+       u16 status_code;
+       u16 comeback_delay;
+       u16 slen;
+       u16 update_indic;
+
+       if (p2p->state != P2P_SD_DURING_FIND || p2p->sd_peer == NULL ||
+           os_memcmp(sa, p2p->sd_peer->info.p2p_device_addr, ETH_ALEN) != 0) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Ignore unexpected GAS Initial Response from "
+                       MACSTR, MAC2STR(sa));
+               return;
+       }
+       p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+       p2p_clear_timeout(p2p);
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "P2P: Received GAS Initial Response from " MACSTR " (len=%d)",
+               MAC2STR(sa), (int) len);
+
+       if (len < 5 + 2) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Too short GAS Initial Response frame");
+               return;
+       }
+
+       dialog_token = *pos++;
+       /* TODO: check dialog_token match */
+       status_code = WPA_GET_LE16(pos);
+       pos += 2;
+       comeback_delay = WPA_GET_LE16(pos);
+       pos += 2;
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "P2P: dialog_token=%u status_code=%u comeback_delay=%u",
+               dialog_token, status_code, comeback_delay);
+       if (status_code) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Service Discovery failed: status code %u",
+                       status_code);
+               return;
+       }
+
+       if (*pos != WLAN_EID_ADV_PROTO) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Unexpected IE in GAS Initial Response: %u",
+                       *pos);
+               return;
+       }
+       pos++;
+
+       slen = *pos++;
+       next = pos + slen;
+       if (next > end || slen < 2) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Invalid IE in GAS Initial Response");
+               return;
+       }
+       pos++; /* skip QueryRespLenLimit and PAME-BI */
+
+       if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Unsupported GAS advertisement protocol id %u",
+                       *pos);
+               return;
+       }
+
+       pos = next;
+       /* Query Response */
+       if (pos + 2 > end) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Too short Query "
+                       "Response");
+               return;
+       }
+       slen = WPA_GET_LE16(pos);
+       pos += 2;
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Query Response Length: %d",
+               slen);
+       if (pos + slen > end) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Not enough Query "
+                       "Response data");
+               return;
+       }
+       end = pos + slen;
+
+       if (comeback_delay) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Fragmented "
+                       "response - request fragments");
+               if (p2p->sd_rx_resp) {
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Drop "
+                               "old SD reassembly buffer");
+                       wpabuf_free(p2p->sd_rx_resp);
+                       p2p->sd_rx_resp = NULL;
+               }
+               p2p_send_gas_comeback_req(p2p, sa, dialog_token, rx_freq);
+               return;
+       }
+
+       /* ANQP Query Response */
+       if (pos + 4 > end)
+               return;
+       if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Unsupported ANQP Info ID %u", WPA_GET_LE16(pos));
+               return;
+       }
+       pos += 2;
+
+       slen = WPA_GET_LE16(pos);
+       pos += 2;
+       if (pos + slen > end || slen < 3 + 1) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Invalid ANQP Query Response length");
+               return;
+       }
+
+       if (WPA_GET_BE24(pos) != OUI_WFA) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Unsupported ANQP OUI %06x", WPA_GET_BE24(pos));
+               return;
+       }
+       pos += 3;
+
+       if (*pos != P2P_OUI_TYPE) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Unsupported ANQP vendor type %u", *pos);
+               return;
+       }
+       pos++;
+
+       if (pos + 2 > end)
+               return;
+       update_indic = WPA_GET_LE16(pos);
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "P2P: Service Update Indicator: %u", update_indic);
+       pos += 2;
+
+       p2p->sd_peer->flags |= P2P_DEV_SD_INFO;
+       p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE;
+       p2p->sd_peer = NULL;
+
+       if (p2p->sd_query) {
+               if (!p2p->sd_query->for_all_peers) {
+                       struct p2p_sd_query *q;
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                               "P2P: Remove completed SD query %p",
+                               p2p->sd_query);
+                       q = p2p->sd_query;
+                       p2p_unlink_sd_query(p2p, p2p->sd_query);
+                       p2p_free_sd_query(q);
+               }
+               p2p->sd_query = NULL;
+       }
+
+       if (p2p->cfg->sd_response)
+               p2p->cfg->sd_response(p2p->cfg->cb_ctx, sa, update_indic,
+                                     pos, end - pos);
+       p2p_continue_find(p2p);
+}
+
+
+void p2p_rx_gas_comeback_req(struct p2p_data *p2p, const u8 *sa,
+                            const u8 *data, size_t len, int rx_freq)
+{
+       struct wpabuf *resp;
+       u8 dialog_token;
+       size_t frag_len;
+       int more = 0;
+
+       wpa_hexdump(MSG_DEBUG, "P2P: RX GAS Comeback Request", data, len);
+       if (len < 1)
+               return;
+       dialog_token = *data;
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Dialog Token: %u",
+               dialog_token);
+       if (dialog_token != p2p->sd_resp_dialog_token) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending SD "
+                       "response fragment for dialog token %u", dialog_token);
+               return;
+       }
+
+       if (p2p->sd_resp == NULL) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending SD "
+                       "response fragment available");
+               return;
+       }
+       if (os_memcmp(sa, p2p->sd_resp_addr, ETH_ALEN) != 0) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending SD "
+                       "response fragment for " MACSTR, MAC2STR(sa));
+               return;
+       }
+
+       frag_len = wpabuf_len(p2p->sd_resp) - p2p->sd_resp_pos;
+       if (frag_len > 1400) {
+               frag_len = 1400;
+               more = 1;
+       }
+       resp = p2p_build_gas_comeback_resp(dialog_token, WLAN_STATUS_SUCCESS,
+                                          p2p->srv_update_indic,
+                                          wpabuf_head_u8(p2p->sd_resp) +
+                                          p2p->sd_resp_pos, frag_len,
+                                          p2p->sd_frag_id, more,
+                                          wpabuf_len(p2p->sd_resp));
+       if (resp == NULL)
+               return;
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send GAS Comeback "
+               "Response (frag_id %d more=%d frag_len=%d)",
+               p2p->sd_frag_id, more, (int) frag_len);
+       p2p->sd_frag_id++;
+       p2p->sd_resp_pos += frag_len;
+
+       if (more) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: %d more bytes "
+                       "remain to be sent",
+                       (int) (wpabuf_len(p2p->sd_resp) - p2p->sd_resp_pos));
+       } else {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: All fragments of "
+                       "SD response sent");
+               wpabuf_free(p2p->sd_resp);
+               p2p->sd_resp = NULL;
+       }
+
+       p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+       if (p2p_send_action(p2p, rx_freq, sa, p2p->cfg->dev_addr,
+                           p2p->cfg->dev_addr,
+                           wpabuf_head(resp), wpabuf_len(resp), 200) < 0)
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Failed to send Action frame");
+
+       wpabuf_free(resp);
+}
+
+
+void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa,
+                             const u8 *data, size_t len, int rx_freq)
+{
+       const u8 *pos = data;
+       const u8 *end = data + len;
+       const u8 *next;
+       u8 dialog_token;
+       u16 status_code;
+       u8 frag_id;
+       u8 more_frags;
+       u16 comeback_delay;
+       u16 slen;
+
+       wpa_hexdump(MSG_DEBUG, "P2P: RX GAS Comeback Response", data, len);
+
+       if (p2p->state != P2P_SD_DURING_FIND || p2p->sd_peer == NULL ||
+           os_memcmp(sa, p2p->sd_peer->info.p2p_device_addr, ETH_ALEN) != 0) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Ignore unexpected GAS Comeback Response from "
+                       MACSTR, MAC2STR(sa));
+               return;
+       }
+       p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+       p2p_clear_timeout(p2p);
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "P2P: Received GAS Comeback Response from " MACSTR " (len=%d)",
+               MAC2STR(sa), (int) len);
+
+       if (len < 6 + 2) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Too short GAS Comeback Response frame");
+               return;
+       }
+
+       dialog_token = *pos++;
+       /* TODO: check dialog_token match */
+       status_code = WPA_GET_LE16(pos);
+       pos += 2;
+       frag_id = *pos & 0x7f;
+       more_frags = (*pos & 0x80) >> 7;
+       pos++;
+       comeback_delay = WPA_GET_LE16(pos);
+       pos += 2;
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "P2P: dialog_token=%u status_code=%u frag_id=%d more_frags=%d "
+               "comeback_delay=%u",
+               dialog_token, status_code, frag_id, more_frags,
+               comeback_delay);
+       /* TODO: check frag_id match */
+       if (status_code) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Service Discovery failed: status code %u",
+                       status_code);
+               return;
+       }
+
+       if (*pos != WLAN_EID_ADV_PROTO) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Unexpected IE in GAS Comeback Response: %u",
+                       *pos);
+               return;
+       }
+       pos++;
+
+       slen = *pos++;
+       next = pos + slen;
+       if (next > end || slen < 2) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Invalid IE in GAS Comeback Response");
+               return;
+       }
+       pos++; /* skip QueryRespLenLimit and PAME-BI */
+
+       if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Unsupported GAS advertisement protocol id %u",
+                       *pos);
+               return;
+       }
+
+       pos = next;
+       /* Query Response */
+       if (pos + 2 > end) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Too short Query "
+                       "Response");
+               return;
+       }
+       slen = WPA_GET_LE16(pos);
+       pos += 2;
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Query Response Length: %d",
+               slen);
+       if (pos + slen > end) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Not enough Query "
+                       "Response data");
+               return;
+       }
+       if (slen == 0) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No Query Response "
+                       "data");
+               return;
+       }
+       end = pos + slen;
+
+       if (p2p->sd_rx_resp) {
+                /*
+                 * ANQP header is only included in the first fragment; rest of
+                 * the fragments start with continue TLVs.
+                 */
+               goto skip_nqp_header;
+       }
+
+       /* ANQP Query Response */
+       if (pos + 4 > end)
+               return;
+       if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Unsupported ANQP Info ID %u", WPA_GET_LE16(pos));
+               return;
+       }
+       pos += 2;
+
+       slen = WPA_GET_LE16(pos);
+       pos += 2;
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: ANQP Query Response "
+               "length: %u", slen);
+       if (slen < 3 + 1) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Invalid ANQP Query Response length");
+               return;
+       }
+       if (pos + 4 > end)
+               return;
+
+       if (WPA_GET_BE24(pos) != OUI_WFA) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Unsupported ANQP OUI %06x", WPA_GET_BE24(pos));
+               return;
+       }
+       pos += 3;
+
+       if (*pos != P2P_OUI_TYPE) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Unsupported ANQP vendor type %u", *pos);
+               return;
+       }
+       pos++;
+
+       if (pos + 2 > end)
+               return;
+       p2p->sd_rx_update_indic = WPA_GET_LE16(pos);
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "P2P: Service Update Indicator: %u", p2p->sd_rx_update_indic);
+       pos += 2;
+
+skip_nqp_header:
+       if (wpabuf_resize(&p2p->sd_rx_resp, end - pos) < 0)
+               return;
+       wpabuf_put_data(p2p->sd_rx_resp, pos, end - pos);
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Current SD reassembly "
+               "buffer length: %u",
+               (unsigned int) wpabuf_len(p2p->sd_rx_resp));
+
+       if (more_frags) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: More fragments "
+                       "remains");
+               /* TODO: what would be a good size limit? */
+               if (wpabuf_len(p2p->sd_rx_resp) > 64000) {
+                       wpabuf_free(p2p->sd_rx_resp);
+                       p2p->sd_rx_resp = NULL;
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Too long "
+                               "SD response - drop it");
+                       return;
+               }
+               p2p_send_gas_comeback_req(p2p, sa, dialog_token, rx_freq);
+               return;
+       }
+
+       p2p->sd_peer->flags |= P2P_DEV_SD_INFO;
+       p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE;
+       p2p->sd_peer = NULL;
+
+       if (p2p->sd_query) {
+               if (!p2p->sd_query->for_all_peers) {
+                       struct p2p_sd_query *q;
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                               "P2P: Remove completed SD query %p",
+                               p2p->sd_query);
+                       q = p2p->sd_query;
+                       p2p_unlink_sd_query(p2p, p2p->sd_query);
+                       p2p_free_sd_query(q);
+               }
+               p2p->sd_query = NULL;
+       }
+
+       if (p2p->cfg->sd_response)
+               p2p->cfg->sd_response(p2p->cfg->cb_ctx, sa,
+                                     p2p->sd_rx_update_indic,
+                                     wpabuf_head(p2p->sd_rx_resp),
+                                     wpabuf_len(p2p->sd_rx_resp));
+       wpabuf_free(p2p->sd_rx_resp);
+       p2p->sd_rx_resp = NULL;
+
+       p2p_continue_find(p2p);
+}
+
+
+void * p2p_sd_request(struct p2p_data *p2p, const u8 *dst,
+                     const struct wpabuf *tlvs)
+{
+       struct p2p_sd_query *q;
+
+       q = os_zalloc(sizeof(*q));
+       if (q == NULL)
+               return NULL;
+
+       if (dst)
+               os_memcpy(q->peer, dst, ETH_ALEN);
+       else
+               q->for_all_peers = 1;
+
+       q->tlvs = wpabuf_dup(tlvs);
+       if (q->tlvs == NULL) {
+               p2p_free_sd_query(q);
+               return NULL;
+       }
+
+       q->next = p2p->sd_queries;
+       p2p->sd_queries = q;
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Added SD Query %p", q);
+
+       return q;
+}
+
+
+void p2p_sd_service_update(struct p2p_data *p2p)
+{
+       p2p->srv_update_indic++;
+}
+
+
+int p2p_sd_cancel_request(struct p2p_data *p2p, void *req)
+{
+       if (p2p_unlink_sd_query(p2p, req)) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Cancel pending SD query %p", req);
+               p2p_free_sd_query(req);
+               return 0;
+       }
+       return -1;
+}
diff --git a/src/p2p/p2p_utils.c b/src/p2p/p2p_utils.c
new file mode 100644 (file)
index 0000000..da4b6ed
--- /dev/null
@@ -0,0 +1,271 @@
+/*
+ * P2P - generic helper functions
+ * Copyright (c) 2009, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "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;
+}
+
+
+static int p2p_channel_to_freq_j4(int reg_class, int channel)
+{
+       /* Table J-4 in P802.11REVmb/D4.0 - Global operating classes */
+       /* TODO: more regulatory classes */
+       switch (reg_class) {
+       case 81:
+               /* channels 1..13 */
+               if (channel < 1 || channel > 13)
+                       return -1;
+               return 2407 + 5 * channel;
+       case 82:
+               /* channel 14 */
+               if (channel != 14)
+                       return -1;
+               return 2414 + 5 * channel;
+       case 83: /* channels 1..9; 40 MHz */
+       case 84: /* channels 5..13; 40 MHz */
+               if (channel < 1 || channel > 13)
+                       return -1;
+               return 2407 + 5 * channel;
+       case 115: /* channels 36,40,44,48; indoor only */
+       case 118: /* channels 52,56,60,64; dfs */
+               if (channel < 36 || channel > 64)
+                       return -1;
+               return 5000 + 5 * channel;
+       case 124: /* channels 149,153,157,161 */
+       case 125: /* channels 149,153,157,161,165,169 */
+               if (channel < 149 || channel > 161)
+                       return -1;
+               return 5000 + 5 * channel;
+       case 116: /* channels 36,44; 40 MHz; indoor only */
+       case 117: /* channels 40,48; 40 MHz; indoor only */
+       case 119: /* channels 52,60; 40 MHz; dfs */
+       case 120: /* channels 56,64; 40 MHz; dfs */
+               if (channel < 36 || channel > 64)
+                       return -1;
+               return 5000 + 5 * channel;
+       case 126: /* channels 149,157; 40 MHz */
+       case 127: /* channels 153,161; 40 MHz */
+               if (channel < 149 || channel > 161)
+                       return -1;
+               return 5000 + 5 * channel;
+       }
+       return -1;
+}
+
+
+/**
+ * p2p_channel_to_freq - Convert channel info to frequency
+ * @country: Country code
+ * @reg_class: Regulatory class
+ * @channel: Channel number
+ * Returns: Frequency in MHz or -1 if the specified channel is unknown
+ */
+int p2p_channel_to_freq(const char *country, int reg_class, int channel)
+{
+       if (country[2] == 0x04)
+               return p2p_channel_to_freq_j4(reg_class, channel);
+
+       /* These are mainly for backwards compatibility; to be removed */
+       switch (reg_class) {
+       case 1: /* US/1, EU/1, JP/1 = 5 GHz, channels 36,40,44,48 */
+               if (channel < 36 || channel > 48)
+                       return -1;
+               return 5000 + 5 * channel;
+       case 3: /* US/3 = 5 GHz, channels 149,153,157,161 */
+       case 5: /* US/5 = 5 GHz, channels 149,153,157,161 */
+               if (channel < 149 || channel > 161)
+                       return -1;
+               return 5000 + 5 * channel;
+       case 4: /* EU/4 = 2.407 GHz, channels 1..13 */
+       case 12: /* US/12 = 2.407 GHz, channels 1..11 */
+       case 30: /* JP/30 = 2.407 GHz, channels 1..13 */
+               if (channel < 1 || channel > 13)
+                       return -1;
+               return 2407 + 5 * channel;
+       case 31: /* JP/31 = 2.414 GHz, channel 14 */
+               if (channel != 14)
+                       return -1;
+               return 2414 + 5 * channel;
+       }
+
+       return -1;
+}
+
+
+/**
+ * p2p_freq_to_channel - Convert frequency into channel info
+ * @country: Country code
+ * @reg_class: Buffer for returning regulatory class
+ * @channel: Buffer for returning channel number
+ * Returns: 0 on success, -1 if the specified frequency is unknown
+ */
+int p2p_freq_to_channel(const char *country, unsigned int freq, u8 *reg_class,
+                       u8 *channel)
+{
+       /* TODO: more operating classes */
+       if (freq >= 2412 && freq <= 2472) {
+               *reg_class = 81; /* 2.407 GHz, channels 1..13 */
+               *channel = (freq - 2407) / 5;
+               return 0;
+       }
+
+       if (freq == 2484) {
+               *reg_class = 82; /* channel 14 */
+               *channel = 14;
+               return 0;
+       }
+
+       if (freq >= 5180 && freq <= 5240) {
+               *reg_class = 115; /* 5 GHz, channels 36..48 */
+               *channel = (freq - 5000) / 5;
+               return 0;
+       }
+
+       if (freq >= 5745 && freq <= 5805) {
+               *reg_class = 124; /* 5 GHz, channels 149..161 */
+               *channel = (freq - 5000) / 5;
+               return 0;
+       }
+
+       return -1;
+}
+
+
+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;
+                       }
+               }
+       }
+}
+
+
+/**
+ * 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_supported_freq(struct p2p_data *p2p, unsigned int freq)
+{
+       u8 op_reg_class, op_channel;
+       if (p2p_freq_to_channel(p2p->cfg->country, freq,
+                               &op_reg_class, &op_channel) < 0)
+               return 0;
+       return p2p_channels_includes(&p2p->cfg->channels, op_reg_class,
+                                    op_channel);
+}
index 70754ef..fb03a25 100644 (file)
@@ -1090,8 +1090,7 @@ radius_msg_add_attr_user_password(struct radius_msg *msg,
                                  const u8 *secret, size_t secret_len)
 {
        u8 buf[128];
-       int padlen, i;
-       size_t buf_len, pos;
+       size_t padlen, i, buf_len, pos;
        const u8 *addr[2];
        size_t len[2];
        u8 hash[16];
@@ -1103,7 +1102,7 @@ radius_msg_add_attr_user_password(struct radius_msg *msg,
        buf_len = data_len;
 
        padlen = data_len % 16;
-       if (padlen) {
+       if (padlen && data_len < sizeof(buf)) {
                padlen = 16 - padlen;
                os_memset(buf + data_len, 0, padlen);
                buf_len += padlen;
index 171af29..9ec95a2 100644 (file)
@@ -684,7 +684,7 @@ int radius_client_send(struct radius_client_data *radius,
        radius_client_list_add(radius, msg, msg_type, shared_secret,
                               shared_secret_len, addr);
 
-       return res;
+       return 0;
 }
 
 
@@ -1489,3 +1489,11 @@ int radius_client_get_mib(struct radius_client_data *radius, char *buf,
 
        return count;
 }
+
+
+void radius_client_reconfig(struct radius_client_data *radius,
+                           struct hostapd_radius_servers *conf)
+{
+       if (radius)
+               radius->conf = conf;
+}
index 644ea23..18e7290 100644 (file)
@@ -259,5 +259,7 @@ void radius_client_flush_auth(struct radius_client_data *radius,
                              const u8 *addr);
 int radius_client_get_mib(struct radius_client_data *radius, char *buf,
                          size_t buflen);
+void radius_client_reconfig(struct radius_client_data *radius,
+                           struct hostapd_radius_servers *conf);
 
 #endif /* RADIUS_CLIENT_H */
index f8780a6..6f1c3a5 100644 (file)
@@ -222,6 +222,13 @@ struct radius_server_data {
        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;
+
+       /**
         * wps - Wi-Fi Protected Setup context
         *
         * If WPS is used with an external RADIUS server (which is quite
@@ -505,6 +512,7 @@ radius_server_get_new_session(struct radius_server_data *data,
        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;
        sess->eap = eap_server_sm_init(sess, &radius_server_eapol_cb,
                                       &eap_conf);
        if (sess->eap == NULL) {
@@ -1259,6 +1267,7 @@ radius_server_init(struct radius_server_conf *conf)
        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;
        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) {
index f9c951d..126e314 100644 (file)
@@ -143,6 +143,13 @@ struct radius_server_conf {
        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;
+
+       /**
         * wps - Wi-Fi Protected Setup context
         *
         * If WPS is used with an external RADIUS server (which is quite
index 9d60d4a..2b3332e 100644 (file)
@@ -20,6 +20,7 @@
 #include "eloop.h"
 #include "crypto/sha1.h"
 #include "crypto/sha256.h"
+#include "crypto/random.h"
 #include "common/ieee802_11_defs.h"
 #include "wpa.h"
 #include "wpa_i.h"
@@ -254,7 +255,7 @@ static int wpa_supplicant_process_smk_m2(
                peerkey->use_sha256 = 1;
 #endif /* CONFIG_IEEE80211W */
 
-       if (os_get_random(peerkey->pnonce, WPA_NONCE_LEN)) {
+       if (random_get_bytes(peerkey->pnonce, WPA_NONCE_LEN)) {
                wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
                        "WPA: Failed to get random data for PNonce");
                wpa_supplicant_peerkey_free(sm, peerkey);
@@ -370,7 +371,7 @@ static void wpa_supplicant_send_stk_1_of_4(struct wpa_sm *sm,
        wpa_add_kde((u8 *) (msg + 1), RSN_KEY_DATA_PMKID,
                    peerkey->smkid, PMKID_LEN);
 
-       if (os_get_random(peerkey->inonce, WPA_NONCE_LEN)) {
+       if (random_get_bytes(peerkey->inonce, WPA_NONCE_LEN)) {
                wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
                        "RSN: Failed to get random data for INonce (STK)");
                os_free(mbuf);
@@ -697,7 +698,7 @@ static void wpa_supplicant_process_stk_1_of_4(struct wpa_sm *sm,
                return;
        }
 
-       if (os_get_random(peerkey->pnonce, WPA_NONCE_LEN)) {
+       if (random_get_bytes(peerkey->pnonce, WPA_NONCE_LEN)) {
                wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
                        "RSN: Failed to get random data for PNonce");
                return;
@@ -1097,7 +1098,7 @@ int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer)
                  WPA_REPLAY_COUNTER_LEN);
        inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN);
 
-       if (os_get_random(peerkey->inonce, WPA_NONCE_LEN)) {
+       if (random_get_bytes(peerkey->inonce, WPA_NONCE_LEN)) {
                wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
                        "WPA: Failed to get random data for INonce");
                os_free(rbuf);
@@ -1140,6 +1141,7 @@ void peerkey_deinit(struct wpa_sm *sm)
                peerkey = peerkey->next;
                os_free(prev);
        }
+       sm->peerkey = NULL;
 }
 
 
index cac8c83..3877efb 100644 (file)
@@ -49,6 +49,7 @@ static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
                                   struct rsn_pmksa_cache_entry *entry,
                                   int replace)
 {
+       wpa_sm_remove_pmkid(pmksa->sm, entry->aa, entry->pmkid);
        pmksa->pmksa_count--;
        pmksa->free_cb(entry, pmksa->ctx, replace);
        _pmksa_cache_free_entry(entry);
@@ -185,6 +186,14 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
                        wpa_printf(MSG_DEBUG, "RSN: Replace PMKSA entry for "
                                   "the current AP");
                        pmksa_cache_free_entry(pmksa, pos, 1);
+
+                       /*
+                        * If OKC is used, there may be other PMKSA cache
+                        * entries based on the same PMK. These needs to be
+                        * flushed so that a new entry can be created based on
+                        * the new PMK.
+                        */
+                       pmksa_cache_flush(pmksa, network_ctx);
                        break;
                }
                prev = pos;
@@ -198,7 +207,6 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
                wpa_printf(MSG_DEBUG, "RSN: removed the oldest PMKSA cache "
                           "entry (for " MACSTR ") to make room for new one",
                           MAC2STR(pos->aa));
-               wpa_sm_remove_pmkid(pmksa->sm, pos->aa, pos->pmkid);
                pmksa_cache_free_entry(pmksa, pos, 0);
        }
 
@@ -229,6 +237,39 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
 
 
 /**
+ * pmksa_cache_flush - Flush PMKSA cache entries for a specific network
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
+ * @network_ctx: Network configuration context or %NULL to flush all entries
+ */
+void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx)
+{
+       struct rsn_pmksa_cache_entry *entry, *prev = NULL, *tmp;
+       int removed = 0;
+
+       entry = pmksa->pmksa;
+       while (entry) {
+               if (entry->network_ctx == network_ctx || network_ctx == NULL) {
+                       wpa_printf(MSG_DEBUG, "RSN: Flush PMKSA cache entry "
+                                  "for " MACSTR, MAC2STR(entry->aa));
+                       if (prev)
+                               prev->next = entry->next;
+                       else
+                               pmksa->pmksa = entry->next;
+                       tmp = entry;
+                       entry = entry->next;
+                       pmksa_cache_free_entry(pmksa, tmp, 0);
+                       removed++;
+               } else {
+                       prev = entry;
+                       entry = entry->next;
+               }
+       }
+       if (removed)
+               pmksa_cache_set_expiration(pmksa);
+}
+
+
+/**
  * pmksa_cache_deinit - Free all entries in PMKSA cache
  * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
  */
@@ -273,22 +314,6 @@ struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa,
 }
 
 
-/**
- * pmksa_cache_notify_reconfig - Reconfiguration notification for PMKSA cache
- * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
- *
- * Clear references to old data structures when wpa_supplicant is reconfigured.
- */
-void pmksa_cache_notify_reconfig(struct rsn_pmksa_cache *pmksa)
-{
-       struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
-       while (entry) {
-               entry->network_ctx = NULL;
-               entry = entry->next;
-       }
-}
-
-
 static struct rsn_pmksa_cache_entry *
 pmksa_cache_clone_entry(struct rsn_pmksa_cache *pmksa,
                        const struct rsn_pmksa_cache_entry *old_entry,
@@ -327,6 +352,7 @@ pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx,
 {
        struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
 
+       wpa_printf(MSG_DEBUG, "RSN: Consider " MACSTR " for OKC", MAC2STR(aa));
        if (network_ctx == NULL)
                return NULL;
        while (entry) {
index a1447e5..840827d 100644 (file)
@@ -57,7 +57,6 @@ int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len);
 struct rsn_pmksa_cache_entry *
 pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
                const u8 *aa, const u8 *spa, void *network_ctx, int akmp);
-void pmksa_cache_notify_reconfig(struct rsn_pmksa_cache *pmksa);
 struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm);
 void pmksa_cache_clear_current(struct wpa_sm *sm);
 int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
@@ -66,6 +65,7 @@ int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
 struct rsn_pmksa_cache_entry *
 pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa,
                              void *network_ctx, const u8 *aa);
+void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx);
 
 #else /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */
 
@@ -106,10 +106,6 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
        return NULL;
 }
 
-static inline void pmksa_cache_notify_reconfig(struct rsn_pmksa_cache *pmksa)
-{
-}
-
 static inline void pmksa_cache_clear_current(struct wpa_sm *sm)
 {
 }
@@ -122,6 +118,11 @@ static inline int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
        return -1;
 }
 
+static inline void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa,
+                                    void *network_ctx)
+{
+}
+
 #endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */
 
 #endif /* PMKSA_CACHE_H */
diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c
new file mode 100644 (file)
index 0000000..27090e3
--- /dev/null
@@ -0,0 +1,2338 @@
+/*
+ * wpa_supplicant - TDLS
+ * Copyright (c) 2010-2011, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/os.h"
+#include "common/ieee802_11_defs.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)
+unsigned int tdls_testing = 0;
+#endif /* CONFIG_TDLS_TESTING */
+
+#define TPK_LIFETIME 43200 /* 12 hours */
+#define TPK_RETRY_COUNT 3
+#define TPK_TIMEOUT 5000 /* 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);
+
+
+#define TDLS_MAX_IE_LEN 80
+#define IEEE80211_MAX_SUPP_RATES 32
+
+struct wpa_tdls_peer {
+       struct wpa_tdls_peer *next;
+       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;
+
+       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;
+               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;
+};
+
+
+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, const u8 *buf, size_t len)
+{
+       return wpa_sm_send_tdls_mgmt(sm, dst, action_code, dialog_token,
+                                    status_code, buf, len);
+}
+
+
+static int wpa_tdls_tpk_send(struct wpa_sm *sm, const u8 *dest, u8 action_code,
+                            u8 dialog_token, u16 status_code,
+                            const u8 *msg, size_t msg_len)
+{
+       struct wpa_tdls_peer *peer;
+
+       wpa_printf(MSG_DEBUG, "TDLS: TPK send dest=" MACSTR " action_code=%u "
+                  "dialog_token=%u status_code=%u msg_len=%u",
+                  MAC2STR(dest), action_code, dialog_token, status_code,
+                  (unsigned int) msg_len);
+
+       if (wpa_tdls_send_tpk_msg(sm, dest, action_code, dialog_token,
+                                 status_code, 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);
+
+       peer->sm_tmr.count = TPK_RETRY_COUNT;
+       peer->sm_tmr.timer = TPK_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.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, 0,
+                              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 free_peer)
+{
+       int ret;
+
+       if (sm->tdls_external_setup) {
+               ret = wpa_tdls_send_teardown(sm, peer->addr, reason_code);
+
+               /* disable the link after teardown was sent */
+               wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
+       } else {
+               ret = wpa_sm_tdls_oper(sm, TDLS_TEARDOWN, peer->addr);
+       }
+
+       if (sm->tdls_external_setup || free_peer)
+               wpa_tdls_peer_free(sm, peer);
+
+       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--;
+               peer->sm_tmr.timer = TPK_TIMEOUT;
+
+               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.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, 0,
+                                      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, 1);
+       }
+}
+
+
+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(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(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, 1);
+       }
+}
+
+
+static void wpa_tdls_peer_free(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->initiator = 0;
+       os_free(peer->sm_tmr.buf);
+       peer->sm_tmr.buf = NULL;
+       peer->rsnie_i_len = peer->rsnie_p_len = 0;
+       peer->cipher = 0;
+       peer->tpk_set = peer->tpk_success = 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_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);
+       }
+}
+
+
+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;
+       }
+
+       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,
+                         WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED, rbuf,
+                         pos - rbuf);
+       os_free(rbuf);
+
+       /* clear the Peerkey statemachine */
+       wpa_tdls_peer_free(sm, peer);
+
+       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, 0);
+}
+
+
+void wpa_tdls_disable_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) {
+               wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, addr);
+               wpa_tdls_peer_free(sm, peer);
+       }
+}
+
+
+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 */
+       if (wpa_supplicant_parse_ies((const u8 *) pos, ielen, &kde) < 0) {
+               wpa_printf(MSG_INFO, "TDLS: Failed to parse IEs in Teardown");
+               return -1;
+       }
+
+       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_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr);
+
+       /* clear the Peerkey statemachine */
+       wpa_tdls_peer_free(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
+ * @status     - status code mentioning reason
+ */
+
+static int wpa_tdls_send_error(struct wpa_sm *sm, const u8 *dst,
+                              u8 tdls_action, u8 dialog_token, u16 status)
+{
+       wpa_printf(MSG_DEBUG, "TDLS: Sending error to " MACSTR
+                  " (action=%u status=%u)",
+                  MAC2STR(dst), tdls_action, status);
+       return wpa_tdls_tpk_send(sm, dst, tdls_action, dialog_token, status,
+                                NULL, 0);
+}
+
+
+static struct wpa_tdls_peer *
+wpa_tdls_add_peer(struct wpa_sm *sm, const u8 *addr)
+{
+       struct wpa_tdls_peer *peer;
+
+       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;
+
+       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));
+
+       wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_SETUP_REQUEST, 1, 0,
+                         rbuf, pos - rbuf);
+       os_free(rbuf);
+
+       return 0;
+}
+
+
+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;
+
+       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);
+
+skip_ies:
+       wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, dtoken, 0,
+                         rbuf, pos - rbuf);
+       os_free(rbuf);
+
+       return 0;
+}
+
+
+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;
+
+       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);
+
+skip_ies:
+       wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, dtoken, 0,
+                         rbuf, pos - rbuf);
+       os_free(rbuf);
+
+       return 0;
+}
+
+
+static int wpa_tdls_send_discovery_response(struct wpa_sm *sm,
+                                           struct wpa_tdls_peer *peer,
+                                           u8 dialog_token)
+{
+       wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Discovery Response "
+                  "(peer " MACSTR ")", MAC2STR(peer->addr));
+
+       return wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_DISCOVERY_RESPONSE,
+                                dialog_token, 0, NULL, 0);
+}
+
+
+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)];
+
+       if (wpa_supplicant_parse_ies(buf + sizeof(struct wpa_tdls_frame) + 1,
+                                    len - (sizeof(struct wpa_tdls_frame) + 1),
+                                    &kde) < 0)
+               return -1;
+
+       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);
+       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, 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 = kde->supp_rates_len - 2;
+       if (peer->supp_rates_len > IEEE80211_MAX_SUPP_RATES)
+               peer->supp_rates_len = IEEE80211_MAX_SUPP_RATES;
+       os_memcpy(peer->supp_rates, kde->supp_rates + 2, peer->supp_rates_len);
+
+       if (kde->ext_supp_rates) {
+               int clen = kde->ext_supp_rates_len - 2;
+               if (peer->supp_rates_len + clen > IEEE80211_MAX_SUPP_RATES)
+                       clen = IEEE80211_MAX_SUPP_RATES - peer->supp_rates_len;
+               os_memcpy(peer->supp_rates + peer->supp_rates_len,
+                         kde->ext_supp_rates + 2, clen);
+               peer->supp_rates_len += clen;
+       }
+
+       return 0;
+}
+
+
+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);
+
+       for (peer = sm->tdls; peer; peer = peer->next) {
+               if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0) {
+                       existing_peer = 1;
+                       break;
+               }
+       }
+
+       if (peer == NULL) {
+               peer = wpa_tdls_add_peer(sm, src_addr);
+               if (peer == NULL)
+                       goto error;
+       }
+
+       /* capability information */
+       peer->capability = WPA_GET_LE16(cpos);
+       cpos += 2;
+
+       ielen = len - (cpos - buf); /* start of IE in buf */
+       if (wpa_supplicant_parse_ies(cpos, ielen, &kde) < 0) {
+               wpa_printf(MSG_INFO, "TDLS: Failed to parse IEs in TPK M1");
+               goto error;
+       }
+
+       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_NOT_IN_SAME_BSS;
+               goto error;
+       }
+
+       wpa_printf(MSG_DEBUG, "TDLS: TPK M1 - TPK initiator " MACSTR,
+                  MAC2STR(src_addr));
+
+       if (copy_supp_rates(&kde, peer) < 0)
+               goto error;
+
+#ifdef CONFIG_TDLS_TESTING
+       if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT) {
+               for (peer = sm->tdls; peer; peer = peer->next) {
+                       if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0)
+                               break;
+               }
+               if (peer == NULL) {
+                       peer = wpa_tdls_add_peer(sm, src_addr);
+                       if (peer == NULL)
+                               goto error;
+               }
+               wpa_printf(MSG_DEBUG, "TDLS: Testing concurrent initiation of "
+                          "TDLS setup - send own request");
+               peer->initiator = 1;
+               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:
+       /* 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");
+#if 0
+                       /* TODO: Disabling the link would be more proper
+                        * operation here, but it seems to trigger a race with
+                        * some drivers handling the new request frame. */
+                       wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr);
+#else
+                       if (sm->tdls_external_setup)
+                               wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK,
+                                                src_addr);
+                       else
+                               wpa_tdls_del_key(sm, peer);
+#endif
+                       wpa_tdls_peer_free(sm, peer);
+               }
+
+               /*
+                * An entry is already present, so check if we already sent a
+                * TDLS Setup Request. If so, compare MAC addresses and let the
+                * STA with the lower MAC address continue as the initiator.
+                * The other negotiation is terminated.
+                */
+               if (peer->initiator) {
+                       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_tdls_peer_free(sm, peer);
+                       }
+               }
+       }
+
+#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->inonce, ftie->Snonce, WPA_NONCE_LEN);
+       os_memcpy(peer->rsnie_i, kde.rsn_ie, kde.rsn_ie_len);
+       peer->rsnie_i_len = kde.rsn_ie_len;
+       peer->cipher = cipher;
+
+       if (os_get_random(peer->rnonce, WPA_NONCE_LEN)) {
+               wpa_msg(sm->ctx->ctx, MSG_WARNING,
+                       "TDLS: Failed to get random data for responder nonce");
+               wpa_tdls_peer_free(sm, peer);
+               goto error;
+       }
+
+#if 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:
+       /* add the peer to the driver as a "setup in progress" peer */
+       wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, NULL, 0);
+
+       wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Response / TPK M2");
+       if (wpa_tdls_send_tpk_m2(sm, src_addr, dtoken, lnkid, peer) < 0) {
+               wpa_tdls_disable_link(sm, peer->addr);
+               goto error;
+       }
+
+       return 0;
+
+error:
+       wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, dtoken,
+                           status);
+       return -1;
+}
+
+
+static void wpa_tdls_enable_link(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
+{
+       peer->tpk_success = 1;
+       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 */
+       }
+
+       /* add supported rates and capabilities to the TDLS peer */
+       wpa_sm_tdls_peer_addset(sm, peer->addr, 0, peer->capability,
+                               peer->supp_rates, peer->supp_rates_len);
+
+       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;
+
+       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;
+       }
+       wpa_tdls_tpk_retry_timeout_cancel(sm, peer, WLAN_TDLS_SETUP_REQUEST);
+
+       if (len < 3 + 2 + 1)
+               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);
+               if (sm->tdls_external_setup)
+                       wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr);
+               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)
+               return -1;
+
+       /* capability information */
+       peer->capability = WPA_GET_LE16(pos);
+       pos += 2;
+
+       ielen = len - (pos - buf); /* start of IE in buf */
+       if (wpa_supplicant_parse_ies(pos, ielen, &kde) < 0) {
+               wpa_printf(MSG_INFO, "TDLS: Failed to parse IEs in TPK M2");
+               goto error;
+       }
+
+#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 (!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);
+
+       /*
+        * 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_peer_free(sm, peer);
+               if (sm->tdls_external_setup)
+                       wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr);
+               return -1;
+       }
+
+       wpa_tdls_set_key(sm, peer);
+
+skip_rsn:
+       peer->dtoken = dtoken;
+
+       wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Confirm / "
+                  "TPK Handshake Message 3");
+       wpa_tdls_send_tpk_m3(sm, src_addr, dtoken, lnkid, peer);
+
+       wpa_tdls_enable_link(sm, peer);
+
+       return 0;
+
+error:
+       wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, dtoken,
+                           status);
+       if (sm->tdls_external_setup)
+               wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr);
+       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;
+
+       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)
+               return -1;
+       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);
+               if (sm->tdls_external_setup)
+                       wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr);
+               return -1;
+       }
+       pos += 2 /* status code */ + 1 /* dialog token */;
+
+       ielen = len - (pos - buf); /* start of IE in buf */
+       if (wpa_supplicant_parse_ies((const u8 *) pos, ielen, &kde) < 0) {
+               wpa_printf(MSG_INFO, "TDLS: Failed to parse KDEs in TPK M3");
+               return -1;
+       }
+
+       if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
+               wpa_printf(MSG_INFO, "TDLS: No Link Identifier IE in TPK M3");
+               return -1;
+       }
+       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");
+               return -1;
+       }
+
+       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");
+               return -1;
+       }
+       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");
+               return -1;
+       }
+       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");
+               return -1;
+       }
+
+       if (!os_memcmp(peer->rnonce, ftie->Anonce, WPA_NONCE_LEN) == 0) {
+               wpa_printf(MSG_INFO, "TDLS: FTIE ANonce in TPK M3 does "
+                          "not match with FTIE ANonce used in TPK M2");
+               return -1;
+       }
+
+       if (!os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) == 0) {
+               wpa_printf(MSG_INFO, "TDLS: FTIE SNonce in TPK M3 does not "
+                          "match with FTIE SNonce used in TPK M1");
+               return -1;
+       }
+
+       if (kde.key_lifetime == NULL) {
+               wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M3");
+               return -1;
+       }
+       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);
+               if (sm->tdls_external_setup)
+                       wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr);
+               return -1;
+       }
+
+       if (wpa_supplicant_verify_tdls_mic(3, peer, (u8 *) lnkid,
+                                          (u8 *) timeoutie, ftie) < 0) {
+               wpa_tdls_del_key(sm, peer);
+               wpa_tdls_peer_free(sm, peer);
+               return -1;
+       }
+
+       if (wpa_tdls_set_key(sm, peer) < 0)
+               return -1;
+
+skip_rsn:
+       wpa_tdls_enable_link(sm, peer);
+
+       return 0;
+}
+
+
+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;
+       }
+
+       /* Find existing entry and if found, use that instead of adding
+        * a new one */
+       for (peer = sm->tdls; peer; peer = peer->next) {
+               if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
+                       break;
+       }
+
+       if (peer == NULL) {
+               peer = wpa_tdls_add_peer(sm, addr);
+               if (peer == NULL)
+                       return -1;
+       }
+
+       peer->initiator = 1;
+
+       /* add the peer to the driver as a "setup in progress" peer */
+       wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, NULL, 0);
+
+       if (wpa_tdls_send_tpk_m1(sm, peer) < 0) {
+               wpa_tdls_disable_link(sm, peer->addr);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+int wpa_tdls_reneg(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 == NULL || !peer->tpk_success)
+               return -1;
+
+       if (sm->tdls_external_setup) {
+               /*
+                * Disable previous link to allow renegotiation to be completed
+                * on AP path.
+                */
+               wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
+       }
+
+       return wpa_tdls_start(sm, addr);
+}
+
+
+/**
+ * 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->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) < 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");
+
+       return 0;
+}
+
+
+static void wpa_tdls_remove_peers(struct wpa_sm *sm)
+{
+       struct wpa_tdls_peer *peer, *tmp;
+
+       peer = sm->tdls;
+       sm->tdls = NULL;
+
+       while (peer) {
+               int res;
+               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);
+               os_free(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_prohibited(const u8 *ies, size_t len)
+{
+       struct wpa_eapol_ie_parse elems;
+
+       if (ies == NULL)
+               return 0;
+
+       if (wpa_supplicant_parse_ies(ies, len, &elems) < 0)
+               return 0;
+
+       if (elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5)
+               return 0;
+
+        /* bit 38 - TDLS Prohibited */
+       return !!(elems.ext_capab[2 + 4] & 0x40);
+}
+
+
+void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len)
+{
+       sm->tdls_prohibited = wpa_tdls_prohibited(ies, len);
+       wpa_printf(MSG_DEBUG, "TDLS: TDLS is %s in the target BSS",
+                  sm->tdls_prohibited ? "prohibited" : "allowed");
+}
+
+
+void wpa_tdls_assoc_resp_ies(struct wpa_sm *sm, const u8 *ies, size_t len)
+{
+       if (!sm->tdls_prohibited && wpa_tdls_prohibited(ies, len)) {
+               wpa_printf(MSG_DEBUG, "TDLS: TDLS prohibited based on "
+                          "(Re)Association Response IEs");
+               sm->tdls_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;
+}
index 9439f97..f35f9ee 100644 (file)
@@ -17,6 +17,7 @@
 #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"
@@ -49,21 +50,26 @@ void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck,
                 * BSSID from the driver.
                 */
                if (wpa_sm_get_bssid(sm, sm->bssid) < 0) {
-                       wpa_printf(MSG_DEBUG, "WPA: Failed to read BSSID for "
-                                  "EAPOL-Key destination address");
+                       wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+                               "WPA: Failed to read BSSID for "
+                               "EAPOL-Key destination address");
                } else {
                        dest = sm->bssid;
-                       wpa_printf(MSG_DEBUG, "WPA: Use BSSID (" MACSTR
-                                  ") as the destination for EAPOL-Key",
-                                  MAC2STR(dest));
+                       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, ver, msg, msg_len, key_mic)) {
-               wpa_printf(MSG_ERROR, "WPA: Failed to generate EAPOL-Key "
-                          "version %d MIC", ver);
+               wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
+                       "WPA: Failed to generate EAPOL-Key "
+                       "version %d MIC", ver);
                goto out;
        }
+       wpa_hexdump_key(MSG_DEBUG, "WPA: KCK", kck, 16);
+       wpa_hexdump(MSG_DEBUG, "WPA: Derived Key MIC", key_mic, 16);
        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);
@@ -97,8 +103,8 @@ void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise)
                ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
 
        if (wpa_sm_get_bssid(sm, bssid) < 0) {
-               wpa_printf(MSG_WARNING, "Failed to read BSSID for EAPOL-Key "
-                          "request");
+               wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+                       "Failed to read BSSID for EAPOL-Key request");
                return;
        }
 
@@ -124,9 +130,10 @@ void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise)
 
        WPA_PUT_BE16(reply->key_data_length, 0);
 
-       wpa_printf(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_msg(sm->ctx->msg_ctx, MSG_INFO,
+               "WPA: Sending EAPOL-Key Request (error=%d "
+               "pairwise=%d ptk_set=%d len=%lu)",
+               error, pairwise, sm->ptk_set, (unsigned long) rlen);
        wpa_eapol_key_send(sm, sm->ptk.kck, ver, bssid, ETH_P_EAPOL,
                           rbuf, rlen, key_info & WPA_KEY_INFO_MIC ?
                           reply->key_mic : NULL);
@@ -146,10 +153,11 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm,
                 * matching PMKSA cache entry here. */
                sm->cur_pmksa = pmksa_cache_get(sm->pmksa, src_addr, pmkid);
                if (sm->cur_pmksa) {
-                       wpa_printf(MSG_DEBUG, "RSN: found matching PMKID from "
-                                  "PMKSA cache");
+                       wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+                               "RSN: found matching PMKID from PMKSA cache");
                } else {
-                       wpa_printf(MSG_DEBUG, "RSN: no matching PMKID found");
+                       wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+                               "RSN: no matching PMKID found");
                        abort_cached = 1;
                }
        }
@@ -190,26 +198,28 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm,
                        wpa_hexdump_key(MSG_DEBUG, "WPA: PMK from EAPOL state "
                                        "machines", sm->pmk, pmk_len);
                        sm->pmk_len = pmk_len;
-                       if (sm->proto == WPA_PROTO_RSN) {
+                       if (sm->proto == WPA_PROTO_RSN &&
+                           !wpa_key_mgmt_ft(sm->key_mgmt)) {
                                pmksa_cache_add(sm->pmksa, sm->pmk, pmk_len,
                                                src_addr, sm->own_addr,
                                                sm->network_ctx, sm->key_mgmt);
                        }
                        if (!sm->cur_pmksa && pmkid &&
                            pmksa_cache_get(sm->pmksa, src_addr, pmkid)) {
-                               wpa_printf(MSG_DEBUG, "RSN: the new PMK "
-                                          "matches with the PMKID");
+                               wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+                                       "RSN: the new PMK matches with the "
+                                       "PMKID");
                                abort_cached = 0;
                        }
                } else {
                        wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
                                "WPA: Failed to get master session key from "
-                               "EAPOL state machines");
-                       wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
-                               "WPA: Key handshake aborted");
+                               "EAPOL state machines - key handshake "
+                               "aborted");
                        if (sm->cur_pmksa) {
-                               wpa_printf(MSG_DEBUG, "RSN: Cancelled PMKSA "
-                                          "caching attempt");
+                               wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+                                       "RSN: Cancelled PMKSA caching "
+                                       "attempt");
                                sm->cur_pmksa = NULL;
                                abort_cached = 1;
                        } else if (!abort_cached) {
@@ -218,13 +228,15 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm,
                }
        }
 
-       if (abort_cached && wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt)) {
+       if (abort_cached && wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) &&
+           !wpa_key_mgmt_ft(sm->key_mgmt)) {
                /* Send EAPOL-Start to trigger full EAP authentication. */
                u8 *buf;
                size_t buflen;
 
-               wpa_printf(MSG_DEBUG, "RSN: no PMKSA entry found - trigger "
-                          "full EAP authentication");
+               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) {
@@ -265,8 +277,8 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,
        u8 *rsn_ie_buf = NULL;
 
        if (wpa_ie == NULL) {
-               wpa_printf(MSG_WARNING, "WPA: No wpa_ie set - cannot "
-                          "generate msg 2/4");
+               wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: No wpa_ie set - "
+                       "cannot generate msg 2/4");
                return -1;
        }
 
@@ -321,6 +333,8 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,
                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);
 
        WPA_PUT_BE16(reply->key_data_length, wpa_ie_len);
        os_memcpy(reply + 1, wpa_ie, wpa_ie_len);
@@ -328,7 +342,7 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,
 
        os_memcpy(reply->key_nonce, nonce, WPA_NONCE_LEN);
 
-       wpa_printf(MSG_DEBUG, "WPA: Sending EAPOL-Key 2/4");
+       wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/4");
        wpa_eapol_key_send(sm, ptk->kck, ver, dst, ETH_P_EAPOL,
                           rbuf, rlen, reply->key_mic);
 
@@ -365,14 +379,14 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm,
        int res;
 
        if (wpa_sm_get_network_ctx(sm) == NULL) {
-               wpa_printf(MSG_WARNING, "WPA: No SSID info found (msg 1 of "
-                          "4).");
+               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_printf(MSG_DEBUG, "WPA: RX message 1 of 4-Way Handshake from "
-                  MACSTR " (ver=%d)", MAC2STR(src_addr), ver);
+       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));
 
@@ -382,7 +396,8 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm,
                const u8 *_buf = (const u8 *) (key + 1);
                size_t len = WPA_GET_BE16(key->key_data_length);
                wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", _buf, len);
-               wpa_supplicant_parse_ies(_buf, len, &ie);
+               if (wpa_supplicant_parse_ies(_buf, len, &ie) < 0)
+                       goto failed;
                if (ie.pmkid) {
                        wpa_hexdump(MSG_DEBUG, "RSN: PMKID from "
                                    "Authenticator", ie.pmkid, PMKID_LEN);
@@ -392,15 +407,15 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm,
 
        res = wpa_supplicant_get_pmk(sm, src_addr, ie.pmkid);
        if (res == -2) {
-               wpa_printf(MSG_DEBUG, "RSN: Do not reply to msg 1/4 - "
-                          "requesting full EAP authentication");
+               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 (os_get_random(sm->snonce, WPA_NONCE_LEN)) {
+               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;
@@ -462,15 +477,16 @@ static void wpa_supplicant_key_neg_complete(struct wpa_sm *sm,
                 * 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
-                * likelyhood of the first preauth EAPOL-Start frame getting to
+                * 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_printf(MSG_DEBUG, "RSN: Authenticator accepted "
-                          "opportunistic PMKSA entry - marking it valid");
+               wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+                       "RSN: Authenticator accepted "
+                       "opportunistic PMKSA entry - marking it valid");
                sm->cur_pmksa->opportunistic = 0;
        }
 
@@ -486,7 +502,7 @@ static void wpa_supplicant_key_neg_complete(struct wpa_sm *sm,
 static void wpa_sm_rekey_ptk(void *eloop_ctx, void *timeout_ctx)
 {
        struct wpa_sm *sm = eloop_ctx;
-       wpa_printf(MSG_DEBUG, "WPA: Request PTK rekeying");
+       wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Request PTK rekeying");
        wpa_sm_key_request(sm, 0, 1);
 }
 
@@ -499,7 +515,8 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm,
        const u8 *key_rsc;
        u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
 
-       wpa_printf(MSG_DEBUG, "WPA: Installing PTK to the driver.");
+       wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+               "WPA: Installing PTK to the driver");
 
        switch (sm->pairwise_cipher) {
        case WPA_CIPHER_CCMP:
@@ -513,12 +530,13 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm,
                rsclen = 6;
                break;
        case WPA_CIPHER_NONE:
-               wpa_printf(MSG_DEBUG, "WPA: Pairwise Cipher Suite: "
-                          "NONE - do not use pairwise keys");
+               wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Pairwise Cipher "
+                       "Suite: NONE - do not use pairwise keys");
                return 0;
        default:
-               wpa_printf(MSG_WARNING, "WPA: Unsupported pairwise cipher %d",
-                          sm->pairwise_cipher);
+               wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+                       "WPA: Unsupported pairwise cipher %d",
+                       sm->pairwise_cipher);
                return -1;
        }
 
@@ -531,9 +549,10 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm,
 
        if (wpa_sm_set_key(sm, alg, sm->bssid, 0, 1, key_rsc, rsclen,
                           (u8 *) sm->ptk.tk1, keylen) < 0) {
-               wpa_printf(MSG_WARNING, "WPA: Failed to set PTK to the "
-                          "driver (alg=%d keylen=%d bssid=" MACSTR ")",
-                          alg, keylen, MAC2STR(sm->bssid));
+               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;
        }
 
@@ -547,7 +566,8 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm,
 }
 
 
-static int wpa_supplicant_check_group_cipher(int group_cipher,
+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)
@@ -588,15 +608,16 @@ static int wpa_supplicant_check_group_cipher(int group_cipher,
                *alg = WPA_ALG_WEP;
                break;
        default:
-               wpa_printf(MSG_WARNING, "WPA: Unsupported Group Cipher %d",
-                          group_cipher);
+               wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+                       "WPA: Unsupported Group Cipher %d",
+                       group_cipher);
                return -1;
        }
 
        if (ret < 0 ) {
-               wpa_printf(MSG_WARNING, "WPA: Unsupported %s Group Cipher key "
-                          "length %d (%d).",
-                          wpa_cipher_txt(group_cipher), keylen, maxkeylen);
+               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 ret;
@@ -619,9 +640,9 @@ static int wpa_supplicant_install_gtk(struct wpa_sm *sm,
        u8 gtk_buf[32];
 
        wpa_hexdump_key(MSG_DEBUG, "WPA: Group Key", gd->gtk, gd->gtk_len);
-       wpa_printf(MSG_DEBUG, "WPA: Installing GTK to the driver "
-                  "(keyidx=%d tx=%d len=%d).", gd->keyidx, gd->tx,
-                  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 */
@@ -631,21 +652,21 @@ static int wpa_supplicant_install_gtk(struct wpa_sm *sm,
                _gtk = gtk_buf;
        }
        if (sm->pairwise_cipher == WPA_CIPHER_NONE) {
-               if (wpa_sm_set_key(sm, gd->alg,
-                                  (u8 *) "\xff\xff\xff\xff\xff\xff",
+               if (wpa_sm_set_key(sm, gd->alg, NULL,
                                   gd->keyidx, 1, key_rsc, gd->key_rsc_len,
                                   _gtk, gd->gtk_len) < 0) {
-                       wpa_printf(MSG_WARNING, "WPA: Failed to set "
-                                  "GTK to the driver (Group only).");
+                       wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+                               "WPA: Failed to set GTK to the driver "
+                               "(Group only)");
                        return -1;
                }
-       } else if (wpa_sm_set_key(sm, gd->alg,
-                                 (u8 *) "\xff\xff\xff\xff\xff\xff",
+       } 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_printf(MSG_WARNING, "WPA: Failed to set GTK to "
-                          "the driver (alg=%d keylen=%d keyidx=%d)",
-                          gd->alg, gd->gtk_len, gd->keyidx);
+               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);
                return -1;
        }
 
@@ -662,8 +683,9 @@ static int wpa_supplicant_gtk_tx_bit_workaround(const struct wpa_sm *sm,
                 * 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_printf(MSG_INFO, "WPA: Tx bit set for GTK, but pairwise "
-                          "keys are used - ignore Tx bit");
+               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;
@@ -702,11 +724,12 @@ static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm,
        os_memcpy(gd.gtk, gtk, gtk_len);
        gd.gtk_len = gtk_len;
 
-       if (wpa_supplicant_check_group_cipher(sm->group_cipher,
+       if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
                                              gtk_len, gtk_len,
                                              &gd.key_rsc_len, &gd.alg) ||
            wpa_supplicant_install_gtk(sm, &gd, key->key_rsc)) {
-               wpa_printf(MSG_DEBUG, "RSN: Failed to install GTK");
+               wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+                       "RSN: Failed to install GTK");
                return -1;
        }
 
@@ -733,22 +756,21 @@ static int ieee80211w_set_keys(struct wpa_sm *sm,
                        return -1;
                igtk = (const struct wpa_igtk_kde *) ie->igtk;
                keyidx = WPA_GET_LE16(igtk->keyid);
-               wpa_printf(MSG_DEBUG, "WPA: IGTK keyid %d "
-                          "pn %02x%02x%02x%02x%02x%02x",
-                          keyidx, MAC2STR(igtk->pn));
+               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, WPA_IGTK_LEN);
                if (keyidx > 4095) {
-                       wpa_printf(MSG_WARNING, "WPA: Invalid IGTK KeyID %d",
-                                  keyidx);
+                       wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+                               "WPA: Invalid IGTK KeyID %d", keyidx);
                        return -1;
                }
-               if (wpa_sm_set_key(sm, WPA_ALG_IGTK,
-                                  (u8 *) "\xff\xff\xff\xff\xff\xff",
+               if (wpa_sm_set_key(sm, WPA_ALG_IGTK, broadcast_ether_addr,
                                   keyidx, 0, igtk->pn, sizeof(igtk->pn),
                                   igtk->igtk, WPA_IGTK_LEN) < 0) {
-                       wpa_printf(MSG_WARNING, "WPA: Failed to configure IGTK"
-                                  " to the driver");
+                       wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+                               "WPA: Failed to configure IGTK to the driver");
                        return -1;
                }
        }
@@ -774,8 +796,8 @@ static void wpa_report_ie_mismatch(struct wpa_sm *sm,
        }
        if (wpa_ie) {
                if (!sm->ap_wpa_ie) {
-                       wpa_printf(MSG_INFO, "WPA: No WPA IE in "
-                                  "Beacon/ProbeResp");
+                       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);
@@ -787,8 +809,8 @@ static void wpa_report_ie_mismatch(struct wpa_sm *sm,
        }
        if (rsn_ie) {
                if (!sm->ap_rsn_ie) {
-                       wpa_printf(MSG_INFO, "WPA: No RSN IE in "
-                                  "Beacon/ProbeResp");
+                       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);
@@ -811,15 +833,15 @@ static int ft_validate_mdie(struct wpa_sm *sm,
        if (ie->mdie == NULL || ie->mdie_len < 2 + sizeof(*mdie) ||
            os_memcmp(mdie->mobility_domain, sm->mobility_domain,
                      MOBILITY_DOMAIN_ID_LEN) != 0) {
-               wpa_printf(MSG_DEBUG, "FT: MDIE in msg 3/4 did not "
-                          "match with the current mobility domain");
+               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_printf(MSG_DEBUG, "FT: MDIE mismatch");
+               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",
@@ -837,7 +859,8 @@ static int ft_validate_ftie(struct wpa_sm *sm,
                            const u8 *assoc_resp_ftie)
 {
        if (ie->ftie == NULL) {
-               wpa_printf(MSG_DEBUG, "FT: No FTIE in EAPOL-Key msg 3/4");
+               wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+                       "FT: No FTIE in EAPOL-Key msg 3/4");
                return -1;
        }
 
@@ -846,7 +869,7 @@ static int ft_validate_ftie(struct wpa_sm *sm,
 
        if (assoc_resp_ftie[1] != ie->ftie[1] ||
            os_memcmp(assoc_resp_ftie, ie->ftie, 2 + ie->ftie[1]) != 0) {
-               wpa_printf(MSG_DEBUG, "FT: FTIE mismatch");
+               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",
@@ -873,14 +896,15 @@ static int ft_validate_rsnie(struct wpa_sm *sm,
         */
        if (wpa_parse_wpa_ie_rsn(ie->rsn_ie, ie->rsn_ie_len, &rsn) < 0 ||
            rsn.num_pmkid != 1 || rsn.pmkid == NULL) {
-               wpa_printf(MSG_DEBUG, "FT: No PMKR1Name in "
-                          "FT 4-way handshake message 3/4");
+               wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: No PMKR1Name in "
+                       "FT 4-way handshake message 3/4");
                return -1;
        }
 
        if (os_memcmp(rsn.pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN) != 0) {
-               wpa_printf(MSG_DEBUG, "FT: PMKR1Name mismatch in "
-                          "FT 4-way handshake message 3/4");
+               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",
@@ -932,14 +956,17 @@ static int wpa_supplicant_validate_ie(struct wpa_sm *sm,
                                      struct wpa_eapol_ie_parse *ie)
 {
        if (sm->ap_wpa_ie == NULL && sm->ap_rsn_ie == NULL) {
-               wpa_printf(MSG_DEBUG, "WPA: No WPA/RSN IE for this AP known. "
-                          "Trying to get from scan results");
+               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_printf(MSG_WARNING, "WPA: Could not find AP from "
-                                  "the scan results");
+                       wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+                               "WPA: Could not find AP from "
+                               "the scan results");
                } else {
-                       wpa_printf(MSG_DEBUG, "WPA: Found the current AP from "
-                                  "updated scan results");
+                       wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG,
+                               "WPA: Found the current AP from "
+                               "updated scan results");
                }
        }
 
@@ -1034,7 +1061,7 @@ int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst,
        if (kde)
                os_memcpy(reply + 1, kde, kde_len);
 
-       wpa_printf(MSG_DEBUG, "WPA: Sending EAPOL-Key 4/4");
+       wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 4/4");
        wpa_eapol_key_send(sm, ptk->kck, ver, dst, ETH_P_EAPOL,
                           rbuf, rlen, reply->key_mic);
 
@@ -1051,29 +1078,32 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,
        struct wpa_eapol_ie_parse ie;
 
        wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE);
-       wpa_printf(MSG_DEBUG, "WPA: RX message 3 of 4-Way Handshake from "
-                  MACSTR " (ver=%d)", MAC2STR(sm->bssid), ver);
+       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);
 
        pos = (const u8 *) (key + 1);
        len = WPA_GET_BE16(key->key_data_length);
        wpa_hexdump(MSG_DEBUG, "WPA: IE KeyData", pos, len);
-       wpa_supplicant_parse_ies(pos, len, &ie);
+       if (wpa_supplicant_parse_ies(pos, len, &ie) < 0)
+               goto failed;
        if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
-               wpa_printf(MSG_WARNING, "WPA: GTK IE in unencrypted 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_printf(MSG_WARNING, "WPA: IGTK KDE in unencrypted key "
-                          "data");
+               wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+                       "WPA: IGTK KDE in unencrypted key data");
                goto failed;
        }
 
        if (ie.igtk && ie.igtk_len != sizeof(struct wpa_igtk_kde)) {
-               wpa_printf(MSG_WARNING, "WPA: Invalid IGTK KDE length %lu",
-                          (unsigned long) ie.igtk_len);
+               wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+                       "WPA: Invalid IGTK KDE length %lu",
+                       (unsigned long) ie.igtk_len);
                goto failed;
        }
 #endif /* CONFIG_IEEE80211W */
@@ -1082,9 +1112,10 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,
                goto failed;
 
        if (os_memcmp(sm->anonce, key->key_nonce, WPA_NONCE_LEN) != 0) {
-               wpa_printf(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));
+               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;
        }
 
@@ -1092,17 +1123,17 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,
        switch (sm->pairwise_cipher) {
        case WPA_CIPHER_CCMP:
                if (keylen != 16) {
-                       wpa_printf(MSG_WARNING, "WPA: Invalid CCMP key length "
-                                  "%d (src=" MACSTR ")",
-                                  keylen, MAC2STR(sm->bssid));
+                       wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+                               "WPA: Invalid CCMP key length %d (src=" MACSTR
+                               ")", keylen, MAC2STR(sm->bssid));
                        goto failed;
                }
                break;
        case WPA_CIPHER_TKIP:
                if (keylen != 32) {
-                       wpa_printf(MSG_WARNING, "WPA: Invalid TKIP key length "
-                                  "%d (src=" MACSTR ")",
-                                  keylen, MAC2STR(sm->bssid));
+                       wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+                               "WPA: Invalid TKIP key length %d (src=" MACSTR
+                               ")", keylen, MAC2STR(sm->bssid));
                        goto failed;
                }
                break;
@@ -1134,15 +1165,19 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,
        if (ie.gtk &&
            wpa_supplicant_pairwise_gtk(sm, key,
                                        ie.gtk, ie.gtk_len, key_info) < 0) {
-               wpa_printf(MSG_INFO, "RSN: Failed to configure GTK");
+               wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+                       "RSN: Failed to configure GTK");
                goto failed;
        }
 
        if (ieee80211w_set_keys(sm, &ie) < 0) {
-               wpa_printf(MSG_INFO, "RSN: Failed to configure IGTK");
+               wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+                       "RSN: Failed to configure IGTK");
                goto failed;
        }
 
+       wpa_sm_set_rekey_offload(sm);
+
        return;
 
 failed:
@@ -1160,18 +1195,21 @@ static int wpa_supplicant_process_1_of_2_rsn(struct wpa_sm *sm,
        struct wpa_eapol_ie_parse ie;
 
        wpa_hexdump(MSG_DEBUG, "RSN: msg 1/2 key data", keydata, keydatalen);
-       wpa_supplicant_parse_ies(keydata, keydatalen, &ie);
+       if (wpa_supplicant_parse_ies(keydata, keydatalen, &ie) < 0)
+               return -1;
        if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
-               wpa_printf(MSG_WARNING, "WPA: GTK IE in unencrypted key data");
+               wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+                       "WPA: GTK IE in unencrypted key data");
                return -1;
        }
        if (ie.gtk == NULL) {
-               wpa_printf(MSG_INFO, "WPA: No GTK IE in Group Key msg 1/2");
+               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->group_cipher,
+       if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
                                              gd->gtk_len, maxkeylen,
                                              &gd->key_rsc_len, &gd->alg))
                return -1;
@@ -1182,14 +1220,16 @@ static int wpa_supplicant_process_1_of_2_rsn(struct wpa_sm *sm,
        gd->tx = wpa_supplicant_gtk_tx_bit_workaround(sm,
                                                      !!(ie.gtk[0] & BIT(2)));
        if (ie.gtk_len - 2 > sizeof(gd->gtk)) {
-               wpa_printf(MSG_INFO, "RSN: Too long GTK in GTK IE "
-                          "(len=%lu)", (unsigned long) ie.gtk_len - 2);
+               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_printf(MSG_INFO, "RSN: Failed to configure IGTK");
+               wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+                       "RSN: Failed to configure IGTK");
 
        return 0;
 }
@@ -1207,22 +1247,23 @@ static int wpa_supplicant_process_1_of_2_wpa(struct wpa_sm *sm,
        gd->gtk_len = WPA_GET_BE16(key->key_length);
        maxkeylen = keydatalen;
        if (keydatalen > extra_len) {
-               wpa_printf(MSG_INFO, "WPA: Truncated EAPOL-Key packet:"
-                          " key_data_length=%lu > extra_len=%lu",
-                          (unsigned long) keydatalen,
-                          (unsigned long) extra_len);
+               wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+                       "WPA: Truncated EAPOL-Key packet: "
+                       "key_data_length=%lu > extra_len=%lu",
+                       (unsigned long) keydatalen, (unsigned long) extra_len);
                return -1;
        }
        if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
                if (maxkeylen < 8) {
-                       wpa_printf(MSG_INFO, "WPA: Too short maxkeylen (%lu)",
-                                  (unsigned long) maxkeylen);
+                       wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+                               "WPA: Too short maxkeylen (%lu)",
+                               (unsigned long) maxkeylen);
                        return -1;
                }
                maxkeylen -= 8;
        }
 
-       if (wpa_supplicant_check_group_cipher(sm->group_cipher,
+       if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
                                              gd->gtk_len, maxkeylen,
                                              &gd->key_rsc_len, &gd->alg))
                return -1;
@@ -1233,38 +1274,42 @@ static int wpa_supplicant_process_1_of_2_wpa(struct wpa_sm *sm,
                os_memcpy(ek, key->key_iv, 16);
                os_memcpy(ek + 16, sm->ptk.kek, 16);
                if (keydatalen > sizeof(gd->gtk)) {
-                       wpa_printf(MSG_WARNING, "WPA: RC4 key data "
-                                  "too long (%lu)",
-                                  (unsigned long) keydatalen);
+                       wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+                               "WPA: RC4 key data too long (%lu)",
+                               (unsigned long) keydatalen);
                        return -1;
                }
                os_memcpy(gd->gtk, key + 1, keydatalen);
                if (rc4_skip(ek, 32, 256, gd->gtk, keydatalen)) {
-                       wpa_printf(MSG_ERROR, "WPA: RC4 failed");
+                       wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
+                               "WPA: RC4 failed");
                        return -1;
                }
        } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
                if (keydatalen % 8) {
-                       wpa_printf(MSG_WARNING, "WPA: Unsupported AES-WRAP "
-                                  "len %lu", (unsigned long) keydatalen);
+                       wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+                               "WPA: Unsupported AES-WRAP len %lu",
+                               (unsigned long) keydatalen);
                        return -1;
                }
                if (maxkeylen > sizeof(gd->gtk)) {
-                       wpa_printf(MSG_WARNING, "WPA: AES-WRAP key data "
-                                  "too long (keydatalen=%lu maxkeylen=%lu)",
-                                  (unsigned long) keydatalen,
-                                  (unsigned long) maxkeylen);
+                       wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+                               "WPA: AES-WRAP key data "
+                               "too long (keydatalen=%lu maxkeylen=%lu)",
+                               (unsigned long) keydatalen,
+                               (unsigned long) maxkeylen);
                        return -1;
                }
                if (aes_unwrap(sm->ptk.kek, maxkeylen / 8,
                               (const u8 *) (key + 1), gd->gtk)) {
-                       wpa_printf(MSG_WARNING, "WPA: AES unwrap "
-                                  "failed - could not decrypt GTK");
+                       wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+                               "WPA: AES unwrap failed - could not decrypt "
+                               "GTK");
                        return -1;
                }
        } else {
-               wpa_printf(MSG_WARNING, "WPA: Unsupported key_info type %d",
-                          ver);
+               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(
@@ -1300,7 +1345,7 @@ static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm,
 
        WPA_PUT_BE16(reply->key_data_length, 0);
 
-       wpa_printf(MSG_DEBUG, "WPA: Sending EAPOL-Key 2/2");
+       wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/2");
        wpa_eapol_key_send(sm, sm->ptk.kck, ver, sm->bssid, ETH_P_EAPOL,
                           rbuf, rlen, reply->key_mic);
 
@@ -1320,8 +1365,8 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm,
        os_memset(&gd, 0, sizeof(gd));
 
        rekey = wpa_sm_get_state(sm) == WPA_COMPLETED;
-       wpa_printf(MSG_DEBUG, "WPA: RX message 1 of Group Key Handshake from "
-                  MACSTR " (ver=%d)", MAC2STR(src_addr), ver);
+       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);
        keydatalen = WPA_GET_BE16(key->key_data_length);
@@ -1352,6 +1397,8 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm,
                        MAC2STR(sm->bssid), wpa_cipher_txt(sm->group_cipher));
                wpa_sm_cancel_auth_timeout(sm);
                wpa_sm_set_state(sm, WPA_COMPLETED);
+
+               wpa_sm_set_rekey_offload(sm);
        } else {
                wpa_supplicant_key_neg_complete(sm, sm->bssid,
                                                key_info &
@@ -1378,8 +1425,9 @@ static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm,
                wpa_eapol_key_mic(sm->tptk.kck, ver, buf, len,
                                  key->key_mic);
                if (os_memcmp(mic, key->key_mic, 16) != 0) {
-                       wpa_printf(MSG_WARNING, "WPA: Invalid EAPOL-Key MIC "
-                                  "when using TPTK - ignoring TPTK");
+                       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;
@@ -1393,16 +1441,18 @@ static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm,
                wpa_eapol_key_mic(sm->ptk.kck, ver, buf, len,
                                  key->key_mic);
                if (os_memcmp(mic, key->key_mic, 16) != 0) {
-                       wpa_printf(MSG_WARNING, "WPA: Invalid EAPOL-Key MIC "
-                                  "- dropping packet");
+                       wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+                               "WPA: Invalid EAPOL-Key MIC - "
+                               "dropping packet");
                        return -1;
                }
                ok = 1;
        }
 
        if (!ok) {
-               wpa_printf(MSG_WARNING, "WPA: Could not verify EAPOL-Key MIC "
-                          "- dropping packet");
+               wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+                       "WPA: Could not verify EAPOL-Key MIC - "
+                       "dropping packet");
                return -1;
        }
 
@@ -1422,8 +1472,9 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm,
        wpa_hexdump(MSG_DEBUG, "RSN: encrypted key data",
                    (u8 *) (key + 1), keydatalen);
        if (!sm->ptk_set) {
-               wpa_printf(MSG_WARNING, "WPA: PTK not available, "
-                          "cannot decrypt EAPOL-Key key data.");
+               wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+                       "WPA: PTK not available, cannot decrypt EAPOL-Key Key "
+                       "Data");
                return -1;
        }
 
@@ -1434,37 +1485,40 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm,
                os_memcpy(ek, key->key_iv, 16);
                os_memcpy(ek + 16, sm->ptk.kek, 16);
                if (rc4_skip(ek, 32, 256, (u8 *) (key + 1), keydatalen)) {
-                       wpa_printf(MSG_ERROR, "WPA: RC4 failed");
+                       wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
+                               "WPA: RC4 failed");
                        return -1;
                }
        } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
                   ver == WPA_KEY_INFO_TYPE_AES_128_CMAC) {
                u8 *buf;
                if (keydatalen % 8) {
-                       wpa_printf(MSG_WARNING, "WPA: Unsupported "
-                                  "AES-WRAP len %d", keydatalen);
+                       wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+                               "WPA: Unsupported AES-WRAP len %d",
+                               keydatalen);
                        return -1;
                }
                keydatalen -= 8; /* AES-WRAP adds 8 bytes */
                buf = os_malloc(keydatalen);
                if (buf == NULL) {
-                       wpa_printf(MSG_WARNING, "WPA: No memory for "
-                                  "AES-UNWRAP buffer");
+                       wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+                               "WPA: No memory for AES-UNWRAP buffer");
                        return -1;
                }
                if (aes_unwrap(sm->ptk.kek, keydatalen / 8,
                               (u8 *) (key + 1), buf)) {
                        os_free(buf);
-                       wpa_printf(MSG_WARNING, "WPA: AES unwrap failed - "
-                                  "could not decrypt EAPOL-Key key data");
+                       wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+                               "WPA: AES unwrap failed - "
+                               "could not decrypt EAPOL-Key key data");
                        return -1;
                }
                os_memcpy(key + 1, buf, keydatalen);
                os_free(buf);
                WPA_PUT_BE16(key->key_data_length, keydatalen);
        } else {
-               wpa_printf(MSG_WARNING, "WPA: Unsupported key_info type %d",
-                          ver);
+               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",
@@ -1480,35 +1534,38 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm,
 void wpa_sm_aborted_cached(struct wpa_sm *sm)
 {
        if (sm && sm->cur_pmksa) {
-               wpa_printf(MSG_DEBUG, "RSN: Cancelling PMKSA caching attempt");
+               wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+                       "RSN: Cancelling PMKSA caching attempt");
                sm->cur_pmksa = NULL;
        }
 }
 
 
-static void wpa_eapol_key_dump(const struct wpa_eapol_key *key)
+static void wpa_eapol_key_dump(struct wpa_sm *sm,
+                              const struct wpa_eapol_key *key)
 {
 #ifndef CONFIG_NO_STDOUT_DEBUG
        u16 key_info = WPA_GET_BE16(key->key_info);
 
-       wpa_printf(MSG_DEBUG, "  EAPOL-Key type=%d", key->type);
-       wpa_printf(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_printf(MSG_DEBUG, "  key_length=%u key_data_length=%u",
-                  WPA_GET_BE16(key->key_length),
-                  WPA_GET_BE16(key->key_data_length));
+       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),
+               WPA_GET_BE16(key->key_data_length));
        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);
@@ -1552,10 +1609,11 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
 #endif /* CONFIG_IEEE80211R */
 
        if (len < sizeof(*hdr) + sizeof(*key)) {
-               wpa_printf(MSG_DEBUG, "WPA: EAPOL frame too short to be a WPA "
-                          "EAPOL-Key (len %lu, expecting at least %lu)",
-                          (unsigned long) len,
-                          (unsigned long) sizeof(*hdr) + sizeof(*key));
+               wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+                       "WPA: EAPOL frame too short to be a WPA "
+                       "EAPOL-Key (len %lu, expecting at least %lu)",
+                       (unsigned long) len,
+                       (unsigned long) sizeof(*hdr) + sizeof(*key));
                return 0;
        }
 
@@ -1568,40 +1626,45 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
        key = (struct wpa_eapol_key *) (hdr + 1);
        plen = be_to_host16(hdr->length);
        data_len = plen + sizeof(*hdr);
-       wpa_printf(MSG_DEBUG, "IEEE 802.1X RX: version=%d type=%d length=%lu",
-                  hdr->version, hdr->type, (unsigned long) plen);
+       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_printf(MSG_DEBUG, "WPA: EAPOL frame (type %u) discarded, "
+               wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+                       "WPA: EAPOL frame (type %u) discarded, "
                        "not a Key frame", hdr->type);
                ret = 0;
                goto out;
        }
        if (plen > len - sizeof(*hdr) || plen < sizeof(*key)) {
-               wpa_printf(MSG_DEBUG, "WPA: EAPOL frame payload size %lu "
-                          "invalid (frame size %lu)",
-                          (unsigned long) plen, (unsigned long) len);
+               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 (key->type != EAPOL_KEY_TYPE_WPA && key->type != EAPOL_KEY_TYPE_RSN)
        {
-               wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key type (%d) unknown, "
-                          "discarded", key->type);
+               wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+                       "WPA: EAPOL-Key type (%d) unknown, discarded",
+                       key->type);
                ret = 0;
                goto out;
        }
-       wpa_eapol_key_dump(key);
+       wpa_eapol_key_dump(sm, key);
 
        eapol_sm_notify_lower_layer_success(sm->eapol, 0);
        wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL-Key", tmp, len);
        if (data_len < len) {
-               wpa_printf(MSG_DEBUG, "WPA: ignoring %lu bytes after the IEEE "
-                          "802.1X data", (unsigned long) len - data_len);
+               wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+                       "WPA: ignoring %lu bytes after the IEEE 802.1X data",
+                       (unsigned long) len - data_len);
        }
        key_info = WPA_GET_BE16(key->key_info);
        ver = key_info & WPA_KEY_INFO_TYPE_MASK;
@@ -1610,8 +1673,9 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
            ver != WPA_KEY_INFO_TYPE_AES_128_CMAC &&
 #endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
            ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
-               wpa_printf(MSG_INFO, "WPA: Unsupported EAPOL-Key descriptor "
-                          "version %d.", ver);
+               wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+                       "WPA: Unsupported EAPOL-Key descriptor version %d",
+                       ver);
                goto out;
        }
 
@@ -1619,8 +1683,8 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
        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_printf(MSG_INFO, "FT: AP did not use "
-                                  "AES-128-CMAC.");
+                       wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+                               "FT: AP did not use AES-128-CMAC");
                        goto out;
                }
        } else
@@ -1628,25 +1692,27 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
 #ifdef CONFIG_IEEE80211W
        if (wpa_key_mgmt_sha256(sm->key_mgmt)) {
                if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
-                       wpa_printf(MSG_INFO, "WPA: AP did not use the "
-                                  "negotiated AES-128-CMAC.");
+                       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 &&
            ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
-               wpa_printf(MSG_INFO, "WPA: CCMP is used, but EAPOL-Key "
-                          "descriptor version (%d) is not 2.", ver);
+               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_printf(MSG_INFO, "WPA: Backwards compatibility: "
-                                  "allow invalid version for non-CCMP group "
-                                  "keys");
+                       wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+                               "WPA: Backwards compatibility: allow invalid "
+                               "version for non-CCMP group keys");
                } else
                        goto out;
        }
@@ -1661,9 +1727,9 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
                if (!peerkey->initiator && peerkey->replay_counter_set &&
                    os_memcmp(key->replay_counter, peerkey->replay_counter,
                              WPA_REPLAY_COUNTER_LEN) <= 0) {
-                       wpa_printf(MSG_WARNING, "RSN: EAPOL-Key Replay "
-                                  "Counter did not increase (STK) - dropping "
-                                  "packet");
+                       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];
@@ -1672,16 +1738,18 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
                        inc_byte_array(_tmp, WPA_REPLAY_COUNTER_LEN);
                        if (os_memcmp(_tmp, peerkey->replay_counter,
                                      WPA_REPLAY_COUNTER_LEN) != 0) {
-                               wpa_printf(MSG_DEBUG, "RSN: EAPOL-Key Replay "
-                                          "Counter did not match (STK) - "
-                                          "dropping packet");
+                               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_printf(MSG_INFO, "RSN: Ack bit in key_info from STK peer");
+               wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+                       "RSN: Ack bit in key_info from STK peer");
                goto out;
        }
 #endif /* CONFIG_PEERKEY */
@@ -1689,8 +1757,9 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
        if (!peerkey && sm->rx_replay_counter_set &&
            os_memcmp(key->replay_counter, sm->rx_replay_counter,
                      WPA_REPLAY_COUNTER_LEN) <= 0) {
-               wpa_printf(MSG_WARNING, "WPA: EAPOL-Key Replay Counter did not"
-                          " increase - dropping packet");
+               wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+                       "WPA: EAPOL-Key Replay Counter did not increase - "
+                       "dropping packet");
                goto out;
        }
 
@@ -1699,13 +1768,14 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
            && (peerkey == NULL || !peerkey->initiator)
 #endif /* CONFIG_PEERKEY */
                ) {
-               wpa_printf(MSG_INFO, "WPA: No Ack bit in key_info");
+               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_printf(MSG_INFO, "WPA: EAPOL-Key with Request bit - "
-                          "dropped");
+               wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+                       "WPA: EAPOL-Key with Request bit - dropped");
                goto out;
        }
 
@@ -1739,8 +1809,9 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
 
        if (key_info & WPA_KEY_INFO_KEY_TYPE) {
                if (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) {
-                       wpa_printf(MSG_WARNING, "WPA: Ignored EAPOL-Key "
-                                  "(Pairwise) with non-zero key index");
+                       wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+                               "WPA: Ignored EAPOL-Key (Pairwise) with "
+                               "non-zero key index");
                        goto out;
                }
                if (peerkey) {
@@ -1764,8 +1835,9 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
                        wpa_supplicant_process_1_of_2(sm, src_addr, key,
                                                      extra_len, ver);
                } else {
-                       wpa_printf(MSG_WARNING, "WPA: EAPOL-Key (Group) "
-                                  "without Mic bit - dropped");
+                       wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+                               "WPA: EAPOL-Key (Group) without Mic bit - "
+                               "dropped");
                }
        }
 
@@ -1940,7 +2012,8 @@ static void wpa_sm_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry,
        if (sm->cur_pmksa == entry ||
            (sm->pmk_len == entry->pmk_len &&
             os_memcmp(sm->pmk, entry->pmk, sm->pmk_len) == 0)) {
-               wpa_printf(MSG_DEBUG, "RSN: removed current PMKSA entry");
+               wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+                       "RSN: removed current PMKSA entry");
                sm->cur_pmksa = NULL;
 
                if (replace) {
@@ -1982,8 +2055,8 @@ struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx)
 
        sm->pmksa = pmksa_cache_init(wpa_sm_pmksa_free_cb, sm, sm);
        if (sm->pmksa == NULL) {
-               wpa_printf(MSG_ERROR, "RSN: PMKSA cache initialization "
-                          "failed");
+               wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
+                       "RSN: PMKSA cache initialization failed");
                os_free(sm);
                return NULL;
        }
@@ -2030,7 +2103,8 @@ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid)
        if (sm == NULL)
                return;
 
-       wpa_printf(MSG_DEBUG, "WPA: Association event - clear replay counter");
+       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;
@@ -2059,10 +2133,14 @@ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid)
                 * IEEE 802.11, 8.4.10: Delete PTK SA on (re)association if
                 * this is not part of a Fast BSS Transition.
                 */
-               wpa_printf(MSG_DEBUG, "WPA: Clear old PTK");
+               wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PTK");
                sm->ptk_set = 0;
                sm->tptk_set = 0;
        }
+
+#ifdef CONFIG_TDLS
+       wpa_tdls_assoc(sm);
+#endif /* CONFIG_TDLS */
 }
 
 
@@ -2078,6 +2156,9 @@ void wpa_sm_notify_disassoc(struct wpa_sm *sm)
        rsn_preauth_deinit(sm);
        if (wpa_sm_get_state(sm) == WPA_4WAY_HANDSHAKE)
                sm->dot11RSNA4WayHandshakeFailures++;
+#ifdef CONFIG_TDLS
+       wpa_tdls_disassoc(sm);
+#endif /* CONFIG_TDLS */
 }
 
 
@@ -2191,8 +2272,6 @@ void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config)
                sm->ssid_len = 0;
                sm->wpa_ptk_rekey = 0;
        }
-       if (config == NULL || config->network_ctx != sm->network_ctx)
-               pmksa_cache_notify_reconfig(sm->pmksa);
 }
 
 
@@ -2430,7 +2509,8 @@ int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len)
 
        os_free(sm->assoc_wpa_ie);
        if (ie == NULL || len == 0) {
-               wpa_printf(MSG_DEBUG, "WPA: clearing own WPA/RSN IE");
+               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 {
@@ -2464,7 +2544,8 @@ int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len)
 
        os_free(sm->ap_wpa_ie);
        if (ie == NULL || len == 0) {
-               wpa_printf(MSG_DEBUG, "WPA: clearing AP WPA IE");
+               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 {
@@ -2498,7 +2579,8 @@ int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, size_t len)
 
        os_free(sm->ap_rsn_ie);
        if (ie == NULL || len == 0) {
-               wpa_printf(MSG_DEBUG, "WPA: clearing AP RSN IE");
+               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 {
@@ -2526,9 +2608,12 @@ int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, size_t len)
  */
 int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, struct wpa_ie_data *data)
 {
-       if (sm == NULL || sm->assoc_wpa_ie == NULL) {
-               wpa_printf(MSG_DEBUG, "WPA: No WPA/RSN IE available from "
-                          "association info");
+       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))
@@ -2549,7 +2634,7 @@ int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len)
 
 void wpa_sm_drop_sa(struct wpa_sm *sm)
 {
-       wpa_printf(MSG_DEBUG, "WPA: Clear old PMK and PTK");
+       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));
@@ -2564,3 +2649,17 @@ int wpa_sm_has_ptk(struct wpa_sm *sm)
                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)
+{
+#ifndef CONFIG_NO_WPA2
+       pmksa_cache_flush(sm->pmksa, network_ctx);
+#endif /* CONFIG_NO_WPA2 */
+}
index f1a5554..4c1750f 100644 (file)
@@ -55,6 +55,19 @@ struct wpa_sm_ctx {
        int (*send_ft_action)(void *ctx, u8 action, const u8 *target_ap,
                              const u8 *ies, size_t ies_len);
        int (*mark_authenticated)(void *ctx, const u8 *target_ap);
+#ifdef CONFIG_TDLS
+       int (*tdls_get_capa)(void *ctx, int *tdls_supported,
+                            int *tdls_ext_setup);
+       int (*send_tdls_mgmt)(void *ctx, const u8 *dst,
+                             u8 action_code, u8 dialog_token,
+                             u16 status_code, const u8 *buf, size_t len);
+       int (*tdls_oper)(void *ctx, int oper, const u8 *peer);
+       int (*tdls_peer_addset)(void *ctx, const u8 *addr, int add,
+                               u16 capability, const u8 *supp_rates,
+                               size_t supp_rates_len);
+#endif /* CONFIG_TDLS */
+       void (*set_rekey_offload)(void *ctx, const u8 *kek, const u8 *kck,
+                                 const u8 *replay_ctr);
 };
 
 
@@ -126,6 +139,10 @@ int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len);
 void wpa_sm_drop_sa(struct wpa_sm *sm);
 int wpa_sm_has_ptk(struct wpa_sm *sm);
 
+void wpa_sm_update_replay_ctr(struct wpa_sm *sm, const u8 *replay_ctr);
+
+void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx);
+
 #else /* CONFIG_NO_WPA */
 
 static inline struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx)
@@ -271,6 +288,16 @@ static inline int wpa_sm_has_ptk(struct wpa_sm *sm)
        return 0;
 }
 
+static inline void wpa_sm_update_replay_ctr(struct wpa_sm *sm,
+                                           const u8 *replay_ctr)
+{
+}
+
+static inline void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm,
+                                           void *network_ctx)
+{
+}
+
 #endif /* CONFIG_NO_WPA */
 
 #ifdef CONFIG_PEERKEY
@@ -330,4 +357,19 @@ wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
 
 #endif /* CONFIG_IEEE80211R */
 
+
+/* tdls.c */
+void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len);
+void wpa_tdls_assoc_resp_ies(struct wpa_sm *sm, const u8 *ies, size_t len);
+int wpa_tdls_start(struct wpa_sm *sm, const u8 *addr);
+int wpa_tdls_reneg(struct wpa_sm *sm, const u8 *addr);
+int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr, u16 reason_code);
+int wpa_tdls_teardown_link(struct wpa_sm *sm, const u8 *addr, u16 reason_code);
+int wpa_tdls_send_discovery_request(struct wpa_sm *sm, const u8 *addr);
+int wpa_tdls_init(struct wpa_sm *sm);
+void wpa_tdls_deinit(struct wpa_sm *sm);
+void wpa_tdls_enable(struct wpa_sm *sm, int enabled);
+void wpa_tdls_disable_link(struct wpa_sm *sm, const u8 *addr);
+int wpa_tdls_is_external_setup(struct wpa_sm *sm);
+
 #endif /* WPA_H */
index 23063bc..f6fd7da 100644 (file)
@@ -16,6 +16,7 @@
 
 #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"
 
 #ifdef CONFIG_IEEE80211R
 
-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;
-};
-
-static int wpa_ft_parse_ies(const u8 *ies, size_t ies_len,
-                           struct wpa_ft_ies *parse);
-
-
 int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr,
                      const struct wpa_eapol_key *key,
                      struct wpa_ptk *ptk, size_t ptk_len)
@@ -346,155 +322,6 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len,
 }
 
 
-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;
-}
-
-
-static 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:
-                       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:
-                       parse->tie = pos + 2;
-                       parse->tie_len = pos[1];
-                       break;
-               case WLAN_EID_RIC_DATA:
-                       if (parse->ric == NULL)
-                               parse->ric = pos;
-               }
-
-               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 (parse->tie)
-               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;
-}
-
-
 static int wpa_ft_install_ptk(struct wpa_sm *sm, const u8 *bssid)
 {
        int keylen;
@@ -540,7 +367,7 @@ int wpa_ft_prepare_auth_request(struct wpa_sm *sm, const u8 *mdie)
        size_t ft_ies_len;
 
        /* Generate a new SNonce */
-       if (os_get_random(sm->snonce, WPA_NONCE_LEN)) {
+       if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) {
                wpa_printf(MSG_INFO, "FT: Failed to generate a new SNonce");
                return -1;
        }
@@ -795,9 +622,8 @@ static int wpa_ft_process_gtk_subelem(struct wpa_sm *sm, const u8 *gtk_elem,
        }
 
        wpa_hexdump_key(MSG_DEBUG, "FT: GTK from Reassoc Resp", gtk, keylen);
-       if (wpa_sm_set_key(sm, alg, (u8 *) "\xff\xff\xff\xff\xff\xff",
-                          keyidx, 0, gtk_elem + 3, rsc_len, gtk, keylen) <
-           0) {
+       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;
@@ -848,9 +674,8 @@ static int wpa_ft_process_igtk_subelem(struct wpa_sm *sm, const u8 *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, (u8 *) "\xff\xff\xff\xff\xff\xff",
-                          keyidx, 0, igtk_elem + 2, 6, igtk, WPA_IGTK_LEN) <
-           0) {
+       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;
@@ -951,8 +776,8 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies,
        }
 
        count = 3;
-       if (parse.tie)
-               count++;
+       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",
@@ -1020,7 +845,7 @@ int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap,
                   MAC2STR(target_ap));
 
        /* Generate a new SNonce */
-       if (os_get_random(sm->snonce, WPA_NONCE_LEN)) {
+       if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) {
                wpa_printf(MSG_INFO, "FT: Failed to generate a new SNonce");
                return -1;
        }
index 618c090..39124c4 100644 (file)
@@ -18,6 +18,7 @@
 #include "utils/list.h"
 
 struct wpa_peerkey;
+struct wpa_tdls_peer;
 struct wpa_eapol_key;
 
 /**
@@ -43,6 +44,7 @@ struct wpa_sm {
 
        struct l2_packet_data *l2_preauth;
        struct l2_packet_data *l2_preauth_br;
+       struct l2_packet_data *l2_tdls;
        u8 preauth_bssid[ETH_ALEN]; /* current RSN pre-auth peer or
                                     * 00:00:00:00:00:00 if no pre-auth is
                                     * in progress */
@@ -92,6 +94,20 @@ struct wpa_sm {
 #ifdef CONFIG_PEERKEY
        struct wpa_peerkey *peerkey;
 #endif /* CONFIG_PEERKEY */
+#ifdef CONFIG_TDLS
+       struct wpa_tdls_peer *tdls;
+       int tdls_prohibited;
+       int tdls_disabled;
+
+       /* The driver supports TDLS */
+       int tdls_supported;
+
+       /*
+        * The driver requires explicit discovery/setup/teardown frames sent
+        * to it via tdls_mgmt.
+        */
+       int tdls_external_setup;
+#endif /* CONFIG_TDLS */
 
 #ifdef CONFIG_IEEE80211R
        u8 xxkey[PMK_LEN]; /* PSK or the second 256 bits of MSK */
@@ -237,6 +253,57 @@ static inline int wpa_sm_mark_authenticated(struct wpa_sm *sm,
        return -1;
 }
 
+static inline void wpa_sm_set_rekey_offload(struct wpa_sm *sm)
+{
+       if (!sm->ctx->set_rekey_offload)
+               return;
+       sm->ctx->set_rekey_offload(sm->ctx->ctx, sm->ptk.kek,
+                                  sm->ptk.kck, sm->rx_replay_counter);
+}
+
+#ifdef CONFIG_TDLS
+static inline int wpa_sm_tdls_get_capa(struct wpa_sm *sm,
+                                      int *tdls_supported,
+                                      int *tdls_ext_setup)
+{
+       if (sm->ctx->tdls_get_capa)
+               return sm->ctx->tdls_get_capa(sm->ctx->ctx, tdls_supported,
+                                             tdls_ext_setup);
+       return -1;
+}
+
+static inline int wpa_sm_send_tdls_mgmt(struct wpa_sm *sm, const u8 *dst,
+                                       u8 action_code, u8 dialog_token,
+                                       u16 status_code, const u8 *buf,
+                                       size_t len)
+{
+       if (sm->ctx->send_tdls_mgmt)
+               return sm->ctx->send_tdls_mgmt(sm->ctx->ctx, dst, action_code,
+                                              dialog_token, status_code,
+                                              buf, len);
+       return -1;
+}
+
+static inline int wpa_sm_tdls_oper(struct wpa_sm *sm, int oper,
+                                  const u8 *peer)
+{
+       if (sm->ctx->tdls_oper)
+               return sm->ctx->tdls_oper(sm->ctx->ctx, oper, peer);
+       return -1;
+}
+
+static inline int
+wpa_sm_tdls_peer_addset(struct wpa_sm *sm, const u8 *addr, int add,
+                       u16 capability, const u8 *supp_rates,
+                       size_t supp_rates_len)
+{
+       if (sm->ctx->tdls_peer_addset)
+               return sm->ctx->tdls_peer_addset(sm->ctx->ctx, addr, add,
+                                                capability, supp_rates,
+                                                supp_rates_len);
+       return -1;
+}
+#endif /* CONFIG_TDLS */
 
 void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck,
                        int ver, const u8 *dest, u16 proto,
@@ -256,4 +323,7 @@ int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr,
                      const struct wpa_eapol_key *key,
                      struct wpa_ptk *ptk, size_t ptk_len);
 
+void wpa_tdls_assoc(struct wpa_sm *sm);
+void wpa_tdls_disassoc(struct wpa_sm *sm);
+
 #endif /* WPA_I_H */
index f447223..cbbc54f 100644 (file)
 #include "wpa_ie.h"
 
 
-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_WEP40)
-               return WPA_CIPHER_WEP40;
-       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 (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP104)
-               return WPA_CIPHER_WEP104;
-       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;
-}
-
-
-static 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;
-
-       if (wpa_ie_len == 0) {
-               /* No WPA IE - fail silently */
-               return -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 -1;
-       }
-
-       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 -1;
-       }
-
-       if (left >= 2) {
-               data->pairwise_cipher = 0;
-               count = WPA_GET_LE16(pos);
-               pos += 2;
-               left -= 2;
-               if (count == 0 || left < count * WPA_SELECTOR_LEN) {
-                       wpa_printf(MSG_DEBUG, "%s: ie count botch (pairwise), "
-                                  "count %u left %u", __func__, count, left);
-                       return -1;
-               }
-               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 -1;
-       }
-
-       if (left >= 2) {
-               data->key_mgmt = 0;
-               count = WPA_GET_LE16(pos);
-               pos += 2;
-               left -= 2;
-               if (count == 0 || left < count * WPA_SELECTOR_LEN) {
-                       wpa_printf(MSG_DEBUG, "%s: ie count botch (key mgmt), "
-                                  "count %u left %u", __func__, count, left);
-                       return -1;
-               }
-               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 -1;
-       }
-
-       if (left >= 2) {
-               data->capabilities = WPA_GET_LE16(pos);
-               pos += 2;
-               left -= 2;
-       }
-
-       if (left > 0) {
-               wpa_printf(MSG_DEBUG, "%s: ie has %u trailing bytes - ignored",
-                          __func__, left);
-       }
-
-       return 0;
-}
-
-
 /**
  * wpa_parse_wpa_ie - Parse WPA/RSN IE
  * @wpa_ie: Pointer to WPA or RSN IE
@@ -535,7 +397,6 @@ int wpa_supplicant_parse_ies(const u8 *buf, size_t len,
                        ie->rsn_ie_len = pos[1] + 2;
                        wpa_hexdump(MSG_DEBUG, "WPA: RSN IE in EAPOL-Key",
                                    ie->rsn_ie, ie->rsn_ie_len);
-#ifdef CONFIG_IEEE80211R
                } else if (*pos == WLAN_EID_MOBILITY_DOMAIN) {
                        ie->mdie = pos;
                        ie->mdie_len = pos[1] + 2;
@@ -562,7 +423,20 @@ int wpa_supplicant_parse_ies(const u8 *buf, size_t len,
                                            "EAPOL-Key Key Data IE",
                                            pos, 2 + pos[1]);
                        }
-#endif /* CONFIG_IEEE80211R */
+               } 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;
                } else if (*pos == WLAN_EID_VENDOR_SPECIFIC) {
                        ret = wpa_parse_generic(pos, end, ie);
                        if (ret < 0)
index 94518d8..c13d94c 100644 (file)
@@ -15,6 +15,8 @@
 #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;
@@ -39,14 +41,20 @@ struct wpa_eapol_ie_parse {
        const u8 *igtk;
        size_t igtk_len;
 #endif /* CONFIG_IEEE80211W */
-#ifdef CONFIG_IEEE80211R
        const u8 *mdie;
        size_t mdie_len;
        const u8 *ftie;
        size_t ftie_len;
        const u8 *reassoc_deadline;
        const u8 *key_lifetime;
-#endif /* CONFIG_IEEE80211R */
+       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;
 };
 
 int wpa_supplicant_parse_ies(const u8 *buf, size_t len,
index 1374264..1c02167 100644 (file)
@@ -572,7 +572,7 @@ static int mp_mod (mp_int * a, mp_int * b, mp_int * c)
 
 /* 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 alot of stack space
+ * 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)
@@ -2207,7 +2207,7 @@ static int mp_2expt (mp_int * a, int b)
   /* zero a as per default */
   mp_zero (a);
 
-  /* grow a to accomodate the single bit */
+  /* grow a to accommodate the single bit */
   if ((res = mp_grow (a, b / DIGIT_BIT + 1)) != MP_OKAY) {
     return res;
   }
@@ -2319,7 +2319,7 @@ CLEANUP:
 }
 
 
-/* multiplies |a| * |b| and only computes upto digs digits of result
+/* 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.
  */
@@ -2829,7 +2829,7 @@ static int mp_mul_2(mp_int * a, mp_int * b)
 {
   int     x, res, oldused;
 
-  /* grow to accomodate result */
+  /* grow to accommodate result */
   if (b->alloc < a->used + 1) {
     if ((res = mp_grow (b, a->used + 1)) != MP_OKAY) {
       return res;
@@ -2891,8 +2891,8 @@ static int mp_mul_2(mp_int * a, mp_int * b)
 /*
  * shifts with subtractions when the result is greater than b.
  *
- * The method is slightly modified to shift B unconditionally upto just under
- * the leading bit of b.  This saves alot of multiple precision shifting.
+ * 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)
 {
index afb6031..75b8612 100644 (file)
@@ -1,6 +1,6 @@
 /*
- * TLSv1 client (RFC 2246)
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * TLS v1.0 (RFC 2246) and v1.1 (RFC 4346) client
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -80,8 +80,9 @@ int tls_derive_keys(struct tlsv1_client *conn,
 
        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);
+       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->master_secret, TLS_MASTER_SECRET_LEN,
                    "key expansion", seed, 2 * TLS_RANDOM_LEN,
                    key_block, key_block_len)) {
@@ -107,12 +108,21 @@ int tls_derive_keys(struct tlsv1_client *conn,
        os_memcpy(conn->rl.read_key, pos, conn->rl.key_material_len);
        pos += conn->rl.key_material_len;
 
-       /* 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;
+       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;
 }
@@ -227,10 +237,8 @@ int tlsv1_client_encrypt(struct tlsv1_client *conn,
        wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Plaintext AppData",
                        in_data, in_len);
 
-       os_memcpy(out_data + TLS_RECORD_HEADER_LEN, in_data, in_len);
-
        if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_APPLICATION_DATA,
-                             out_data, out_len, in_len, &rlen) < 0) {
+                             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);
@@ -360,6 +368,8 @@ struct tlsv1_client * tlsv1_client_init(void)
        suites[count++] = TLS_RSA_WITH_RC4_128_MD5;
        conn->num_cipher_suites = count;
 
+       conn->rl.tls_version = TLS_VERSION;
+
        return conn;
 }
 
@@ -656,6 +666,12 @@ int tlsv1_client_set_cred(struct tlsv1_client *conn,
 }
 
 
+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)
index 16ad57d..a620d62 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * TLSv1 client (RFC 2246)
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -47,6 +47,7 @@ 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,
index 7fe179f..f091bcf 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * TLSv1 client - internal structures
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -39,6 +39,7 @@ struct tlsv1_client {
        unsigned int session_resumed:1;
        unsigned int session_ticket_included:1;
        unsigned int use_session_ticket:1;
+       unsigned int disable_time_checks:1;
 
        struct crypto_public_key *server_rsa_key;
 
index ed3f260..457c3b0 100644 (file)
@@ -1,6 +1,6 @@
 /*
- * TLSv1 client - read handshake message
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * TLS v1.0 (RFC 2246) and v1.1 (RFC 4346) client - read handshake message
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -38,6 +38,7 @@ static int tls_process_server_hello(struct tlsv1_client *conn, u8 ct,
        const u8 *pos, *end;
        size_t left, len, i;
        u16 cipher_suite;
+       u16 tls_version;
 
        if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
                wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
@@ -79,15 +80,22 @@ static int tls_process_server_hello(struct tlsv1_client *conn, u8 ct,
        /* ProtocolVersion server_version */
        if (end - pos < 2)
                goto decode_error;
-       if (WPA_GET_BE16(pos) != TLS_VERSION) {
+       tls_version = WPA_GET_BE16(pos);
+       if (tls_version != TLS_VERSION_1 &&
+           (tls_version != TLS_VERSION_1_1 ||
+            TLS_VERSION == TLS_VERSION_1)) {
                wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in "
-                          "ServerHello");
+                          "ServerHello %u.%u", pos[0], pos[1]);
                tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
                          TLS_ALERT_PROTOCOL_VERSION);
                return -1;
        }
        pos += 2;
 
+       wpa_printf(MSG_DEBUG, "TLSv1: Using TLS v%s",
+                  tls_version == TLS_VERSION_1_1 ? "1.1" : "1.0");
+       conn->rl.tls_version = tls_version;
+
        /* Random random */
        if (end - pos < TLS_RANDOM_LEN)
                goto decode_error;
@@ -365,7 +373,8 @@ static int tls_process_certificate(struct tlsv1_client *conn, u8 ct,
 
        if (conn->cred &&
            x509_certificate_chain_validate(conn->cred->trusted_certs, chain,
-                                           &reason) < 0) {
+                                           &reason, conn->disable_time_checks)
+           < 0) {
                int tls_reason;
                wpa_printf(MSG_DEBUG, "TLSv1: Server certificate chain "
                           "validation failed (reason=%d)", reason);
index b47425f..9d53dd1 100644 (file)
@@ -1,6 +1,6 @@
 /*
- * TLSv1 client - write handshake message
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * TLS v1.0 (RFC 2246) and v1.1 (RFC 4346) client - write handshake message
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -18,6 +18,7 @@
 #include "crypto/md5.h"
 #include "crypto/sha1.h"
 #include "crypto/tls.h"
+#include "crypto/random.h"
 #include "x509v3.h"
 #include "tlsv1_common.h"
 #include "tlsv1_record.h"
@@ -57,7 +58,7 @@ u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len)
 
        os_get_time(&now);
        WPA_PUT_BE32(conn->client_random, now.sec);
-       if (os_get_random(conn->client_random + 4, TLS_RANDOM_LEN - 4)) {
+       if (random_get_bytes(conn->client_random + 4, TLS_RANDOM_LEN - 4)) {
                wpa_printf(MSG_ERROR, "TLSv1: Could not generate "
                           "client_random");
                return NULL;
@@ -115,7 +116,8 @@ u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len)
        tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
 
        if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
-                             rhdr, end - rhdr, pos - hs_start, out_len) < 0) {
+                             rhdr, end - rhdr, hs_start, pos - hs_start,
+                             out_len) < 0) {
                wpa_printf(MSG_DEBUG, "TLSv1: Failed to create TLS record");
                tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
                          TLS_ALERT_INTERNAL_ERROR);
@@ -191,7 +193,8 @@ static int tls_write_client_certificate(struct tlsv1_client *conn,
        WPA_PUT_BE24(hs_length, pos - hs_length - 3);
 
        if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
-                             rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+                             rhdr, end - rhdr, hs_start, pos - hs_start,
+                             &rlen) < 0) {
                wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
                tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
                          TLS_ALERT_INTERNAL_ERROR);
@@ -222,7 +225,7 @@ static int tlsv1_key_x_anon_dh(struct tlsv1_client *conn, u8 **pos, u8 *end)
                          TLS_ALERT_INTERNAL_ERROR);
                return -1;
        }
-       if (os_get_random(csecret, csecret_len)) {
+       if (random_get_bytes(csecret, csecret_len)) {
                wpa_printf(MSG_DEBUG, "TLSv1: Failed to get random "
                           "data for Diffie-Hellman");
                tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
@@ -412,7 +415,8 @@ static int tls_write_client_key_exchange(struct tlsv1_client *conn,
        WPA_PUT_BE24(hs_length, pos - hs_length - 3);
 
        if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
-                             rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+                             rhdr, end - rhdr, hs_start, pos - hs_start,
+                             &rlen) < 0) {
                wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
                tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
                          TLS_ALERT_INTERNAL_ERROR);
@@ -533,7 +537,8 @@ static int tls_write_client_certificate_verify(struct tlsv1_client *conn,
        WPA_PUT_BE24(hs_length, pos - hs_length - 3);
 
        if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
-                             rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+                             rhdr, end - rhdr, hs_start, pos - hs_start,
+                             &rlen) < 0) {
                wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
                tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
                          TLS_ALERT_INTERNAL_ERROR);
@@ -552,17 +557,16 @@ static int tls_write_client_certificate_verify(struct tlsv1_client *conn,
 static int tls_write_client_change_cipher_spec(struct tlsv1_client *conn,
                                               u8 **msgpos, u8 *end)
 {
-       u8 *pos, *rhdr;
        size_t rlen;
-
-       pos = *msgpos;
+       u8 payload[1];
 
        wpa_printf(MSG_DEBUG, "TLSv1: Send ChangeCipherSpec");
-       rhdr = pos;
-       pos += TLS_RECORD_HEADER_LEN;
-       *pos = TLS_CHANGE_CIPHER_SPEC;
+
+       payload[0] = TLS_CHANGE_CIPHER_SPEC;
+
        if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC,
-                             rhdr, end - rhdr, 1, &rlen) < 0) {
+                             *msgpos, end - *msgpos, payload, sizeof(payload),
+                             &rlen) < 0) {
                wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
                tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
                          TLS_ALERT_INTERNAL_ERROR);
@@ -577,7 +581,7 @@ static int tls_write_client_change_cipher_spec(struct tlsv1_client *conn,
                return -1;
        }
 
-       *msgpos = rhdr + rlen;
+       *msgpos += rlen;
 
        return 0;
 }
@@ -586,13 +590,11 @@ static int tls_write_client_change_cipher_spec(struct tlsv1_client *conn,
 static int tls_write_client_finished(struct tlsv1_client *conn,
                                     u8 **msgpos, u8 *end)
 {
-       u8 *pos, *rhdr, *hs_start, *hs_length;
+       u8 *pos, *hs_start;
        size_t rlen, hlen;
-       u8 verify_data[TLS_VERIFY_DATA_LEN];
+       u8 verify_data[1 + 3 + TLS_VERIFY_DATA_LEN];
        u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
 
-       pos = *msgpos;
-
        wpa_printf(MSG_DEBUG, "TLSv1: Send Finished");
 
        /* Encrypted Handshake Message: Finished */
@@ -621,40 +623,35 @@ static int tls_write_client_finished(struct tlsv1_client *conn,
 
        if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN,
                    "client finished", hash, MD5_MAC_LEN + SHA1_MAC_LEN,
-                   verify_data, TLS_VERIFY_DATA_LEN)) {
+                   verify_data + 1 + 3, TLS_VERIFY_DATA_LEN)) {
                wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate verify_data");
                tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
                          TLS_ALERT_INTERNAL_ERROR);
                return -1;
        }
        wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (client)",
-                       verify_data, TLS_VERIFY_DATA_LEN);
+                       verify_data + 1 + 3, TLS_VERIFY_DATA_LEN);
 
-       rhdr = pos;
-       pos += TLS_RECORD_HEADER_LEN;
        /* Handshake */
-       hs_start = pos;
+       pos = hs_start = verify_data;
        /* HandshakeType msg_type */
        *pos++ = TLS_HANDSHAKE_TYPE_FINISHED;
-       /* uint24 length (to be filled) */
-       hs_length = pos;
+       /* uint24 length */
+       WPA_PUT_BE24(pos, TLS_VERIFY_DATA_LEN);
        pos += 3;
-       os_memcpy(pos, verify_data, TLS_VERIFY_DATA_LEN);
-       pos += TLS_VERIFY_DATA_LEN;
-       WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+       pos += TLS_VERIFY_DATA_LEN; /* verify_data already in place */
        tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
 
        if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
-                             rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+                             *msgpos, end - *msgpos, hs_start, pos - hs_start,
+                             &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;
        }
 
-       pos = rhdr + rlen;
-
-       *msgpos = pos;
+       *msgpos += rlen;
 
        return 0;
 }
@@ -668,7 +665,7 @@ static u8 * tls_send_client_key_exchange(struct tlsv1_client *conn,
 
        *out_len = 0;
 
-       msglen = 1000;
+       msglen = 2000;
        if (conn->certificate_requested)
                msglen += tls_client_cert_chain_der_len(conn);
 
@@ -777,7 +774,8 @@ u8 * tlsv1_client_send_alert(struct tlsv1_client *conn, u8 level,
        /* ContentType type */
        *pos++ = TLS_CONTENT_TYPE_ALERT;
        /* ProtocolVersion version */
-       WPA_PUT_BE16(pos, TLS_VERSION);
+       WPA_PUT_BE16(pos, conn->rl.tls_version ? conn->rl.tls_version :
+                    TLS_VERSION);
        pos += 2;
        /* uint16 length (to be filled) */
        length = pos;
index 763a4af..712d276 100644 (file)
@@ -1,6 +1,6 @@
 /*
- * TLSv1 common definitions
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * TLS v1.0 (RFC 2246) and v1.1 (RFC 4346) common definitions
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
 
 #include "crypto/crypto.h"
 
-#define TLS_VERSION 0x0301 /* TLSv1 */
+#define TLS_VERSION_1 0x0301 /* TLSv1 */
+#define TLS_VERSION_1_1 0x0302 /* TLSv1.1 */
+#ifdef CONFIG_TLSV11
+#define TLS_VERSION TLS_VERSION_1_1
+#else /* CONFIG_TLSV11 */
+#define TLS_VERSION TLS_VERSION_1
+#endif /* CONFIG_TLSV11 */
 #define TLS_RANDOM_LEN 32
 #define TLS_PRE_MASTER_SECRET_LEN 48
 #define TLS_MASTER_SECRET_LEN 48
index aa467ef..3e07245 100644 (file)
@@ -46,7 +46,7 @@ void tlsv1_cred_free(struct tlsv1_credentials *cred)
 static int tlsv1_add_cert_der(struct x509_certificate **chain,
                              const u8 *buf, size_t len)
 {
-       struct x509_certificate *cert;
+       struct x509_certificate *cert, *p;
        char name[128];
 
        cert = x509_certificate_parse(buf, len);
@@ -56,8 +56,20 @@ static int tlsv1_add_cert_der(struct x509_certificate **chain,
                return -1;
        }
 
-       cert->next = *chain;
-       *chain = cert;
+       p = *chain;
+       while (p && p->next)
+               p = p->next;
+       if (p && x509_name_compare(&cert->subject, &p->issuer) == 0) {
+               /*
+                * The new certificate is the issuer of the last certificate in
+                * the chain - add the new certificate to the end.
+                */
+               p->next = cert;
+       } else {
+               /* Add to the beginning of the chain */
+               cert->next = *chain;
+               *chain = cert;
+       }
 
        x509_name_string(&cert->subject, name, sizeof(name));
        wpa_printf(MSG_DEBUG, "TLSv1: Added certificate: %s", name);
index e811f0e..e072324 100644 (file)
@@ -1,6 +1,6 @@
 /*
- * TLSv1 Record Protocol
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * TLS v1.0 (RFC 2246) and v1.1 (RFC 4346) Record Protocol
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -138,10 +138,10 @@ int tlsv1_record_change_read_cipher(struct tlsv1_record_layer *rl)
  * tlsv1_record_send - TLS record layer: Send a message
  * @rl: Pointer to TLS record layer data
  * @content_type: Content type (TLS_CONTENT_TYPE_*)
- * @buf: Buffer to send (with TLS_RECORD_HEADER_LEN octets reserved in the
- * beginning for record layer to fill in; payload filled in after this and
- * extra space in the end for HMAC).
+ * @buf: Buffer for the generated TLS message (needs to have extra space for
+ * header, IV (TLS v1.1), and HMAC)
  * @buf_size: Maximum buf size
+ * @payload: Payload to be sent
  * @payload_len: Length of the payload
  * @out_len: Buffer for returning the used buf length
  * Returns: 0 on success, -1 on failure
@@ -150,29 +150,62 @@ int tlsv1_record_change_read_cipher(struct tlsv1_record_layer *rl)
  * the data using the current write cipher.
  */
 int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf,
-                     size_t buf_size, size_t payload_len, size_t *out_len)
+                     size_t buf_size, const u8 *payload, size_t payload_len,
+                     size_t *out_len)
 {
-       u8 *pos, *ct_start, *length, *payload;
+       u8 *pos, *ct_start, *length, *cpayload;
        struct crypto_hash *hmac;
        size_t clen;
+       int explicit_iv;
 
        pos = buf;
+       if (pos + TLS_RECORD_HEADER_LEN > buf + buf_size)
+               return -1;
+
        /* ContentType type */
        ct_start = pos;
        *pos++ = content_type;
        /* ProtocolVersion version */
-       WPA_PUT_BE16(pos, TLS_VERSION);
+       WPA_PUT_BE16(pos, rl->tls_version);
        pos += 2;
        /* uint16 length */
        length = pos;
        WPA_PUT_BE16(length, payload_len);
        pos += 2;
 
-       /* opaque fragment[TLSPlaintext.length] */
-       payload = pos;
+       cpayload = pos;
+       explicit_iv = rl->write_cipher_suite != TLS_NULL_WITH_NULL_NULL &&
+               rl->iv_size && rl->tls_version == TLS_VERSION_1_1;
+       if (explicit_iv) {
+               /* opaque IV[Cipherspec.block_length] */
+               if (pos + rl->iv_size > buf + buf_size)
+                       return -1;
+
+               /*
+                * Use random number R per the RFC 4346, 6.2.3.2 CBC Block
+                * Cipher option 2a.
+                */
+
+               if (os_get_random(pos, rl->iv_size))
+                       return -1;
+               pos += rl->iv_size;
+       }
+
+       /*
+        * opaque fragment[TLSPlaintext.length]
+        * (opaque content[TLSCompressed.length] in GenericBlockCipher)
+        */
+       if (pos + payload_len > buf + buf_size)
+               return -1;
+       os_memmove(pos, payload, payload_len);
        pos += payload_len;
 
        if (rl->write_cipher_suite != TLS_NULL_WITH_NULL_NULL) {
+               /*
+                * MAC calculated over seq_num + TLSCompressed.type +
+                * TLSCompressed.version + TLSCompressed.length +
+                * TLSCompressed.fragment
+                */
                hmac = crypto_hash_init(rl->hash_alg, rl->write_mac_secret,
                                        rl->hash_size);
                if (hmac == NULL) {
@@ -182,7 +215,8 @@ int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf,
                }
                crypto_hash_update(hmac, rl->write_seq_num, TLS_SEQ_NUM_LEN);
                /* type + version + length + fragment */
-               crypto_hash_update(hmac, ct_start, pos - ct_start);
+               crypto_hash_update(hmac, ct_start, TLS_RECORD_HEADER_LEN);
+               crypto_hash_update(hmac, payload, payload_len);
                clen = buf + buf_size - pos;
                if (clen < rl->hash_size) {
                        wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Not "
@@ -200,7 +234,7 @@ int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf,
                            pos, clen);
                pos += clen;
                if (rl->iv_size) {
-                       size_t len = pos - payload;
+                       size_t len = pos - cpayload;
                        size_t pad;
                        pad = (len + 1) % rl->iv_size;
                        if (pad)
@@ -214,8 +248,8 @@ int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf,
                        pos += pad + 1;
                }
 
-               if (crypto_cipher_encrypt(rl->write_cbc, payload,
-                                         payload, pos - payload) < 0)
+               if (crypto_cipher_encrypt(rl->write_cbc, cpayload,
+                                         cpayload, pos - cpayload) < 0)
                        return -1;
        }
 
@@ -250,6 +284,7 @@ int tlsv1_record_receive(struct tlsv1_record_layer *rl,
        u8 padlen;
        struct crypto_hash *hmac;
        u8 len[2], hash[100];
+       int force_mac_error = 0;
 
        wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Received",
                    in_data, in_len);
@@ -275,9 +310,14 @@ int tlsv1_record_receive(struct tlsv1_record_layer *rl,
                return -1;
        }
 
-       if (WPA_GET_BE16(in_data + 1) != TLS_VERSION) {
+       /*
+        * TLS v1.0 and v1.1 RFCs were not exactly clear on the use of the
+        * protocol version in record layer. As such, accept any {03,xx} value
+        * to remain compatible with existing implementations.
+        */
+       if (in_data[1] != 0x03) {
                wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version "
-                          "%d.%d", in_data[1], in_data[2]);
+                          "%u.%u", in_data[1], in_data[2]);
                *alert = TLS_ALERT_PROTOCOL_VERSION;
                return -1;
        }
@@ -312,58 +352,86 @@ int tlsv1_record_receive(struct tlsv1_record_layer *rl,
                return -1;
        }
 
-       os_memcpy(out_data, in_data, in_len);
-       *out_len = in_len;
-
        if (rl->read_cipher_suite != TLS_NULL_WITH_NULL_NULL) {
-               if (crypto_cipher_decrypt(rl->read_cbc, out_data,
+               size_t plen;
+               if (crypto_cipher_decrypt(rl->read_cbc, in_data,
                                          out_data, in_len) < 0) {
                        *alert = TLS_ALERT_DECRYPTION_FAILED;
                        return -1;
                }
+               plen = in_len;
+               wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Record Layer - Decrypted "
+                               "data", out_data, plen);
+
                if (rl->iv_size) {
-                       if (in_len == 0) {
+                       /*
+                        * TLS v1.0 defines different alert values for various
+                        * failures. That may information to aid in attacks, so
+                        * use the same bad_record_mac alert regardless of the
+                        * issues.
+                        *
+                        * In addition, instead of returning immediately on
+                        * error, run through the MAC check to make timing
+                        * attacks more difficult.
+                        */
+
+                       if (rl->tls_version == TLS_VERSION_1_1) {
+                               /* Remove opaque IV[Cipherspec.block_length] */
+                               if (plen < rl->iv_size) {
+                                       wpa_printf(MSG_DEBUG, "TLSv1.1: Not "
+                                                  "enough room for IV");
+                                       force_mac_error = 1;
+                                       goto check_mac;
+                               }
+                               os_memmove(out_data, out_data + rl->iv_size,
+                                          plen - rl->iv_size);
+                               plen -= rl->iv_size;
+                       }
+
+                       /* Verify and remove padding */
+                       if (plen == 0) {
                                wpa_printf(MSG_DEBUG, "TLSv1: Too short record"
                                           " (no pad)");
-                               *alert = TLS_ALERT_DECODE_ERROR;
-                               return -1;
+                               force_mac_error = 1;
+                               goto check_mac;
                        }
-                       padlen = out_data[in_len - 1];
-                       if (padlen >= in_len) {
+                       padlen = out_data[plen - 1];
+                       if (padlen >= plen) {
                                wpa_printf(MSG_DEBUG, "TLSv1: Incorrect pad "
-                                          "length (%u, in_len=%lu) in "
+                                          "length (%u, plen=%lu) in "
                                           "received record",
-                                          padlen, (unsigned long) in_len);
-                               *alert = TLS_ALERT_DECRYPTION_FAILED;
-                               return -1;
+                                          padlen, (unsigned long) plen);
+                               force_mac_error = 1;
+                               goto check_mac;
                        }
-                       for (i = in_len - padlen; i < in_len; i++) {
+                       for (i = plen - padlen - 1; i < plen - 1; i++) {
                                if (out_data[i] != padlen) {
                                        wpa_hexdump(MSG_DEBUG,
                                                    "TLSv1: Invalid pad in "
                                                    "received record",
-                                                   out_data + in_len - padlen,
-                                                   padlen);
-                                       *alert = TLS_ALERT_DECRYPTION_FAILED;
-                                       return -1;
+                                                   out_data + plen - padlen -
+                                                   1, padlen + 1);
+                                       force_mac_error = 1;
+                                       goto check_mac;
                                }
                        }
 
-                       *out_len -= padlen + 1;
+                       plen -= padlen + 1;
                }
 
-               wpa_hexdump(MSG_MSGDUMP,
-                           "TLSv1: Record Layer - Decrypted data",
-                           out_data, in_len);
+       check_mac:
+               wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Record Layer - Decrypted "
+                               "data with IV and padding removed",
+                               out_data, plen);
 
-               if (*out_len < rl->hash_size) {
+               if (plen < rl->hash_size) {
                        wpa_printf(MSG_DEBUG, "TLSv1: Too short record; no "
                                   "hash value");
-                       *alert = TLS_ALERT_INTERNAL_ERROR;
+                       *alert = TLS_ALERT_BAD_RECORD_MAC;
                        return -1;
                }
 
-               *out_len -= rl->hash_size;
+               plen -= rl->hash_size;
 
                hmac = crypto_hash_init(rl->hash_alg, rl->read_mac_secret,
                                        rl->hash_size);
@@ -377,22 +445,30 @@ int tlsv1_record_receive(struct tlsv1_record_layer *rl,
                crypto_hash_update(hmac, rl->read_seq_num, TLS_SEQ_NUM_LEN);
                /* type + version + length + fragment */
                crypto_hash_update(hmac, in_data - TLS_RECORD_HEADER_LEN, 3);
-               WPA_PUT_BE16(len, *out_len);
+               WPA_PUT_BE16(len, plen);
                crypto_hash_update(hmac, len, 2);
-               crypto_hash_update(hmac, out_data, *out_len);
+               crypto_hash_update(hmac, out_data, plen);
                hlen = sizeof(hash);
                if (crypto_hash_finish(hmac, hash, &hlen) < 0) {
                        wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed "
                                   "to calculate HMAC");
+                       *alert = TLS_ALERT_INTERNAL_ERROR;
                        return -1;
                }
                if (hlen != rl->hash_size ||
-                   os_memcmp(hash, out_data + *out_len, hlen) != 0) {
+                   os_memcmp(hash, out_data + plen, hlen) != 0 ||
+                   force_mac_error) {
                        wpa_printf(MSG_DEBUG, "TLSv1: Invalid HMAC value in "
-                                  "received message");
+                                  "received message (force_mac_error=%d)",
+                                  force_mac_error);
                        *alert = TLS_ALERT_BAD_RECORD_MAC;
                        return -1;
                }
+
+               *out_len = plen;
+       } else {
+               os_memcpy(out_data, in_data, in_len);
+               *out_len = in_len;
        }
 
        /* TLSCompressed must not be more than 2^14+1024 bytes */
index 9c7c0a4..0d62816 100644 (file)
@@ -1,6 +1,6 @@
 /*
- * TLSv1 Record Protocol
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * TLS v1.0 (RFC 2246) and v1.1 (RFC 4346) Record Protocol
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -35,6 +35,8 @@ enum {
 };
 
 struct tlsv1_record_layer {
+       u16 tls_version;
+
        u8 write_mac_secret[TLS_MAX_WRITE_MAC_SECRET_LEN];
        u8 read_mac_secret[TLS_MAX_WRITE_MAC_SECRET_LEN];
        u8 write_key[TLS_MAX_WRITE_KEY_LEN];
@@ -66,7 +68,8 @@ int tlsv1_record_set_cipher_suite(struct tlsv1_record_layer *rl,
 int tlsv1_record_change_write_cipher(struct tlsv1_record_layer *rl);
 int tlsv1_record_change_read_cipher(struct tlsv1_record_layer *rl);
 int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf,
-                     size_t buf_size, size_t payload_len, size_t *out_len);
+                     size_t buf_size, const u8 *payload, size_t payload_len,
+                     size_t *out_len);
 int tlsv1_record_receive(struct tlsv1_record_layer *rl,
                         const u8 *in_data, size_t in_len,
                         u8 *out_data, size_t *out_len, u8 *alert);
index 6a61235..1f48aa5 100644 (file)
@@ -1,6 +1,6 @@
 /*
- * TLSv1 server (RFC 2246)
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * TLS v1.0 (RFC 2246) and v1.1 (RFC 4346) server
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -201,10 +201,8 @@ int tlsv1_server_encrypt(struct tlsv1_server *conn,
        wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Plaintext AppData",
                        in_data, in_len);
 
-       os_memcpy(out_data + TLS_RECORD_HEADER_LEN, in_data, in_len);
-
        if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_APPLICATION_DATA,
-                             out_data, out_len, in_len, &rlen) < 0) {
+                             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);
index 49e811f..9ffe05c 100644 (file)
@@ -85,15 +85,26 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct,
        conn->client_version = WPA_GET_BE16(pos);
        wpa_printf(MSG_DEBUG, "TLSv1: Client version %d.%d",
                   conn->client_version >> 8, conn->client_version & 0xff);
-       if (conn->client_version < TLS_VERSION) {
+       if (conn->client_version < TLS_VERSION_1) {
                wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in "
-                          "ClientHello");
+                          "ClientHello %u.%u",
+                          conn->client_version >> 8,
+                          conn->client_version & 0xff);
                tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                   TLS_ALERT_PROTOCOL_VERSION);
                return -1;
        }
        pos += 2;
 
+       if (TLS_VERSION == TLS_VERSION_1)
+               conn->rl.tls_version = TLS_VERSION_1;
+       else if (conn->client_version > TLS_VERSION_1_1)
+               conn->rl.tls_version = TLS_VERSION_1_1;
+       else
+               conn->rl.tls_version = conn->client_version;
+       wpa_printf(MSG_DEBUG, "TLSv1: Using TLS v%s",
+                  conn->rl.tls_version == TLS_VERSION_1_1 ? "1.1" : "1.0");
+
        /* Random random */
        if (end - pos < TLS_RANDOM_LEN)
                goto decode_error;
@@ -424,7 +435,7 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct,
        }
 
        if (x509_certificate_chain_validate(conn->cred->trusted_certs, chain,
-                                           &reason) < 0) {
+                                           &reason, 0) < 0) {
                int tls_reason;
                wpa_printf(MSG_DEBUG, "TLSv1: Server certificate chain "
                           "validation failed (reason=%d)", reason);
@@ -483,6 +494,14 @@ static int tls_process_client_key_exchange_rsa(
 
        encr_len = WPA_GET_BE16(pos);
        pos += 2;
+       if (pos + encr_len > end) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Invalid ClientKeyExchange "
+                          "format: encr_len=%u left=%u",
+                          encr_len, (unsigned int) (end - pos));
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
 
        outbuflen = outlen = end - pos;
        out = os_malloc(outlen >= TLS_PRE_MASTER_SECRET_LEN ?
@@ -512,21 +531,21 @@ static int tls_process_client_key_exchange_rsa(
         */
 
        if (crypto_private_key_decrypt_pkcs1_v15(conn->cred->key,
-                                                pos, end - pos,
+                                                pos, encr_len,
                                                 out, &outlen) < 0) {
                wpa_printf(MSG_DEBUG, "TLSv1: Failed to decrypt "
-                          "PreMasterSecret (encr_len=%d outlen=%lu)",
-                          (int) (end - pos), (unsigned long) outlen);
+                          "PreMasterSecret (encr_len=%u outlen=%lu)",
+                          encr_len, (unsigned long) outlen);
                use_random = 1;
        }
 
-       if (outlen != TLS_PRE_MASTER_SECRET_LEN) {
+       if (!use_random && outlen != TLS_PRE_MASTER_SECRET_LEN) {
                wpa_printf(MSG_DEBUG, "TLSv1: Unexpected PreMasterSecret "
                           "length %lu", (unsigned long) outlen);
                use_random = 1;
        }
 
-       if (WPA_GET_BE16(out) != conn->client_version) {
+       if (!use_random && WPA_GET_BE16(out) != conn->client_version) {
                wpa_printf(MSG_DEBUG, "TLSv1: Client version in "
                           "ClientKeyExchange does not match with version in "
                           "ClientHello");
index 6d1df7f..63d70a2 100644 (file)
@@ -1,6 +1,6 @@
 /*
- * TLSv1 server - write handshake message
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * TLS v1.0 (RFC 2246) and v1.1 (RFC 4346) server - write handshake message
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -18,6 +18,7 @@
 #include "crypto/md5.h"
 #include "crypto/sha1.h"
 #include "crypto/tls.h"
+#include "crypto/random.h"
 #include "x509v3.h"
 #include "tlsv1_common.h"
 #include "tlsv1_record.h"
@@ -58,7 +59,7 @@ static int tls_write_server_hello(struct tlsv1_server *conn,
 
        os_get_time(&now);
        WPA_PUT_BE32(conn->server_random, now.sec);
-       if (os_get_random(conn->server_random + 4, TLS_RANDOM_LEN - 4)) {
+       if (random_get_bytes(conn->server_random + 4, TLS_RANDOM_LEN - 4)) {
                wpa_printf(MSG_ERROR, "TLSv1: Could not generate "
                           "server_random");
                return -1;
@@ -67,7 +68,7 @@ static int tls_write_server_hello(struct tlsv1_server *conn,
                    conn->server_random, TLS_RANDOM_LEN);
 
        conn->session_id_len = TLS_SESSION_ID_MAX_LEN;
-       if (os_get_random(conn->session_id, conn->session_id_len)) {
+       if (random_get_bytes(conn->session_id, conn->session_id_len)) {
                wpa_printf(MSG_ERROR, "TLSv1: Could not generate "
                           "session_id");
                return -1;
@@ -86,7 +87,7 @@ static int tls_write_server_hello(struct tlsv1_server *conn,
        pos += 3;
        /* body - ServerHello */
        /* ProtocolVersion server_version */
-       WPA_PUT_BE16(pos, TLS_VERSION);
+       WPA_PUT_BE16(pos, conn->rl.tls_version);
        pos += 2;
        /* Random random: uint32 gmt_unix_time, opaque random_bytes */
        os_memcpy(pos, conn->server_random, TLS_RANDOM_LEN);
@@ -142,7 +143,8 @@ static int tls_write_server_hello(struct tlsv1_server *conn,
        tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
 
        if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
-                             rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+                             rhdr, end - rhdr, hs_start, pos - hs_start,
+                             &rlen) < 0) {
                wpa_printf(MSG_DEBUG, "TLSv1: Failed to create TLS record");
                tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                   TLS_ALERT_INTERNAL_ERROR);
@@ -226,7 +228,8 @@ static int tls_write_server_certificate(struct tlsv1_server *conn,
        WPA_PUT_BE24(hs_length, pos - hs_length - 3);
 
        if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
-                             rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+                             rhdr, end - rhdr, hs_start, pos - hs_start,
+                             &rlen) < 0) {
                wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
                tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                   TLS_ALERT_INTERNAL_ERROR);
@@ -287,7 +290,7 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn,
                                   TLS_ALERT_INTERNAL_ERROR);
                return -1;
        }
-       if (os_get_random(conn->dh_secret, conn->dh_secret_len)) {
+       if (random_get_bytes(conn->dh_secret, conn->dh_secret_len)) {
                wpa_printf(MSG_DEBUG, "TLSv1: Failed to get random "
                           "data for Diffie-Hellman");
                tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
@@ -417,7 +420,8 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn,
        WPA_PUT_BE24(hs_length, pos - hs_length - 3);
 
        if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
-                             rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+                             rhdr, end - rhdr, hs_start, pos - hs_start,
+                             &rlen) < 0) {
                wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
                tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                   TLS_ALERT_INTERNAL_ERROR);
@@ -482,7 +486,8 @@ static int tls_write_server_certificate_request(struct tlsv1_server *conn,
        WPA_PUT_BE24(hs_length, pos - hs_length - 3);
 
        if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
-                             rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+                             rhdr, end - rhdr, hs_start, pos - hs_start,
+                             &rlen) < 0) {
                wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
                tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                   TLS_ALERT_INTERNAL_ERROR);
@@ -501,40 +506,35 @@ static int tls_write_server_certificate_request(struct tlsv1_server *conn,
 static int tls_write_server_hello_done(struct tlsv1_server *conn,
                                       u8 **msgpos, u8 *end)
 {
-       u8 *pos, *rhdr, *hs_start, *hs_length;
+       u8 *pos;
        size_t rlen;
-
-       pos = *msgpos;
+       u8 payload[4];
 
        wpa_printf(MSG_DEBUG, "TLSv1: Send ServerHelloDone");
-       rhdr = pos;
-       pos += TLS_RECORD_HEADER_LEN;
 
        /* opaque fragment[TLSPlaintext.length] */
 
        /* Handshake */
-       hs_start = pos;
+       pos = payload;
        /* HandshakeType msg_type */
        *pos++ = TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE;
-       /* uint24 length (to be filled) */
-       hs_length = pos;
+       /* uint24 length */
+       WPA_PUT_BE24(pos, 0);
        pos += 3;
        /* body - ServerHelloDone (empty) */
 
-       WPA_PUT_BE24(hs_length, pos - hs_length - 3);
-
        if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
-                             rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+                             *msgpos, end - *msgpos, payload, pos - payload,
+                             &rlen) < 0) {
                wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
                tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                   TLS_ALERT_INTERNAL_ERROR);
                return -1;
        }
-       pos = rhdr + rlen;
 
-       tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+       tls_verify_hash_add(&conn->verify, payload, pos - payload);
 
-       *msgpos = pos;
+       *msgpos += rlen;
 
        return 0;
 }
@@ -543,17 +543,16 @@ static int tls_write_server_hello_done(struct tlsv1_server *conn,
 static int tls_write_server_change_cipher_spec(struct tlsv1_server *conn,
                                               u8 **msgpos, u8 *end)
 {
-       u8 *pos, *rhdr;
        size_t rlen;
-
-       pos = *msgpos;
+       u8 payload[1];
 
        wpa_printf(MSG_DEBUG, "TLSv1: Send ChangeCipherSpec");
-       rhdr = pos;
-       pos += TLS_RECORD_HEADER_LEN;
-       *pos = TLS_CHANGE_CIPHER_SPEC;
+
+       payload[0] = TLS_CHANGE_CIPHER_SPEC;
+
        if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC,
-                             rhdr, end - rhdr, 1, &rlen) < 0) {
+                             *msgpos, end - *msgpos, payload, sizeof(payload),
+                             &rlen) < 0) {
                wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
                tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                   TLS_ALERT_INTERNAL_ERROR);
@@ -568,7 +567,7 @@ static int tls_write_server_change_cipher_spec(struct tlsv1_server *conn,
                return -1;
        }
 
-       *msgpos = rhdr + rlen;
+       *msgpos += rlen;
 
        return 0;
 }
@@ -577,9 +576,9 @@ static int tls_write_server_change_cipher_spec(struct tlsv1_server *conn,
 static int tls_write_server_finished(struct tlsv1_server *conn,
                                     u8 **msgpos, u8 *end)
 {
-       u8 *pos, *rhdr, *hs_start, *hs_length;
+       u8 *pos, *hs_start;
        size_t rlen, hlen;
-       u8 verify_data[TLS_VERIFY_DATA_LEN];
+       u8 verify_data[1 + 3 + TLS_VERIFY_DATA_LEN];
        u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
 
        pos = *msgpos;
@@ -612,40 +611,35 @@ static int tls_write_server_finished(struct tlsv1_server *conn,
 
        if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN,
                    "server finished", hash, MD5_MAC_LEN + SHA1_MAC_LEN,
-                   verify_data, TLS_VERIFY_DATA_LEN)) {
+                   verify_data + 1 + 3, TLS_VERIFY_DATA_LEN)) {
                wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate verify_data");
                tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                   TLS_ALERT_INTERNAL_ERROR);
                return -1;
        }
        wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (server)",
-                       verify_data, TLS_VERIFY_DATA_LEN);
+                       verify_data + 1 + 3, TLS_VERIFY_DATA_LEN);
 
-       rhdr = pos;
-       pos += TLS_RECORD_HEADER_LEN;
        /* Handshake */
-       hs_start = pos;
+       pos = hs_start = verify_data;
        /* HandshakeType msg_type */
        *pos++ = TLS_HANDSHAKE_TYPE_FINISHED;
-       /* uint24 length (to be filled) */
-       hs_length = pos;
+       /* uint24 length */
+       WPA_PUT_BE24(pos, TLS_VERIFY_DATA_LEN);
        pos += 3;
-       os_memcpy(pos, verify_data, TLS_VERIFY_DATA_LEN);
        pos += TLS_VERIFY_DATA_LEN;
-       WPA_PUT_BE24(hs_length, pos - hs_length - 3);
        tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
 
        if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
-                             rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+                             *msgpos, end - *msgpos, hs_start, pos - hs_start,
+                             &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;
        }
 
-       pos = rhdr + rlen;
-
-       *msgpos = pos;
+       *msgpos += rlen;
 
        return 0;
 }
@@ -770,7 +764,8 @@ u8 * tlsv1_server_send_alert(struct tlsv1_server *conn, u8 level,
        /* ContentType type */
        *pos++ = TLS_CONTENT_TYPE_ALERT;
        /* ProtocolVersion version */
-       WPA_PUT_BE16(pos, TLS_VERSION);
+       WPA_PUT_BE16(pos, conn->rl.tls_version ? conn->rl.tls_version :
+                    TLS_VERSION);
        pos += 2;
        /* uint16 length (to be filled) */
        length = pos;
index bc93df6..347f975 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * X.509v3 certificate parsing and processing (RFC 3280 profile)
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -1834,7 +1834,7 @@ static int x509_valid_issuer(const struct x509_certificate *cert)
  */
 int x509_certificate_chain_validate(struct x509_certificate *trusted,
                                    struct x509_certificate *chain,
-                                   int *reason)
+                                   int *reason, int disable_time_checks)
 {
        long unsigned idx;
        int chain_trusted = 0;
@@ -1854,10 +1854,11 @@ int x509_certificate_chain_validate(struct x509_certificate *trusted,
                if (chain_trusted)
                        continue;
 
-               if ((unsigned long) now.sec <
-                   (unsigned long) cert->not_before ||
-                   (unsigned long) now.sec >
-                   (unsigned long) cert->not_after) {
+               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);
index 37292d7..3e2005b 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * X.509v3 certificate parsing and processing
- * Copyright (c) 2006, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -120,7 +120,7 @@ int x509_certificate_check_signature(struct x509_certificate *issuer,
                                     struct x509_certificate *cert);
 int x509_certificate_chain_validate(struct x509_certificate *trusted,
                                    struct x509_certificate *chain,
-                                   int *reason);
+                                   int *reason, int disable_time_checks);
 struct x509_certificate *
 x509_certificate_get_subject(struct x509_certificate *chain,
                             struct x509_name *name);
index 527cf3e..0f1f191 100644 (file)
@@ -28,6 +28,9 @@ LIB_OBJS += os_unix.o
 # Pick correct event loop implementation
 LIB_OBJS += eloop.o
 
+# Pick correct edit implementation
+LIB_OBJS += edit.o
+
 #LIB_OBJS += pcsc_funcs.o
 
 libutils.a: $(LIB_OBJS)
index 155bfce..fb1224b 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Base64 encoding/decoding (RFC1341)
- * Copyright (c) 2005, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2005-2011, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -103,8 +103,9 @@ unsigned char * base64_encode(const unsigned char *src, size_t len,
 unsigned char * base64_decode(const unsigned char *src, size_t len,
                              size_t *out_len)
 {
-       unsigned char dtable[256], *out, *pos, in[4], block[4], tmp;
+       unsigned char dtable[256], *out, *pos, block[4], tmp;
        size_t i, count, olen;
+       int pad = 0;
 
        os_memset(dtable, 0x80, 256);
        for (i = 0; i < sizeof(base64_table) - 1; i++)
@@ -131,7 +132,8 @@ unsigned char * base64_decode(const unsigned char *src, size_t len,
                if (tmp == 0x80)
                        continue;
 
-               in[count] = src[i];
+               if (src[i] == '=')
+                       pad++;
                block[count] = tmp;
                count++;
                if (count == 4) {
@@ -139,16 +141,21 @@ unsigned char * base64_decode(const unsigned char *src, size_t len,
                        *pos++ = (block[1] << 4) | (block[2] >> 2);
                        *pos++ = (block[2] << 6) | block[3];
                        count = 0;
+                       if (pad) {
+                               if (pad == 1)
+                                       pos--;
+                               else if (pad == 2)
+                                       pos -= 2;
+                               else {
+                                       /* Invalid padding */
+                                       os_free(out);
+                                       return NULL;
+                               }
+                               break;
+                       }
                }
        }
 
-       if (pos > out) {
-               if (in[2] == '=')
-                       pos -= 2;
-               else if (in[3] == '=')
-                       pos--;
-       }
-
        *out_len = pos - out;
        return out;
 }
index 73312dd..b87a168 100644 (file)
@@ -13,7 +13,7 @@
  */
 
 #ifndef BASE64_H
-#define BASE64_h
+#define BASE64_H
 
 unsigned char * base64_encode(const unsigned char *src, size_t len,
                              size_t *out_len);
index 1b8ea80..89eca1c 100644 (file)
@@ -29,7 +29,7 @@ static int hex2num(char c)
 }
 
 
-static int hex2byte(const char *hex)
+int hex2byte(const char *hex)
 {
        int a, b;
        a = hex2num(*hex++);
@@ -69,6 +69,30 @@ int hwaddr_aton(const char *txt, u8 *addr)
        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)
index f17bf69..14ab297 100644 (file)
@@ -320,6 +320,9 @@ static inline unsigned int wpa_swap_32(unsigned int v)
 #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 */
@@ -402,6 +405,12 @@ void perror(const char *s);
 #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
@@ -436,7 +445,9 @@ typedef u64 __bitwise le64;
 #endif /* __must_check */
 
 int hwaddr_aton(const char *txt, u8 *addr);
+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);
@@ -459,6 +470,13 @@ 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;
+}
+
+#define broadcast_ether_addr (const u8 *) "\xff\xff\xff\xff\xff\xff"
+
 #include "wpa_debug.h"
 
 
@@ -474,4 +492,11 @@ static inline int is_zero_ether_addr(const u8 *a)
 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 */
diff --git a/src/utils/edit.c b/src/utils/edit.c
new file mode 100644 (file)
index 0000000..c5b17ac
--- /dev/null
@@ -0,0 +1,1178 @@
+/*
+ * Command line editing and history
+ * Copyright (c) 2010-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include <termios.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "list.h"
+#include "edit.h"
+
+#define CMD_BUF_LEN 256
+static char cmdbuf[CMD_BUF_LEN];
+static int cmdbuf_pos = 0;
+static int cmdbuf_len = 0;
+static char currbuf[CMD_BUF_LEN];
+static int currbuf_valid = 0;
+
+#define HISTORY_MAX 100
+
+struct edit_history {
+       struct dl_list list;
+       char str[1];
+};
+
+static struct dl_list history_list;
+static struct edit_history *history_curr;
+
+static void *edit_cb_ctx;
+static void (*edit_cmd_cb)(void *ctx, char *cmd);
+static void (*edit_eof_cb)(void *ctx);
+static char ** (*edit_completion_cb)(void *ctx, const char *cmd, int pos) =
+       NULL;
+
+static struct termios prevt, newt;
+
+
+#define CLEAR_END_LINE "\e[K"
+
+
+void edit_clear_line(void)
+{
+       int i;
+       putchar('\r');
+       for (i = 0; i < cmdbuf_len + 2; i++)
+               putchar(' ');
+}
+
+
+static void move_start(void)
+{
+       cmdbuf_pos = 0;
+       edit_redraw();
+}
+
+
+static void move_end(void)
+{
+       cmdbuf_pos = cmdbuf_len;
+       edit_redraw();
+}
+
+
+static void move_left(void)
+{
+       if (cmdbuf_pos > 0) {
+               cmdbuf_pos--;
+               edit_redraw();
+       }
+}
+
+
+static void move_right(void)
+{
+       if (cmdbuf_pos < cmdbuf_len) {
+               cmdbuf_pos++;
+               edit_redraw();
+       }
+}
+
+
+static void move_word_left(void)
+{
+       while (cmdbuf_pos > 0 && cmdbuf[cmdbuf_pos - 1] == ' ')
+               cmdbuf_pos--;
+       while (cmdbuf_pos > 0 && cmdbuf[cmdbuf_pos - 1] != ' ')
+               cmdbuf_pos--;
+       edit_redraw();
+}
+
+
+static void move_word_right(void)
+{
+       while (cmdbuf_pos < cmdbuf_len && cmdbuf[cmdbuf_pos] == ' ')
+               cmdbuf_pos++;
+       while (cmdbuf_pos < cmdbuf_len && cmdbuf[cmdbuf_pos] != ' ')
+               cmdbuf_pos++;
+       edit_redraw();
+}
+
+
+static void delete_left(void)
+{
+       if (cmdbuf_pos == 0)
+               return;
+
+       edit_clear_line();
+       os_memmove(cmdbuf + cmdbuf_pos - 1, cmdbuf + cmdbuf_pos,
+                  cmdbuf_len - cmdbuf_pos);
+       cmdbuf_pos--;
+       cmdbuf_len--;
+       edit_redraw();
+}
+
+
+static void delete_current(void)
+{
+       if (cmdbuf_pos == cmdbuf_len)
+               return;
+
+       edit_clear_line();
+       os_memmove(cmdbuf + cmdbuf_pos, cmdbuf + cmdbuf_pos + 1,
+                  cmdbuf_len - cmdbuf_pos);
+       cmdbuf_len--;
+       edit_redraw();
+}
+
+
+static void delete_word(void)
+{
+       int pos;
+
+       edit_clear_line();
+       pos = cmdbuf_pos;
+       while (pos > 0 && cmdbuf[pos - 1] == ' ')
+               pos--;
+       while (pos > 0 && cmdbuf[pos - 1] != ' ')
+               pos--;
+       os_memmove(cmdbuf + pos, cmdbuf + cmdbuf_pos, cmdbuf_len - cmdbuf_pos);
+       cmdbuf_len -= cmdbuf_pos - pos;
+       cmdbuf_pos = pos;
+       edit_redraw();
+}
+
+
+static void clear_left(void)
+{
+       if (cmdbuf_pos == 0)
+               return;
+
+       edit_clear_line();
+       os_memmove(cmdbuf, cmdbuf + cmdbuf_pos, cmdbuf_len - cmdbuf_pos);
+       cmdbuf_len -= cmdbuf_pos;
+       cmdbuf_pos = 0;
+       edit_redraw();
+}
+
+
+static void clear_right(void)
+{
+       if (cmdbuf_pos == cmdbuf_len)
+               return;
+
+       edit_clear_line();
+       cmdbuf_len = cmdbuf_pos;
+       edit_redraw();
+}
+
+
+static void history_add(const char *str)
+{
+       struct edit_history *h, *match = NULL, *last = NULL;
+       size_t len, count = 0;
+
+       if (str[0] == '\0')
+               return;
+
+       dl_list_for_each(h, &history_list, struct edit_history, list) {
+               if (os_strcmp(str, h->str) == 0) {
+                       match = h;
+                       break;
+               }
+               last = h;
+               count++;
+       }
+
+       if (match) {
+               dl_list_del(&h->list);
+               dl_list_add(&history_list, &h->list);
+               history_curr = h;
+               return;
+       }
+
+       if (count >= HISTORY_MAX && last) {
+               dl_list_del(&last->list);
+               os_free(last);
+       }
+
+       len = os_strlen(str);
+       h = os_zalloc(sizeof(*h) + len);
+       if (h == NULL)
+               return;
+       dl_list_add(&history_list, &h->list);
+       os_strlcpy(h->str, str, len + 1);
+       history_curr = h;
+}
+
+
+static void history_use(void)
+{
+       edit_clear_line();
+       cmdbuf_len = cmdbuf_pos = os_strlen(history_curr->str);
+       os_memcpy(cmdbuf, history_curr->str, cmdbuf_len);
+       edit_redraw();
+}
+
+
+static void history_prev(void)
+{
+       if (history_curr == NULL)
+               return;
+
+       if (history_curr ==
+           dl_list_first(&history_list, struct edit_history, list)) {
+               if (!currbuf_valid) {
+                       cmdbuf[cmdbuf_len] = '\0';
+                       os_memcpy(currbuf, cmdbuf, cmdbuf_len + 1);
+                       currbuf_valid = 1;
+                       history_use();
+                       return;
+               }
+       }
+
+       if (history_curr ==
+           dl_list_last(&history_list, struct edit_history, list))
+               return;
+
+       history_curr = dl_list_entry(history_curr->list.next,
+                                    struct edit_history, list);
+       history_use();
+}
+
+
+static void history_next(void)
+{
+       if (history_curr == NULL ||
+           history_curr ==
+           dl_list_first(&history_list, struct edit_history, list)) {
+               if (currbuf_valid) {
+                       currbuf_valid = 0;
+                       edit_clear_line();
+                       cmdbuf_len = cmdbuf_pos = os_strlen(currbuf);
+                       os_memcpy(cmdbuf, currbuf, cmdbuf_len);
+                       edit_redraw();
+               }
+               return;
+       }
+
+       history_curr = dl_list_entry(history_curr->list.prev,
+                                    struct edit_history, list);
+       history_use();
+}
+
+
+static void history_read(const char *fname)
+{
+       FILE *f;
+       char buf[CMD_BUF_LEN], *pos;
+
+       f = fopen(fname, "r");
+       if (f == NULL)
+               return;
+
+       while (fgets(buf, CMD_BUF_LEN, f)) {
+               for (pos = buf; *pos; pos++) {
+                       if (*pos == '\r' || *pos == '\n') {
+                               *pos = '\0';
+                               break;
+                       }
+               }
+               history_add(buf);
+       }
+
+       fclose(f);
+}
+
+
+static void history_write(const char *fname,
+                         int (*filter_cb)(void *ctx, const char *cmd))
+{
+       FILE *f;
+       struct edit_history *h;
+
+       f = fopen(fname, "w");
+       if (f == NULL)
+               return;
+
+       dl_list_for_each_reverse(h, &history_list, struct edit_history, list) {
+               if (filter_cb && filter_cb(edit_cb_ctx, h->str))
+                       continue;
+               fprintf(f, "%s\n", h->str);
+       }
+
+       fclose(f);
+}
+
+
+static void history_debug_dump(void)
+{
+       struct edit_history *h;
+       edit_clear_line();
+       printf("\r");
+       dl_list_for_each_reverse(h, &history_list, struct edit_history, list)
+               printf("%s%s\n", h == history_curr ? "[C]" : "", h->str);
+       if (currbuf_valid)
+               printf("{%s}\n", currbuf);
+       edit_redraw();
+}
+
+
+static void insert_char(int c)
+{
+       if (cmdbuf_len >= (int) sizeof(cmdbuf) - 1)
+               return;
+       if (cmdbuf_len == cmdbuf_pos) {
+               cmdbuf[cmdbuf_pos++] = c;
+               cmdbuf_len++;
+               putchar(c);
+               fflush(stdout);
+       } else {
+               os_memmove(cmdbuf + cmdbuf_pos + 1, cmdbuf + cmdbuf_pos,
+                          cmdbuf_len - cmdbuf_pos);
+               cmdbuf[cmdbuf_pos++] = c;
+               cmdbuf_len++;
+               edit_redraw();
+       }
+}
+
+
+static void process_cmd(void)
+{
+
+       if (cmdbuf_len == 0) {
+               printf("\n> ");
+               fflush(stdout);
+               return;
+       }
+       printf("\n");
+       cmdbuf[cmdbuf_len] = '\0';
+       history_add(cmdbuf);
+       cmdbuf_pos = 0;
+       cmdbuf_len = 0;
+       edit_cmd_cb(edit_cb_ctx, cmdbuf);
+       printf("> ");
+       fflush(stdout);
+}
+
+
+static void free_completions(char **c)
+{
+       int i;
+       if (c == NULL)
+               return;
+       for (i = 0; c[i]; i++)
+               os_free(c[i]);
+       os_free(c);
+}
+
+
+static int filter_strings(char **c, char *str, size_t len)
+{
+       int i, j;
+
+       for (i = 0, j = 0; c[j]; j++) {
+               if (os_strncasecmp(c[j], str, len) == 0) {
+                       if (i != j) {
+                               c[i] = c[j];
+                               c[j] = NULL;
+                       }
+                       i++;
+               } else {
+                       os_free(c[j]);
+                       c[j] = NULL;
+               }
+       }
+       c[i] = NULL;
+       return i;
+}
+
+
+static int common_len(const char *a, const char *b)
+{
+       int len = 0;
+       while (a[len] && a[len] == b[len])
+               len++;
+       return len;
+}
+
+
+static int max_common_length(char **c)
+{
+       int len, i;
+
+       len = os_strlen(c[0]);
+       for (i = 1; c[i]; i++) {
+               int same = common_len(c[0], c[i]);
+               if (same < len)
+                       len = same;
+       }
+
+       return len;
+}
+
+
+static int cmp_str(const void *a, const void *b)
+{
+       return os_strcmp(* (const char **) a, * (const char **) b);
+}
+
+static void complete(int list)
+{
+       char **c;
+       int i, len, count;
+       int start, end;
+       int room, plen, add_space;
+
+       if (edit_completion_cb == NULL)
+               return;
+
+       cmdbuf[cmdbuf_len] = '\0';
+       c = edit_completion_cb(edit_cb_ctx, cmdbuf, cmdbuf_pos);
+       if (c == NULL)
+               return;
+
+       end = cmdbuf_pos;
+       start = end;
+       while (start > 0 && cmdbuf[start - 1] != ' ')
+               start--;
+       plen = end - start;
+
+       count = filter_strings(c, &cmdbuf[start], plen);
+       if (count == 0) {
+               free_completions(c);
+               return;
+       }
+
+       len = max_common_length(c);
+       if (len <= plen && count > 1) {
+               if (list) {
+                       qsort(c, count, sizeof(char *), cmp_str);
+                       edit_clear_line();
+                       printf("\r");
+                       for (i = 0; c[i]; i++)
+                               printf("%s%s", i > 0 ? " " : "", c[i]);
+                       printf("\n");
+                       edit_redraw();
+               }
+               free_completions(c);
+               return;
+       }
+       len -= plen;
+
+       room = sizeof(cmdbuf) - 1 - cmdbuf_len;
+       if (room < len)
+               len = room;
+       add_space = count == 1 && len < room;
+
+       os_memmove(cmdbuf + cmdbuf_pos + len + add_space, cmdbuf + cmdbuf_pos,
+                  cmdbuf_len - cmdbuf_pos);
+       os_memcpy(&cmdbuf[cmdbuf_pos - plen], c[0], plen + len);
+       if (add_space)
+               cmdbuf[cmdbuf_pos + len] = ' ';
+
+       cmdbuf_pos += len + add_space;
+       cmdbuf_len += len + add_space;
+
+       edit_redraw();
+
+       free_completions(c);
+}
+
+
+enum edit_key_code {
+       EDIT_KEY_NONE = 256,
+       EDIT_KEY_TAB,
+       EDIT_KEY_UP,
+       EDIT_KEY_DOWN,
+       EDIT_KEY_RIGHT,
+       EDIT_KEY_LEFT,
+       EDIT_KEY_ENTER,
+       EDIT_KEY_BACKSPACE,
+       EDIT_KEY_INSERT,
+       EDIT_KEY_DELETE,
+       EDIT_KEY_HOME,
+       EDIT_KEY_END,
+       EDIT_KEY_PAGE_UP,
+       EDIT_KEY_PAGE_DOWN,
+       EDIT_KEY_F1,
+       EDIT_KEY_F2,
+       EDIT_KEY_F3,
+       EDIT_KEY_F4,
+       EDIT_KEY_F5,
+       EDIT_KEY_F6,
+       EDIT_KEY_F7,
+       EDIT_KEY_F8,
+       EDIT_KEY_F9,
+       EDIT_KEY_F10,
+       EDIT_KEY_F11,
+       EDIT_KEY_F12,
+       EDIT_KEY_CTRL_UP,
+       EDIT_KEY_CTRL_DOWN,
+       EDIT_KEY_CTRL_RIGHT,
+       EDIT_KEY_CTRL_LEFT,
+       EDIT_KEY_CTRL_A,
+       EDIT_KEY_CTRL_B,
+       EDIT_KEY_CTRL_D,
+       EDIT_KEY_CTRL_E,
+       EDIT_KEY_CTRL_F,
+       EDIT_KEY_CTRL_G,
+       EDIT_KEY_CTRL_H,
+       EDIT_KEY_CTRL_J,
+       EDIT_KEY_CTRL_K,
+       EDIT_KEY_CTRL_L,
+       EDIT_KEY_CTRL_N,
+       EDIT_KEY_CTRL_O,
+       EDIT_KEY_CTRL_P,
+       EDIT_KEY_CTRL_R,
+       EDIT_KEY_CTRL_T,
+       EDIT_KEY_CTRL_U,
+       EDIT_KEY_CTRL_V,
+       EDIT_KEY_CTRL_W,
+       EDIT_KEY_ALT_UP,
+       EDIT_KEY_ALT_DOWN,
+       EDIT_KEY_ALT_RIGHT,
+       EDIT_KEY_ALT_LEFT,
+       EDIT_KEY_SHIFT_UP,
+       EDIT_KEY_SHIFT_DOWN,
+       EDIT_KEY_SHIFT_RIGHT,
+       EDIT_KEY_SHIFT_LEFT,
+       EDIT_KEY_ALT_SHIFT_UP,
+       EDIT_KEY_ALT_SHIFT_DOWN,
+       EDIT_KEY_ALT_SHIFT_RIGHT,
+       EDIT_KEY_ALT_SHIFT_LEFT,
+       EDIT_KEY_EOF
+};
+
+static void show_esc_buf(const char *esc_buf, char c, int i)
+{
+       edit_clear_line();
+       printf("\rESC buffer '%s' c='%c' [%d]\n", esc_buf, c, i);
+       edit_redraw();
+}
+
+
+static enum edit_key_code esc_seq_to_key1_no(char last)
+{
+       switch (last) {
+       case 'A':
+               return EDIT_KEY_UP;
+       case 'B':
+               return EDIT_KEY_DOWN;
+       case 'C':
+               return EDIT_KEY_RIGHT;
+       case 'D':
+               return EDIT_KEY_LEFT;
+       default:
+               return EDIT_KEY_NONE;
+       }
+}
+
+
+static enum edit_key_code esc_seq_to_key1_shift(char last)
+{
+       switch (last) {
+       case 'A':
+               return EDIT_KEY_SHIFT_UP;
+       case 'B':
+               return EDIT_KEY_SHIFT_DOWN;
+       case 'C':
+               return EDIT_KEY_SHIFT_RIGHT;
+       case 'D':
+               return EDIT_KEY_SHIFT_LEFT;
+       default:
+               return EDIT_KEY_NONE;
+       }
+}
+
+
+static enum edit_key_code esc_seq_to_key1_alt(char last)
+{
+       switch (last) {
+       case 'A':
+               return EDIT_KEY_ALT_UP;
+       case 'B':
+               return EDIT_KEY_ALT_DOWN;
+       case 'C':
+               return EDIT_KEY_ALT_RIGHT;
+       case 'D':
+               return EDIT_KEY_ALT_LEFT;
+       default:
+               return EDIT_KEY_NONE;
+       }
+}
+
+
+static enum edit_key_code esc_seq_to_key1_alt_shift(char last)
+{
+       switch (last) {
+       case 'A':
+               return EDIT_KEY_ALT_SHIFT_UP;
+       case 'B':
+               return EDIT_KEY_ALT_SHIFT_DOWN;
+       case 'C':
+               return EDIT_KEY_ALT_SHIFT_RIGHT;
+       case 'D':
+               return EDIT_KEY_ALT_SHIFT_LEFT;
+       default:
+               return EDIT_KEY_NONE;
+       }
+}
+
+
+static enum edit_key_code esc_seq_to_key1_ctrl(char last)
+{
+       switch (last) {
+       case 'A':
+               return EDIT_KEY_CTRL_UP;
+       case 'B':
+               return EDIT_KEY_CTRL_DOWN;
+       case 'C':
+               return EDIT_KEY_CTRL_RIGHT;
+       case 'D':
+               return EDIT_KEY_CTRL_LEFT;
+       default:
+               return EDIT_KEY_NONE;
+       }
+}
+
+
+static enum edit_key_code esc_seq_to_key1(int param1, int param2, char last)
+{
+       /* ESC-[<param1>;<param2><last> */
+
+       if (param1 < 0 && param2 < 0)
+               return esc_seq_to_key1_no(last);
+
+       if (param1 == 1 && param2 == 2)
+               return esc_seq_to_key1_shift(last);
+
+       if (param1 == 1 && param2 == 3)
+               return esc_seq_to_key1_alt(last);
+
+       if (param1 == 1 && param2 == 4)
+               return esc_seq_to_key1_alt_shift(last);
+
+       if (param1 == 1 && param2 == 5)
+               return esc_seq_to_key1_ctrl(last);
+
+       if (param2 < 0) {
+               if (last != '~')
+                       return EDIT_KEY_NONE;
+               switch (param1) {
+               case 2:
+                       return EDIT_KEY_INSERT;
+               case 3:
+                       return EDIT_KEY_DELETE;
+               case 5:
+                       return EDIT_KEY_PAGE_UP;
+               case 6:
+                       return EDIT_KEY_PAGE_DOWN;
+               case 15:
+                       return EDIT_KEY_F5;
+               case 17:
+                       return EDIT_KEY_F6;
+               case 18:
+                       return EDIT_KEY_F7;
+               case 19:
+                       return EDIT_KEY_F8;
+               case 20:
+                       return EDIT_KEY_F9;
+               case 21:
+                       return EDIT_KEY_F10;
+               case 23:
+                       return EDIT_KEY_F11;
+               case 24:
+                       return EDIT_KEY_F12;
+               }
+       }
+
+       return EDIT_KEY_NONE;
+}
+
+
+static enum edit_key_code esc_seq_to_key2(int param1, int param2, char last)
+{
+       /* ESC-O<param1>;<param2><last> */
+
+       if (param1 >= 0 || param2 >= 0)
+               return EDIT_KEY_NONE;
+
+       switch (last) {
+       case 'F':
+               return EDIT_KEY_END;
+       case 'H':
+               return EDIT_KEY_HOME;
+       case 'P':
+               return EDIT_KEY_F1;
+       case 'Q':
+               return EDIT_KEY_F2;
+       case 'R':
+               return EDIT_KEY_F3;
+       case 'S':
+               return EDIT_KEY_F4;
+       default:
+               return EDIT_KEY_NONE;
+       }
+}
+
+
+static enum edit_key_code esc_seq_to_key(char *seq)
+{
+       char last, *pos;
+       int param1 = -1, param2 = -1;
+       enum edit_key_code ret = EDIT_KEY_NONE;
+
+       last = '\0';
+       for (pos = seq; *pos; pos++)
+               last = *pos;
+
+       if (seq[1] >= '0' && seq[1] <= '9') {
+               param1 = atoi(&seq[1]);
+               pos = os_strchr(seq, ';');
+               if (pos)
+                       param2 = atoi(pos + 1);
+       }
+
+       if (seq[0] == '[')
+               ret = esc_seq_to_key1(param1, param2, last);
+       else if (seq[0] == 'O')
+               ret = esc_seq_to_key2(param1, param2, last);
+
+       if (ret != EDIT_KEY_NONE)
+               return ret;
+
+       edit_clear_line();
+       printf("\rUnknown escape sequence '%s'\n", seq);
+       edit_redraw();
+       return EDIT_KEY_NONE;
+}
+
+
+static enum edit_key_code edit_read_key(int sock)
+{
+       int c;
+       unsigned char buf[1];
+       int res;
+       static int esc = -1;
+       static char esc_buf[7];
+
+       res = read(sock, buf, 1);
+       if (res < 0)
+               perror("read");
+       if (res <= 0)
+               return EDIT_KEY_EOF;
+
+       c = buf[0];
+
+       if (esc >= 0) {
+               if (c == 27 /* ESC */) {
+                       esc = 0;
+                       return EDIT_KEY_NONE;
+               }
+
+               if (esc == 6) {
+                       show_esc_buf(esc_buf, c, 0);
+                       esc = -1;
+               } else {
+                       esc_buf[esc++] = c;
+                       esc_buf[esc] = '\0';
+               }
+       }
+
+       if (esc == 1) {
+               if (esc_buf[0] != '[' && esc_buf[0] != 'O') {
+                       show_esc_buf(esc_buf, c, 1);
+                       esc = -1;
+                       return EDIT_KEY_NONE;
+               } else
+                       return EDIT_KEY_NONE; /* Escape sequence continues */
+       }
+
+       if (esc > 1) {
+               if ((c >= '0' && c <= '9') || c == ';')
+                       return EDIT_KEY_NONE; /* Escape sequence continues */
+
+               if (c == '~' || (c >= 'A' && c <= 'Z')) {
+                       esc = -1;
+                       return esc_seq_to_key(esc_buf);
+               }
+
+               show_esc_buf(esc_buf, c, 2);
+               esc = -1;
+               return EDIT_KEY_NONE;
+       }
+
+       switch (c) {
+       case 1:
+               return EDIT_KEY_CTRL_A;
+       case 2:
+               return EDIT_KEY_CTRL_B;
+       case 4:
+               return EDIT_KEY_CTRL_D;
+       case 5:
+               return EDIT_KEY_CTRL_E;
+       case 6:
+               return EDIT_KEY_CTRL_F;
+       case 7:
+               return EDIT_KEY_CTRL_G;
+       case 8:
+               return EDIT_KEY_CTRL_H;
+       case 9:
+               return EDIT_KEY_TAB;
+       case 10:
+               return EDIT_KEY_CTRL_J;
+       case 13: /* CR */
+               return EDIT_KEY_ENTER;
+       case 11:
+               return EDIT_KEY_CTRL_K;
+       case 12:
+               return EDIT_KEY_CTRL_L;
+       case 14:
+               return EDIT_KEY_CTRL_N;
+       case 15:
+               return EDIT_KEY_CTRL_O;
+       case 16:
+               return EDIT_KEY_CTRL_P;
+       case 18:
+               return EDIT_KEY_CTRL_R;
+       case 20:
+               return EDIT_KEY_CTRL_T;
+       case 21:
+               return EDIT_KEY_CTRL_U;
+       case 22:
+               return EDIT_KEY_CTRL_V;
+       case 23:
+               return EDIT_KEY_CTRL_W;
+       case 27: /* ESC */
+               esc = 0;
+               return EDIT_KEY_NONE;
+       case 127:
+               return EDIT_KEY_BACKSPACE;
+       default:
+               return c;
+       }
+}
+
+
+static char search_buf[21];
+static int search_skip;
+
+static char * search_find(void)
+{
+       struct edit_history *h;
+       size_t len = os_strlen(search_buf);
+       int skip = search_skip;
+
+       if (len == 0)
+               return NULL;
+
+       dl_list_for_each(h, &history_list, struct edit_history, list) {
+               if (os_strstr(h->str, search_buf)) {
+                       if (skip == 0)
+                               return h->str;
+                       skip--;
+               }
+       }
+
+       search_skip = 0;
+       return NULL;
+}
+
+
+static void search_redraw(void)
+{
+       char *match = search_find();
+       printf("\rsearch '%s': %s" CLEAR_END_LINE,
+              search_buf, match ? match : "");
+       printf("\rsearch '%s", search_buf);
+       fflush(stdout);
+}
+
+
+static void search_start(void)
+{
+       edit_clear_line();
+       search_buf[0] = '\0';
+       search_skip = 0;
+       search_redraw();
+}
+
+
+static void search_clear(void)
+{
+       search_redraw();
+       printf("\r" CLEAR_END_LINE);
+}
+
+
+static void search_stop(void)
+{
+       char *match = search_find();
+       search_buf[0] = '\0';
+       search_clear();
+       if (match) {
+               os_strlcpy(cmdbuf, match, CMD_BUF_LEN);
+               cmdbuf_len = os_strlen(cmdbuf);
+               cmdbuf_pos = cmdbuf_len;
+       }
+       edit_redraw();
+}
+
+
+static void search_cancel(void)
+{
+       search_buf[0] = '\0';
+       search_clear();
+       edit_redraw();
+}
+
+
+static void search_backspace(void)
+{
+       size_t len;
+       len = os_strlen(search_buf);
+       if (len == 0)
+               return;
+       search_buf[len - 1] = '\0';
+       search_skip = 0;
+       search_redraw();
+}
+
+
+static void search_next(void)
+{
+       search_skip++;
+       search_find();
+       search_redraw();
+}
+
+
+static void search_char(char c)
+{
+       size_t len;
+       len = os_strlen(search_buf);
+       if (len == sizeof(search_buf) - 1)
+               return;
+       search_buf[len] = c;
+       search_buf[len + 1] = '\0';
+       search_skip = 0;
+       search_redraw();
+}
+
+
+static enum edit_key_code search_key(enum edit_key_code c)
+{
+       switch (c) {
+       case EDIT_KEY_ENTER:
+       case EDIT_KEY_CTRL_J:
+       case EDIT_KEY_LEFT:
+       case EDIT_KEY_RIGHT:
+       case EDIT_KEY_HOME:
+       case EDIT_KEY_END:
+       case EDIT_KEY_CTRL_A:
+       case EDIT_KEY_CTRL_E:
+               search_stop();
+               return c;
+       case EDIT_KEY_DOWN:
+       case EDIT_KEY_UP:
+               search_cancel();
+               return EDIT_KEY_EOF;
+       case EDIT_KEY_CTRL_H:
+       case EDIT_KEY_BACKSPACE:
+               search_backspace();
+               break;
+       case EDIT_KEY_CTRL_R:
+               search_next();
+               break;
+       default:
+               if (c >= 32 && c <= 255)
+                       search_char(c);
+               break;
+       }
+
+       return EDIT_KEY_NONE;
+}
+
+
+static void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx)
+{
+       static int last_tab = 0;
+       static int search = 0;
+       enum edit_key_code c;
+
+       c = edit_read_key(sock);
+
+       if (search) {
+               c = search_key(c);
+               if (c == EDIT_KEY_NONE)
+                       return;
+               search = 0;
+               if (c == EDIT_KEY_EOF)
+                       return;
+       }
+
+       if (c != EDIT_KEY_TAB && c != EDIT_KEY_NONE)
+               last_tab = 0;
+
+       switch (c) {
+       case EDIT_KEY_NONE:
+               break;
+       case EDIT_KEY_EOF:
+               edit_eof_cb(edit_cb_ctx);
+               break;
+       case EDIT_KEY_TAB:
+               complete(last_tab);
+               last_tab = 1;
+               break;
+       case EDIT_KEY_UP:
+       case EDIT_KEY_CTRL_P:
+               history_prev();
+               break;
+       case EDIT_KEY_DOWN:
+       case EDIT_KEY_CTRL_N:
+               history_next();
+               break;
+       case EDIT_KEY_RIGHT:
+       case EDIT_KEY_CTRL_F:
+               move_right();
+               break;
+       case EDIT_KEY_LEFT:
+       case EDIT_KEY_CTRL_B:
+               move_left();
+               break;
+       case EDIT_KEY_CTRL_RIGHT:
+               move_word_right();
+               break;
+       case EDIT_KEY_CTRL_LEFT:
+               move_word_left();
+               break;
+       case EDIT_KEY_DELETE:
+               delete_current();
+               break;
+       case EDIT_KEY_END:
+               move_end();
+               break;
+       case EDIT_KEY_HOME:
+       case EDIT_KEY_CTRL_A:
+               move_start();
+               break;
+       case EDIT_KEY_F2:
+               history_debug_dump();
+               break;
+       case EDIT_KEY_CTRL_D:
+               if (cmdbuf_len > 0) {
+                       delete_current();
+                       return;
+               }
+               printf("\n");
+               edit_eof_cb(edit_cb_ctx);
+               break;
+       case EDIT_KEY_CTRL_E:
+               move_end();
+               break;
+       case EDIT_KEY_CTRL_H:
+       case EDIT_KEY_BACKSPACE:
+               delete_left();
+               break;
+       case EDIT_KEY_ENTER:
+       case EDIT_KEY_CTRL_J:
+               process_cmd();
+               break;
+       case EDIT_KEY_CTRL_K:
+               clear_right();
+               break;
+       case EDIT_KEY_CTRL_L:
+               edit_clear_line();
+               edit_redraw();
+               break;
+       case EDIT_KEY_CTRL_R:
+               search = 1;
+               search_start();
+               break;
+       case EDIT_KEY_CTRL_U:
+               clear_left();
+               break;
+       case EDIT_KEY_CTRL_W:
+               delete_word();
+               break;
+       default:
+               if (c >= 32 && c <= 255)
+                       insert_char(c);
+               break;
+       }
+}
+
+
+int edit_init(void (*cmd_cb)(void *ctx, char *cmd),
+             void (*eof_cb)(void *ctx),
+             char ** (*completion_cb)(void *ctx, const char *cmd, int pos),
+             void *ctx, const char *history_file)
+{
+       currbuf[0] = '\0';
+       dl_list_init(&history_list);
+       history_curr = NULL;
+       if (history_file)
+               history_read(history_file);
+
+       edit_cb_ctx = ctx;
+       edit_cmd_cb = cmd_cb;
+       edit_eof_cb = eof_cb;
+       edit_completion_cb = completion_cb;
+
+       tcgetattr(STDIN_FILENO, &prevt);
+       newt = prevt;
+       newt.c_lflag &= ~(ICANON | ECHO);
+       tcsetattr(STDIN_FILENO, TCSANOW, &newt);
+
+       eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL);
+
+       printf("> ");
+       fflush(stdout);
+
+       return 0;
+}
+
+
+void edit_deinit(const char *history_file,
+                int (*filter_cb)(void *ctx, const char *cmd))
+{
+       struct edit_history *h;
+       if (history_file)
+               history_write(history_file, filter_cb);
+       while ((h = dl_list_first(&history_list, struct edit_history, list))) {
+               dl_list_del(&h->list);
+               os_free(h);
+       }
+       edit_clear_line();
+       putchar('\r');
+       fflush(stdout);
+       eloop_unregister_read_sock(STDIN_FILENO);
+       tcsetattr(STDIN_FILENO, TCSANOW, &prevt);
+}
+
+
+void edit_redraw(void)
+{
+       char tmp;
+       cmdbuf[cmdbuf_len] = '\0';
+       printf("\r> %s", cmdbuf);
+       if (cmdbuf_pos != cmdbuf_len) {
+               tmp = cmdbuf[cmdbuf_pos];
+               cmdbuf[cmdbuf_pos] = '\0';
+               printf("\r> %s", cmdbuf);
+               cmdbuf[cmdbuf_pos] = tmp;
+       }
+       fflush(stdout);
+}
diff --git a/src/utils/edit.h b/src/utils/edit.h
new file mode 100644 (file)
index 0000000..fc4474b
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Command line editing and history
+ * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef EDIT_H
+#define EDIT_H
+
+int edit_init(void (*cmd_cb)(void *ctx, char *cmd),
+             void (*eof_cb)(void *ctx),
+             char ** (*completion_cb)(void *ctx, const char *cmd, int pos),
+             void *ctx, const char *history_file);
+void edit_deinit(const char *history_file,
+                int (*filter_cb)(void *ctx, const char *cmd));
+void edit_clear_line(void);
+void edit_redraw(void);
+
+#endif /* EDIT_H */
diff --git a/src/utils/edit_readline.c b/src/utils/edit_readline.c
new file mode 100644 (file)
index 0000000..1fef7b9
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * Command line editing and history wrapper for readline
+ * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include <readline/readline.h>
+#include <readline/history.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "edit.h"
+
+
+static void *edit_cb_ctx;
+static void (*edit_cmd_cb)(void *ctx, char *cmd);
+static void (*edit_eof_cb)(void *ctx);
+static char ** (*edit_completion_cb)(void *ctx, const char *cmd, int pos) =
+       NULL;
+
+static char **pending_completions = NULL;
+
+
+static void readline_free_completions(void)
+{
+       int i;
+       if (pending_completions == NULL)
+               return;
+       for (i = 0; pending_completions[i]; i++)
+               os_free(pending_completions[i]);
+       os_free(pending_completions);
+       pending_completions = NULL;
+}
+
+
+static char * readline_completion_func(const char *text, int state)
+{
+       static int pos = 0;
+       static size_t len = 0;
+
+       if (pending_completions == NULL) {
+               rl_attempted_completion_over = 1;
+               return NULL;
+       }
+
+       if (state == 0) {
+               pos = 0;
+               len = os_strlen(text);
+       }
+       for (; pending_completions[pos]; pos++) {
+               if (strncmp(pending_completions[pos], text, len) == 0)
+                       return strdup(pending_completions[pos++]);
+       }
+
+       rl_attempted_completion_over = 1;
+       return NULL;
+}
+
+
+static char ** readline_completion(const char *text, int start, int end)
+{
+       readline_free_completions();
+       if (edit_completion_cb)
+               pending_completions = edit_completion_cb(edit_cb_ctx,
+                                                        rl_line_buffer, end);
+       return rl_completion_matches(text, readline_completion_func);
+}
+
+
+static void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx)
+{
+       rl_callback_read_char();
+}
+
+
+static void trunc_nl(char *str)
+{
+       char *pos = str;
+       while (*pos != '\0') {
+               if (*pos == '\n') {
+                       *pos = '\0';
+                       break;
+               }
+               pos++;
+       }
+}
+
+
+static void readline_cmd_handler(char *cmd)
+{
+       if (cmd && *cmd) {
+               HIST_ENTRY *h;
+               while (next_history())
+                       ;
+               h = previous_history();
+               if (h == NULL || os_strcmp(cmd, h->line) != 0)
+                       add_history(cmd);
+               next_history();
+       }
+       if (cmd == NULL) {
+               edit_eof_cb(edit_cb_ctx);
+               return;
+       }
+       trunc_nl(cmd);
+       edit_cmd_cb(edit_cb_ctx, cmd);
+}
+
+
+int edit_init(void (*cmd_cb)(void *ctx, char *cmd),
+             void (*eof_cb)(void *ctx),
+             char ** (*completion_cb)(void *ctx, const char *cmd, int pos),
+             void *ctx, const char *history_file)
+{
+       edit_cb_ctx = ctx;
+       edit_cmd_cb = cmd_cb;
+       edit_eof_cb = eof_cb;
+       edit_completion_cb = completion_cb;
+
+       rl_attempted_completion_function = readline_completion;
+       if (history_file) {
+               read_history(history_file);
+               stifle_history(100);
+       }
+
+       eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL);
+
+       rl_callback_handler_install("> ", readline_cmd_handler);
+
+       return 0;
+}
+
+
+void edit_deinit(const char *history_file,
+                int (*filter_cb)(void *ctx, const char *cmd))
+{
+       rl_callback_handler_remove();
+       readline_free_completions();
+
+       eloop_unregister_read_sock(STDIN_FILENO);
+
+       if (history_file) {
+               /* Save command history, excluding lines that may contain
+                * passwords. */
+               HIST_ENTRY *h;
+               history_set_pos(0);
+               while ((h = current_history())) {
+                       char *p = h->line;
+                       while (*p == ' ' || *p == '\t')
+                               p++;
+                       if (filter_cb && filter_cb(edit_cb_ctx, p)) {
+                               h = remove_history(where_history());
+                               if (h) {
+                                       os_free(h->line);
+                                       free(h->data);
+                                       os_free(h);
+                               } else
+                                       next_history();
+                       } else
+                               next_history();
+               }
+               write_history(history_file);
+       }
+}
+
+
+void edit_clear_line(void)
+{
+}
+
+
+void edit_redraw(void)
+{
+       rl_on_new_line();
+       rl_redisplay();
+}
diff --git a/src/utils/edit_simple.c b/src/utils/edit_simple.c
new file mode 100644 (file)
index 0000000..61fb24e
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Minimal command line editing
+ * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "edit.h"
+
+
+#define CMD_BUF_LEN 256
+static char cmdbuf[CMD_BUF_LEN];
+static int cmdbuf_pos = 0;
+
+static void *edit_cb_ctx;
+static void (*edit_cmd_cb)(void *ctx, char *cmd);
+static void (*edit_eof_cb)(void *ctx);
+
+
+static void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx)
+{
+       int c;
+       unsigned char buf[1];
+       int res;
+
+       res = read(sock, buf, 1);
+       if (res < 0)
+               perror("read");
+       if (res <= 0) {
+               edit_eof_cb(edit_cb_ctx);
+               return;
+       }
+       c = buf[0];
+
+       if (c == '\r' || c == '\n') {
+               cmdbuf[cmdbuf_pos] = '\0';
+               cmdbuf_pos = 0;
+               edit_cmd_cb(edit_cb_ctx, cmdbuf);
+               printf("> ");
+               fflush(stdout);
+               return;
+       }
+
+       if (c >= 32 && c <= 255) {
+               if (cmdbuf_pos < (int) sizeof(cmdbuf) - 1) {
+                       cmdbuf[cmdbuf_pos++] = c;
+               }
+       }
+}
+
+
+int edit_init(void (*cmd_cb)(void *ctx, char *cmd),
+             void (*eof_cb)(void *ctx),
+             char ** (*completion_cb)(void *ctx, const char *cmd, int pos),
+             void *ctx, const char *history_file)
+{
+       edit_cb_ctx = ctx;
+       edit_cmd_cb = cmd_cb;
+       edit_eof_cb = eof_cb;
+       eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL);
+
+       printf("> ");
+       fflush(stdout);
+
+       return 0;
+}
+
+
+void edit_deinit(const char *history_file,
+                int (*filter_cb)(void *ctx, const char *cmd))
+{
+       eloop_unregister_read_sock(STDIN_FILENO);
+}
+
+
+void edit_clear_line(void)
+{
+}
+
+
+void edit_redraw(void)
+{
+       cmdbuf[cmdbuf_pos] = '\0';
+       printf("\r> %s", cmdbuf);
+}
index 4b61598..b550c63 100644 (file)
@@ -300,6 +300,7 @@ int eloop_register_timeout(unsigned int secs, unsigned int usecs,
                           void *eloop_data, void *user_data)
 {
        struct eloop_timeout *timeout, *tmp;
+       os_time_t now_sec;
 
        timeout = os_zalloc(sizeof(*timeout));
        if (timeout == NULL)
@@ -308,7 +309,18 @@ int eloop_register_timeout(unsigned int secs, unsigned int usecs,
                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++;
index 1228f24..a656bf8 100644 (file)
@@ -144,7 +144,7 @@ void eloop_unregister_sock(int sock, eloop_event_type type);
  * Returns: 0 on success, -1 on failure
  *
  * Register an event handler for the given event. This function is used to
- * register eloop implementation specific events which are mainly targetted for
+ * register eloop implementation specific events which are mainly targeted for
  * operating system specific code (driver interface and l2_packet) since the
  * portable code will not be able to use such an OS-specific call. The handler
  * function will be called whenever the event is triggered. The handler
index 94cc72d..c726ece 100644 (file)
@@ -243,12 +243,24 @@ int eloop_register_timeout(unsigned int secs, unsigned int usecs,
                           void *eloop_data, void *user_data)
 {
        struct eloop_timeout *timeout, *tmp, *prev;
+       os_time_t now_sec;
 
        timeout = os_malloc(sizeof(*timeout));
        if (timeout == NULL)
                return -1;
        os_get_time(&timeout->time);
+       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++;
index 63b5c23..cf2a42b 100644 (file)
@@ -34,7 +34,6 @@
 #include <errno.h>
 #endif /* _WIN32_WCE */
 #include <ctype.h>
-#include <time.h>
 
 #ifndef CONFIG_TI_COMPILER
 #ifndef _MSC_VER
index ed7c022..c8dccee 100644 (file)
@@ -75,6 +75,10 @@ static inline unsigned int dl_list_len(struct dl_list *list)
        (dl_list_empty((list)) ? NULL : \
         dl_list_entry((list)->next, type, member))
 
+#define dl_list_last(list, type, member) \
+       (dl_list_empty((list)) ? NULL : \
+        dl_list_entry((list)->prev, type, member))
+
 #define dl_list_for_each(item, list, type, member) \
        for (item = dl_list_entry((list)->next, type, member); \
             &item->member != (list); \
@@ -86,4 +90,12 @@ static inline unsigned int dl_list_len(struct dl_list *list)
             &item->member != (list); \
             item = n, n = dl_list_entry(n->member.next, type, member))
 
+#define dl_list_for_each_reverse(item, list, type, member) \
+       for (item = dl_list_entry((list)->prev, type, member); \
+            &item->member != (list); \
+            item = dl_list_entry(item->member.prev, type, member))
+
+#define DEFINE_DL_LIST(name) \
+       struct dl_list name = { &(name), &(name) }
+
 #endif /* LIST_H */
index f4723d8..f69478a 100644 (file)
@@ -70,6 +70,16 @@ int os_get_time(struct os_time *t);
 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)
index 5260e23..8024a30 100644 (file)
@@ -70,6 +70,24 @@ int os_mktime(int year, int month, int day, int hour, int min, int sec,
 }
 
 
+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)) {
index bab8f17..3fbb777 100644 (file)
@@ -38,6 +38,11 @@ int os_mktime(int year, int month, int day, int hour, int min, int sec,
        return -1;
 }
 
+int os_gmtime(os_time_t t, struct os_tm *tm)
+{
+       return -1;
+}
+
 
 int os_daemonize(const char *pid_file)
 {
index 6f58fa4..b546201 100644 (file)
 
 #include "includes.h"
 
+#include <time.h>
+
+#ifdef ANDROID
+#include <linux/capability.h>
+#include <linux/prctl.h>
+#include <private/android_filesystem_config.h>
+#endif /* ANDROID */
+
 #include "os.h"
 
 #ifdef WPA_TRACE
@@ -98,6 +106,24 @@ int os_mktime(int year, int month, int day, int hour, int min, int sec,
 }
 
 
+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)
@@ -135,9 +161,9 @@ static int os_daemon(int nochdir, int noclose)
 
 int os_daemonize(const char *pid_file)
 {
-#ifdef __uClinux__
+#if defined(__uClinux__) || defined(__sun__)
        return -1;
-#else /* __uClinux__ */
+#else /* defined(__uClinux__) || defined(__sun__) */
        if (os_daemon(0, 0)) {
                perror("daemon");
                return -1;
@@ -152,7 +178,7 @@ int os_daemonize(const char *pid_file)
        }
 
        return -0;
-#endif /* __uClinux__ */
+#endif /* defined(__uClinux__) || defined(__sun__) */
 }
 
 
@@ -232,6 +258,30 @@ char * os_rel2abs_path(const char *rel_path)
 
 int os_program_init(void)
 {
+#ifdef ANDROID
+       /*
+        * We ignore errors here since errors are normal if we
+        * are already running as non-root.
+        */
+       gid_t groups[] = { AID_INET, AID_WIFI, AID_KEYSTORE };
+       struct __user_cap_header_struct header;
+       struct __user_cap_data_struct cap;
+
+       setgroups(sizeof(groups)/sizeof(groups[0]), 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 */
+
 #ifdef WPA_TRACE
        dl_list_init(&alloc_list);
 #endif /* WPA_TRACE */
@@ -285,14 +335,21 @@ char * os_readfile(const char *name, size_t *len)
 {
        FILE *f;
        char *buf;
+       long pos;
 
        f = fopen(name, "rb");
        if (f == NULL)
                return NULL;
 
-       fseek(f, 0, SEEK_END);
-       *len = ftell(f);
-       fseek(f, 0, SEEK_SET);
+       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) {
index 0740964..51bd545 100644 (file)
@@ -13,6 +13,7 @@
  */
 
 #include "includes.h"
+#include <time.h>
 #include <winsock2.h>
 #include <wincrypt.h>
 
@@ -92,6 +93,24 @@ int os_mktime(int year, int month, int day, int hour, int min, int sec,
 }
 
 
+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 */
index bf9f04a..c36193e 100644 (file)
@@ -812,7 +812,7 @@ static int scard_get_record_len(struct scard_data *scard, unsigned char recnum,
        wpa_hexdump(MSG_DEBUG, "SCARD: file length determination response",
                    buf, blen);
 
-       if (blen < 2 || buf[0] != 0x6c) {
+       if (blen < 2 || (buf[0] != 0x6c && buf[0] != 0x67)) {
                wpa_printf(MSG_DEBUG, "SCARD: unexpected response to file "
                           "length determination");
                return -1;
index 92a798a..2e0e872 100644 (file)
@@ -1,3 +1,18 @@
+/*
+ * Radiotap parser
+ *
+ * Copyright 2007              Andy Green <andy@warmcat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
 #ifndef __RADIOTAP_ITER_H
 #define __RADIOTAP_ITER_H
 
index 6f6fc69..c3fd4cf 100644 (file)
@@ -23,16 +23,46 @@ static int wpa_debug_syslog = 0;
 #endif /* CONFIG_DEBUG_SYSLOG */
 
 
-#ifdef CONFIG_DEBUG_FILE
-static FILE *out_file = NULL;
-#endif /* CONFIG_DEBUG_FILE */
 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 */
+
+void android_printf(int level, char *format, ...)
+{
+       if (level >= wpa_debug_level) {
+               va_list ap;
+               if (level == MSG_ERROR)
+                       level = ANDROID_LOG_ERROR;
+               else if (level == MSG_WARNING)
+                       level = ANDROID_LOG_WARN;
+               else if (level == MSG_INFO)
+                       level = ANDROID_LOG_INFO;
+               else
+                       level = ANDROID_LOG_DEBUG;
+               va_start(ap, format);
+               __android_log_vprint(level, ANDROID_LOG_NAME, format, ap);
+               va_end(ap);
+       }
+}
+
+#else /* 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)
 {
        struct os_time tv;
@@ -52,9 +82,13 @@ void wpa_debug_print_timestamp(void)
 
 
 #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_DAEMON);
+       openlog("wpa_supplicant", LOG_PID | LOG_NDELAY, LOG_HOSTAPD);
        wpa_debug_syslog++;
 }
 
@@ -132,6 +166,38 @@ static void _wpa_hexdump(int level, const char *title, const u8 *buf,
        size_t i;
        if (level < wpa_debug_level)
                return;
+#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, len, display);
+               os_free(strbuf);
+               return;
+       }
+#endif /* CONFIG_DEBUG_SYSLOG */
        wpa_debug_print_timestamp();
 #ifdef CONFIG_DEBUG_FILE
        if (out_file) {
@@ -273,11 +339,43 @@ void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf,
 }
 
 
+#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 "
@@ -299,11 +397,14 @@ void wpa_debug_close_file(void)
                return;
        fclose(out_file);
        out_file = NULL;
+       os_free(last_path);
+       last_path = NULL;
 #endif /* CONFIG_DEBUG_FILE */
 }
 
 #endif /* CONFIG_NO_STDOUT_DEBUG */
 
+#endif /* CONFIG_ANDROID_LOG */
 
 #ifndef CONFIG_NO_WPA_MSG
 static wpa_msg_cb_func wpa_msg_cb = NULL;
@@ -314,12 +415,21 @@ void wpa_msg_register_cb(wpa_msg_cb_func 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;
        const int buflen = 2048;
        int len;
+       char prefix[130];
 
        buf = os_malloc(buflen);
        if (buf == NULL) {
@@ -328,9 +438,19 @@ void wpa_msg(void *ctx, int level, const char *fmt, ...)
                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 (res < 0 || res >= (int) sizeof(prefix))
+                               prefix[0] = '\0';
+               }
+       }
        len = vsnprintf(buf, buflen, fmt, ap);
        va_end(ap);
-       wpa_printf(level, "%s", buf);
+       wpa_printf(level, "%s%s", prefix, buf);
        if (wpa_msg_cb)
                wpa_msg_cb(ctx, level, buf, len);
        os_free(buf);
index 6e5e79e..ae36afe 100644 (file)
 /* Debugging function - conditional printf and hex dump. Driver wrappers can
  * use these for debugging purposes. */
 
-enum { MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR };
+enum {
+       MSG_EXCESSIVE, MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR
+};
+
+#ifdef CONFIG_ANDROID_LOG
+
+#define wpa_debug_print_timestamp() do {} while (0)
+#define wpa_hexdump(...)            do {} while (0)
+#define wpa_hexdump_key(...)        do {} while (0)
+#define wpa_hexdump_buf(l,t,b)      do {} while (0)
+#define wpa_hexdump_buf_key(l,t,b)  do {} while (0)
+#define wpa_hexdump_ascii(...)      do {} while (0)
+#define wpa_hexdump_ascii_key(...)  do {} while (0)
+#define wpa_debug_open_file(...)    do {} while (0)
+#define wpa_debug_close_file()      do {} while (0)
+#define wpa_dbg(...)                do {} while (0)
+
+static inline int wpa_debug_reopen_file(void)
+{
+       return 0;
+}
+
+
+void android_printf(int level, char *format, ...)
+PRINTF_FORMAT(2, 3);
+
+#define wpa_printf android_printf
+
+#else /* CONFIG_ANDROID_LOG */
 
 #ifdef CONFIG_NO_STDOUT_DEBUG
 
@@ -34,10 +62,17 @@ enum { MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR };
 #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_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);
 
 /**
@@ -79,7 +114,8 @@ void wpa_hexdump(int level, const char *title, const u8 *buf, size_t len);
 static inline void wpa_hexdump_buf(int level, const char *title,
                                   const struct wpabuf *buf)
 {
-       wpa_hexdump(level, title, wpabuf_head(buf), wpabuf_len(buf));
+       wpa_hexdump(level, title, buf ? wpabuf_head(buf) : NULL,
+                   buf ? wpabuf_len(buf) : 0);
 }
 
 /**
@@ -100,7 +136,8 @@ void wpa_hexdump_key(int level, const char *title, const u8 *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, wpabuf_head(buf), wpabuf_len(buf));
+       wpa_hexdump_key(level, title, buf ? wpabuf_head(buf) : 0,
+                       buf ? wpabuf_len(buf) : 0);
 }
 
 /**
@@ -136,13 +173,24 @@ void wpa_hexdump_ascii(int level, const char *title, const u8 *buf,
 void wpa_hexdump_ascii_key(int level, const char *title, const u8 *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 */
 
+#endif /* CONFIG_ANDROID_LOG */
+
 
 #ifdef CONFIG_NO_WPA_MSG
 #define wpa_msg(args...) do { } while (0)
 #define wpa_msg_ctrl(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
@@ -183,8 +231,11 @@ typedef void (*wpa_msg_cb_func)(void *ctx, int level, const char *txt,
  * @func: Callback function (%NULL to unregister)
  */
 void wpa_msg_register_cb(wpa_msg_cb_func func);
-#endif /* CONFIG_NO_WPA_MSG */
 
+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)
index a150455..cccfcc8 100644 (file)
@@ -117,6 +117,12 @@ static inline void wpabuf_put_le16(struct wpabuf *buf, u16 data)
        WPA_PUT_LE16(pos, data);
 }
 
+static inline void wpabuf_put_le32(struct wpabuf *buf, u32 data)
+{
+       u8 *pos = wpabuf_put(buf, 4);
+       WPA_PUT_LE32(pos, data);
+}
+
 static inline void wpabuf_put_be16(struct wpabuf *buf, u16 data)
 {
        u8 *pos = wpabuf_put(buf, 2);
index fea2a04..9b53b80 100644 (file)
@@ -21,7 +21,7 @@
 #include "http_client.h"
 
 
-#define HTTP_CLIENT_TIMEOUT 30
+#define HTTP_CLIENT_TIMEOUT_SEC 30
 
 
 struct http_client {
@@ -42,7 +42,7 @@ struct http_client {
 static void http_client_timeout(void *eloop_data, void *user_ctx)
 {
        struct http_client *c = eloop_data;
-       wpa_printf(MSG_DEBUG, "HTTP: Timeout");
+       wpa_printf(MSG_DEBUG, "HTTP: Timeout (c=%p)", c);
        c->cb(c->cb_ctx, c, HTTP_CLIENT_TIMEOUT);
 }
 
@@ -52,6 +52,9 @@ static void http_client_got_response(struct httpread *handle, void *cookie,
 {
        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:
@@ -122,7 +125,7 @@ static void http_client_tx_ready(int sock, void *eloop_ctx, void *sock_ctx)
        c->req = NULL;
 
        c->hread = httpread_create(c->sd, http_client_got_response, c,
-                                  c->max_response, HTTP_CLIENT_TIMEOUT);
+                                  c->max_response, HTTP_CLIENT_TIMEOUT_SEC);
        if (c->hread == NULL) {
                c->cb(c->cb_ctx, c, HTTP_CLIENT_FAILED);
                return;
@@ -181,8 +184,8 @@ struct http_client * http_client_addr(struct sockaddr_in *dst,
                return NULL;
        }
 
-       if (eloop_register_timeout(HTTP_CLIENT_TIMEOUT, 0, http_client_timeout,
-                                  c, NULL)) {
+       if (eloop_register_timeout(HTTP_CLIENT_TIMEOUT_SEC, 0,
+                                  http_client_timeout, c, NULL)) {
                http_client_free(c);
                return NULL;
        }
index b1b1e2b..a9958ee 100644 (file)
@@ -75,8 +75,8 @@
  * Note that angle brackets present in the original data must have been encoded
  * as &lt; and &gt; so they will not trouble us.
  */
-static int xml_next_tag(const char *in, const char **out,
-                       const char **out_tagname, const char **end)
+int xml_next_tag(const char *in, const char **out,
+                const char **out_tagname, const char **end)
 {
        while (*in && *in != '<')
                in++;
index 62dbe60..616af3d 100644 (file)
@@ -16,6 +16,8 @@
 void xml_data_encode(struct wpabuf *buf, const char *data, int len);
 void xml_add_tagged_data(struct wpabuf *buf, const char *tag,
                         const char *data);
+int xml_next_tag(const char *in, const char **out,
+                const char **out_tagname, const char **end);
 char * xml_get_first_item(const char *doc, const char *item);
 struct wpabuf * xml_get_base64_item(const char *data, const char *name,
                                    enum http_reply_code *ret);
index 619af15..2ba3d4b 100644 (file)
 #include "wps_dev_attr.h"
 
 
+#ifdef CONFIG_WPS_TESTING
+int wps_version_number = 0x20;
+int wps_testing_dummy_cred = 0;
+#endif /* CONFIG_WPS_TESTING */
+
+
 /**
  * wps_init - Initialize WPS Registration protocol data
  * @cfg: WPS configuration
@@ -46,7 +52,7 @@ struct wps_data * wps_init(const struct wps_config *cfg)
        }
        if (cfg->pin) {
                data->dev_pw_id = data->wps->oob_dev_pw_id == 0 ?
-                       DEV_PW_DEFAULT : data->wps->oob_dev_pw_id;
+                       cfg->dev_pw_id : data->wps->oob_dev_pw_id;
                data->dev_password = os_malloc(cfg->pin_len);
                if (data->dev_password == NULL) {
                        os_free(data);
@@ -61,12 +67,11 @@ struct wps_data * wps_init(const struct wps_config *cfg)
                /* Use special PIN '00000000' for PBC */
                data->dev_pw_id = DEV_PW_PUSHBUTTON;
                os_free(data->dev_password);
-               data->dev_password = os_malloc(8);
+               data->dev_password = (u8 *) os_strdup("00000000");
                if (data->dev_password == NULL) {
                        os_free(data);
                        return NULL;
                }
-               os_memset(data->dev_password, '0', 8);
                data->dev_password_len = 8;
        }
 
@@ -103,8 +108,11 @@ struct wps_data * wps_init(const struct wps_config *cfg)
 
        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;
 
        return data;
 }
@@ -201,19 +209,19 @@ int wps_is_selected_pbc_registrar(const struct wpabuf *msg)
            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;
 }
 
 
-/**
- * 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)
+static int is_selected_pin_registrar(struct wps_parse_attr *attr)
 {
-       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,
@@ -222,21 +230,112 @@ int wps_is_selected_pin_registrar(const struct wpabuf *msg)
         * Device Password ID here.
         */
 
-       if (wps_parse_msg(msg, &attr) < 0)
+       if (!attr->selected_registrar || *attr->selected_registrar == 0)
                return 0;
 
-       if (!attr.selected_registrar || *attr.selected_registrar == 0)
+       if (attr->dev_password_id != NULL &&
+           WPA_GET_BE16(attr->dev_password_id) == DEV_PW_PUSHBUTTON)
                return 0;
 
-       if (attr.dev_password_id != NULL &&
-           WPA_GET_BE16(attr.dev_password_id) == DEV_PW_PUSHBUTTON)
+#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: 1 if address is authorized, 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 ||
+                   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)
+{
+       struct wps_parse_attr attr_a, attr_b;
+       int sel_a, sel_b;
+
+       if (wps_a == NULL || wps_parse_msg(wps_a, &attr_a) < 0)
+               return 1;
+       if (wps_b == NULL || wps_parse_msg(wps_b, &attr_b) < 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;
+
+       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
@@ -255,6 +354,19 @@ const u8 * wps_get_uuid_e(const struct wpabuf *msg)
 
 
 /**
+ * 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
@@ -277,7 +389,8 @@ struct wpabuf * wps_build_assoc_req_ie(enum wps_request_type req_type)
        wpabuf_put_be32(ie, WPS_DEV_OUI_WFA);
 
        if (wps_build_version(ie) ||
-           wps_build_req_type(ie, req_type)) {
+           wps_build_req_type(ie, req_type) ||
+           wps_build_wfa_ext(ie, 0, NULL, 0)) {
                wpabuf_free(ie);
                return NULL;
        }
@@ -310,7 +423,8 @@ struct wpabuf * wps_build_assoc_resp_ie(void)
        wpabuf_put_be32(ie, WPS_DEV_OUI_WFA);
 
        if (wps_build_version(ie) ||
-           wps_build_resp_type(ie, WPS_RESP_AP)) {
+           wps_build_resp_type(ie, WPS_RESP_AP) ||
+           wps_build_wfa_ext(ie, 0, NULL, 0)) {
                wpabuf_free(ie);
                return NULL;
        }
@@ -327,58 +441,60 @@ struct wpabuf * wps_build_assoc_resp_ie(void)
  * @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(int pbc, struct wps_device_data *dev,
                                       const u8 *uuid,
-                                      enum wps_request_type req_type)
+                                      enum wps_request_type req_type,
+                                      unsigned int num_req_dev_types,
+                                      const u8 *req_dev_types)
 {
        struct wpabuf *ie;
-       u8 *len;
-       u16 methods;
 
        wpa_printf(MSG_DEBUG, "WPS: Building WPS IE for Probe Request");
 
-       ie = wpabuf_alloc(200);
+       ie = wpabuf_alloc(500);
        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 (pbc)
-               methods = WPS_CONFIG_PUSHBUTTON;
-       else {
-               methods = WPS_CONFIG_LABEL | WPS_CONFIG_DISPLAY |
-                       WPS_CONFIG_KEYPAD;
-#ifdef CONFIG_WPS_UFD
-               methods |= WPS_CONFIG_USBA;
-#endif /* CONFIG_WPS_UFD */
-#ifdef CONFIG_WPS_NFC
-               methods |= WPS_CONFIG_NFC_INTERFACE;
-#endif /* CONFIG_WPS_NFC */
-       }
-
        if (wps_build_version(ie) ||
            wps_build_req_type(ie, req_type) ||
-           wps_build_config_methods(ie, methods) ||
+           wps_build_config_methods(ie, dev->config_methods) ||
            wps_build_uuid_e(ie, uuid) ||
            wps_build_primary_dev_type(dev, ie) ||
            wps_build_rf_bands(dev, ie) ||
            wps_build_assoc_state(NULL, ie) ||
            wps_build_config_error(ie, WPS_CFG_NO_ERROR) ||
            wps_build_dev_password_id(ie, pbc ? DEV_PW_PUSHBUTTON :
-                                     DEV_PW_DEFAULT)) {
+                                     DEV_PW_DEFAULT) ||
+#ifdef CONFIG_WPS2
+           wps_build_manufacturer(dev, ie) ||
+           wps_build_model_name(dev, ie) ||
+           wps_build_model_number(dev, ie) ||
+           wps_build_dev_name(dev, ie) ||
+           wps_build_wfa_ext(ie, req_type == WPS_REQ_ENROLLEE, NULL, 0) ||
+#endif /* CONFIG_WPS2 */
+           wps_build_req_dev_type(dev, ie, num_req_dev_types, req_dev_types)
+           ||
+           wps_build_secondary_dev_type(dev, ie)
+               ) {
                wpabuf_free(ie);
                return NULL;
        }
 
-       *len = wpabuf_len(ie) - 2;
+#ifndef CONFIG_WPS2
+       if (dev->p2p && wps_build_dev_name(dev, ie)) {
+               wpabuf_free(ie);
+               return NULL;
+       }
+#endif /* CONFIG_WPS2 */
 
-       return ie;
+       return wps_ie_encapsulate(ie);
 }
 
 
index 1fd1e52..389be3e 100644 (file)
@@ -63,6 +63,13 @@ struct wps_credential {
 
 #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
@@ -73,8 +80,11 @@ struct wps_credential {
  * @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
  * @rf_bands: RF bands (WPS_RF_24GHZ, WPS_RF_50GHZ flags)
+ * @p2p: Whether the device is a P2P device
  */
 struct wps_device_data {
        u8 mac_addr[ETH_ALEN];
@@ -84,8 +94,15 @@ struct wps_device_data {
        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[MAX_WPS_VENDOR_EXTENSIONS];
+
+       int p2p;
 };
 
 struct oob_conf_data {
@@ -156,6 +173,29 @@ struct wps_config {
         * 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;
 };
 
 struct wps_data * wps_init(const struct wps_config *cfg);
@@ -195,13 +235,20 @@ 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(int pbc, struct wps_device_data *dev,
                                       const u8 *uuid,
-                                      enum wps_request_type req_type);
+                                      enum wps_request_type req_type,
+                                      unsigned int num_req_dev_types,
+                                      const u8 *req_dev_types);
 
 
 /**
@@ -340,6 +387,11 @@ struct wps_registrar_config {
         * static_wep_only - Whether the BSS supports only static WEP
         */
        int static_wep_only;
+
+       /**
+        * dualband - Whether this is a concurrent dualband AP
+        */
+       int dualband;
 };
 
 
@@ -395,7 +447,22 @@ enum wps_event {
        /**
         * WPS_EV_ER_ENROLLEE_REMOVE - ER: Enrollee removed
         */
-       WPS_EV_ER_ENROLLEE_REMOVE
+       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
 };
 
 /**
@@ -428,6 +495,8 @@ union wps_event_data {
         */
        struct wps_event_fail {
                int msg;
+               u16 config_error;
+               u16 error_indication;
        } fail;
 
        struct wps_event_pwd_auth_fail {
@@ -464,6 +533,23 @@ union wps_event_data {
                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;
 };
 
 /**
@@ -695,16 +781,26 @@ 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 *uuid,
-                         const u8 *pin, size_t pin_len, int timeout);
+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);
+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);
 void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr,
-                               const struct wpabuf *wps_data);
+                               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_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);
@@ -718,7 +814,8 @@ int wps_process_oob(struct wps_context *wps, struct oob_device_data *oob_dev,
                    int registrar);
 int wps_attr_text(struct wpabuf *data, char *buf, char *end);
 
-struct wps_er * wps_er_init(struct wps_context *wps, const char *ifname);
+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,
@@ -726,6 +823,10 @@ void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id,
 int wps_er_pbc(struct wps_er *er, const u8 *uuid);
 int wps_er_learn(struct wps_er *er, const u8 *uuid, const u8 *pin,
                 size_t pin_len);
+int wps_er_set_config(struct wps_er *er, const u8 *uuid,
+                     const struct wps_credential *cred);
+int wps_er_config(struct wps_er *er, const u8 *uuid, const u8 *pin,
+                 size_t pin_len, const struct wps_credential *cred);
 
 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,
@@ -733,4 +834,150 @@ char * wps_dev_type_bin2str(const u8 dev_type[WPS_DEV_TYPE_LEN], char *buf,
 void uuid_gen_mac_addr(const u8 *mac_addr, u8 *uuid);
 u16 wps_config_methods_str2bin(const char *str);
 
+#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 9da556a..d2ca31a 100644 (file)
@@ -19,6 +19,8 @@
 #include "crypto/crypto.h"
 #include "crypto/dh_group5.h"
 #include "crypto/sha256.h"
+#include "crypto/random.h"
+#include "common/ieee802_11_defs.h"
 #include "wps_i.h"
 
 
@@ -47,6 +49,8 @@ int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg)
                wpabuf_free(pubkey);
                return -1;
        }
+       wpa_hexdump_buf_key(MSG_DEBUG, "WPS: DH Private Key", wps->dh_privkey);
+       wpa_hexdump_buf(MSG_DEBUG, "WPS: DH own Public Key", pubkey);
 
        wpabuf_put_be16(msg, ATTR_PUBLIC_KEY);
        wpabuf_put_be16(msg, wpabuf_len(pubkey));
@@ -156,10 +160,65 @@ int wps_build_authenticator(struct wps_data *wps, struct wpabuf *msg)
 
 int wps_build_version(struct wpabuf *msg)
 {
-       wpa_printf(MSG_DEBUG, "WPS:  * Version");
+       /*
+        * Note: This attribute is deprecated and set to hardcoded 0x10 for
+        * backwards compatibility reasons. The real version negotiation is
+        * done with Version2.
+        */
+       wpa_printf(MSG_DEBUG, "WPS:  * Version (hardcoded 0x10)");
        wpabuf_put_be16(msg, ATTR_VERSION);
        wpabuf_put_be16(msg, 1);
+       wpabuf_put_u8(msg, 0x10);
+       return 0;
+}
+
+
+int wps_build_wfa_ext(struct wpabuf *msg, int req_to_enroll,
+                     const u8 *auth_macs, size_t auth_macs_count)
+{
+#ifdef CONFIG_WPS2
+       u8 *len;
+
+       wpabuf_put_be16(msg, ATTR_VENDOR_EXT);
+       len = wpabuf_put(msg, 2); /* to be filled */
+       wpabuf_put_be24(msg, WPS_VENDOR_ID_WFA);
+
+       wpa_printf(MSG_DEBUG, "WPS:  * Version2 (0x%x)", WPS_VERSION);
+       wpabuf_put_u8(msg, WFA_ELEM_VERSION2);
+       wpabuf_put_u8(msg, 1);
        wpabuf_put_u8(msg, WPS_VERSION);
+
+       if (req_to_enroll) {
+               wpa_printf(MSG_DEBUG, "WPS:  * Request to Enroll (1)");
+               wpabuf_put_u8(msg, WFA_ELEM_REQUEST_TO_ENROLL);
+               wpabuf_put_u8(msg, 1);
+               wpabuf_put_u8(msg, 1);
+       }
+
+       if (auth_macs && auth_macs_count) {
+               size_t i;
+               wpa_printf(MSG_DEBUG, "WPS:  * AuthorizedMACs (count=%d)",
+                          (int) auth_macs_count);
+               wpabuf_put_u8(msg, WFA_ELEM_AUTHORIZEDMACS);
+               wpabuf_put_u8(msg, auth_macs_count * ETH_ALEN);
+               wpabuf_put_data(msg, auth_macs, auth_macs_count * ETH_ALEN);
+               for (i = 0; i < auth_macs_count; i++)
+                       wpa_printf(MSG_DEBUG, "WPS:    AuthorizedMAC: " MACSTR,
+                                  MAC2STR(&auth_macs[i * ETH_ALEN]));
+       }
+
+       WPA_PUT_BE16(len, (u8 *) wpabuf_put(msg, 0) - len - 2);
+#endif /* CONFIG_WPS2 */
+
+#ifdef CONFIG_WPS_TESTING
+       if (WPS_VERSION > 0x20) {
+               wpa_printf(MSG_DEBUG, "WPS:  * Extensibility Testing - extra "
+                          "attribute");
+               wpabuf_put_be16(msg, ATTR_EXTENSIBILITY_TEST);
+               wpabuf_put_be16(msg, 1);
+               wpabuf_put_u8(msg, 42);
+       }
+#endif /* CONFIG_WPS_TESTING */
        return 0;
 }
 
@@ -196,20 +255,28 @@ int wps_build_registrar_nonce(struct wps_data *wps, struct wpabuf *msg)
 
 int wps_build_auth_type_flags(struct wps_data *wps, struct wpabuf *msg)
 {
+       u16 auth_types = WPS_AUTH_TYPES;
+#ifdef CONFIG_WPS2
+       auth_types &= ~WPS_AUTH_SHARED;
+#endif /* CONFIG_WPS2 */
        wpa_printf(MSG_DEBUG, "WPS:  * Authentication Type Flags");
        wpabuf_put_be16(msg, ATTR_AUTH_TYPE_FLAGS);
        wpabuf_put_be16(msg, 2);
-       wpabuf_put_be16(msg, WPS_AUTH_TYPES);
+       wpabuf_put_be16(msg, auth_types);
        return 0;
 }
 
 
 int wps_build_encr_type_flags(struct wps_data *wps, struct wpabuf *msg)
 {
+       u16 encr_types = WPS_ENCR_TYPES;
+#ifdef CONFIG_WPS2
+       encr_types &= ~WPS_ENCR_WEP;
+#endif /* CONFIG_WPS2 */
        wpa_printf(MSG_DEBUG, "WPS:  * Encryption Type Flags");
        wpabuf_put_be16(msg, ATTR_ENCR_TYPE_FLAGS);
        wpabuf_put_be16(msg, 2);
-       wpabuf_put_be16(msg, WPS_ENCR_TYPES);
+       wpabuf_put_be16(msg, encr_types);
        return 0;
 }
 
@@ -266,7 +333,7 @@ int wps_build_encr_settings(struct wps_data *wps, struct wpabuf *msg,
        wpabuf_put_be16(msg, block_size + wpabuf_len(plain));
 
        iv = wpabuf_put(msg, block_size);
-       if (os_get_random(iv, block_size) < 0)
+       if (random_get_bytes(iv, block_size) < 0)
                return -1;
 
        data = wpabuf_put(msg, 0);
@@ -299,7 +366,8 @@ int wps_build_oob_dev_password(struct wpabuf *msg, struct wps_context *wps)
        }
        wps->oob_dev_pw_id |= 0x0010;
 
-       if (os_get_random(dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN) < 0) {
+       if (random_get_bytes(dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN) <
+           0) {
                wpa_printf(MSG_ERROR, "WPS: OOB device password "
                           "generation error");
                return -1;
@@ -320,3 +388,35 @@ int wps_build_oob_dev_password(struct wpabuf *msg, struct wps_context *wps)
        return 0;
 }
 #endif /* CONFIG_WPS_OOB */
+
+
+/* Encapsulate WPS IE data with one (or more, if needed) IE headers */
+struct wpabuf * wps_ie_encapsulate(struct wpabuf *data)
+{
+       struct wpabuf *ie;
+       const u8 *pos, *end;
+
+       ie = wpabuf_alloc(wpabuf_len(data) + 100);
+       if (ie == NULL) {
+               wpabuf_free(data);
+               return NULL;
+       }
+
+       pos = wpabuf_head(data);
+       end = pos + wpabuf_len(data);
+
+       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, WPS_DEV_OUI_WFA);
+               wpabuf_put_data(ie, pos, frag_len);
+               pos += frag_len;
+       }
+
+       wpabuf_free(data);
+
+       return ie;
+}
index 30b0e79..55b5573 100644 (file)
 #include "common.h"
 #include "wps_i.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;
+       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,
@@ -399,6 +514,35 @@ static int wps_set_attr(struct wps_parse_attr *attr, u16 type,
                }
                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;
        default:
                wpa_printf(MSG_DEBUG, "WPS: Unsupported attribute type 0x%x "
                           "len=%u", type, len);
@@ -413,6 +557,9 @@ 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);
@@ -430,10 +577,28 @@ int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr)
                pos += 2;
                len = WPA_GET_BE16(pos);
                pos += 2;
-               wpa_printf(MSG_MSGDUMP, "WPS: attr type=0x%x len=%u",
+               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;
                }
 
@@ -459,6 +624,9 @@ int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr)
                if (wps_set_attr(attr, type, pos, len) < 0)
                        return -1;
 
+#ifdef WPS_WORKAROUNDS
+               prev_type = type;
+#endif /* WPS_WORKAROUNDS */
                pos += len;
        }
 
index 4751bbc..07e087d 100644 (file)
@@ -264,11 +264,18 @@ static int wps_process_cred_802_1x_enabled(struct wps_credential *cred,
 }
 
 
-static void wps_workaround_cred_key(struct wps_credential *cred)
+static int wps_workaround_cred_key(struct wps_credential *cred)
 {
        if (cred->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK) &&
            cred->key_len > 8 && cred->key_len < 64 &&
            cred->key[cred->key_len - 1] == 0) {
+#ifdef CONFIG_WPS_STRICT
+               wpa_printf(MSG_INFO, "WPS: WPA/WPA2-Personal passphrase uses "
+                          "forbidden NULL termination");
+               wpa_hexdump_ascii_key(MSG_INFO, "WPS: Network Key",
+                                     cred->key, cred->key_len);
+               return -1;
+#else /* CONFIG_WPS_STRICT */
                /*
                 * A deployed external registrar is known to encode ASCII
                 * passphrases incorrectly. Remove the extra NULL termination
@@ -277,7 +284,9 @@ static void wps_workaround_cred_key(struct wps_credential *cred)
                wpa_printf(MSG_DEBUG, "WPS: Workaround - remove NULL "
                           "termination from ASCII passphrase");
                cred->key_len--;
+#endif /* CONFIG_WPS_STRICT */
        }
+       return 0;
 }
 
 
@@ -303,9 +312,7 @@ int wps_process_cred(struct wps_parse_attr *attr,
            wps_process_cred_802_1x_enabled(cred, attr->dot1x_enabled))
                return -1;
 
-       wps_workaround_cred_key(cred);
-
-       return 0;
+       return wps_workaround_cred_key(cred);
 }
 
 
@@ -324,7 +331,5 @@ int wps_process_ap_settings(struct wps_parse_attr *attr,
            wps_process_cred_mac_addr(cred, attr->mac_addr))
                return -1;
 
-       wps_workaround_cred_key(cred);
-
-       return 0;
+       return wps_workaround_cred_key(cred);
 }
index 6ef14db..5d0508c 100644 (file)
@@ -20,6 +20,7 @@
 #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"
 
@@ -81,6 +82,8 @@ int wps_derive_keys(struct wps_data *wps)
                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;
@@ -241,7 +244,7 @@ unsigned int wps_generate_pin(void)
        unsigned int val;
 
        /* Generate seven random digits for the PIN */
-       if (os_get_random((unsigned char *) &val, sizeof(val)) < 0) {
+       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;
@@ -253,7 +256,8 @@ unsigned int wps_generate_pin(void)
 }
 
 
-void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg)
+void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg,
+                   u16 config_error, u16 error_indication)
 {
        union wps_event_data data;
 
@@ -262,6 +266,8 @@ void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg)
 
        os_memset(&data, 0, sizeof(data));
        data.fail.msg = msg;
+       data.fail.config_error = config_error;
+       data.fail.error_indication = error_indication;
        wps->event_cb(wps->cb_ctx, WPS_EV_FAIL, &data);
 }
 
@@ -325,7 +331,9 @@ static struct wpabuf * wps_get_oob_cred(struct wps_context *wps)
        data.wps = wps;
        data.auth_type = wps->auth_types;
        data.encr_type = wps->encr_types;
-       if (wps_build_version(plain) || wps_build_cred(&data, plain)) {
+       if (wps_build_version(plain) ||
+           wps_build_cred(&data, plain) ||
+           wps_build_wfa_ext(plain, 0, NULL, 0)) {
                wpabuf_free(plain);
                return NULL;
        }
@@ -356,7 +364,8 @@ static struct wpabuf * wps_get_oob_dev_pwd(struct wps_context *wps)
        }
 
        if (wps_build_version(data) ||
-           wps_build_oob_dev_password(data, wps)) {
+           wps_build_oob_dev_password(data, wps) ||
+           wps_build_wfa_ext(data, 0, NULL, 0)) {
                wpa_printf(MSG_ERROR, "WPS: Build OOB device password "
                           "attribute error");
                wpabuf_free(data);
@@ -604,6 +613,9 @@ u16 wps_config_methods_str2bin(const char *str)
        if (str == NULL) {
                /* Default to enabling methods based on build configuration */
                methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
+#ifdef CONFIG_WPS2
+               methods |= WPS_CONFIG_VIRT_DISPLAY;
+#endif /* CONFIG_WPS2 */
 #ifdef CONFIG_WPS_UFD
                methods |= WPS_CONFIG_USBA;
 #endif /* CONFIG_WPS_UFD */
@@ -629,7 +641,64 @@ u16 wps_config_methods_str2bin(const char *str)
                        methods |= WPS_CONFIG_PUSHBUTTON;
                if (os_strstr(str, "keypad"))
                        methods |= WPS_CONFIG_KEYPAD;
+#ifdef CONFIG_WPS2
+               if (os_strstr(str, "virtual_display"))
+                       methods |= WPS_CONFIG_VIRT_DISPLAY;
+               if (os_strstr(str, "physical_display"))
+                       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;
+#endif /* CONFIG_WPS2 */
        }
 
        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;
+}
index 750ca41..43311f3 100644 (file)
 #ifndef WPS_DEFS_H
 #define WPS_DEFS_H
 
+#ifdef CONFIG_WPS_TESTING
+
+extern int wps_version_number;
+extern int wps_testing_dummy_cred;
+#define WPS_VERSION wps_version_number
+
+#else /* CONFIG_WPS_TESTING */
+
+#ifdef CONFIG_WPS2
+#define WPS_VERSION 0x20
+#else /* CONFIG_WPS2 */
 #define WPS_VERSION 0x10
+#endif /* CONFIG_WPS2 */
+
+#endif /* CONFIG_WPS_TESTING */
 
 /* Diffie-Hellman 1536-bit MODP Group; RFC 3526, Group 5 */
 #define WPS_DH_GROUP 5
@@ -124,7 +138,20 @@ enum wps_attribute {
        ATTR_KEY_PROVIDED_AUTO = 0x1061,
        ATTR_802_1X_ENABLED = 0x1062,
        ATTR_APPSESSIONKEY = 0x1063,
-       ATTR_WEPTRANSMITKEY = 0x1064
+       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
 };
 
 /* Device Password ID */
@@ -197,6 +224,14 @@ enum wps_config_error {
        WPS_CFG_DEV_PASSWORD_AUTH_FAILURE = 18
 };
 
+/* 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,
+       NUM_WPS_EI_VALUES
+};
+
 /* RF Bands */
 #define WPS_RF_24GHZ 0x01
 #define WPS_RF_50GHZ 0x02
@@ -211,6 +246,12 @@ enum wps_config_error {
 #define WPS_CONFIG_NFC_INTERFACE 0x0040
 #define WPS_CONFIG_PUSHBUTTON 0x0080
 #define WPS_CONFIG_KEYPAD 0x0100
+#ifdef CONFIG_WPS2
+#define WPS_CONFIG_VIRT_PUSHBUTTON 0x0280
+#define WPS_CONFIG_PHY_PUSHBUTTON 0x0480
+#define WPS_CONFIG_VIRT_DISPLAY 0x2008
+#define WPS_CONFIG_PHY_DISPLAY 0x4008
+#endif /* CONFIG_WPS2 */
 
 /* Connection Type Flags */
 #define WPS_CONN_ESS 0x01
@@ -290,4 +331,6 @@ enum wps_response_type {
 /* 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 090bfa2..f2fb03a 100644 (file)
 #include "wps_dev_attr.h"
 
 
-static int wps_build_manufacturer(struct wps_device_data *dev,
-                                 struct wpabuf *msg)
+int wps_build_manufacturer(struct wps_device_data *dev, struct wpabuf *msg)
 {
        size_t len;
        wpa_printf(MSG_DEBUG, "WPS:  * Manufacturer");
        wpabuf_put_be16(msg, ATTR_MANUFACTURER);
        len = dev->manufacturer ? os_strlen(dev->manufacturer) : 0;
+#ifndef CONFIG_WPS_STRICT
        if (len == 0) {
                /*
                 * Some deployed WPS implementations fail to parse zero-length
-                * attributes. As a workaround, send a null character if the
+                * attributes. As a workaround, send a space character if the
                 * device attribute string is empty.
                 */
                wpabuf_put_be16(msg, 1);
-               wpabuf_put_u8(msg, '\0');
-       } else {
-               wpabuf_put_be16(msg, len);
-               wpabuf_put_data(msg, dev->manufacturer, len);
+               wpabuf_put_u8(msg, ' ');
+               return 0;
        }
+#endif /* CONFIG_WPS_STRICT */
+       wpabuf_put_be16(msg, len);
+       wpabuf_put_data(msg, dev->manufacturer, len);
        return 0;
 }
 
 
-static int wps_build_model_name(struct wps_device_data *dev,
-                               struct wpabuf *msg)
+int wps_build_model_name(struct wps_device_data *dev, struct wpabuf *msg)
 {
        size_t len;
        wpa_printf(MSG_DEBUG, "WPS:  * Model Name");
        wpabuf_put_be16(msg, ATTR_MODEL_NAME);
        len = dev->model_name ? os_strlen(dev->model_name) : 0;
+#ifndef CONFIG_WPS_STRICT
        if (len == 0) {
                /*
                 * Some deployed WPS implementations fail to parse zero-length
-                * attributes. As a workaround, send a null character if the
+                * attributes. As a workaround, send a space character if the
                 * device attribute string is empty.
                 */
                wpabuf_put_be16(msg, 1);
-               wpabuf_put_u8(msg, '\0');
-       } else {
-               wpabuf_put_be16(msg, len);
-               wpabuf_put_data(msg, dev->model_name, len);
+               wpabuf_put_u8(msg, ' ');
+               return 0;
        }
+#endif /* CONFIG_WPS_STRICT */
+       wpabuf_put_be16(msg, len);
+       wpabuf_put_data(msg, dev->model_name, len);
        return 0;
 }
 
 
-static int wps_build_model_number(struct wps_device_data *dev,
-                                 struct wpabuf *msg)
+int wps_build_model_number(struct wps_device_data *dev, struct wpabuf *msg)
 {
        size_t len;
        wpa_printf(MSG_DEBUG, "WPS:  * Model Number");
        wpabuf_put_be16(msg, ATTR_MODEL_NUMBER);
        len = dev->model_number ? os_strlen(dev->model_number) : 0;
+#ifndef CONFIG_WPS_STRICT
        if (len == 0) {
                /*
                 * Some deployed WPS implementations fail to parse zero-length
-                * attributes. As a workaround, send a null character if the
+                * attributes. As a workaround, send a space character if the
                 * device attribute string is empty.
                 */
                wpabuf_put_be16(msg, 1);
-               wpabuf_put_u8(msg, '\0');
-       } else {
-               wpabuf_put_be16(msg, len);
-               wpabuf_put_data(msg, dev->model_number, len);
+               wpabuf_put_u8(msg, ' ');
+               return 0;
        }
+#endif /* CONFIG_WPS_STRICT */
+       wpabuf_put_be16(msg, len);
+       wpabuf_put_data(msg, dev->model_number, len);
        return 0;
 }
 
@@ -95,18 +98,20 @@ static int wps_build_serial_number(struct wps_device_data *dev,
        wpa_printf(MSG_DEBUG, "WPS:  * Serial Number");
        wpabuf_put_be16(msg, ATTR_SERIAL_NUMBER);
        len = dev->serial_number ? os_strlen(dev->serial_number) : 0;
+#ifndef CONFIG_WPS_STRICT
        if (len == 0) {
                /*
                 * Some deployed WPS implementations fail to parse zero-length
-                * attributes. As a workaround, send a null character if the
+                * attributes. As a workaround, send a space character if the
                 * device attribute string is empty.
                 */
                wpabuf_put_be16(msg, 1);
-               wpabuf_put_u8(msg, '\0');
-       } else {
-               wpabuf_put_be16(msg, len);
-               wpabuf_put_data(msg, dev->serial_number, len);
+               wpabuf_put_u8(msg, ' ');
+               return 0;
        }
+#endif /* CONFIG_WPS_STRICT */
+       wpabuf_put_be16(msg, len);
+       wpabuf_put_data(msg, dev->serial_number, len);
        return 0;
 }
 
@@ -121,24 +126,62 @@ int wps_build_primary_dev_type(struct wps_device_data *dev, struct wpabuf *msg)
 }
 
 
-static int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg)
+int wps_build_secondary_dev_type(struct wps_device_data *dev,
+                                 struct wpabuf *msg)
+{
+       if (!dev->num_sec_dev_types)
+               return 0;
+
+       wpa_printf(MSG_DEBUG, "WPS:  * Secondary Device Type");
+       wpabuf_put_be16(msg, ATTR_SECONDARY_DEV_TYPE_LIST);
+       wpabuf_put_be16(msg, WPS_DEV_TYPE_LEN * dev->num_sec_dev_types);
+       wpabuf_put_data(msg, dev->sec_dev_type,
+                       WPS_DEV_TYPE_LEN * dev->num_sec_dev_types);
+
+       return 0;
+}
+
+
+int wps_build_req_dev_type(struct wps_device_data *dev, struct wpabuf *msg,
+                          unsigned int num_req_dev_types,
+                          const u8 *req_dev_types)
+{
+       unsigned int i;
+
+       for (i = 0; i < num_req_dev_types; i++) {
+               wpa_hexdump(MSG_DEBUG, "WPS: * Requested Device Type",
+                           req_dev_types + i * WPS_DEV_TYPE_LEN,
+                           WPS_DEV_TYPE_LEN);
+               wpabuf_put_be16(msg, ATTR_REQUESTED_DEV_TYPE);
+               wpabuf_put_be16(msg, WPS_DEV_TYPE_LEN);
+               wpabuf_put_data(msg, req_dev_types + i * WPS_DEV_TYPE_LEN,
+                               WPS_DEV_TYPE_LEN);
+       }
+
+       return 0;
+}
+
+
+int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg)
 {
        size_t len;
        wpa_printf(MSG_DEBUG, "WPS:  * Device Name");
        wpabuf_put_be16(msg, ATTR_DEV_NAME);
        len = dev->device_name ? os_strlen(dev->device_name) : 0;
+#ifndef CONFIG_WPS_STRICT
        if (len == 0) {
                /*
                 * Some deployed WPS implementations fail to parse zero-length
-                * attributes. As a workaround, send a null character if the
+                * attributes. As a workaround, send a space character if the
                 * device attribute string is empty.
                 */
                wpabuf_put_be16(msg, 1);
-               wpabuf_put_u8(msg, '\0');
-       } else {
-               wpabuf_put_be16(msg, len);
-               wpabuf_put_data(msg, dev->device_name, len);
+               wpabuf_put_u8(msg, ' ');
+               return 0;
        }
+#endif /* CONFIG_WPS_STRICT */
+       wpabuf_put_be16(msg, len);
+       wpabuf_put_data(msg, dev->device_name, len);
        return 0;
 }
 
@@ -176,6 +219,25 @@ int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg)
 }
 
 
+int wps_build_vendor_ext(struct wps_device_data *dev, struct wpabuf *msg)
+{
+       int i;
+
+       for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) {
+               if (dev->vendor_ext[i] == NULL)
+                       continue;
+               wpa_hexdump(MSG_DEBUG, "WPS:  * Vendor Extension",
+                           wpabuf_head_u8(dev->vendor_ext[i]),
+                           wpabuf_len(dev->vendor_ext[i]));
+               wpabuf_put_be16(msg, ATTR_VENDOR_EXT);
+               wpabuf_put_be16(msg, wpabuf_len(dev->vendor_ext[i]));
+               wpabuf_put_buf(msg, dev->vendor_ext[i]);
+       }
+
+       return 0;
+}
+
+
 static int wps_process_manufacturer(struct wps_device_data *dev, const u8 *str,
                                    size_t str_len)
 {
index a9c16ea..f26a05b 100644 (file)
 
 struct wps_parse_attr;
 
+int wps_build_manufacturer(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_model_name(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_model_number(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg);
 int wps_build_device_attrs(struct wps_device_data *dev, struct wpabuf *msg);
 int wps_build_os_version(struct wps_device_data *dev, struct wpabuf *msg);
 int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg);
 int wps_build_primary_dev_type(struct wps_device_data *dev,
                               struct wpabuf *msg);
+int wps_build_secondary_dev_type(struct wps_device_data *dev,
+                                struct wpabuf *msg);
+int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg);
 int wps_process_device_attrs(struct wps_device_data *dev,
                             struct wps_parse_attr *attr);
 int wps_process_os_version(struct wps_device_data *dev, const u8 *ver);
@@ -29,5 +36,9 @@ int wps_process_rf_bands(struct wps_device_data *dev, const u8 *bands);
 void wps_device_data_dup(struct wps_device_data *dst,
                         const struct wps_device_data *src);
 void wps_device_data_free(struct wps_device_data *dev);
+int wps_build_vendor_ext(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_req_dev_type(struct wps_device_data *dev, struct wpabuf *msg,
+                          unsigned int num_req_dev_types,
+                          const u8 *req_dev_types);
 
 #endif /* WPS_DEV_ATTR_H */
index dff24d4..7e58291 100644 (file)
@@ -17,6 +17,7 @@
 #include "common.h"
 #include "crypto/crypto.h"
 #include "crypto/sha256.h"
+#include "crypto/random.h"
 #include "wps_i.h"
 #include "wps_dev_attr.h"
 
@@ -53,7 +54,7 @@ static int wps_build_e_hash(struct wps_data *wps, struct wpabuf *msg)
        const u8 *addr[4];
        size_t len[4];
 
-       if (os_get_random(wps->snonce, 2 * WPS_SECRET_NONCE_LEN) < 0)
+       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",
@@ -119,8 +120,9 @@ static int wps_build_e_snonce2(struct wps_data *wps, struct wpabuf *msg)
 static struct wpabuf * wps_build_m1(struct wps_data *wps)
 {
        struct wpabuf *msg;
+       u16 config_methods;
 
-       if (os_get_random(wps->nonce_e, WPS_NONCE_LEN) < 0)
+       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);
@@ -130,6 +132,26 @@ static struct wpabuf * wps_build_m1(struct wps_data *wps)
        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;
+#ifdef CONFIG_WPS2
+               config_methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
+                                   WPS_CONFIG_PHY_PUSHBUTTON);
+#endif /* CONFIG_WPS2 */
+       }
+
        if (wps_build_version(msg) ||
            wps_build_msg_type(msg, WPS_M1) ||
            wps_build_uuid_e(msg, wps->uuid_e) ||
@@ -139,14 +161,15 @@ static struct wpabuf * wps_build_m1(struct wps_data *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(msg, wps->wps->config_methods) ||
+           wps_build_config_methods(msg, config_methods) ||
            wps_build_wps_state(wps, msg) ||
            wps_build_device_attrs(&wps->wps->dev, msg) ||
            wps_build_rf_bands(&wps->wps->dev, msg) ||
            wps_build_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_os_version(&wps->wps->dev, msg) ||
+           wps_build_wfa_ext(msg, 0, NULL, 0)) {
                wpabuf_free(msg);
                return NULL;
        }
@@ -176,6 +199,7 @@ static struct wpabuf * wps_build_m3(struct wps_data *wps)
            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;
@@ -208,6 +232,7 @@ static struct wpabuf * wps_build_m5(struct wps_data *wps)
            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);
@@ -232,20 +257,47 @@ static int wps_build_cred_ssid(struct wps_data *wps, struct wpabuf *msg)
 
 static int wps_build_cred_auth_type(struct wps_data *wps, struct wpabuf *msg)
 {
-       wpa_printf(MSG_DEBUG, "WPS:  * Authentication Type");
+       u16 auth_type = wps->wps->auth_types;
+
+       /* Select the best authentication type */
+       if (auth_type & WPS_AUTH_WPA2PSK)
+               auth_type = WPS_AUTH_WPA2PSK;
+       else if (auth_type & WPS_AUTH_WPAPSK)
+               auth_type = WPS_AUTH_WPAPSK;
+       else if (auth_type & WPS_AUTH_OPEN)
+               auth_type = WPS_AUTH_OPEN;
+       else if (auth_type & WPS_AUTH_SHARED)
+               auth_type = WPS_AUTH_SHARED;
+
+       wpa_printf(MSG_DEBUG, "WPS:  * Authentication Type (0x%x)", auth_type);
        wpabuf_put_be16(msg, ATTR_AUTH_TYPE);
        wpabuf_put_be16(msg, 2);
-       wpabuf_put_be16(msg, wps->wps->auth_types);
+       wpabuf_put_be16(msg, auth_type);
        return 0;
 }
 
 
 static int wps_build_cred_encr_type(struct wps_data *wps, struct wpabuf *msg)
 {
-       wpa_printf(MSG_DEBUG, "WPS:  * Encryption Type");
+       u16 encr_type = wps->wps->encr_types;
+
+       /* Select the best encryption type */
+       if (wps->wps->auth_types & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) {
+               if (encr_type & WPS_ENCR_AES)
+                       encr_type = WPS_ENCR_AES;
+               else if (encr_type & WPS_ENCR_TKIP)
+                       encr_type = WPS_ENCR_TKIP;
+       } else {
+               if (encr_type & WPS_ENCR_WEP)
+                       encr_type = WPS_ENCR_WEP;
+               else if (encr_type & WPS_ENCR_NONE)
+                       encr_type = WPS_ENCR_NONE;
+       }
+
+       wpa_printf(MSG_DEBUG, "WPS:  * Encryption Type (0x%x)", encr_type);
        wpabuf_put_be16(msg, ATTR_ENCR_TYPE);
        wpabuf_put_be16(msg, 2);
-       wpabuf_put_be16(msg, wps->wps->encr_types);
+       wpabuf_put_be16(msg, encr_type);
        return 0;
 }
 
@@ -310,6 +362,7 @@ static struct wpabuf * wps_build_m7(struct wps_data *wps)
            (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);
@@ -345,7 +398,8 @@ static struct wpabuf * wps_build_wsc_done(struct wps_data *wps)
        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_registrar_nonce(wps, msg) ||
+           wps_build_wfa_ext(msg, 0, NULL, 0)) {
                wpabuf_free(msg);
                return NULL;
        }
@@ -360,51 +414,6 @@ static struct wpabuf * wps_build_wsc_done(struct wps_data *wps)
 }
 
 
-static 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)) {
-               wpabuf_free(msg);
-               return NULL;
-       }
-
-       return msg;
-}
-
-
-static 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)) {
-               wpabuf_free(msg);
-               return NULL;
-       }
-
-       return msg;
-}
-
-
 struct wpabuf * wps_enrollee_get_msg(struct wps_data *wps,
                                     enum wsc_op_code *op_code)
 {
@@ -657,7 +666,7 @@ static int wps_process_r_snonce2(struct wps_data *wps, const u8 *r_snonce2)
 
 
 static int wps_process_cred_e(struct wps_data *wps, const u8 *cred,
-                             size_t cred_len)
+                             size_t cred_len, int wps2)
 {
        struct wps_parse_attr attr;
        struct wpabuf msg;
@@ -682,7 +691,30 @@ static int wps_process_cred_e(struct wps_data *wps, const u8 *cred,
                 * 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 */
+       }
+
+#ifdef CONFIG_WPS2
+       if (!(wps->cred.encr_type &
+             (WPS_ENCR_NONE | WPS_ENCR_TKIP | WPS_ENCR_AES))) {
+               if (wps->cred.encr_type & WPS_ENCR_WEP) {
+                       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;
        }
+#endif /* CONFIG_WPS2 */
 
        if (wps->wps->cred_cb) {
                wps->cred.cred_attr = cred - 4;
@@ -697,9 +729,10 @@ static int wps_process_cred_e(struct wps_data *wps, const u8 *cred,
 
 
 static int wps_process_creds(struct wps_data *wps, const u8 *cred[],
-                            size_t cred_len[], size_t num_cred)
+                            size_t cred_len[], size_t num_cred, int wps2)
 {
        size_t i;
+       int ok = 0;
 
        if (wps->wps->ap)
                return 0;
@@ -711,17 +744,29 @@ static int wps_process_creds(struct wps_data *wps, const u8 *cred[],
        }
 
        for (i = 0; i < num_cred; i++) {
-               if (wps_process_cred_e(wps, cred[i], cred_len[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)
+                                    struct wpabuf *attrs, int wps2)
 {
        struct wps_credential cred;
 
@@ -747,8 +792,62 @@ static int wps_process_ap_settings_e(struct wps_data *wps,
                 * 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 */
        }
 
+#ifdef CONFIG_WPS2
+       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;
+       }
+#endif /* CONFIG_WPS2 */
+
+#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 */
+
+#ifdef CONFIG_WPS2
+       if ((cred.encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) == WPS_ENCR_TKIP)
+       {
+               wpa_printf(MSG_DEBUG, "WPS: Upgrade encr_type TKIP -> "
+                          "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;
+       }
+#endif /* CONFIG_WPS2 */
+
        if (wps->wps->cred_cb) {
                cred.cred_attr = wpabuf_head(attrs);
                cred.cred_attr_len = wpabuf_len(attrs);
@@ -779,8 +878,15 @@ static enum wps_process_res wps_process_m2(struct wps_data *wps,
                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->dev_password == NULL)) {
+           ((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;
@@ -888,6 +994,12 @@ static enum wps_process_res wps_process_m4(struct wps_data *wps,
                return WPS_CONTINUE;
        }
 
+       if (wps_validate_m4_encr(decrypted, attr->version2 != 0) < 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 ||
@@ -935,6 +1047,12 @@ static enum wps_process_res wps_process_m6(struct wps_data *wps,
                return WPS_CONTINUE;
        }
 
+       if (wps_validate_m6_encr(decrypted, attr->version2 != 0) < 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 ||
@@ -946,6 +1064,10 @@ static enum wps_process_res wps_process_m6(struct wps_data *wps,
        }
        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;
 }
@@ -973,6 +1095,19 @@ static enum wps_process_res wps_process_m8(struct wps_data *wps,
                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) {
@@ -982,13 +1117,21 @@ static enum wps_process_res wps_process_m8(struct wps_data *wps,
                return WPS_CONTINUE;
        }
 
+       if (wps_validate_m8_encr(decrypted, wps->wps->ap, attr->version2 != 0)
+           < 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) ||
-           wps_process_ap_settings_e(wps, &eattr, decrypted)) {
+                             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;
@@ -1011,12 +1154,6 @@ static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps,
        if (wps_parse_msg(msg, &attr) < 0)
                return WPS_FAILURE;
 
-       if (!wps_version_supported(attr.version)) {
-               wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
-                          attr.version ? *attr.version : 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");
@@ -1025,30 +1162,44 @@ static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps,
 
        if (attr.msg_type == NULL) {
                wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
-               return WPS_FAILURE;
+               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_fail_event(wps->wps, WPS_M4, wps->config_error,
+                                      wps->error_indication);
                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_fail_event(wps->wps, WPS_M6, wps->config_error,
+                                      wps->error_indication);
                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_fail_event(wps->wps, WPS_M8, wps->config_error,
+                                      wps->error_indication);
                break;
        default:
                wpa_printf(MSG_DEBUG, "WPS: Unsupported Message Type %d",
@@ -1084,12 +1235,6 @@ static enum wps_process_res wps_process_wsc_ack(struct wps_data *wps,
        if (wps_parse_msg(msg, &attr) < 0)
                return WPS_FAILURE;
 
-       if (!wps_version_supported(attr.version)) {
-               wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
-                          attr.version ? *attr.version : 0);
-               return WPS_FAILURE;
-       }
-
        if (attr.msg_type == NULL) {
                wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
                return WPS_FAILURE;
@@ -1130,18 +1275,13 @@ 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 (!wps_version_supported(attr.version)) {
-               wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
-                          attr.version ? *attr.version : 0);
-               return WPS_FAILURE;
-       }
-
        if (attr.msg_type == NULL) {
                wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
                return WPS_FAILURE;
@@ -1180,18 +1320,22 @@ static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps,
                return WPS_FAILURE;
        }
 
+       config_error = WPA_GET_BE16(attr.config_error);
        wpa_printf(MSG_DEBUG, "WPS: Registrar terminated negotiation with "
-                  "Configuration Error %d", WPA_GET_BE16(attr.config_error));
+                  "Configuration Error %d", config_error);
 
        switch (wps->state) {
        case RECV_M4:
-               wps_fail_event(wps->wps, WPS_M3);
+               wps_fail_event(wps->wps, WPS_M3, config_error,
+                              wps->error_indication);
                break;
        case RECV_M6:
-               wps_fail_event(wps->wps, WPS_M5);
+               wps_fail_event(wps->wps, WPS_M5, config_error,
+                              wps->error_indication);
                break;
        case RECV_M8:
-               wps_fail_event(wps->wps, WPS_M7);
+               wps_fail_event(wps->wps, WPS_M7, config_error,
+                              wps->error_indication);
                break;
        default:
                break;
@@ -1230,8 +1374,12 @@ enum wps_process_res wps_enrollee_process_msg(struct wps_data *wps,
        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);
index e0cdd1d..856e9fb 100644 (file)
@@ -62,11 +62,15 @@ static void wps_er_sta_event(struct wps_context *wps, struct wps_er_sta *sta,
 }
 
 
-static struct wps_er_sta * wps_er_sta_get(struct wps_er_ap *ap, const u8 *addr)
+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 (os_memcmp(sta->addr, addr, ETH_ALEN) == 0)
+               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;
@@ -275,6 +279,64 @@ fail:
        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);
+       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)",
@@ -352,6 +414,7 @@ static void wps_er_http_subscribe_cb(void *ctx, struct http_client *c,
                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:
@@ -439,16 +502,61 @@ static void wps_er_get_device_info(struct wps_er_ap *ap)
 }
 
 
+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 *data = wpabuf_head(reply);
+       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);
 
@@ -583,8 +691,12 @@ void wps_er_ap_remove(struct wps_er *er, struct in_addr *addr)
 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);
 }
 
 
@@ -649,7 +761,7 @@ static struct wps_er_sta * wps_er_add_sta_data(struct wps_er_ap *ap,
                                               struct wps_parse_attr *attr,
                                               int probe_req)
 {
-       struct wps_er_sta *sta = wps_er_sta_get(ap, addr);
+       struct wps_er_sta *sta = wps_er_sta_get(ap, addr, NULL);
        int new_sta = 0;
        int m1;
 
@@ -756,6 +868,12 @@ static void wps_er_process_wlanevent_probe_req(struct wps_er_ap *ap,
        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");
@@ -763,6 +881,7 @@ static void wps_er_process_wlanevent_probe_req(struct wps_er_ap *ap,
        }
 
        wps_er_add_sta_data(ap, addr, &attr, 1);
+       wps_registrar_probe_req_rx(ap->er->wps->registrar, addr, msg, 0);
 }
 
 
@@ -1151,7 +1270,7 @@ static void wps_er_http_req(void *ctx, struct http_request *req)
 
 
 struct wps_er *
-wps_er_init(struct wps_context *wps, const char *ifname)
+wps_er_init(struct wps_context *wps, const char *ifname, const char *filter)
 {
        struct wps_er *er;
        struct in_addr addr;
@@ -1161,6 +1280,7 @@ wps_er_init(struct wps_context *wps, const char *ifname)
                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;
@@ -1175,6 +1295,16 @@ wps_er_init(struct wps_context *wps, const char *ifname)
        /* Limit event_id to < 32 bits to avoid issues with atoi() */
        er->event_id &= 0x0fffffff;
 
+       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(ifname, &er->ip_addr, &er->ip_addr_text,
                           er->mac_addr)) {
                wpa_printf(MSG_INFO, "WPS UPnP: Could not get IP/MAC address "
@@ -1184,6 +1314,7 @@ wps_er_init(struct wps_context *wps, const char *ifname)
        }
 
        if (wps_er_ssdp_init(er) < 0) {
+               wpa_printf(MSG_INFO, "WPS UPnP: SSDP initialization failed");
                wps_er_deinit(er, NULL, NULL);
                return NULL;
        }
@@ -1191,6 +1322,7 @@ wps_er_init(struct wps_context *wps, const char *ifname)
        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;
        }
@@ -1256,19 +1388,30 @@ 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);
 }
 
 
@@ -1290,6 +1433,12 @@ static void wps_er_send_set_sel_reg(struct wps_er_ap *ap, struct wpabuf *msg)
                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");
@@ -1339,26 +1488,74 @@ static int wps_er_build_sel_reg_config_methods(struct wpabuf *msg,
 }
 
 
+static int wps_er_build_uuid_r(struct wpabuf *msg, const u8 *uuid_r)
+{
+#ifdef CONFIG_WPS2
+       wpabuf_put_be16(msg, ATTR_UUID_R);
+       wpabuf_put_be16(msg, WPS_UUID_LEN);
+       wpabuf_put_data(msg, uuid_r, WPS_UUID_LEN);
+#endif /* CONFIG_WPS2 */
+       return 0;
+}
+
+
 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;
+#ifdef CONFIG_WPS2
+       u8 bcast[ETH_ALEN];
+#endif /* CONFIG_WPS2 */
+       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);
+#ifdef CONFIG_WPS2
+       if (count == 0) {
+               os_memset(bcast, 0xff, ETH_ALEN);
+               auth_macs = bcast;
+               count = 1;
+       }
+#endif /* CONFIG_WPS2 */
+
        if (wps_build_version(msg) ||
            wps_er_build_selected_registrar(msg, sel_reg) ||
            wps_er_build_dev_password_id(msg, dev_passwd_id) ||
-           wps_er_build_sel_reg_config_methods(msg, sel_reg_config_methods)) {
+           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;
        }
 
-       dl_list_for_each(ap, &er->ap, struct wps_er_ap, list)
+       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);
 }
@@ -1366,16 +1563,41 @@ void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id,
 
 int wps_er_pbc(struct wps_er *er, const u8 *uuid)
 {
+       int res;
+       struct wps_er_ap *ap;
+
        if (er == NULL || er->wps == NULL)
                return -1;
 
-       /*
-        * TODO: Should enable PBC mode only in a single AP based on which AP
-        * the Enrollee (uuid) is using. Now, we may end up enabling multiple
-        * APs in PBC mode which could result in session overlap at the
-        * Enrollee.
-        */
-       if (wps_registrar_button_pushed(er->wps->registrar))
+       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;
+       }
+
+       ap = wps_er_ap_get(er, NULL, uuid);
+       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, NULL, 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;
@@ -1385,6 +1607,8 @@ int wps_er_pbc(struct wps_er *er, const u8 *uuid)
 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));
@@ -1393,7 +1617,11 @@ static void wps_er_ap_settings_cb(void *ctx, const struct wps_credential *cred)
                ap->ap_settings->cred_attr = NULL;
        }
 
-       /* TODO: send info through ctrl_iface */
+       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);
 }
 
 
@@ -1436,6 +1664,8 @@ static void wps_er_http_put_message_cb(void *ctx, struct http_client *c,
                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);
@@ -1487,10 +1717,26 @@ static void wps_er_ap_put_message(struct wps_er_ap *ap,
 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, WSC_MSG, msg);
+       res = wps_process_msg(ap->wps, op_code, msg);
        if (res == WPS_CONTINUE) {
-               enum wsc_op_code op_code;
                struct wpabuf *next = wps_get_msg(ap->wps, &op_code);
                if (next) {
                        wps_er_ap_put_message(ap, next);
@@ -1501,6 +1747,10 @@ static void wps_er_ap_process(struct wps_er_ap *ap, struct wpabuf *msg)
                        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);
@@ -1656,8 +1906,99 @@ int wps_er_learn(struct wps_er *er, const u8 *uuid, const u8 *pin,
        if (wps_er_send_get_device_info(ap, wps_er_ap_learn_m1) < 0)
                return -1;
 
-       /* TODO: add PIN without SetSelectedRegistrar trigger to all APs */
-       wps_registrar_add_pin(er->wps->registrar, uuid, pin, pin_len, 0);
+       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 struct wps_credential *cred)
+{
+       struct wps_er_ap *ap;
+
+       if (er == NULL)
+               return -1;
+
+       ap = wps_er_ap_get(er, NULL, uuid);
+       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 *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);
+       if (ap == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS ER: AP not found for config "
+                          "request");
+               return -1;
+       }
+       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;
 }
index b13b950..5388ed1 100644 (file)
@@ -73,6 +73,12 @@ struct wps_er_ap {
        void (*m1_handler)(struct wps_er_ap *ap, struct wpabuf *m1);
 };
 
+struct wps_er_ap_settings {
+       struct dl_list list;
+       u8 uuid[WPS_UUID_LEN];
+       struct wps_credential ap_settings;
+};
+
 struct wps_er {
        struct wps_context *wps;
        char ifname[17];
@@ -83,6 +89,7 @@ struct wps_er {
        int ssdp_sd;
        struct dl_list ap;
        struct dl_list ap_unsubscribing;
+       struct dl_list ap_settings;
        struct http_server *http_srv;
        int http_port;
        unsigned int next_ap_id;
@@ -90,6 +97,9 @@ struct wps_er {
        int deinitializing;
        void (*deinit_done_cb)(void *ctx);
        void *deinit_done_ctx;
+       struct in_addr filter_addr;
+       int skip_set_sel_reg;
+       const u8 *set_sel_reg_uuid_filter;
 };
 
 
@@ -97,6 +107,7 @@ struct wps_er {
 void wps_er_ap_add(struct wps_er *er, const u8 *uuid, struct in_addr *addr,
                   const char *location, int max_age);
 void wps_er_ap_remove(struct wps_er *er, struct in_addr *addr);
+int wps_er_ap_cache_settings(struct wps_er *er, struct in_addr *addr);
 
 /* wps_er_ssdp.c */
 int wps_er_ssdp_init(struct wps_er *er);
index f108435..83de9ad 100644 (file)
@@ -41,6 +41,9 @@ static void wps_er_ssdp_rx(int sd, void *eloop_ctx, void *sock_ctx)
        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));
@@ -110,6 +113,7 @@ static void wps_er_ssdp_rx(int sd, void *eloop_ctx, void *sock_ctx)
                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;
        }
@@ -162,16 +166,25 @@ void wps_er_send_ssdp_msearch(struct wps_er *er)
 
 int wps_er_ssdp_init(struct wps_er *er)
 {
-       if (add_ssdp_network(er->ifname))
+       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);
-       if (er->multicast_sd < 0)
+       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)
+       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) ||
index 50e66f6..bdb6da2 100644 (file)
@@ -102,6 +102,7 @@ struct wps_data {
         * config_error - Configuration Error value to be used in NACK
         */
        u16 config_error;
+       u16 error_indication;
 
        int ext_reg;
        int int_reg;
@@ -116,12 +117,16 @@ struct wps_data {
        struct wps_credential *use_cred;
 
        int use_psk_key;
+       u8 p2p_dev_addr[ETH_ALEN]; /* P2P Device Address of the client or
+                                   * 00:00:00:00:00:00 if not a P2p client */
+       int pbc_in_m1;
 };
 
 
 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 */
@@ -162,6 +167,9 @@ struct wps_parse_attr {
        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) */
 
        /* variable length fields */
        const u8 *manufacturer;
@@ -186,12 +194,24 @@ struct wps_parse_attr {
        size_t eap_type_len;
        const u8 *eap_identity; /* <= 64 octets */
        size_t eap_identity_len;
+       const u8 *authorized_macs; /* <= 30 octets */
+       size_t authorized_macs_len;
+       const u8 *sec_dev_type_list; /* <= 128 octets */
+       size_t sec_dev_type_list_len;
 
        /* attributes that can occur multiple times */
 #define MAX_CRED_COUNT 10
        const u8 *cred[MAX_CRED_COUNT];
        size_t cred_len[MAX_CRED_COUNT];
        size_t num_cred;
+
+#define MAX_REQ_DEV_TYPE_COUNT 10
+       const u8 *req_dev_type[MAX_REQ_DEV_TYPE_COUNT];
+       size_t num_req_dev_type;
+
+       const u8 *vendor_ext[MAX_WPS_PARSE_VENDOR_EXT];
+       size_t vendor_ext_len[MAX_WPS_PARSE_VENDOR_EXT];
+       size_t num_vendor_ext;
 };
 
 /* wps_common.c */
@@ -202,7 +222,8 @@ void wps_derive_psk(struct wps_data *wps, const u8 *dev_passwd,
                    size_t dev_passwd_len);
 struct wpabuf * wps_decrypt_encr_settings(struct wps_data *wps, const u8 *encr,
                                          size_t encr_len);
-void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg);
+void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg,
+                   u16 config_error, u16 error_indication);
 void wps_success_event(struct wps_context *wps);
 void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part);
 void wps_pbc_overlap_event(struct wps_context *wps);
@@ -212,6 +233,9 @@ extern struct oob_device_data oob_ufd_device_data;
 extern struct oob_device_data oob_nfc_device_data;
 extern struct oob_nfc_device_data oob_nfc_pn531_device_data;
 
+struct wpabuf * wps_build_wsc_ack(struct wps_data *wps);
+struct wpabuf * wps_build_wsc_nack(struct wps_data *wps);
+
 /* wps_attr_parse.c */
 int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr);
 
@@ -228,6 +252,8 @@ int wps_build_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg);
 int wps_build_encr_settings(struct wps_data *wps, struct wpabuf *msg,
                            struct wpabuf *plain);
 int wps_build_version(struct wpabuf *msg);
+int wps_build_wfa_ext(struct wpabuf *msg, int req_to_enroll,
+                     const u8 *auth_macs, size_t auth_macs_count);
 int wps_build_msg_type(struct wpabuf *msg, enum wps_msg_type msg_type);
 int wps_build_enrollee_nonce(struct wps_data *wps, struct wpabuf *msg);
 int wps_build_registrar_nonce(struct wps_data *wps, struct wpabuf *msg);
@@ -236,6 +262,7 @@ int wps_build_encr_type_flags(struct wps_data *wps, struct wpabuf *msg);
 int wps_build_conn_type_flags(struct wps_data *wps, struct wpabuf *msg);
 int wps_build_assoc_state(struct wps_data *wps, struct wpabuf *msg);
 int wps_build_oob_dev_password(struct wpabuf *msg, struct wps_context *wps);
+struct wpabuf * wps_ie_encapsulate(struct wpabuf *data);
 
 /* wps_attr_process.c */
 int wps_process_authenticator(struct wps_data *wps, const u8 *authenticator,
@@ -264,15 +291,12 @@ int wps_build_cred(struct wps_data *wps, struct wpabuf *msg);
 int wps_device_store(struct wps_registrar *reg,
                     struct wps_device_data *dev, const u8 *uuid);
 void wps_registrar_selected_registrar_changed(struct wps_registrar *reg);
+const u8 * wps_authorized_macs(struct wps_registrar *reg, size_t *count);
+int wps_registrar_pbc_overlap(struct wps_registrar *reg,
+                             const u8 *addr, const u8 *uuid_e);
 
 /* ndef.c */
 struct wpabuf * ndef_parse_wifi(struct wpabuf *buf);
 struct wpabuf * ndef_build_wifi(struct wpabuf *buf);
 
-static inline int wps_version_supported(const u8 *version)
-{
-       /* Require major version match, but allow minor version differences */
-       return version && (*version & 0xf0) == (WPS_VERSION & 0xf0);
-}
-
 #endif /* WPS_I_H */
index 81ddf3a..e5e8d28 100644 (file)
 #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 */
 
 struct wps_uuid_pin {
        struct dl_list list;
@@ -39,6 +42,7 @@ struct wps_uuid_pin {
 #define PIN_EXPIRES BIT(1)
        int flags;
        struct os_time expiration;
+       u8 enrollee_addr[ETH_ALEN];
 };
 
 
@@ -123,10 +127,16 @@ struct wps_registrar {
        int sel_reg_dev_password_id_override;
        int sel_reg_config_methods_override;
        int static_wep_only;
+       int dualband;
 
        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];
 };
 
 
@@ -136,6 +146,52 @@ static void wps_registrar_set_selected_timeout(void *eloop_ctx,
                                               void *timeout_ctx);
 
 
+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;
@@ -254,20 +310,29 @@ static void wps_registrar_add_pbc_session(struct wps_registrar *reg,
 
 
 static void wps_registrar_remove_pbc_session(struct wps_registrar *reg,
-                                            const u8 *addr, const u8 *uuid_e)
+                                            const u8 *uuid_e,
+                                            const u8 *p2p_dev_addr)
 {
-       struct wps_pbc_session *pbc, *prev = NULL;
+       struct wps_pbc_session *pbc, *prev = NULL, *tmp;
 
        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 (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;
-                       os_free(pbc);
-                       break;
+                       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;
@@ -275,26 +340,50 @@ static void wps_registrar_remove_pbc_session(struct wps_registrar *reg,
 }
 
 
-static int wps_registrar_pbc_overlap(struct wps_registrar *reg,
-                                    const u8 *addr, const u8 *uuid_e)
+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_time now;
 
        os_get_time(&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) {
-               if (now.sec > pbc->timestamp.sec + WPS_PBC_WALK_TIME)
+               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 (now.sec > pbc->timestamp.sec + WPS_PBC_WALK_TIME) {
+                       wpa_printf(MSG_DEBUG, "WPS: PBC walk time has "
+                                  "expired");
                        break;
-               if (addr == NULL || os_memcmp(addr, pbc->addr, ETH_ALEN) ||
-                   uuid_e == NULL ||
-                   os_memcmp(uuid_e, pbc->uuid_e, WPS_UUID_LEN))
+               }
+               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;
        }
 
-       if (addr || uuid_e)
-               count++;
+       wpa_printf(MSG_DEBUG, "WPS: %u active PBC session(s) found", count);
 
        return count > 1 ? 1 : 0;
 }
@@ -339,7 +428,7 @@ static void wps_registrar_free_pending_m2(struct wps_context *wps)
 static int wps_build_ap_setup_locked(struct wps_context *wps,
                                     struct wpabuf *msg)
 {
-       if (wps->ap_setup_locked) {
+       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);
@@ -378,15 +467,55 @@ static int wps_build_sel_reg_dev_password_id(struct wps_registrar *reg,
 }
 
 
+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;
+#ifdef CONFIG_WPS2
+       if (conf_methods & WPS_CONFIG_VIRT_PUSHBUTTON)
+               *methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
+       if (conf_methods & WPS_CONFIG_PHY_PUSHBUTTON)
+               *methods |= WPS_CONFIG_PHY_PUSHBUTTON;
+       if (!(*methods & (WPS_CONFIG_VIRT_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;
+       }
+#endif /* CONFIG_WPS2 */
+}
+
+
 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 & ~WPS_CONFIG_PUSHBUTTON;
+       methods = reg->wps->config_methods;
+       methods &= ~WPS_CONFIG_PUSHBUTTON;
+#ifdef CONFIG_WPS2
+       methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
+                    WPS_CONFIG_PHY_PUSHBUTTON);
+#endif /* CONFIG_WPS2 */
        if (reg->pbc)
-               methods |= WPS_CONFIG_PUSHBUTTON;
+               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)",
@@ -407,6 +536,10 @@ static int wps_build_probe_config_methods(struct wps_registrar *reg,
         * external Registrars.
         */
        methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
+#ifdef CONFIG_WPS2
+       methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
+                    WPS_CONFIG_PHY_PUSHBUTTON);
+#endif /* CONFIG_WPS2 */
        wpa_printf(MSG_DEBUG, "WPS:  * Config Methods (%x)", methods);
        wpabuf_put_be16(msg, ATTR_CONFIG_METHODS);
        wpabuf_put_be16(msg, 2);
@@ -418,11 +551,23 @@ static int wps_build_probe_config_methods(struct wps_registrar *reg,
 static int wps_build_config_methods_r(struct wps_registrar *reg,
                                      struct wpabuf *msg)
 {
-       u16 methods;
-       methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
-       if (reg->pbc)
-               methods |= WPS_CONFIG_PUSHBUTTON;
-       return wps_build_config_methods(msg, methods);
+       return wps_build_config_methods(msg, reg->wps->config_methods);
+}
+
+
+const u8 * wps_authorized_macs(struct wps_registrar *reg, size_t *count)
+{
+       *count = 0;
+
+#ifdef CONFIG_WPS2
+       while (*count < WPS_MAX_AUTHORIZED_MACS) {
+               if (is_zero_ether_addr(reg->authorized_macs_union[*count]))
+                       break;
+               (*count)++;
+       }
+#endif /* CONFIG_WPS2 */
+
+       return (const u8 *) reg->authorized_macs_union;
 }
 
 
@@ -468,6 +613,7 @@ wps_registrar_init(struct wps_context *wps,
        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;
 
        if (wps_set_ie(reg)) {
                wps_registrar_deinit(reg);
@@ -499,20 +645,24 @@ void wps_registrar_deinit(struct wps_registrar *reg)
 /**
  * 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 *uuid,
-                         const u8 *pin, size_t pin_len, int timeout)
+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
@@ -539,6 +689,11 @@ int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *uuid,
        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);
        eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
        eloop_register_timeout(WPS_PBC_WALK_TIME, 0,
@@ -549,6 +704,22 @@ int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *uuid,
 }
 
 
+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);
+}
+
+
 static void wps_registrar_expire_pins(struct wps_registrar *reg)
 {
        struct wps_uuid_pin *pin, *prev;
@@ -561,9 +732,32 @@ static void wps_registrar_expire_pins(struct wps_registrar *reg)
                    os_time_before(&pin->expiration, &now)) {
                        wpa_hexdump(MSG_DEBUG, "WPS: Expired PIN for UUID",
                                    pin->uuid, WPS_UUID_LEN);
-                       wps_remove_pin(pin);
+                       wps_registrar_remove_pin(reg, pin);
+               }
+       }
+}
+
+
+/**
+ * wps_registrar_invalidate_wildcard_pin - Invalidate a wildcard PIN
+ * @reg: Registrar data from wps_registrar_init()
+ * Returns: 0 on success, -1 if not wildcard PIN is enabled
+ */
+static int wps_registrar_invalidate_wildcard_pin(struct wps_registrar *reg)
+{
+       struct wps_uuid_pin *pin, *prev;
+
+       dl_list_for_each_safe(pin, prev, &reg->pins, struct wps_uuid_pin, list)
+       {
+               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;
 }
 
 
@@ -582,7 +776,7 @@ int wps_registrar_invalidate_pin(struct wps_registrar *reg, const u8 *uuid)
                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_remove_pin(pin);
+                       wps_registrar_remove_pin(reg, pin);
                        return 0;
                }
        }
@@ -673,6 +867,9 @@ 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);
 }
 
@@ -690,26 +887,40 @@ static void wps_registrar_pbc_timeout(void *eloop_ctx, void *timeout_ctx)
 /**
  * wps_registrar_button_pushed - Notify Registrar that AP button was pushed
  * @reg: Registrar data from wps_registrar_init()
- * Returns: 0 on success, -1 on failure
+ * @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.
+ * 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)
+int wps_registrar_button_pushed(struct wps_registrar *reg,
+                               const u8 *p2p_dev_addr)
 {
-       if (wps_registrar_pbc_overlap(reg, NULL, NULL)) {
+       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 -1;
+               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);
 
+       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);
@@ -734,6 +945,36 @@ static void wps_registrar_pin_completed(struct wps_registrar *reg)
 }
 
 
+void wps_registrar_complete(struct wps_registrar *registrar, const u8 *uuid_e)
+{
+       if (registrar->pbc) {
+               wps_registrar_remove_pbc_session(registrar,
+                                                uuid_e, NULL);
+               wps_registrar_pbc_completed(registrar);
+       } else {
+               wps_registrar_pin_completed(registrar);
+       }
+}
+
+
+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);
+               return 1;
+       }
+       return 0;
+}
+
+
 /**
  * wps_registrar_probe_req_rx - Notify Registrar of Probe Request
  * @reg: Registrar data from wps_registrar_init()
@@ -745,7 +986,8 @@ static void wps_registrar_pin_completed(struct wps_registrar *reg)
  * situation with other WPS APs.
  */
 void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr,
-                               const struct wpabuf *wps_data)
+                               const struct wpabuf *wps_data,
+                               int p2p_wildcard)
 {
        struct wps_parse_attr attr;
 
@@ -755,11 +997,6 @@ void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr,
 
        if (wps_parse_msg(wps_data, &attr) < 0)
                return;
-       if (!wps_version_supported(attr.version)) {
-               wpa_printf(MSG_DEBUG, "WPS: Unsupported ProbeReq WPS IE "
-                          "version 0x%x", attr.version ? *attr.version : 0);
-               return;
-       }
 
        if (attr.config_methods == NULL) {
                wpa_printf(MSG_DEBUG, "WPS: No Config Methods attribute in "
@@ -774,7 +1011,7 @@ void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr,
        }
 
        if (reg->enrollee_seen_cb && attr.uuid_e &&
-           attr.primary_dev_type && attr.request_type) {
+           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);
@@ -801,6 +1038,8 @@ void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr,
                           "UUID-E included");
                return;
        }
+       wpa_hexdump(MSG_DEBUG, "WPS: UUID-E from Probe Request", attr.uuid_e,
+                   WPS_UUID_LEN);
 
        wps_registrar_add_pbc_session(reg, addr, attr.uuid_e);
        if (wps_registrar_pbc_overlap(reg, addr, attr.uuid_e)) {
@@ -856,74 +1095,84 @@ static void wps_cb_set_sel_reg(struct wps_registrar *reg)
 
        if (reg->selected_registrar) {
                methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
+#ifdef CONFIG_WPS2
+               methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
+                            WPS_CONFIG_PHY_PUSHBUTTON);
+#endif /* CONFIG_WPS2 */
                if (reg->pbc)
-                       methods |= WPS_CONFIG_PUSHBUTTON;
+                       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);
 }
 
 
-/* Encapsulate WPS IE data with one (or more, if needed) IE headers */
-static struct wpabuf * wps_ie_encapsulate(struct wpabuf *data)
-{
-       struct wpabuf *ie;
-       const u8 *pos, *end;
-
-       ie = wpabuf_alloc(wpabuf_len(data) + 100);
-       if (ie == NULL) {
-               wpabuf_free(data);
-               return NULL;
-       }
-
-       pos = wpabuf_head(data);
-       end = pos + wpabuf_len(data);
-
-       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, WPS_DEV_OUI_WFA);
-               wpabuf_put_data(ie, pos, frag_len);
-               pos += frag_len;
-       }
-
-       wpabuf_free(data);
-
-       return ie;
-}
-
-
 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;
 
-       wpa_printf(MSG_DEBUG, "WPS: Build Beacon and Probe Response IEs");
+       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(300);
+       beacon = wpabuf_alloc(400 + vendor_len);
        if (beacon == NULL)
                return -1;
-       probe = wpabuf_alloc(400);
+       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_version(probe) ||
+           wps_build_sel_pbc_reg_uuid_e(reg, beacon) ||
+           (reg->dualband && wps_build_rf_bands(&reg->wps->dev, beacon)) ||
+           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) ||
@@ -934,7 +1183,9 @@ static int wps_set_ie(struct wps_registrar *reg)
            wps_build_uuid_e(probe, reg->wps->uuid) ||
            wps_build_device_attrs(&reg->wps->dev, probe) ||
            wps_build_probe_config_methods(reg, probe) ||
-           wps_build_rf_bands(&reg->wps->dev, probe)) {
+           wps_build_rf_bands(&reg->wps->dev, probe) ||
+           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;
@@ -1025,7 +1276,7 @@ static int wps_build_r_hash(struct wps_data *wps, struct wpabuf *msg)
        const u8 *addr[4];
        size_t len[4];
 
-       if (os_get_random(wps->snonce, 2 * WPS_SECRET_NONCE_LEN) < 0)
+       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",
@@ -1091,7 +1342,7 @@ static int wps_build_r_snonce2(struct wps_data *wps, struct wpabuf *msg)
 static int wps_build_cred_network_idx(struct wpabuf *msg,
                                      const struct wps_credential *cred)
 {
-       wpa_printf(MSG_DEBUG, "WPS:  * Network Index");
+       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);
@@ -1103,6 +1354,8 @@ 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);
@@ -1139,6 +1392,8 @@ static int wps_build_cred_network_key(struct wpabuf *msg,
 {
        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);
@@ -1172,6 +1427,25 @@ static int wps_build_credential(struct wpabuf *msg,
 }
 
 
+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;
@@ -1237,7 +1511,7 @@ int wps_build_cred(struct wps_data *wps, struct wpabuf *msg)
            !wps->wps->registrar->disable_auto_conf) {
                u8 r[16];
                /* Generate a random passphrase */
-               if (os_get_random(r, sizeof(r)) < 0)
+               if (random_get_bytes(r, sizeof(r)) < 0)
                        return -1;
                os_free(wps->new_psk);
                wps->new_psk = base64_encode(r, sizeof(r), &wps->new_psk_len);
@@ -1269,7 +1543,7 @@ int wps_build_cred(struct wps_data *wps, struct wpabuf *msg)
                wps->new_psk = os_malloc(wps->new_psk_len);
                if (wps->new_psk == NULL)
                        return -1;
-               if (os_get_random(wps->new_psk, wps->new_psk_len) < 0) {
+               if (random_get_bytes(wps->new_psk, wps->new_psk_len) < 0) {
                        os_free(wps->new_psk);
                        wps->new_psk = NULL;
                        return -1;
@@ -1283,6 +1557,33 @@ int wps_build_cred(struct wps_data *wps, struct wpabuf *msg)
        }
 
 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;
@@ -1318,11 +1619,40 @@ static int wps_build_ap_settings(struct wps_data *wps, struct wpabuf *msg)
 }
 
 
+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;
 
-       if (os_get_random(wps->nonce_r, WPS_NONCE_LEN) < 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);
@@ -1350,6 +1680,7 @@ static struct wpabuf * wps_build_m2(struct wps_data *wps)
            wps_build_config_error(msg, WPS_CFG_NO_ERROR) ||
            wps_build_dev_password_id(msg, wps->dev_pw_id) ||
            wps_build_os_version(&wps->wps->dev, msg) ||
+           wps_build_wfa_ext(msg, 0, NULL, 0) ||
            wps_build_authenticator(wps, msg)) {
                wpabuf_free(msg);
                return NULL;
@@ -1388,7 +1719,8 @@ static struct wpabuf * wps_build_m2d(struct wps_data *wps)
            wps_build_rf_bands(&wps->wps->dev, msg) ||
            wps_build_assoc_state(wps, msg) ||
            wps_build_config_error(msg, err) ||
-           wps_build_os_version(&wps->wps->dev, msg)) {
+           wps_build_os_version(&wps->wps->dev, msg) ||
+           wps_build_wfa_ext(msg, 0, NULL, 0)) {
                wpabuf_free(msg);
                return NULL;
        }
@@ -1423,6 +1755,7 @@ static struct wpabuf * wps_build_m4(struct wps_data *wps)
            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);
@@ -1457,6 +1790,7 @@ static struct wpabuf * wps_build_m6(struct wps_data *wps)
            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);
@@ -1493,6 +1827,7 @@ static struct wpabuf * wps_build_m8(struct wps_data *wps)
            (!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);
@@ -1505,51 +1840,6 @@ static struct wpabuf * wps_build_m8(struct wps_data *wps)
 }
 
 
-static 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)) {
-               wpabuf_free(msg);
-               return NULL;
-       }
-
-       return msg;
-}
-
-
-static 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)) {
-               wpabuf_free(msg);
-               return NULL;
-       }
-
-       return msg;
-}
-
-
 struct wpabuf * wps_registrar_get_msg(struct wps_data *wps,
                                      enum wsc_op_code *op_code)
 {
@@ -2047,6 +2337,45 @@ static int wps_process_config_error(struct wps_data *wps, const u8 *err)
 }
 
 
+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)
 {
@@ -2099,14 +2428,19 @@ static enum wps_process_res wps_process_m1(struct wps_data *wps,
 #endif /* CONFIG_WPS_OOB */
 
        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)) {
+               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->wps->registrar->force_pbc_overlap = 1;
                        return WPS_CONTINUE;
                }
@@ -2150,7 +2484,8 @@ static enum wps_process_res wps_process_m3(struct wps_data *wps,
                return WPS_CONTINUE;
        }
 
-       if (wps->pbc && wps->wps->registrar->force_pbc_overlap) {
+       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;
@@ -2187,7 +2522,8 @@ static enum wps_process_res wps_process_m5(struct wps_data *wps,
                return WPS_CONTINUE;
        }
 
-       if (wps->pbc && wps->wps->registrar->force_pbc_overlap) {
+       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;
@@ -2210,6 +2546,12 @@ static enum wps_process_res wps_process_m5(struct wps_data *wps,
                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 ||
@@ -2264,6 +2606,8 @@ static void wps_cred_update(struct wps_credential *dst,
 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;
 
@@ -2283,12 +2627,31 @@ static int wps_process_ap_settings_r(struct wps_data *wps,
                 * 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;
        }
 }
@@ -2310,7 +2673,8 @@ static enum wps_process_res wps_process_m7(struct wps_data *wps,
                return WPS_CONTINUE;
        }
 
-       if (wps->pbc && wps->wps->registrar->force_pbc_overlap) {
+       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;
@@ -2333,6 +2697,13 @@ static enum wps_process_res wps_process_m7(struct wps_data *wps,
                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 ||
@@ -2362,15 +2733,10 @@ static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps,
        if (wps_parse_msg(msg, &attr) < 0)
                return WPS_FAILURE;
 
-       if (!wps_version_supported(attr.version)) {
-               wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
-                          attr.version ? *attr.version : 0);
-               return WPS_FAILURE;
-       }
-
        if (attr.msg_type == NULL) {
                wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
-               return WPS_FAILURE;
+               wps->state = SEND_WSC_NACK;
+               return WPS_CONTINUE;
        }
 
        if (*attr.msg_type != WPS_M1 &&
@@ -2383,6 +2749,8 @@ static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps,
 
        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 */
@@ -2397,19 +2765,28 @@ static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps,
                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_fail_event(wps->wps, WPS_M3, wps->config_error,
+                                      wps->error_indication);
                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_fail_event(wps->wps, WPS_M5, wps->config_error,
+                                      wps->error_indication);
                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_fail_event(wps->wps, WPS_M7, wps->config_error,
+                                      wps->error_indication);
                break;
        default:
                wpa_printf(MSG_DEBUG, "WPS: Unsupported Message Type %d",
@@ -2438,12 +2815,6 @@ static enum wps_process_res wps_process_wsc_ack(struct wps_data *wps,
        if (wps_parse_msg(msg, &attr) < 0)
                return WPS_FAILURE;
 
-       if (!wps_version_supported(attr.version)) {
-               wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
-                          attr.version ? *attr.version : 0);
-               return WPS_FAILURE;
-       }
-
        if (attr.msg_type == NULL) {
                wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
                return WPS_FAILURE;
@@ -2506,6 +2877,7 @@ static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps,
 {
        struct wps_parse_attr attr;
        int old_state;
+       u16 config_error;
 
        wpa_printf(MSG_DEBUG, "WPS: Received WSC_NACK");
 
@@ -2515,12 +2887,6 @@ static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps,
        if (wps_parse_msg(msg, &attr) < 0)
                return WPS_FAILURE;
 
-       if (!wps_version_supported(attr.version)) {
-               wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
-                          attr.version ? *attr.version : 0);
-               return WPS_FAILURE;
-       }
-
        if (attr.msg_type == NULL) {
                wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
                return WPS_FAILURE;
@@ -2559,21 +2925,26 @@ static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps,
                return WPS_FAILURE;
        }
 
+       config_error = WPA_GET_BE16(attr.config_error);
        wpa_printf(MSG_DEBUG, "WPS: Enrollee terminated negotiation with "
-                  "Configuration Error %d", WPA_GET_BE16(attr.config_error));
+                  "Configuration Error %d", config_error);
 
        switch (old_state) {
        case RECV_M3:
-               wps_fail_event(wps->wps, WPS_M2);
+               wps_fail_event(wps->wps, WPS_M2, config_error,
+                              wps->error_indication);
                break;
        case RECV_M5:
-               wps_fail_event(wps->wps, WPS_M4);
+               wps_fail_event(wps->wps, WPS_M4, config_error,
+                              wps->error_indication);
                break;
        case RECV_M7:
-               wps_fail_event(wps->wps, WPS_M6);
+               wps_fail_event(wps->wps, WPS_M6, config_error,
+                              wps->error_indication);
                break;
        case RECV_DONE:
-               wps_fail_event(wps->wps, WPS_M8);
+               wps_fail_event(wps->wps, WPS_M8, config_error,
+                              wps->error_indication);
                break;
        default:
                break;
@@ -2600,12 +2971,6 @@ static enum wps_process_res wps_process_wsc_done(struct wps_data *wps,
        if (wps_parse_msg(msg, &attr) < 0)
                return WPS_FAILURE;
 
-       if (!wps_version_supported(attr.version)) {
-               wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
-                          attr.version ? *attr.version : 0);
-               return WPS_FAILURE;
-       }
-
        if (attr.msg_type == NULL) {
                wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
                return WPS_FAILURE;
@@ -2687,11 +3052,14 @@ static enum wps_process_res wps_process_wsc_done(struct wps_data *wps,
 
        if (wps->pbc) {
                wps_registrar_remove_pbc_session(wps->wps->registrar,
-                                                wps->mac_addr_e, wps->uuid_e);
+                                                wps->uuid_e,
+                                                wps->p2p_dev_addr);
                wps_registrar_pbc_completed(wps->wps->registrar);
        } 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);
 
@@ -2747,14 +3115,22 @@ enum wps_process_res wps_registrar_process_msg(struct wps_data *wps,
        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_fail_event(wps->wps, WPS_WSC_DONE,
+                                      wps->config_error,
+                                      wps->error_indication);
                }
                return ret;
        default:
@@ -2787,6 +3163,7 @@ static void wps_registrar_set_selected_timeout(void *eloop_ctx,
 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);
@@ -2796,6 +3173,22 @@ static void wps_registrar_sel_reg_add(struct wps_registrar *reg,
        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 */
 
@@ -2841,17 +3234,27 @@ void wps_registrar_selected_registrar_changed(struct wps_registrar *reg)
        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) {
-               reg->sel_reg_config_methods_override =
-                       reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
+               u16 methods;
+
+               methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
+#ifdef CONFIG_WPS2
+               methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
+                            WPS_CONFIG_PHY_PUSHBUTTON);
+#endif /* CONFIG_WPS2 */
                if (reg->pbc) {
                        reg->sel_reg_dev_password_id_override =
                                DEV_PW_PUSHBUTTON;
-                       reg->sel_reg_config_methods_override |=
-                               WPS_CONFIG_PUSHBUTTON;
+                       wps_set_pushbutton(&methods, reg->wps->config_methods);
                }
                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");
 
@@ -2898,3 +3301,43 @@ int wps_registrar_get_info(struct wps_registrar *reg, const u8 *addr,
 
        return len;
 }
+
+
+int wps_registrar_config_ap(struct wps_registrar *reg,
+                           struct wps_credential *cred)
+{
+#ifdef CONFIG_WPS2
+       printf("encr_type=0x%x\n", cred->encr_type);
+       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;
+       }
+#endif /* CONFIG_WPS2 */
+
+       if (reg->wps->cred_cb)
+               return reg->wps->cred_cb(reg->wps->cb_ctx, cred);
+
+       return -1;
+}
index f99b859..06dcd20 100644 (file)
@@ -3,7 +3,7 @@
  * 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>
+ * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
  *
  * See below for more details on licensing and code history.
  */
@@ -47,7 +47,7 @@
  * -- 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 noone cares.
+ * -- 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.
 
 #include "includes.h"
 
-#include <assert.h>
+#include <time.h>
 #include <net/if.h>
 #include <netdb.h>
 #include <sys/ioctl.h>
 #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)
@@ -270,7 +276,7 @@ static void uuid_make(u8 uuid[UUID_LEN])
 /* subscr_addr_delete -- delete single unlinked subscriber address
  * (be sure to unlink first if need be)
  */
-static void subscr_addr_delete(struct subscr_addr *a)
+void subscr_addr_delete(struct subscr_addr *a)
 {
        /*
         * Note: do NOT free domain_and_port or path because they point to
@@ -293,7 +299,8 @@ static void subscr_addr_free_all(struct subscription *s)
 
 
 /* subscr_addr_add_url -- add address(es) for one url to subscription */
-static void subscr_addr_add_url(struct subscription *s, const char *url)
+static void subscr_addr_add_url(struct subscription *s, const char *url,
+                               size_t url_len)
 {
        int alloc_len;
        char *scratch_mem = NULL;
@@ -307,20 +314,21 @@ static void subscr_addr_add_url(struct subscription *s, const char *url)
        struct addrinfo *result = NULL;
        struct addrinfo *rp;
        int rerr;
-       struct subscr_addr *a = NULL;
 
        /* url MUST begin with http: */
-       if (os_strncasecmp(url, "http://", 7))
+       if (url_len < 7 || os_strncasecmp(url, "http://", 7))
                goto fail;
        url += 7;
+       url_len -= 7;
 
        /* allocate memory for the extra stuff we need */
-       alloc_len = (2 * (os_strlen(url) + 1));
+       alloc_len = 2 * (url_len + 1);
        scratch_mem = os_zalloc(alloc_len);
        if (scratch_mem == NULL)
                goto fail;
        mem = scratch_mem;
-       strcpy(mem, url);
+       os_strncpy(mem, url, url_len);
+       wpa_printf(MSG_DEBUG, "WPS UPnP: Adding URL '%s'", mem);
        domain_and_port = mem;
        mem += 1 + os_strlen(mem);
        delim = os_strchr(domain_and_port, '/');
@@ -332,7 +340,7 @@ static void subscr_addr_add_url(struct subscription *s, const char *url)
        }
        domain = mem;
        strcpy(domain, domain_and_port);
-       delim = strchr(domain, ':');
+       delim = os_strchr(domain, ':');
        if (delim) {
                *delim++ = 0;   /* null terminate domain */
                if (isdigit(*delim))
@@ -367,6 +375,8 @@ static void subscr_addr_add_url(struct subscription *s, const char *url)
                goto fail;
        }
        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: "
@@ -385,19 +395,17 @@ static void subscr_addr_add_url(struct subscription *s, const char *url)
                if (path[0] != '/')
                        *mem++ = '/';
                strcpy(mem, path);
-               mem += 1 + strlen(mem);
+               mem += 1 + os_strlen(mem);
                os_memcpy(&a->saddr, rp->ai_addr, sizeof(a->saddr));
                a->saddr.sin_port = htons(port);
 
                dl_list_add(&s->addr_list, &a->list);
-               a = NULL;       /* don't free it below */
        }
 
 fail:
        if (result)
                freeaddrinfo(result);
        os_free(scratch_mem);
-       os_free(a);
 }
 
 
@@ -407,7 +415,8 @@ fail:
 static void subscr_addr_list_create(struct subscription *s,
                                    const char *url_list)
 {
-       char *end;
+       const char *end;
+       wpa_printf(MSG_DEBUG, "WPS UPnP: Parsing URL list '%s'", url_list);
        for (;;) {
                while (*url_list == ' ' || *url_list == '\t')
                        url_list++;
@@ -417,9 +426,8 @@ static void subscr_addr_list_create(struct subscription *s,
                end = os_strchr(url_list, '>');
                if (end == NULL)
                        break;
-               *end++ = 0;
-               subscr_addr_add_url(s, url_list);
-               url_list = end;
+               subscr_addr_add_url(s, url_list, end - url_list);
+               url_list = end + 1;
        }
 }
 
@@ -472,12 +480,38 @@ static void upnp_wps_device_send_event(struct upnp_wps_device_sm *sm)
                "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
                "<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\">\n";
        const char *format_tail = "</e:propertyset>\n";
+       struct os_time now;
 
        if (dl_list_empty(&sm->subscriptions)) {
                /* optimize */
                return;
        }
 
+       if (os_get_time(&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");
@@ -497,12 +531,8 @@ static void upnp_wps_device_send_event(struct upnp_wps_device_sm *sm)
 
        dl_list_for_each_safe(s, tmp, &sm->subscriptions, struct subscription,
                              list) {
-               if (event_add(s, buf)) {
-                       wpa_printf(MSG_INFO, "WPS UPnP: Dropping "
-                                  "subscriber due to event backlog");
-                       dl_list_del(&s->list);
-                       subscription_destroy(s);
-               }
+               event_add(s, buf,
+                         sm->wlanevent_type == UPNP_WPS_WLANEVENT_TYPE_PROBE);
        }
 
        wpabuf_free(buf);
@@ -578,6 +608,7 @@ static struct wpabuf * build_fake_wsc_ack(void)
        wpabuf_put_be16(msg, ATTR_REGISTRAR_NONCE);
        wpabuf_put_be16(msg, WPS_NONCE_LEN);
        wpabuf_put(msg, WPS_NONCE_LEN);
+       wps_build_wfa_ext(msg, 0, NULL, 0);
        return msg;
 }
 
@@ -605,6 +636,7 @@ static int subscription_first_event(struct subscription *s)
                "<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) {
                /*
@@ -636,7 +668,7 @@ static int subscription_first_event(struct subscription *s)
        }
        buf = wpabuf_alloc(500 + os_strlen(wlan_event));
        if (buf == NULL)
-               return 1;
+               return -1;
 
        wpabuf_put_str(buf, head);
        wpabuf_put_property(buf, "STAStatus", "1");
@@ -646,9 +678,10 @@ static int subscription_first_event(struct subscription *s)
                wpabuf_put_property(buf, "WLANEvent", wlan_event);
        wpabuf_put_str(buf, tail);
 
-       if (event_add(s, buf)) {
+       ret = event_add(s, buf, 0);
+       if (ret) {
                wpabuf_free(buf);
-               return 1;
+               return ret;
        }
        wpabuf_free(buf);
 
@@ -692,6 +725,13 @@ struct subscription * subscription_start(struct upnp_wps_device_sm *sm,
        s->timeout_time = expire;
        uuid_make(s->uuid);
        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)
@@ -786,6 +826,7 @@ int upnp_wps_device_send_wlan_event(struct upnp_wps_device_sm *sm,
 
        os_free(sm->wlanevent);
        sm->wlanevent = val;
+       sm->wlanevent_type = ev_type;
        upnp_wps_device_send_event(sm);
 
        ret = 0;
@@ -914,10 +955,13 @@ static void upnp_wps_free_msearchreply(struct dl_list *head)
 }
 
 
-static void upnp_wps_free_subscriptions(struct dl_list *head)
+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);
        }
@@ -928,7 +972,7 @@ static void upnp_wps_free_subscriptions(struct dl_list *head)
  * upnp_wps_device_stop - Stop WPS UPnP operations on an interface
  * @sm: WPS UPnP state machine from upnp_wps_device_init()
  */
-void upnp_wps_device_stop(struct upnp_wps_device_sm *sm)
+static void upnp_wps_device_stop(struct upnp_wps_device_sm *sm)
 {
        if (!sm || !sm->started)
                return;
@@ -936,7 +980,7 @@ void upnp_wps_device_stop(struct upnp_wps_device_sm *sm)
        wpa_printf(MSG_DEBUG, "WPS UPnP: Stop device");
        web_listener_stop(sm);
        upnp_wps_free_msearchreply(&sm->msearch_replies);
-       upnp_wps_free_subscriptions(&sm->subscriptions);
+       upnp_wps_free_subscriptions(&sm->subscriptions, NULL);
 
        advertisement_state_machine_stop(sm, 1);
 
@@ -960,7 +1004,7 @@ void upnp_wps_device_stop(struct upnp_wps_device_sm *sm)
  * @net_if: Selected network interface name
  * Returns: 0 on success, -1 on failure
  */
-int upnp_wps_device_start(struct upnp_wps_device_sm *sm, char *net_if)
+static int upnp_wps_device_start(struct upnp_wps_device_sm *sm, char *net_if)
 {
        if (!sm || !net_if)
                return -1;
@@ -1015,24 +1059,59 @@ fail:
 }
 
 
+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 upnp_wps_device_deinit(struct upnp_wps_device_sm *sm, void *priv)
 {
+       struct upnp_wps_device_interface *iface;
+
        if (!sm)
                return;
 
-       upnp_wps_device_stop(sm);
-
-       if (sm->peer.wps)
-               wps_deinit(sm->peer.wps);
-       os_free(sm->root_dir);
-       os_free(sm->desc_url);
-       os_free(sm->ctx->ap_pin);
-       os_free(sm->ctx);
-       os_free(sm);
+       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;
+       }
 }
 
 
@@ -1041,25 +1120,59 @@ void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm)
  * @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)
+                    void *priv, char *net_if)
 {
        struct upnp_wps_device_sm *sm;
+       struct upnp_wps_device_interface *iface;
+       int start = 0;
 
-       sm = os_zalloc(sizeof(*sm));
-       if (!sm) {
-               wpa_printf(MSG_ERROR, "WPS UPnP: upnp_wps_device_init failed");
+       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;
        }
 
-       sm->ctx = ctx;
-       sm->wps = wps;
-       sm->priv = priv;
-       dl_list_init(&sm->msearch_replies);
-       dl_list_init(&sm->subscriptions);
 
        return sm;
 }
@@ -1078,16 +1191,20 @@ int upnp_wps_subscribers(struct upnp_wps_device_sm *sm)
 
 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;
 
-       os_free(sm->ctx->ap_pin);
-       if (ap_pin) {
-               sm->ctx->ap_pin = os_strdup(ap_pin);
-               if (sm->ctx->ap_pin == NULL)
-                       return -1;
-       } else
-               sm->ctx->ap_pin = NULL;
+       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 06bc31f..87b7ab1 100644 (file)
@@ -35,11 +35,8 @@ struct upnp_wps_device_ctx {
 
 struct upnp_wps_device_sm *
 upnp_wps_device_init(struct upnp_wps_device_ctx *ctx, struct wps_context *wps,
-                    void *priv);
-void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm);
-
-int upnp_wps_device_start(struct upnp_wps_device_sm *sm, char *net_if);
-void upnp_wps_device_stop(struct upnp_wps_device_sm *sm);
+                    void *priv, char *net_if);
+void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm, void *priv);
 
 int upnp_wps_device_send_wlan_event(struct upnp_wps_device_sm *sm,
                                    const u8 from_mac_addr[ETH_ALEN],
index 93746da..501ecbc 100644 (file)
@@ -39,18 +39,16 @@ int upnp_er_set_selected_registrar(struct wps_registrar *reg,
 
        wpa_hexdump_buf(MSG_MSGDUMP, "WPS: SetSelectedRegistrar attributes",
                        msg);
+       if (wps_validate_upnp_set_selected_registrar(msg) < 0)
+               return -1;
 
        if (wps_parse_msg(msg, &attr) < 0)
                return -1;
-       if (!wps_version_supported(attr.version)) {
-               wpa_printf(MSG_DEBUG, "WPS: Unsupported SetSelectedRegistrar "
-                          "version 0x%x", attr.version ? *attr.version : 0);
-               return -1;
-       }
 
        s->reg = reg;
        eloop_cancel_timeout(upnp_er_set_selected_timeout, s, NULL);
 
+       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");
@@ -61,6 +59,19 @@ int upnp_er_set_selected_registrar(struct wps_registrar *reg,
                        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) {
+#ifdef CONFIG_WPS2
+                       wpa_printf(MSG_DEBUG, "WPS: Add broadcast "
+                                  "AuthorizedMACs for WPS 1.0 ER");
+                       os_memset(s->authorized_macs, 0xff, ETH_ALEN);
+#endif /* CONFIG_WPS2 */
+               }
                eloop_register_timeout(WPS_PBC_WALK_TIME, 0,
                                       upnp_er_set_selected_timeout, s, NULL);
        }
index ae5efdb..2c8ed4f 100644 (file)
@@ -3,7 +3,7 @@
  * 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>
+ * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
  *
  * See wps_upnp.c for more details on licensing and code history.
  */
@@ -31,7 +31,7 @@
  */
 
 #define MAX_EVENTS_QUEUED 20   /* How far behind queued events */
-#define EVENT_TIMEOUT_SEC 30   /* Drop sending event after timeout */
+#define MAX_FAILURES 10 /* Drop subscription after this many failures */
 
 /* How long to wait before sending event */
 #define EVENT_DELAY_SECONDS 0
@@ -73,6 +73,7 @@ static void event_clean(struct wps_event_ *e)
  */
 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);
@@ -86,8 +87,11 @@ 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)
+       if (e) {
+               wpa_printf(MSG_DEBUG, "WPS UPnP: Dequeue event %p for "
+                          "subscription %p", e, s);
                dl_list_del(&e->list);
+       }
        return e;
 }
 
@@ -115,14 +119,22 @@ 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)
+       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);
@@ -158,17 +170,60 @@ static struct wpabuf * event_build_message(struct wps_event_ *e)
 }
 
 
+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 reply OK from "
-                          "%s", e->addr->domain_and_port);
+                          "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 */
@@ -176,24 +231,17 @@ static void event_http_cb(void *ctx, struct http_client *c,
                        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: Failed to send event to %s",
-                          e->addr->domain_and_port);
-
-               /*
-                * 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...).
-                * Alternately, we could just do event_delete(e)
-                */
-               wpa_printf(MSG_DEBUG, "WPS UPnP: Deleting subscription due to "
-                          "errors");
-               dl_list_del(&s->list);
-               subscription_destroy(s);
+               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_retry(e, 1);
+               event_addr_failure(e);
+               break;
        }
 }
 
@@ -228,9 +276,12 @@ static int event_send_start(struct subscription *s)
         * 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.
         */
-       assert(!dl_list_empty(&s->addr_list));
-       assert(s->current_event == NULL);
-       assert(!dl_list_empty(&s->event_queue));
+       if (dl_list_empty(&s->addr_list))
+               return -1;
+       if (s->current_event)
+               return -1;
+       if (dl_list_empty(&s->event_queue))
+               return -1;
 
        s->current_event = e = event_dequeue(s);
 
@@ -270,18 +321,10 @@ static void event_send_all_later_handler(void *eloop_data, void *user_ctx)
        sm->event_send_all_queued = 0;
        dl_list_for_each_safe(s, tmp, &sm->subscriptions, struct subscription,
                              list) {
-               if (dl_list_empty(&s->addr_list)) {
-                       /* if we've given up on all addresses */
-                       wpa_printf(MSG_DEBUG, "WPS UPnP: Removing "
-                                  "subscription with no addresses");
-                       dl_list_del(&s->list);
-                       subscription_destroy(s);
-               } else {
-                       if (s->current_event == NULL /* not busy */ &&
-                           !dl_list_empty(&s->event_queue) /* more to do */) {
-                               if (event_send_start(s))
-                                       nerrors++;
-                       }
+               if (s->current_event == NULL /* not busy */ &&
+                   !dl_list_empty(&s->event_queue) /* more to do */) {
+                       if (event_send_start(s))
+                               nerrors++;
                }
        }
 
@@ -326,31 +369,54 @@ void event_send_stop_all(struct upnp_wps_device_sm *sm)
  * event_add - Add a new event to a queue
  * @s: Subscription
  * @data: Event data (is copied; caller retains ownership)
- * Returns: 0 on success, 1 on error
+ * @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 event_add(struct subscription *s, const struct wpabuf *data, int probereq)
 {
        struct wps_event_ *e;
+       unsigned int len;
 
-       if (dl_list_len(&s->event_queue) >= MAX_EVENTS_QUEUED) {
+       len = dl_list_len(&s->event_queue);
+       if (len >= MAX_EVENTS_QUEUED) {
                wpa_printf(MSG_DEBUG, "WPS UPnP: Too many events queued for "
-                          "subscriber");
-               return 1;
+                          "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;
+               return -1;
        dl_list_init(&e->list);
        e->s = s;
        e->data = wpabuf_dup(data);
        if (e->data == NULL) {
                os_free(e);
-               return 1;
+               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 b31875a..3ecf05d 100644 (file)
@@ -67,6 +67,7 @@ struct subscr_addr {
        char *domain_and_port; /* domain and port part of url */
        char *path; /* "filepath" part of url (from "mem") */
        struct sockaddr_in saddr; /* address for doing connect */
+       unsigned num_failures;
 };
 
 
@@ -91,25 +92,37 @@ struct subscription {
        struct dl_list event_queue; /* Queued event messages. */
        struct wps_event_ *current_event; /* non-NULL if being sent (not in q)
                                           */
+       int last_event_failed; /* Whether delivery of last event failed */
 
        /* Information from SetSelectedRegistrar action */
        u8 selected_registrar;
        u16 dev_password_id;
        u16 config_methods;
+       u8 authorized_macs[WPS_MAX_AUTHORIZED_MACS][ETH_ALEN];
        struct wps_registrar *reg;
 };
 
 
+struct upnp_wps_device_interface {
+       struct dl_list list;
+       struct upnp_wps_device_ctx *ctx; /* callback table */
+       struct wps_context *wps;
+       void *priv;
+
+       /* FIX: maintain separate structures for each UPnP peer */
+       struct upnp_wps_peer peer;
+};
+
 /*
- * Our instance data corresponding to one WiFi network interface
- * (multiple might share the same wired network interface!).
+ * Our instance data corresponding to the AP device. Note that there may be
+ * multiple wireless interfaces sharing the same UPnP device instance. Each
+ * such interface is stored in the list of struct upnp_wps_device_interface
+ * instances.
  *
  * This is known as an opaque struct declaration to users of the WPS UPnP code.
  */
 struct upnp_wps_device_sm {
-       struct upnp_wps_device_ctx *ctx; /* callback table */
-       struct wps_context *wps;
-       void *priv;
+       struct dl_list interfaces; /* struct upnp_wps_device_interface */
        char *root_dir;
        char *desc_url;
        int started; /* nonzero if we are active */
@@ -130,9 +143,9 @@ struct upnp_wps_device_sm {
                                    */
 
        char *wlanevent; /* the last WLANEvent data */
-
-       /* FIX: maintain separate structures for each UPnP peer */
-       struct upnp_wps_peer peer;
+       enum upnp_wps_wlanevent_type wlanevent_type;
+       os_time_t last_event_sec;
+       unsigned int num_events_in_sec;
 };
 
 /* wps_upnp.c */
@@ -144,6 +157,7 @@ struct subscription * subscription_renew(struct upnp_wps_device_sm *sm,
 void subscription_destroy(struct subscription *s);
 struct subscription * subscription_find(struct upnp_wps_device_sm *sm,
                                        const u8 uuid[UUID_LEN]);
+void subscr_addr_delete(struct subscr_addr *a);
 int send_wpabuf(int fd, struct wpabuf *buf);
 int get_netif_info(const char *net_if, unsigned *ip_addr, char **ip_addr_text,
                   u8 mac[ETH_ALEN]);
@@ -165,7 +179,7 @@ int web_listener_start(struct upnp_wps_device_sm *sm);
 void web_listener_stop(struct upnp_wps_device_sm *sm);
 
 /* wps_upnp_event.c */
-int event_add(struct subscription *s, const struct wpabuf *data);
+int event_add(struct subscription *s, const struct wpabuf *data, int probereq);
 void event_delete_all(struct subscription *s);
 void event_send_all_later(struct upnp_wps_device_sm *sm);
 void event_send_stop_all(struct upnp_wps_device_sm *sm);
index 8505d05..4c4aebf 100644 (file)
@@ -97,16 +97,6 @@ static int line_length(const char *l)
 }
 
 
-/* No. of chars excluding trailing whitespace */
-static int line_length_stripped(const char *l)
-{
-       const char *lp = l + line_length(l);
-       while (lp > l && !isgraph(lp[-1]))
-               lp--;
-       return lp - l;
-}
-
-
 static int str_starts(const char *str, const char *start)
 {
        return os_strncmp(str, start, os_strlen(start)) == 0;
@@ -136,9 +126,12 @@ next_advertisement(struct upnp_wps_device_sm *sm,
        struct wpabuf *msg;
        char *NTString = "";
        char uuid_string[80];
+       struct upnp_wps_device_interface *iface;
 
        *islast = 0;
-       uuid_bin2str(sm->wps->uuid, uuid_string, sizeof(uuid_string));
+       iface = dl_list_first(&sm->interfaces,
+                             struct upnp_wps_device_interface, list);
+       uuid_bin2str(iface->wps->uuid, uuid_string, sizeof(uuid_string));
        msg = wpabuf_alloc(800); /* more than big enough */
        if (msg == NULL)
                goto fail;
@@ -527,7 +520,6 @@ static void ssdp_parse_msearch(struct upnp_wps_device_sm *sm,
 #ifndef CONFIG_NO_STDOUT_DEBUG
        const char *start = data;
 #endif /* CONFIG_NO_STDOUT_DEBUG */
-       const char *end;
        int got_host = 0;
        int got_st = 0, st_match = 0;
        int got_man = 0;
@@ -542,7 +534,6 @@ static void ssdp_parse_msearch(struct upnp_wps_device_sm *sm,
 
        /* Parse remaining lines */
        for (; *data != '\0'; data += line_length(data)) {
-               end = data + line_length_stripped(data);
                if (token_eq(data, "host")) {
                        /* The host line indicates who the packet
                         * is addressed to... but do we really care?
@@ -588,8 +579,13 @@ static void ssdp_parse_msearch(struct upnp_wps_device_sm *sm,
                        }
                        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);
                                data += os_strlen("uuid:");
-                               uuid_bin2str(sm->wps->uuid, uuid_string,
+                               uuid_bin2str(iface->wps->uuid, uuid_string,
                                             sizeof(uuid_string));
                                if (str_starts(data, uuid_string))
                                        st_match = 1;
@@ -875,11 +871,17 @@ int ssdp_open_multicast_sock(u32 ip_addr)
 #endif
 
        if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF,
-                      &ip_addr, sizeof(ip_addr)))
+                      &ip_addr, sizeof(ip_addr))) {
+               wpa_printf(MSG_DEBUG, "WPS: setsockopt(IP_MULTICAST_IF) %x: "
+                          "%d (%s)", ip_addr, errno, strerror(errno));
                return -1;
+       }
        if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL,
-                      &ttl, sizeof(ttl)))
+                      &ttl, sizeof(ttl))) {
+               wpa_printf(MSG_DEBUG, "WPS: setsockopt(IP_MULTICAST_TTL): "
+                          "%d (%s)", errno, strerror(errno));
                return -1;
+       }
 
 #if 0   /* not needed, because we don't receive using multicast_sd */
        {
index 9a6b36e..ce0bede 100644 (file)
@@ -184,6 +184,10 @@ static void format_wps_device_xml(struct upnp_wps_device_sm *sm,
 {
        const char *s;
        char uuid_string[80];
+       struct upnp_wps_device_interface *iface;
+
+       iface = dl_list_first(&sm->interfaces,
+                             struct upnp_wps_device_interface, list);
 
        wpabuf_put_str(buf, wps_device_xml_prefix);
 
@@ -191,38 +195,38 @@ static void format_wps_device_xml(struct upnp_wps_device_sm *sm,
         * Add required fields with default values if not configured. Add
         * optional and recommended fields only if configured.
         */
-       s = sm->wps->friendly_name;
+       s = iface->wps->friendly_name;
        s = ((s && *s) ? s : "WPS Access Point");
        xml_add_tagged_data(buf, "friendlyName", s);
 
-       s = sm->wps->dev.manufacturer;
+       s = iface->wps->dev.manufacturer;
        s = ((s && *s) ? s : "");
        xml_add_tagged_data(buf, "manufacturer", s);
 
-       if (sm->wps->manufacturer_url)
+       if (iface->wps->manufacturer_url)
                xml_add_tagged_data(buf, "manufacturerURL",
-                                   sm->wps->manufacturer_url);
+                                   iface->wps->manufacturer_url);
 
-       if (sm->wps->model_description)
+       if (iface->wps->model_description)
                xml_add_tagged_data(buf, "modelDescription",
-                                   sm->wps->model_description);
+                                   iface->wps->model_description);
 
-       s = sm->wps->dev.model_name;
+       s = iface->wps->dev.model_name;
        s = ((s && *s) ? s : "");
        xml_add_tagged_data(buf, "modelName", s);
 
-       if (sm->wps->dev.model_number)
+       if (iface->wps->dev.model_number)
                xml_add_tagged_data(buf, "modelNumber",
-                                   sm->wps->dev.model_number);
+                                   iface->wps->dev.model_number);
 
-       if (sm->wps->model_url)
-               xml_add_tagged_data(buf, "modelURL", sm->wps->model_url);
+       if (iface->wps->model_url)
+               xml_add_tagged_data(buf, "modelURL", iface->wps->model_url);
 
-       if (sm->wps->dev.serial_number)
+       if (iface->wps->dev.serial_number)
                xml_add_tagged_data(buf, "serialNumber",
-                                   sm->wps->dev.serial_number);
+                                   iface->wps->dev.serial_number);
 
-       uuid_bin2str(sm->wps->uuid, uuid_string, sizeof(uuid_string));
+       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...
@@ -231,8 +235,8 @@ static void format_wps_device_xml(struct upnp_wps_device_sm *sm,
        xml_data_encode(buf, s, os_strlen(s));
        wpabuf_put_str(buf, "</UDN>\n");
 
-       if (sm->wps->upc)
-               xml_add_tagged_data(buf, "UPC", sm->wps->upc);
+       if (iface->wps->upc)
+               xml_add_tagged_data(buf, "UPC", iface->wps->upc);
 
        wpabuf_put_str(buf, wps_device_xml_postfix);
 }
@@ -311,6 +315,10 @@ static void web_connection_parse_get(struct upnp_wps_device_sm *sm,
        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);
 
        /*
         * It is not required that filenames be case insensitive but it is
@@ -322,16 +330,16 @@ static void web_connection_parse_get(struct upnp_wps_device_sm *sm,
                wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for device XML");
                req = GET_DEVICE_XML_FILE;
                extra_len = 3000;
-               if (sm->wps->friendly_name)
-                       extra_len += os_strlen(sm->wps->friendly_name);
-               if (sm->wps->manufacturer_url)
-                       extra_len += os_strlen(sm->wps->manufacturer_url);
-               if (sm->wps->model_description)
-                       extra_len += os_strlen(sm->wps->model_description);
-               if (sm->wps->model_url)
-                       extra_len += os_strlen(sm->wps->model_url);
-               if (sm->wps->upc)
-                       extra_len += os_strlen(sm->wps->upc);
+               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;
@@ -408,11 +416,16 @@ web_process_get_device_info(struct upnp_wps_device_sm *sm,
 {
        static const char *name = "NewDeviceInfo";
        struct wps_config cfg;
-       struct upnp_wps_peer *peer = &sm->peer;
+       struct upnp_wps_device_interface *iface;
+       struct upnp_wps_peer *peer;
+
+       iface = dl_list_first(&sm->interfaces,
+                             struct upnp_wps_device_interface, list);
+       peer = &iface->peer;
 
        wpa_printf(MSG_DEBUG, "WPS UPnP: GetDeviceInfo");
 
-       if (sm->ctx->ap_pin == NULL)
+       if (iface->ctx->ap_pin == NULL)
                return HTTP_INTERNAL_SERVER_ERROR;
 
        /*
@@ -427,9 +440,9 @@ web_process_get_device_info(struct upnp_wps_device_sm *sm,
                wps_deinit(peer->wps);
 
        os_memset(&cfg, 0, sizeof(cfg));
-       cfg.wps = sm->wps;
-       cfg.pin = (u8 *) sm->ctx->ap_pin;
-       cfg.pin_len = os_strlen(sm->ctx->ap_pin);
+       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;
@@ -458,6 +471,10 @@ web_process_put_message(struct upnp_wps_device_sm *sm, char *data,
        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);
 
        /*
         * PutMessage is used by external UPnP-based Registrar to perform WPS
@@ -468,11 +485,11 @@ web_process_put_message(struct upnp_wps_device_sm *sm, char *data,
        msg = xml_get_base64_item(data, "NewInMessage", &ret);
        if (msg == NULL)
                return ret;
-       res = wps_process_msg(sm->peer.wps, WSC_UPnP, msg);
+       res = wps_process_msg(iface->peer.wps, WSC_UPnP, msg);
        if (res == WPS_FAILURE)
                *reply = NULL;
        else
-               *reply = wps_get_msg(sm->peer.wps, &op_code);
+               *reply = wps_get_msg(iface->peer.wps, &op_code);
        wpabuf_free(msg);
        if (*reply == NULL)
                return HTTP_INTERNAL_SERVER_ERROR;
@@ -491,6 +508,8 @@ web_process_put_wlan_response(struct upnp_wps_device_sm *sm, char *data,
        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
@@ -523,6 +542,16 @@ web_process_put_wlan_response(struct upnp_wps_device_sm *sm, char *data,
        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
@@ -530,7 +559,8 @@ web_process_put_wlan_response(struct upnp_wps_device_sm *sm, char *data,
                         */
                        wpa_printf(MSG_DEBUG, "WPS UPnP: Workaround - allow "
                                   "incorrect MAC address format in "
-                                  "NewWLANEventMAC");
+                                  "NewWLANEventMAC: %s -> " MACSTR,
+                                  val, MAC2STR(macaddr));
                } else {
                        wpabuf_free(msg);
                        os_free(val);
@@ -548,9 +578,16 @@ web_process_put_wlan_response(struct upnp_wps_device_sm *sm, char *data,
                wpa_printf(MSG_DEBUG, "WPS UPnP: Message Type %d", type);
        } else
                type = -1;
-       if (!sm->ctx->rx_req_put_wlan_response ||
-           sm->ctx->rx_req_put_wlan_response(sm->priv, ev_type, macaddr, msg,
-                                             type)) {
+       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);
@@ -595,6 +632,8 @@ web_process_set_selected_registrar(struct upnp_wps_device_sm *sm,
        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);
@@ -606,11 +645,15 @@ web_process_set_selected_registrar(struct upnp_wps_device_sm *sm,
        msg = xml_get_base64_item(data, "NewMessage", &ret);
        if (msg == NULL)
                return ret;
-       if (upnp_er_set_selected_registrar(sm->wps->registrar, s, msg)) {
-               wpabuf_free(msg);
-               return HTTP_INTERNAL_SERVER_ERROR;
+       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;
@@ -890,6 +933,9 @@ static void web_connection_parse_subscribe(struct upnp_wps_device_sm *sm,
                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
@@ -910,7 +956,7 @@ static void web_connection_parse_subscribe(struct upnp_wps_device_sm *sm,
                        break; /* no unterminated lines allowed */
 
                /* NT assures that it is our type of subscription;
-                * not used for a renewl.
+                * not used for a renewal.
                 **/
                match = "NT:";
                match_len = os_strlen(match);
@@ -989,16 +1035,22 @@ static void web_connection_parse_subscribe(struct upnp_wps_device_sm *sm,
 
        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;
@@ -1022,6 +1074,7 @@ static void web_connection_parse_subscribe(struct upnp_wps_device_sm *sm,
        /* 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);
@@ -1055,6 +1108,7 @@ error:
        *     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);
diff --git a/src/wps/wps_validate.c b/src/wps/wps_validate.c
new file mode 100644 (file)
index 0000000..c3071a0
--- /dev/null
@@ -0,0 +1,1981 @@
+/*
+ * Wi-Fi Protected Setup - Strict protocol validation routines
+ * Copyright (c) 2010, Atheros Communications, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#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_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 > 18) {
+               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 >= 0x0006 && 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 e7e034c..0e3ad1b 100644 (file)
@@ -1,8 +1 @@
-*.d
-.config
-eapol_test
-preauth_test
-wpa_cli
-wpa_passphrase
-wpa_supplicant
-wpa_priv
+*.service
diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk
new file mode 100644 (file)
index 0000000..40fde34
--- /dev/null
@@ -0,0 +1,1463 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#
+
+LOCAL_PATH := $(call my-dir)
+PKG_CONFIG ?= pkg-config
+
+WPA_BUILD_SUPPLICANT := false
+ifneq ($(TARGET_SIMULATOR),true)
+  ifneq ($(BOARD_WPA_SUPPLICANT_DRIVER),)
+    WPA_BUILD_SUPPLICANT := true
+    CONFIG_DRIVER_$(BOARD_WPA_SUPPLICANT_DRIVER) := y
+  endif
+endif
+
+include $(LOCAL_PATH)/.config
+
+# To ignore possible wrong network configurations
+L_CFLAGS = -DWPA_IGNORE_CONFIG_ERRORS
+
+# Set Android log name
+L_CFLAGS += -DANDROID_LOG_NAME=\"wpa_supplicant\"
+
+# Use Android specific directory for control interface sockets
+L_CFLAGS += -DCONFIG_CTRL_IFACE_CLIENT_DIR=\"/data/misc/wifi/sockets\"
+L_CFLAGS += -DCONFIG_CTRL_IFACE_DIR=\"/data/system/wpa_supplicant\"
+
+# To force sizeof(enum) = 4
+ifeq ($(TARGET_ARCH),arm)
+L_CFLAGS += -mabi=aapcs-linux
+endif
+
+# To allow non-ASCII characters in SSID
+L_CFLAGS += -DWPA_UNICODE_SSID
+
+# OpenSSL is configured without engines on Android
+L_CFLAGS += -DOPENSSL_NO_ENGINE
+
+INCLUDES = $(LOCAL_PATH)
+INCLUDES += $(LOCAL_PATH)/src
+INCLUDES += $(LOCAL_PATH)/src/common
+# INCLUDES += $(LOCAL_PATH)/src/crypto # To force proper includes
+INCLUDES += $(LOCAL_PATH)/src/drivers
+INCLUDES += $(LOCAL_PATH)/src/eap_common
+INCLUDES += $(LOCAL_PATH)/src/eapol_supp
+INCLUDES += $(LOCAL_PATH)/src/eap_peer
+INCLUDES += $(LOCAL_PATH)/src/eap_server
+INCLUDES += $(LOCAL_PATH)/src/hlr_auc_gw
+INCLUDES += $(LOCAL_PATH)/src/l2_packet
+INCLUDES += $(LOCAL_PATH)/src/radius
+INCLUDES += $(LOCAL_PATH)/src/rsn_supp
+INCLUDES += $(LOCAL_PATH)/src/tls
+INCLUDES += $(LOCAL_PATH)/src/utils
+INCLUDES += $(LOCAL_PATH)/src/wps
+INCLUDES += external/openssl/include
+INCLUDES += frameworks/base/cmds/keystore
+ifdef CONFIG_DRIVER_NL80211
+INCLUDES += external/libnl-headers
+endif
+
+OBJS = config.c
+OBJS += notify.c
+OBJS += bss.c
+OBJS += eap_register.c
+OBJS += src/utils/common.c
+OBJS += src/utils/wpa_debug.c
+OBJS += src/utils/wpabuf.c
+OBJS_p = wpa_passphrase.c
+OBJS_p += src/utils/common.c
+OBJS_p += src/utils/wpa_debug.c
+OBJS_p += src/utils/wpabuf.c
+OBJS_c = wpa_cli.c src/common/wpa_ctrl.c
+OBJS_c += src/utils/wpa_debug.c
+OBJS_c += src/utils/common.c
+OBJS_d =
+OBJS_priv =
+
+ifndef CONFIG_OS
+ifdef CONFIG_NATIVE_WINDOWS
+CONFIG_OS=win32
+else
+CONFIG_OS=unix
+endif
+endif
+
+ifeq ($(CONFIG_OS), internal)
+L_CFLAGS += -DOS_NO_C_LIB_DEFINES
+endif
+
+OBJS += src/utils/os_$(CONFIG_OS).c
+OBJS_p += src/utils/os_$(CONFIG_OS).c
+OBJS_c += src/utils/os_$(CONFIG_OS).c
+
+ifdef CONFIG_WPA_TRACE
+L_CFLAGS += -DWPA_TRACE
+OBJS += src/utils/trace.c
+OBJS_p += src/utils/trace.c
+OBJS_c += src/utils/trace.c
+LDFLAGS += -rdynamic
+L_CFLAGS += -funwind-tables
+ifdef CONFIG_WPA_TRACE_BFD
+L_CFLAGS += -DWPA_TRACE_BFD
+LIBS += -lbfd
+LIBS_p += -lbfd
+LIBS_c += -lbfd
+endif
+endif
+
+ifndef CONFIG_ELOOP
+CONFIG_ELOOP=eloop
+endif
+OBJS += src/utils/$(CONFIG_ELOOP).c
+OBJS_c += src/utils/$(CONFIG_ELOOP).c
+
+
+ifdef CONFIG_EAPOL_TEST
+L_CFLAGS += -Werror -DEAPOL_TEST
+endif
+
+ifndef CONFIG_BACKEND
+CONFIG_BACKEND=file
+endif
+
+ifeq ($(CONFIG_BACKEND), file)
+OBJS += config_file.c
+ifndef CONFIG_NO_CONFIG_BLOBS
+NEED_BASE64=y
+endif
+L_CFLAGS += -DCONFIG_BACKEND_FILE
+endif
+
+ifeq ($(CONFIG_BACKEND), winreg)
+OBJS += config_winreg.c
+endif
+
+ifeq ($(CONFIG_BACKEND), none)
+OBJS += config_none.c
+endif
+
+ifdef CONFIG_NO_CONFIG_WRITE
+L_CFLAGS += -DCONFIG_NO_CONFIG_WRITE
+endif
+
+ifdef CONFIG_NO_CONFIG_BLOBS
+L_CFLAGS += -DCONFIG_NO_CONFIG_BLOBS
+endif
+
+ifdef CONFIG_NO_SCAN_PROCESSING
+L_CFLAGS += -DCONFIG_NO_SCAN_PROCESSING
+endif
+
+ifdef CONFIG_IEEE80211W
+L_CFLAGS += -DCONFIG_IEEE80211W
+NEED_SHA256=y
+NEED_AES_OMAC1=y
+endif
+
+ifdef CONFIG_IEEE80211R
+L_CFLAGS += -DCONFIG_IEEE80211R
+OBJS += src/rsn_supp/wpa_ft.c
+NEED_80211_COMMON=y
+NEED_SHA256=y
+NEED_AES_OMAC1=y
+endif
+
+ifdef CONFIG_TDLS
+L_CFLAGS += -DCONFIG_TDLS
+OBJS += src/rsn_supp/tdls.c
+NEED_SHA256=y
+NEED_AES_OMAC1=y
+endif
+
+ifdef CONFIG_TDLS_TESTING
+L_CFLAGS += -DCONFIG_TDLS_TESTING
+endif
+
+ifdef CONFIG_PEERKEY
+L_CFLAGS += -DCONFIG_PEERKEY
+endif
+
+ifndef CONFIG_NO_WPA
+OBJS += src/rsn_supp/wpa.c
+OBJS += src/rsn_supp/preauth.c
+OBJS += src/rsn_supp/pmksa_cache.c
+OBJS += src/rsn_supp/peerkey.c
+OBJS += src/rsn_supp/wpa_ie.c
+OBJS += src/common/wpa_common.c
+NEED_AES=y
+NEED_SHA1=y
+NEED_MD5=y
+NEED_RC4=y
+else
+L_CFLAGS += -DCONFIG_NO_WPA -DCONFIG_NO_WPA2
+endif
+
+ifdef CONFIG_IBSS_RSN
+NEED_RSN_AUTHENTICATOR=y
+L_CFLAGS += -DCONFIG_IBSS_RSN
+OBJS += ibss_rsn.c
+endif
+
+ifdef CONFIG_P2P
+OBJS += p2p_supplicant.c
+OBJS += src/p2p/p2p.c
+OBJS += src/p2p/p2p_utils.c
+OBJS += src/p2p/p2p_parse.c
+OBJS += src/p2p/p2p_build.c
+OBJS += src/p2p/p2p_go_neg.c
+OBJS += src/p2p/p2p_sd.c
+OBJS += src/p2p/p2p_pd.c
+OBJS += src/p2p/p2p_invitation.c
+OBJS += src/p2p/p2p_dev_disc.c
+OBJS += src/p2p/p2p_group.c
+OBJS += src/ap/p2p_hostapd.c
+L_CFLAGS += -DCONFIG_P2P
+NEED_GAS=y
+NEED_OFFCHANNEL=y
+NEED_80211_COMMON=y
+CONFIG_WPS=y
+CONFIG_AP=y
+ifdef CONFIG_P2P_STRICT
+L_CFLAGS += -DCONFIG_P2P_STRICT
+endif
+endif
+
+ifdef CONFIG_INTERWORKING
+OBJS += interworking.c
+L_CFLAGS += -DCONFIG_INTERWORKING
+NEED_GAS=y
+endif
+
+ifdef CONFIG_NO_WPA2
+L_CFLAGS += -DCONFIG_NO_WPA2
+endif
+
+include $(LOCAL_PATH)/src/drivers/drivers.mk
+
+ifdef CONFIG_AP
+OBJS_d += $(DRV_BOTH_OBJS)
+L_CFLAGS += $(DRV_BOTH_CFLAGS)
+LDFLAGS += $(DRV_BOTH_LDFLAGS)
+LIBS += $(DRV_BOTH_LIBS)
+else
+NEED_AP_MLME=
+OBJS_d += $(DRV_WPA_OBJS)
+L_CFLAGS += $(DRV_WPA_CFLAGS)
+LDFLAGS += $(DRV_WPA_LDFLAGS)
+LIBS += $(DRV_WPA_LIBS)
+endif
+
+ifndef CONFIG_L2_PACKET
+CONFIG_L2_PACKET=linux
+endif
+
+OBJS_l2 += src/l2_packet/l2_packet_$(CONFIG_L2_PACKET).c
+
+ifeq ($(CONFIG_L2_PACKET), pcap)
+ifdef CONFIG_WINPCAP
+L_CFLAGS += -DCONFIG_WINPCAP
+LIBS += -lwpcap -lpacket
+LIBS_w += -lwpcap
+else
+LIBS += -ldnet -lpcap
+endif
+endif
+
+ifeq ($(CONFIG_L2_PACKET), winpcap)
+LIBS += -lwpcap -lpacket
+LIBS_w += -lwpcap
+endif
+
+ifeq ($(CONFIG_L2_PACKET), freebsd)
+LIBS += -lpcap
+endif
+
+ifdef CONFIG_EAP_TLS
+# EAP-TLS
+ifeq ($(CONFIG_EAP_TLS), dyn)
+L_CFLAGS += -DEAP_TLS_DYNAMIC
+EAPDYN += src/eap_peer/eap_tls.so
+else
+L_CFLAGS += -DEAP_TLS
+OBJS += src/eap_peer/eap_tls.c
+OBJS_h += src/eap_server/eap_server_tls.c
+endif
+TLS_FUNCS=y
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_PEAP
+# EAP-PEAP
+ifeq ($(CONFIG_EAP_PEAP), dyn)
+L_CFLAGS += -DEAP_PEAP_DYNAMIC
+EAPDYN += src/eap_peer/eap_peap.so
+else
+L_CFLAGS += -DEAP_PEAP
+OBJS += src/eap_peer/eap_peap.c
+OBJS += src/eap_common/eap_peap_common.c
+OBJS_h += src/eap_server/eap_server_peap.c
+endif
+TLS_FUNCS=y
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_TTLS
+# EAP-TTLS
+ifeq ($(CONFIG_EAP_TTLS), dyn)
+L_CFLAGS += -DEAP_TTLS_DYNAMIC
+EAPDYN += src/eap_peer/eap_ttls.so
+else
+L_CFLAGS += -DEAP_TTLS
+OBJS += src/eap_peer/eap_ttls.c
+OBJS_h += src/eap_server/eap_server_ttls.c
+endif
+MS_FUNCS=y
+TLS_FUNCS=y
+CHAP=y
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_MD5
+# EAP-MD5
+ifeq ($(CONFIG_EAP_MD5), dyn)
+L_CFLAGS += -DEAP_MD5_DYNAMIC
+EAPDYN += src/eap_peer/eap_md5.so
+else
+L_CFLAGS += -DEAP_MD5
+OBJS += src/eap_peer/eap_md5.c
+OBJS_h += src/eap_server/eap_server_md5.c
+endif
+CHAP=y
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+# backwards compatibility for old spelling
+ifdef CONFIG_MSCHAPV2
+ifndef CONFIG_EAP_MSCHAPV2
+CONFIG_EAP_MSCHAPV2=y
+endif
+endif
+
+ifdef CONFIG_EAP_MSCHAPV2
+# EAP-MSCHAPv2
+ifeq ($(CONFIG_EAP_MSCHAPV2), dyn)
+L_CFLAGS += -DEAP_MSCHAPv2_DYNAMIC
+EAPDYN += src/eap_peer/eap_mschapv2.so
+EAPDYN += src/eap_peer/mschapv2.so
+else
+L_CFLAGS += -DEAP_MSCHAPv2
+OBJS += src/eap_peer/eap_mschapv2.c
+OBJS += src/eap_peer/mschapv2.c
+OBJS_h += src/eap_server/eap_server_mschapv2.c
+endif
+MS_FUNCS=y
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_GTC
+# EAP-GTC
+ifeq ($(CONFIG_EAP_GTC), dyn)
+L_CFLAGS += -DEAP_GTC_DYNAMIC
+EAPDYN += src/eap_peer/eap_gtc.so
+else
+L_CFLAGS += -DEAP_GTC
+OBJS += src/eap_peer/eap_gtc.c
+OBJS_h += src/eap_server/eap_server_gtc.c
+endif
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_OTP
+# EAP-OTP
+ifeq ($(CONFIG_EAP_OTP), dyn)
+L_CFLAGS += -DEAP_OTP_DYNAMIC
+EAPDYN += src/eap_peer/eap_otp.so
+else
+L_CFLAGS += -DEAP_OTP
+OBJS += src/eap_peer/eap_otp.c
+endif
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_SIM
+# EAP-SIM
+ifeq ($(CONFIG_EAP_SIM), dyn)
+L_CFLAGS += -DEAP_SIM_DYNAMIC
+EAPDYN += src/eap_peer/eap_sim.so
+else
+L_CFLAGS += -DEAP_SIM
+OBJS += src/eap_peer/eap_sim.c
+OBJS_h += src/eap_server/eap_server_sim.c
+endif
+CONFIG_IEEE8021X_EAPOL=y
+CONFIG_EAP_SIM_COMMON=y
+NEED_AES_CBC=y
+endif
+
+ifdef CONFIG_EAP_LEAP
+# EAP-LEAP
+ifeq ($(CONFIG_EAP_LEAP), dyn)
+L_CFLAGS += -DEAP_LEAP_DYNAMIC
+EAPDYN += src/eap_peer/eap_leap.so
+else
+L_CFLAGS += -DEAP_LEAP
+OBJS += src/eap_peer/eap_leap.c
+endif
+MS_FUNCS=y
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_PSK
+# EAP-PSK
+ifeq ($(CONFIG_EAP_PSK), dyn)
+L_CFLAGS += -DEAP_PSK_DYNAMIC
+EAPDYN += src/eap_peer/eap_psk.so
+else
+L_CFLAGS += -DEAP_PSK
+OBJS += src/eap_peer/eap_psk.c src/eap_common/eap_psk_common.c
+OBJS_h += src/eap_server/eap_server_psk.c
+endif
+CONFIG_IEEE8021X_EAPOL=y
+NEED_AES=y
+NEED_AES_OMAC1=y
+NEED_AES_ENCBLOCK=y
+NEED_AES_EAX=y
+endif
+
+ifdef CONFIG_EAP_AKA
+# EAP-AKA
+ifeq ($(CONFIG_EAP_AKA), dyn)
+L_CFLAGS += -DEAP_AKA_DYNAMIC
+EAPDYN += src/eap_peer/eap_aka.so
+else
+L_CFLAGS += -DEAP_AKA
+OBJS += src/eap_peer/eap_aka.c
+OBJS_h += src/eap_server/eap_server_aka.c
+endif
+CONFIG_IEEE8021X_EAPOL=y
+CONFIG_EAP_SIM_COMMON=y
+NEED_AES_CBC=y
+endif
+
+ifdef CONFIG_EAP_AKA_PRIME
+# EAP-AKA'
+ifeq ($(CONFIG_EAP_AKA_PRIME), dyn)
+L_CFLAGS += -DEAP_AKA_PRIME_DYNAMIC
+else
+L_CFLAGS += -DEAP_AKA_PRIME
+endif
+NEED_SHA256=y
+endif
+
+ifdef CONFIG_EAP_SIM_COMMON
+OBJS += src/eap_common/eap_sim_common.c
+OBJS_h += src/eap_server/eap_sim_db.c
+NEED_AES=y
+NEED_FIPS186_2_PRF=y
+endif
+
+ifdef CONFIG_EAP_FAST
+# EAP-FAST
+ifeq ($(CONFIG_EAP_FAST), dyn)
+L_CFLAGS += -DEAP_FAST_DYNAMIC
+EAPDYN += src/eap_peer/eap_fast.so
+EAPDYN += src/eap_common/eap_fast_common.c
+else
+L_CFLAGS += -DEAP_FAST
+OBJS += src/eap_peer/eap_fast.c src/eap_peer/eap_fast_pac.c
+OBJS += src/eap_common/eap_fast_common.c
+OBJS_h += src/eap_server/eap_server_fast.c
+endif
+TLS_FUNCS=y
+CONFIG_IEEE8021X_EAPOL=y
+NEED_T_PRF=y
+endif
+
+ifdef CONFIG_EAP_PAX
+# EAP-PAX
+ifeq ($(CONFIG_EAP_PAX), dyn)
+L_CFLAGS += -DEAP_PAX_DYNAMIC
+EAPDYN += src/eap_peer/eap_pax.so
+else
+L_CFLAGS += -DEAP_PAX
+OBJS += src/eap_peer/eap_pax.c src/eap_common/eap_pax_common.c
+OBJS_h += src/eap_server/eap_server_pax.c
+endif
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_SAKE
+# EAP-SAKE
+ifeq ($(CONFIG_EAP_SAKE), dyn)
+L_CFLAGS += -DEAP_SAKE_DYNAMIC
+EAPDYN += src/eap_peer/eap_sake.so
+else
+L_CFLAGS += -DEAP_SAKE
+OBJS += src/eap_peer/eap_sake.c src/eap_common/eap_sake_common.c
+OBJS_h += src/eap_server/eap_server_sake.c
+endif
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_GPSK
+# EAP-GPSK
+ifeq ($(CONFIG_EAP_GPSK), dyn)
+L_CFLAGS += -DEAP_GPSK_DYNAMIC
+EAPDYN += src/eap_peer/eap_gpsk.so
+else
+L_CFLAGS += -DEAP_GPSK
+OBJS += src/eap_peer/eap_gpsk.c src/eap_common/eap_gpsk_common.c
+OBJS_h += src/eap_server/eap_server_gpsk.c
+endif
+CONFIG_IEEE8021X_EAPOL=y
+ifdef CONFIG_EAP_GPSK_SHA256
+L_CFLAGS += -DEAP_GPSK_SHA256
+endif
+NEED_SHA256=y
+NEED_AES_OMAC1=y
+endif
+
+ifdef CONFIG_EAP_PWD
+L_CFLAGS += -DEAP_PWD
+OBJS += src/eap_peer/eap_pwd.c src/eap_common/eap_pwd_common.c
+OBJS_h += src/eap_server/eap_pwd.c
+CONFIG_IEEE8021X_EAPOL=y
+NEED_SHA256=y
+endif
+
+ifdef CONFIG_WPS
+ifdef CONFIG_WPS2
+L_CFLAGS += -DCONFIG_WPS2
+endif
+
+# EAP-WSC
+L_CFLAGS += -DCONFIG_WPS -DEAP_WSC
+OBJS += wps_supplicant.c
+OBJS += src/utils/uuid.c
+OBJS += src/eap_peer/eap_wsc.c src/eap_common/eap_wsc_common.c
+OBJS += src/wps/wps.c
+OBJS += src/wps/wps_common.c
+OBJS += src/wps/wps_attr_parse.c
+OBJS += src/wps/wps_attr_build.c
+OBJS += src/wps/wps_attr_process.c
+OBJS += src/wps/wps_dev_attr.c
+OBJS += src/wps/wps_enrollee.c
+OBJS += src/wps/wps_registrar.c
+OBJS_h += src/eap_server/eap_server_wsc.c
+CONFIG_IEEE8021X_EAPOL=y
+NEED_DH_GROUPS=y
+NEED_SHA256=y
+NEED_BASE64=y
+NEED_80211_COMMON=y
+NEED_AES_CBC=y
+NEED_MODEXP=y
+
+ifdef CONFIG_WPS_UFD
+L_CFLAGS += -DCONFIG_WPS_UFD
+OBJS += src/wps/wps_ufd.c
+NEED_WPS_OOB=y
+endif
+
+ifdef CONFIG_WPS_NFC
+L_CFLAGS += -DCONFIG_WPS_NFC
+OBJS += src/wps/ndef.c
+OBJS += src/wps/wps_nfc.c
+NEED_WPS_OOB=y
+ifdef CONFIG_WPS_NFC_PN531
+PN531_PATH ?= /usr/local/src/nfc
+L_CFLAGS += -DCONFIG_WPS_NFC_PN531
+L_CFLAGS += -I${PN531_PATH}/inc
+OBJS += src/wps/wps_nfc_pn531.c
+LIBS += ${PN531_PATH}/lib/wpsnfc.dll
+LIBS += ${PN531_PATH}/lib/libnfc_mapping_pn53x.dll
+endif
+endif
+
+ifdef NEED_WPS_OOB
+L_CFLAGS += -DCONFIG_WPS_OOB
+endif
+
+ifdef CONFIG_WPS_ER
+CONFIG_WPS_UPNP=y
+L_CFLAGS += -DCONFIG_WPS_ER
+OBJS += src/wps/wps_er.c
+OBJS += src/wps/wps_er_ssdp.c
+endif
+
+ifdef CONFIG_WPS_UPNP
+L_CFLAGS += -DCONFIG_WPS_UPNP
+OBJS += src/wps/wps_upnp.c
+OBJS += src/wps/wps_upnp_ssdp.c
+OBJS += src/wps/wps_upnp_web.c
+OBJS += src/wps/wps_upnp_event.c
+OBJS += src/wps/wps_upnp_ap.c
+OBJS += src/wps/upnp_xml.c
+OBJS += src/wps/httpread.c
+OBJS += src/wps/http_client.c
+OBJS += src/wps/http_server.c
+endif
+
+ifdef CONFIG_WPS_STRICT
+L_CFLAGS += -DCONFIG_WPS_STRICT
+OBJS += src/wps/wps_validate.c
+endif
+
+ifdef CONFIG_WPS_TESTING
+L_CFLAGS += -DCONFIG_WPS_TESTING
+endif
+
+ifdef CONFIG_WPS_REG_DISABLE_OPEN
+L_CFLAGS += -DCONFIG_WPS_REG_DISABLE_OPEN
+endif
+
+endif
+
+ifdef CONFIG_EAP_IKEV2
+# EAP-IKEv2
+ifeq ($(CONFIG_EAP_IKEV2), dyn)
+L_CFLAGS += -DEAP_IKEV2_DYNAMIC
+EAPDYN += src/eap_peer/eap_ikev2.so src/eap_peer/ikev2.c
+EAPDYN += src/eap_common/eap_ikev2_common.c src/eap_common/ikev2_common.c
+else
+L_CFLAGS += -DEAP_IKEV2
+OBJS += src/eap_peer/eap_ikev2.c src/eap_peer/ikev2.c
+OBJS += src/eap_common/eap_ikev2_common.c src/eap_common/ikev2_common.c
+OBJS_h += src/eap_server/eap_server_ikev2.c
+OBJS_h += src/eap_server/ikev2.c
+endif
+CONFIG_IEEE8021X_EAPOL=y
+NEED_DH_GROUPS=y
+NEED_DH_GROUPS_ALL=y
+NEED_MODEXP=y
+NEED_CIPHER=y
+endif
+
+ifdef CONFIG_EAP_VENDOR_TEST
+ifeq ($(CONFIG_EAP_VENDOR_TEST), dyn)
+L_CFLAGS += -DEAP_VENDOR_TEST_DYNAMIC
+EAPDYN += src/eap_peer/eap_vendor_test.so
+else
+L_CFLAGS += -DEAP_VENDOR_TEST
+OBJS += src/eap_peer/eap_vendor_test.c
+OBJS_h += src/eap_server/eap_server_vendor_test.c
+endif
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_TNC
+# EAP-TNC
+L_CFLAGS += -DEAP_TNC
+OBJS += src/eap_peer/eap_tnc.c
+OBJS += src/eap_peer/tncc.c
+OBJS_h += src/eap_server/eap_server_tnc.c
+OBJS_h += src/eap_server/tncs.c
+NEED_BASE64=y
+ifndef CONFIG_NATIVE_WINDOWS
+ifndef CONFIG_DRIVER_BSD
+LIBS += -ldl
+endif
+endif
+endif
+
+ifdef CONFIG_IEEE8021X_EAPOL
+# IEEE 802.1X/EAPOL state machines (e.g., for RADIUS authentication)
+L_CFLAGS += -DIEEE8021X_EAPOL
+OBJS += src/eapol_supp/eapol_supp_sm.c
+OBJS += src/eap_peer/eap.c src/eap_peer/eap_methods.c
+NEED_EAP_COMMON=y
+ifdef CONFIG_DYNAMIC_EAP_METHODS
+L_CFLAGS += -DCONFIG_DYNAMIC_EAP_METHODS
+LIBS += -ldl -rdynamic
+endif
+endif
+
+ifdef CONFIG_AP
+NEED_80211_COMMON=y
+NEED_EAP_COMMON=y
+NEED_RSN_AUTHENTICATOR=y
+L_CFLAGS += -DCONFIG_AP
+OBJS += ap.c
+L_CFLAGS += -DCONFIG_NO_RADIUS
+L_CFLAGS += -DCONFIG_NO_ACCOUNTING
+L_CFLAGS += -DCONFIG_NO_VLAN
+OBJS += src/ap/hostapd.c
+OBJS += src/ap/wpa_auth_glue.c
+OBJS += src/ap/utils.c
+OBJS += src/ap/authsrv.c
+OBJS += src/ap/ap_config.c
+OBJS += src/utils/ip_addr.c
+OBJS += src/ap/sta_info.c
+OBJS += src/ap/tkip_countermeasures.c
+OBJS += src/ap/ap_mlme.c
+OBJS += src/ap/ieee802_1x.c
+OBJS += src/eapol_auth/eapol_auth_sm.c
+OBJS += src/ap/ieee802_11_auth.c
+OBJS += src/ap/ieee802_11_shared.c
+OBJS += src/ap/drv_callbacks.c
+OBJS += src/ap/ap_drv_ops.c
+OBJS += src/ap/beacon.c
+ifdef CONFIG_IEEE80211N
+OBJS += src/ap/ieee802_11_ht.c
+endif
+ifdef CONFIG_CTRL_IFACE
+OBJS += src/ap/ctrl_iface_ap.c
+endif
+
+L_CFLAGS += -DEAP_SERVER -DEAP_SERVER_IDENTITY
+OBJS += src/eap_server/eap_server.c
+OBJS += src/eap_server/eap_server_identity.c
+OBJS += src/eap_server/eap_server_methods.c
+
+ifdef CONFIG_IEEE80211N
+L_CFLAGS += -DCONFIG_IEEE80211N
+endif
+
+ifdef NEED_AP_MLME
+OBJS += src/ap/wmm.c
+OBJS += src/ap/ap_list.c
+OBJS += src/ap/ieee802_11.c
+OBJS += src/ap/hw_features.c
+L_CFLAGS += -DNEED_AP_MLME
+endif
+ifdef CONFIG_WPS
+L_CFLAGS += -DEAP_SERVER_WSC
+OBJS += src/ap/wps_hostapd.c
+OBJS += src/eap_server/eap_server_wsc.c
+endif
+endif
+
+ifdef NEED_RSN_AUTHENTICATOR
+L_CFLAGS += -DCONFIG_NO_RADIUS
+NEED_AES_WRAP=y
+OBJS += src/ap/wpa_auth.c
+OBJS += src/ap/wpa_auth_ie.c
+OBJS += src/ap/pmksa_cache_auth.c
+ifdef CONFIG_IEEE80211R
+OBJS += src/ap/wpa_auth_ft.c
+endif
+ifdef CONFIG_PEERKEY
+OBJS += src/ap/peerkey_auth.c
+endif
+endif
+
+ifdef CONFIG_EAP_SERVER
+L_CFLAGS += -DEAP_SERVER
+OBJS_h += src/eap_server/eap_server.c
+OBJS_h += src/eap_server/eap_server_identity.c
+OBJS_h += src/eap_server/eap_server_methods.c
+endif
+
+ifdef CONFIG_RADIUS_CLIENT
+OBJS_h += src/utils/ip_addr.c
+OBJS_h += src/radius/radius.c
+OBJS_h += src/radius/radius_client.c
+endif
+
+ifdef CONFIG_AUTHENTICATOR
+OBJS_h += src/eapol_auth/eapol_auth_sm.c
+OBJS_h += src/ap/ieee802_1x.c
+endif
+
+ifdef CONFIG_WPA_AUTHENTICATOR
+OBJS_h += src/ap/wpa_auth.c
+OBJS_h += src/ap/wpa_auth_ie.c
+OBJS_h += src/ap/pmksa_cache_auth.c
+ifdef CONFIG_IEEE80211R
+OBJS_h += src/ap/wpa_auth_ft.c
+endif
+ifdef CONFIG_PEERKEY
+OBJS_h += src/ap/peerkey_auth.c
+endif
+endif
+
+ifdef CONFIG_PCSC
+# PC/SC interface for smartcards (USIM, GSM SIM)
+L_CFLAGS += -DPCSC_FUNCS -I/usr/include/PCSC
+OBJS += src/utils/pcsc_funcs.c
+# -lpthread may not be needed depending on how pcsc-lite was configured
+ifdef CONFIG_NATIVE_WINDOWS
+#Once MinGW gets support for WinScard, -lwinscard could be used instead of the
+#dynamic symbol loading that is now used in pcsc_funcs.c
+#LIBS += -lwinscard
+else
+LIBS += -lpcsclite -lpthread
+endif
+endif
+
+ifdef CONFIG_SIM_SIMULATOR
+L_CFLAGS += -DCONFIG_SIM_SIMULATOR
+NEED_MILENAGE=y
+endif
+
+ifdef CONFIG_USIM_SIMULATOR
+L_CFLAGS += -DCONFIG_USIM_SIMULATOR
+NEED_MILENAGE=y
+endif
+
+ifdef NEED_MILENAGE
+OBJS += src/crypto/milenage.c
+NEED_AES_ENCBLOCK=y
+endif
+
+ifdef CONFIG_PKCS12
+L_CFLAGS += -DPKCS12_FUNCS
+endif
+
+ifdef CONFIG_SMARTCARD
+L_CFLAGS += -DCONFIG_SMARTCARD
+endif
+
+ifdef MS_FUNCS
+OBJS += src/crypto/ms_funcs.c
+NEED_DES=y
+NEED_MD4=y
+endif
+
+ifdef CHAP
+OBJS += src/eap_common/chap.c
+endif
+
+ifdef TLS_FUNCS
+NEED_DES=y
+# Shared TLS functions (needed for EAP_TLS, EAP_PEAP, EAP_TTLS, and EAP_FAST)
+OBJS += src/eap_peer/eap_tls_common.c
+OBJS_h += src/eap_server/eap_server_tls_common.c
+NEED_TLS_PRF=y
+endif
+
+ifndef CONFIG_TLS
+CONFIG_TLS=openssl
+endif
+
+ifdef CONFIG_TLSV11
+L_CFLAGS += -DCONFIG_TLSV11
+endif
+
+ifeq ($(CONFIG_TLS), openssl)
+ifdef TLS_FUNCS
+L_CFLAGS += -DEAP_TLS_OPENSSL
+OBJS += src/crypto/tls_openssl.c
+LIBS += -lssl
+endif
+OBJS += src/crypto/crypto_openssl.c
+OBJS_p += src/crypto/crypto_openssl.c
+ifdef NEED_FIPS186_2_PRF
+OBJS += src/crypto/fips_prf_openssl.c
+endif
+LIBS += -lcrypto
+LIBS_p += -lcrypto
+endif
+
+ifeq ($(CONFIG_TLS), gnutls)
+ifdef TLS_FUNCS
+OBJS += src/crypto/tls_gnutls.c
+LIBS += -lgnutls -lgpg-error
+endif
+OBJS += src/crypto/crypto_gnutls.c
+OBJS_p += src/crypto/crypto_gnutls.c
+ifdef NEED_FIPS186_2_PRF
+OBJS += src/crypto/fips_prf_gnutls.c
+endif
+LIBS += -lgcrypt
+LIBS_p += -lgcrypt
+CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_RC4=y
+CONFIG_INTERNAL_DH_GROUP5=y
+endif
+
+ifeq ($(CONFIG_TLS), schannel)
+ifdef TLS_FUNCS
+OBJS += src/crypto/tls_schannel.c
+endif
+OBJS += src/crypto/crypto_cryptoapi.c
+OBJS_p += src/crypto/crypto_cryptoapi.c
+ifdef NEED_FIPS186_2_PRF
+OBJS += src/crypto/fips_prf_cryptoapi.c
+endif
+CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_RC4=y
+CONFIG_INTERNAL_DH_GROUP5=y
+endif
+
+ifeq ($(CONFIG_TLS), nss)
+ifdef TLS_FUNCS
+OBJS += src/crypto/tls_nss.c
+LIBS += -lssl3
+endif
+OBJS += src/crypto/crypto_nss.c
+OBJS_p += src/crypto/crypto_nss.c
+ifdef NEED_FIPS186_2_PRF
+OBJS += src/crypto/fips_prf_nss.c
+endif
+LIBS += -lnss3
+LIBS_p += -lnss3
+CONFIG_INTERNAL_MD4=y
+CONFIG_INTERNAL_DH_GROUP5=y
+endif
+
+ifeq ($(CONFIG_TLS), internal)
+ifndef CONFIG_CRYPTO
+CONFIG_CRYPTO=internal
+endif
+ifdef TLS_FUNCS
+OBJS += src/crypto/crypto_internal-rsa.c
+OBJS += src/crypto/tls_internal.c
+OBJS += src/tls/tlsv1_common.c
+OBJS += src/tls/tlsv1_record.c
+OBJS += src/tls/tlsv1_cred.c
+OBJS += src/tls/tlsv1_client.c
+OBJS += src/tls/tlsv1_client_write.c
+OBJS += src/tls/tlsv1_client_read.c
+OBJS += src/tls/asn1.c
+OBJS += src/tls/rsa.c
+OBJS += src/tls/x509v3.c
+OBJS += src/tls/pkcs1.c
+OBJS += src/tls/pkcs5.c
+OBJS += src/tls/pkcs8.c
+NEED_SHA256=y
+NEED_BASE64=y
+NEED_TLS_PRF=y
+NEED_MODEXP=y
+NEED_CIPHER=y
+L_CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT
+endif
+ifdef NEED_CIPHER
+NEED_DES=y
+OBJS += src/crypto/crypto_internal-cipher.c
+endif
+ifdef NEED_MODEXP
+OBJS += src/crypto/crypto_internal-modexp.c
+OBJS += src/tls/bignum.c
+endif
+ifeq ($(CONFIG_CRYPTO), libtomcrypt)
+OBJS += src/crypto/crypto_libtomcrypt.c
+OBJS_p += src/crypto/crypto_libtomcrypt.c
+LIBS += -ltomcrypt -ltfm
+LIBS_p += -ltomcrypt -ltfm
+CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_RC4=y
+CONFIG_INTERNAL_DH_GROUP5=y
+endif
+ifeq ($(CONFIG_CRYPTO), internal)
+OBJS += src/crypto/crypto_internal.c
+OBJS_p += src/crypto/crypto_internal.c
+NEED_AES_ENC=y
+L_CFLAGS += -DCONFIG_CRYPTO_INTERNAL
+ifdef CONFIG_INTERNAL_LIBTOMMATH
+L_CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH
+ifdef CONFIG_INTERNAL_LIBTOMMATH_FAST
+L_CFLAGS += -DLTM_FAST
+endif
+else
+LIBS += -ltommath
+LIBS_p += -ltommath
+endif
+CONFIG_INTERNAL_AES=y
+CONFIG_INTERNAL_DES=y
+CONFIG_INTERNAL_SHA1=y
+CONFIG_INTERNAL_MD4=y
+CONFIG_INTERNAL_MD5=y
+CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_RC4=y
+CONFIG_INTERNAL_DH_GROUP5=y
+endif
+ifeq ($(CONFIG_CRYPTO), cryptoapi)
+OBJS += src/crypto/crypto_cryptoapi.c
+OBJS_p += src/crypto/crypto_cryptoapi.c
+L_CFLAGS += -DCONFIG_CRYPTO_CRYPTOAPI
+CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_RC4=y
+endif
+endif
+
+ifeq ($(CONFIG_TLS), none)
+ifdef TLS_FUNCS
+OBJS += src/crypto/tls_none.c
+L_CFLAGS += -DEAP_TLS_NONE
+CONFIG_INTERNAL_AES=y
+CONFIG_INTERNAL_SHA1=y
+CONFIG_INTERNAL_MD5=y
+endif
+OBJS += src/crypto/crypto_none.c
+OBJS_p += src/crypto/crypto_none.c
+CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_RC4=y
+endif
+
+ifdef TLS_FUNCS
+ifdef CONFIG_SMARTCARD
+ifndef CONFIG_NATIVE_WINDOWS
+ifneq ($(CONFIG_L2_PACKET), freebsd)
+LIBS += -ldl
+endif
+endif
+endif
+endif
+
+ifndef TLS_FUNCS
+OBJS += src/crypto/tls_none.c
+ifeq ($(CONFIG_TLS), internal)
+CONFIG_INTERNAL_AES=y
+CONFIG_INTERNAL_SHA1=y
+CONFIG_INTERNAL_MD5=y
+CONFIG_INTERNAL_RC4=y
+endif
+endif
+
+AESOBJS = # none so far (see below)
+ifdef CONFIG_INTERNAL_AES
+AESOBJS += src/crypto/aes-internal.c src/crypto/aes-internal-dec.c
+endif
+
+AESOBJS += src/crypto/aes-unwrap.c
+ifdef NEED_AES_EAX
+AESOBJS += src/crypto/aes-eax.c
+NEED_AES_CTR=y
+endif
+ifdef NEED_AES_CTR
+AESOBJS += src/crypto/aes-ctr.c
+endif
+ifdef NEED_AES_ENCBLOCK
+AESOBJS += src/crypto/aes-encblock.c
+endif
+ifdef NEED_AES_OMAC1
+NEED_AES_ENC=y
+AESOBJS += src/crypto/aes-omac1.c
+endif
+ifdef NEED_AES_WRAP
+NEED_AES_ENC=y
+AESOBJS += src/crypto/aes-wrap.c
+endif
+ifdef NEED_AES_CBC
+NEED_AES_ENC=y
+AESOBJS += src/crypto/aes-cbc.c
+endif
+ifdef NEED_AES_ENC
+ifdef CONFIG_INTERNAL_AES
+AESOBJS += src/crypto/aes-internal-enc.c
+endif
+endif
+ifdef NEED_AES
+OBJS += $(AESOBJS)
+endif
+
+SHA1OBJS =
+ifdef NEED_SHA1
+SHA1OBJS += src/crypto/sha1.c
+ifdef CONFIG_INTERNAL_SHA1
+SHA1OBJS += src/crypto/sha1-internal.c
+ifdef NEED_FIPS186_2_PRF
+SHA1OBJS += src/crypto/fips_prf_internal.c
+endif
+endif
+ifdef CONFIG_NO_WPA_PASSPHRASE
+L_CFLAGS += -DCONFIG_NO_PBKDF2
+else
+SHA1OBJS += src/crypto/sha1-pbkdf2.c
+endif
+ifdef NEED_T_PRF
+SHA1OBJS += src/crypto/sha1-tprf.c
+endif
+ifdef NEED_TLS_PRF
+SHA1OBJS += src/crypto/sha1-tlsprf.c
+endif
+endif
+
+MD5OBJS = src/crypto/md5.c
+ifdef NEED_MD5
+ifdef CONFIG_INTERNAL_MD5
+MD5OBJS += src/crypto/md5-internal.c
+endif
+ifdef CONFIG_FIPS
+MD5OBJS += src/crypto/md5-non-fips.c
+endif
+OBJS += $(MD5OBJS)
+OBJS_p += $(MD5OBJS)
+endif
+
+ifdef NEED_MD4
+ifdef CONFIG_INTERNAL_MD4
+OBJS += src/crypto/md4-internal.c
+endif
+endif
+
+DESOBJS = # none needed when not internal
+ifdef NEED_DES
+ifdef CONFIG_INTERNAL_DES
+DESOBJS += src/crypto/des-internal.c
+endif
+endif
+
+ifdef NEED_RC4
+ifdef CONFIG_INTERNAL_RC4
+OBJS += src/crypto/rc4.c
+endif
+endif
+
+SHA256OBJS = # none by default
+ifdef NEED_SHA256
+L_CFLAGS += -DCONFIG_SHA256
+SHA256OBJS += src/crypto/sha256.c
+ifdef CONFIG_INTERNAL_SHA256
+SHA256OBJS += src/crypto/sha256-internal.c
+endif
+OBJS += $(SHA256OBJS)
+endif
+
+ifdef NEED_DH_GROUPS
+OBJS += src/crypto/dh_groups.c
+endif
+ifdef NEED_DH_GROUPS_ALL
+L_CFLAGS += -DALL_DH_GROUPS
+endif
+ifdef CONFIG_INTERNAL_DH_GROUP5
+ifdef NEED_DH_GROUPS
+OBJS += src/crypto/dh_group5.c
+endif
+endif
+
+ifdef CONFIG_NO_RANDOM_POOL
+L_CFLAGS += -DCONFIG_NO_RANDOM_POOL
+else
+OBJS += src/crypto/random.c
+endif
+
+ifdef CONFIG_CTRL_IFACE
+ifeq ($(CONFIG_CTRL_IFACE), y)
+ifdef CONFIG_NATIVE_WINDOWS
+CONFIG_CTRL_IFACE=named_pipe
+else
+CONFIG_CTRL_IFACE=unix
+endif
+endif
+L_CFLAGS += -DCONFIG_CTRL_IFACE
+ifeq ($(CONFIG_CTRL_IFACE), unix)
+L_CFLAGS += -DCONFIG_CTRL_IFACE_UNIX
+endif
+ifeq ($(CONFIG_CTRL_IFACE), udp)
+L_CFLAGS += -DCONFIG_CTRL_IFACE_UDP
+endif
+ifeq ($(CONFIG_CTRL_IFACE), named_pipe)
+L_CFLAGS += -DCONFIG_CTRL_IFACE_NAMED_PIPE
+endif
+OBJS += ctrl_iface.c ctrl_iface_$(CONFIG_CTRL_IFACE).c
+endif
+
+ifdef CONFIG_CTRL_IFACE_DBUS
+DBUS=y
+DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS -DDBUS_API_SUBJECT_TO_CHANGE
+DBUS_OBJS += dbus/dbus_old.c dbus/dbus_old_handlers.c
+ifdef CONFIG_WPS
+DBUS_OBJS += dbus/dbus_old_handlers_wps.c
+endif
+DBUS_OBJS += dbus/dbus_dict_helpers.c
+ifndef DBUS_LIBS
+DBUS_LIBS := $(shell $(PKG_CONFIG) --libs dbus-1)
+endif
+ifndef DBUS_INCLUDE
+DBUS_INCLUDE := $(shell $(PKG_CONFIG) --cflags dbus-1)
+endif
+DBUS_CFLAGS += $(DBUS_INCLUDE)
+endif
+
+ifdef CONFIG_CTRL_IFACE_DBUS_NEW
+DBUS=y
+DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_NEW
+DBUS_OBJS ?= dbus/dbus_dict_helpers.c
+DBUS_OBJS += dbus/dbus_new_helpers.c
+DBUS_OBJS += dbus/dbus_new.c dbus/dbus_new_handlers.c
+ifdef CONFIG_WPS
+DBUS_OBJS += dbus/dbus_new_handlers_wps.c
+endif
+ifdef CONFIG_P2P
+DBUS_OBJS += dbus/dbus_new_handlers_p2p.c
+endif
+ifndef DBUS_LIBS
+DBUS_LIBS := $(shell $(PKG_CONFIG) --libs dbus-1)
+endif
+ifndef DBUS_INCLUDE
+DBUS_INCLUDE := $(shell $(PKG_CONFIG) --cflags dbus-1)
+endif
+ifdef CONFIG_CTRL_IFACE_DBUS_INTRO
+DBUS_OBJS += dbus/dbus_new_introspect.c
+DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_INTRO
+endif
+DBUS_CFLAGS += $(DBUS_INCLUDE)
+endif
+
+ifdef DBUS
+DBUS_CFLAGS += -DCONFIG_DBUS
+DBUS_OBJS += dbus/dbus_common.c
+endif
+
+OBJS += $(DBUS_OBJS)
+L_CFLAGS += $(DBUS_CFLAGS)
+LIBS += $(DBUS_LIBS)
+
+ifdef CONFIG_READLINE
+OBJS_c += src/utils/edit_readline.c
+LIBS_c += -lncurses -lreadline
+else
+ifdef CONFIG_WPA_CLI_EDIT
+OBJS_c += src/utils/edit.c
+else
+OBJS_c += src/utils/edit_simple.c
+endif
+endif
+
+ifdef CONFIG_NATIVE_WINDOWS
+L_CFLAGS += -DCONFIG_NATIVE_WINDOWS
+LIBS += -lws2_32 -lgdi32 -lcrypt32
+LIBS_c += -lws2_32
+LIBS_p += -lws2_32 -lgdi32
+ifeq ($(CONFIG_CRYPTO), cryptoapi)
+LIBS_p += -lcrypt32
+endif
+endif
+
+ifdef CONFIG_NO_STDOUT_DEBUG
+L_CFLAGS += -DCONFIG_NO_STDOUT_DEBUG
+ifndef CONFIG_CTRL_IFACE
+L_CFLAGS += -DCONFIG_NO_WPA_MSG
+endif
+endif
+
+ifdef CONFIG_ANDROID_LOG
+L_CFLAGS += -DCONFIG_ANDROID_LOG
+endif
+
+ifdef CONFIG_IPV6
+# for eapol_test only
+L_CFLAGS += -DCONFIG_IPV6
+endif
+
+ifdef NEED_BASE64
+OBJS += src/utils/base64.c
+endif
+
+ifdef NEED_SME
+NEED_80211_COMMON=y
+OBJS += sme.c
+L_CFLAGS += -DCONFIG_SME
+endif
+
+ifdef NEED_80211_COMMON
+OBJS += src/common/ieee802_11_common.c
+endif
+
+ifdef NEED_EAP_COMMON
+OBJS += src/eap_common/eap_common.c
+endif
+
+ifndef CONFIG_MAIN
+CONFIG_MAIN=main
+endif
+
+ifdef CONFIG_DEBUG_SYSLOG
+L_CFLAGS += -DCONFIG_DEBUG_SYSLOG
+ifdef CONFIG_DEBUG_SYSLOG_FACILITY
+L_CFLAGS += -DLOG_HOSTAPD="$(CONFIG_DEBUG_SYSLOG_FACILITY)"
+endif
+endif
+
+ifdef CONFIG_DEBUG_FILE
+L_CFLAGS += -DCONFIG_DEBUG_FILE
+endif
+
+ifdef CONFIG_DELAYED_MIC_ERROR_REPORT
+L_CFLAGS += -DCONFIG_DELAYED_MIC_ERROR_REPORT
+endif
+
+ifdef CONFIG_FIPS
+L_CFLAGS += -DCONFIG_FIPS
+endif
+
+OBJS += $(SHA1OBJS) $(DESOBJS)
+
+OBJS_p += $(SHA1OBJS)
+
+ifdef CONFIG_BGSCAN_SIMPLE
+L_CFLAGS += -DCONFIG_BGSCAN_SIMPLE
+OBJS += bgscan_simple.c
+NEED_BGSCAN=y
+endif
+
+ifdef CONFIG_BGSCAN_LEARN
+L_CFLAGS += -DCONFIG_BGSCAN_LEARN
+OBJS += bgscan_learn.c
+NEED_BGSCAN=y
+endif
+
+ifdef NEED_BGSCAN
+L_CFLAGS += -DCONFIG_BGSCAN
+OBJS += bgscan.c
+endif
+
+ifdef NEED_GAS
+OBJS += ../src/common/gas.c
+OBJS += gas_query.c
+L_CFLAGS += -DCONFIG_GAS
+NEED_OFFCHANNEL=y
+endif
+
+ifdef NEED_OFFCHANNEL
+OBJS += offchannel.c
+L_CFLAGS += -DCONFIG_OFFCHANNEL
+endif
+
+OBJS += src/drivers/driver_common.c
+
+OBJS_wpa_rm := ctrl_iface.c ctrl_iface_unix.c
+OBJS_wpa := $(filter-out $(OBJS_wpa_rm),$(OBJS)) $(OBJS_h) tests/test_wpa.c
+ifdef CONFIG_AUTHENTICATOR
+OBJS_wpa += tests/link_test.c
+endif
+OBJS_wpa += $(OBJS_l2)
+OBJS += wpa_supplicant.c events.c blacklist.c wpas_glue.c scan.c
+OBJS_t := $(OBJS) $(OBJS_l2) eapol_test.c
+OBJS_t += src/radius/radius_client.c
+OBJS_t += src/radius/radius.c
+ifndef CONFIG_AP
+OBJS_t += src/utils/ip_addr.c
+endif
+OBJS_t2 := $(OBJS) $(OBJS_l2) preauth_test.c
+OBJS += $(CONFIG_MAIN).c
+
+ifdef CONFIG_PRIVSEP
+OBJS_priv += $(OBJS_d) src/drivers/drivers.c
+OBJS_priv += $(OBJS_l2)
+OBJS_priv += src/utils/os_$(CONFIG_OS).c
+OBJS_priv += src/utils/$(CONFIG_ELOOP).c
+OBJS_priv += src/utils/common.c
+OBJS_priv += src/utils/wpa_debug.c
+OBJS_priv += src/utils/wpabuf.c
+OBJS_priv += wpa_priv.c
+ifdef CONFIG_DRIVER_TEST
+OBJS_priv += $(SHA1OBJS)
+OBJS_priv += $(MD5OBJS)
+ifeq ($(CONFIG_TLS), openssl)
+OBJS_priv += src/crypto/crypto_openssl.c
+endif
+ifeq ($(CONFIG_TLS), gnutls)
+OBJS_priv += src/crypto/crypto_gnutls.c
+endif
+ifeq ($(CONFIG_TLS), nss)
+OBJS_priv += src/crypto/crypto_nss.c
+endif
+ifeq ($(CONFIG_TLS), internal)
+ifeq ($(CONFIG_CRYPTO), libtomcrypt)
+OBJS_priv += src/crypto/crypto_libtomcrypt.c
+else
+OBJS_priv += src/crypto/crypto_internal.c
+endif
+endif
+endif # CONFIG_DRIVER_TEST
+OBJS += src/l2_packet/l2_packet_privsep.c
+OBJS += src/drivers/driver_privsep.c
+EXTRA_progs += wpa_priv
+else
+OBJS += $(OBJS_d) src/drivers/drivers.c
+OBJS += $(OBJS_l2)
+endif
+
+ifdef CONFIG_NDIS_EVENTS_INTEGRATED
+L_CFLAGS += -DCONFIG_NDIS_EVENTS_INTEGRATED
+OBJS += src/drivers/ndis_events.c
+EXTRALIBS += -loleaut32 -lole32 -luuid
+ifdef PLATFORMSDKLIB
+EXTRALIBS += $(PLATFORMSDKLIB)/WbemUuid.Lib
+else
+EXTRALIBS += WbemUuid.Lib
+endif
+endif
+
+ifndef LDO
+LDO=$(CC)
+endif
+
+ifeq ($(WPA_BUILD_SUPPLICANT),true)
+
+########################
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := wpa_cli
+LOCAL_MODULE_TAGS := debug
+LOCAL_SHARED_LIBRARIES := libc libcutils
+LOCAL_CFLAGS := $(L_CFLAGS)
+LOCAL_SRC_FILES := $(OBJS_c)
+LOCAL_C_INCLUDES := $(INCLUDES)
+include $(BUILD_EXECUTABLE)
+
+########################
+include $(CLEAR_VARS)
+LOCAL_MODULE := wpa_supplicant
+ifdef CONFIG_DRIVER_CUSTOM
+LOCAL_STATIC_LIBRARIES := libCustomWifi
+endif
+ifneq ($(BOARD_WPA_SUPPLICANT_PRIVATE_LIB),)
+LOCAL_STATIC_LIBRARIES += $(BOARD_WPA_SUPPLICANT_PRIVATE_LIB)
+endif
+LOCAL_SHARED_LIBRARIES := libc libcutils
+ifeq ($(CONFIG_TLS), openssl)
+LOCAL_SHARED_LIBRARIES += libcrypto libssl
+endif
+ifdef CONFIG_DRIVER_NL80211
+LOCAL_STATIC_LIBRARIES += libnl_2
+endif
+LOCAL_CFLAGS := $(L_CFLAGS)
+LOCAL_SRC_FILES := $(OBJS)
+LOCAL_C_INCLUDES := $(INCLUDES)
+include $(BUILD_EXECUTABLE)
+
+########################
+#
+#include $(CLEAR_VARS)
+#LOCAL_MODULE := eapol_test
+#ifdef CONFIG_DRIVER_CUSTOM
+#LOCAL_STATIC_LIBRARIES := libCustomWifi
+#endif
+#LOCAL_SHARED_LIBRARIES := libc libcrypto libssl
+#LOCAL_CFLAGS := $(L_CFLAGS)
+#LOCAL_SRC_FILES := $(OBJS_t)
+#LOCAL_C_INCLUDES := $(INCLUDES)
+#include $(BUILD_EXECUTABLE)
+#
+########################
+#
+#local_target_dir := $(TARGET_OUT)/etc/wifi
+#
+#include $(CLEAR_VARS)
+#LOCAL_MODULE := wpa_supplicant.conf
+#LOCAL_MODULE_TAGS := user
+#LOCAL_MODULE_CLASS := ETC
+#LOCAL_MODULE_PATH := $(local_target_dir)
+#LOCAL_SRC_FILES := $(LOCAL_MODULE)
+#include $(BUILD_PREBUILT)
+#
+########################
+
+endif # ifeq ($(WPA_BUILD_SUPPLICANT),true)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE = libwpa_client
+LOCAL_CFLAGS = $(L_CFLAGS)
+LOCAL_SRC_FILES = src/common/wpa_ctrl.c src/utils/os_$(CONFIG_OS).c
+LOCAL_C_INCLUDES = $(INCLUDES)
+LOCAL_SHARED_LIBRARIES := libcutils
+LOCAL_COPY_HEADERS_TO := libwpa_client
+LOCAL_COPY_HEADERS := src/common/wpa_ctrl.h
+include $(BUILD_SHARED_LIBRARY)
index 56046c3..a51298b 100644 (file)
 ChangeLog for wpa_supplicant
 
-2010-09-07 - v0.7.3
-       * fixed fallback from failed PMKSA caching into full EAP authentication
-         [Bug 355]
-       * fixed issue with early D-Bus signals during initialization
-       * fixed X.509 name handling in internal TLS
-       * fixed WPS ER to use corrent Enrollee MAC Address in Credential
-       * fixed scanning routines ot improve AP selection for WPS
-       * added WPS workaround for open networks
-       * fixed WPS Diffie-Hellman derivation to use correct public key length
-       * fixed wpa_supplicant AP mode operations to ignore Supplicant and
-         scan result events
-       * improved SME operations with nl80211
-       * fixed WPS ER event_id handling in some cases
-       * fixed some issues with bgscan simple to avoid unnecessary scans
-       * fixed issue with l2_packet_ndis overlapped writes corrupting stack
-         [Bug 328]
-       * updated WinPcap to the latest stable version 4.1.2 in Windows
-         installer
+2012-04-18 - 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
index 1d25623..22df8a0 100644 (file)
@@ -8,11 +8,27 @@ endif
 
 export LIBDIR ?= /usr/local/lib/
 export BINDIR ?= /usr/local/sbin/
+PKG_CONFIG ?= pkg-config
 
 CFLAGS += -I../src
 CFLAGS += -I../src/utils
 
-ALL=wpa_supplicant wpa_passphrase wpa_cli
+-include .config
+
+BINALL=wpa_supplicant wpa_cli
+
+ifndef CONFIG_NO_WPA_PASSPHRASE
+BINALL += wpa_passphrase
+endif
+
+ALL = $(BINALL)
+ALL += systemd/wpa_supplicant.service
+ALL += systemd/wpa_supplicant@.service
+ALL += systemd/wpa_supplicant-nl80211@.service
+ALL += systemd/wpa_supplicant-wired@.service
+ALL += dbus/fi.epitest.hostap.WPASupplicant.service
+ALL += dbus/fi.w1.wpa_supplicant1.service
+
 
 all: verify_config $(ALL) dynamic_eap_methods
 
@@ -33,9 +49,10 @@ mkconfig:
        echo CONFIG_DRIVER_HOSTAP=y >> .config
        echo CONFIG_DRIVER_WEXT=y >> .config
 
-install: all
-       mkdir -p $(DESTDIR)$(BINDIR)
-       for i in $(ALL); do cp $$i $(DESTDIR)$(BINDIR)/$$i; done
+$(DESTDIR)$(BINDIR)/%: %
+       install -D $(<) $(@)
+
+install: $(addprefix $(DESTDIR)$(BINDIR)/,$(BINALL))
        $(MAKE) -C ../src install
 
 OBJS = config.o
@@ -50,8 +67,8 @@ OBJS_p += ../src/utils/common.o
 OBJS_p += ../src/utils/wpa_debug.o
 OBJS_p += ../src/utils/wpabuf.o
 OBJS_c = wpa_cli.o ../src/common/wpa_ctrl.o
-
--include .config
+OBJS_c += ../src/utils/wpa_debug.o
+OBJS_c += ../src/utils/common.o
 
 ifndef CONFIG_OS
 ifdef CONFIG_NATIVE_WINDOWS
@@ -74,7 +91,7 @@ CFLAGS += -DWPA_TRACE
 OBJS += ../src/utils/trace.o
 OBJS_p += ../src/utils/trace.o
 OBJS_c += ../src/utils/trace.o
-OBJS_c += ../src/utils/wpa_debug.o
+OBJS_priv += ../src/utils/trace.o
 LDFLAGS += -rdynamic
 CFLAGS += -funwind-tables
 ifdef CONFIG_WPA_TRACE_BFD
@@ -89,6 +106,7 @@ ifndef CONFIG_ELOOP
 CONFIG_ELOOP=eloop
 endif
 OBJS += ../src/utils/$(CONFIG_ELOOP).o
+OBJS_c += ../src/utils/$(CONFIG_ELOOP).o
 
 
 ifdef CONFIG_EAPOL_TEST
@@ -141,6 +159,17 @@ NEED_SHA256=y
 NEED_AES_OMAC1=y
 endif
 
+ifdef CONFIG_TDLS
+CFLAGS += -DCONFIG_TDLS
+OBJS += ../src/rsn_supp/tdls.o
+NEED_SHA256=y
+NEED_AES_OMAC1=y
+endif
+
+ifdef CONFIG_TDLS_TESTING
+CFLAGS += -DCONFIG_TDLS_TESTING
+endif
+
 ifdef CONFIG_PEERKEY
 CFLAGS += -DCONFIG_PEERKEY
 endif
@@ -166,6 +195,36 @@ CFLAGS += -DCONFIG_IBSS_RSN
 OBJS += ibss_rsn.o
 endif
 
+ifdef CONFIG_P2P
+OBJS += p2p_supplicant.o
+OBJS += ../src/p2p/p2p.o
+OBJS += ../src/p2p/p2p_utils.o
+OBJS += ../src/p2p/p2p_parse.o
+OBJS += ../src/p2p/p2p_build.o
+OBJS += ../src/p2p/p2p_go_neg.o
+OBJS += ../src/p2p/p2p_sd.o
+OBJS += ../src/p2p/p2p_pd.o
+OBJS += ../src/p2p/p2p_invitation.o
+OBJS += ../src/p2p/p2p_dev_disc.o
+OBJS += ../src/p2p/p2p_group.o
+OBJS += ../src/ap/p2p_hostapd.o
+CFLAGS += -DCONFIG_P2P
+NEED_GAS=y
+NEED_OFFCHANNEL=y
+NEED_80211_COMMON=y
+CONFIG_WPS=y
+CONFIG_AP=y
+ifdef CONFIG_P2P_STRICT
+CFLAGS += -DCONFIG_P2P_STRICT
+endif
+endif
+
+ifdef CONFIG_INTERWORKING
+OBJS += interworking.o
+CFLAGS += -DCONFIG_INTERWORKING
+NEED_GAS=y
+endif
+
 ifdef CONFIG_NO_WPA2
 CFLAGS += -DCONFIG_NO_WPA2
 endif
@@ -454,7 +513,19 @@ NEED_SHA256=y
 NEED_AES_OMAC1=y
 endif
 
+ifdef CONFIG_EAP_PWD
+CFLAGS += -DEAP_PWD
+OBJS += ../src/eap_peer/eap_pwd.o ../src/eap_common/eap_pwd_common.o
+OBJS_h += ../src/eap_server/eap_pwd.o
+CONFIG_IEEE8021X_EAPOL=y
+NEED_SHA256=y
+endif
+
 ifdef CONFIG_WPS
+ifdef CONFIG_WPS2
+CFLAGS += -DCONFIG_WPS2
+endif
+
 # EAP-WSC
 CFLAGS += -DCONFIG_WPS -DEAP_WSC
 OBJS += wps_supplicant.o
@@ -522,6 +593,19 @@ OBJS += ../src/wps/http_client.o
 OBJS += ../src/wps/http_server.o
 endif
 
+ifdef CONFIG_WPS_STRICT
+CFLAGS += -DCONFIG_WPS_STRICT
+OBJS += ../src/wps/wps_validate.o
+endif
+
+ifdef CONFIG_WPS_TESTING
+CFLAGS += -DCONFIG_WPS_TESTING
+endif
+
+ifdef CONFIG_WPS_REG_DISABLE_OPEN
+CFLAGS += -DCONFIG_WPS_REG_DISABLE_OPEN
+endif
+
 endif
 
 ifdef CONFIG_EAP_IKEV2
@@ -604,8 +688,13 @@ OBJS += ../src/ap/ap_mlme.o
 OBJS += ../src/ap/ieee802_1x.o
 OBJS += ../src/eapol_auth/eapol_auth_sm.o
 OBJS += ../src/ap/ieee802_11_auth.o
+OBJS += ../src/ap/ieee802_11_shared.o
 OBJS += ../src/ap/drv_callbacks.o
 OBJS += ../src/ap/ap_drv_ops.o
+OBJS += ../src/ap/beacon.o
+ifdef CONFIG_IEEE80211N
+OBJS += ../src/ap/ieee802_11_ht.o
+endif
 ifdef CONFIG_CTRL_IFACE
 OBJS += ../src/ap/ctrl_iface_ap.o
 endif
@@ -620,14 +709,10 @@ CFLAGS += -DCONFIG_IEEE80211N
 endif
 
 ifdef NEED_AP_MLME
-OBJS += ../src/ap/beacon.o
 OBJS += ../src/ap/wmm.o
 OBJS += ../src/ap/ap_list.o
 OBJS += ../src/ap/ieee802_11.o
 OBJS += ../src/ap/hw_features.o
-ifdef CONFIG_IEEE80211N
-OBJS += ../src/ap/ieee802_11_ht.o
-endif
 CFLAGS += -DNEED_AP_MLME
 endif
 ifdef CONFIG_WPS
@@ -707,6 +792,7 @@ endif
 
 ifdef NEED_MILENAGE
 OBJS += ../src/crypto/milenage.o
+NEED_AES_ENCBLOCK=y
 endif
 
 ifdef CONFIG_PKCS12
@@ -739,6 +825,10 @@ ifndef CONFIG_TLS
 CONFIG_TLS=openssl
 endif
 
+ifdef CONFIG_TLSV11
+CFLAGS += -DCONFIG_TLSV11
+endif
+
 ifeq ($(CONFIG_TLS), openssl)
 ifdef TLS_FUNCS
 CFLAGS += -DEAP_TLS_OPENSSL
@@ -758,10 +848,6 @@ ifeq ($(CONFIG_TLS), gnutls)
 ifdef TLS_FUNCS
 OBJS += ../src/crypto/tls_gnutls.o
 LIBS += -lgnutls -lgpg-error
-ifdef CONFIG_GNUTLS_EXTRA
-CFLAGS += -DCONFIG_GNUTLS_EXTRA
-LIBS += -lgnutls-extra
-endif
 endif
 OBJS += ../src/crypto/crypto_gnutls.o
 OBJS_p += ../src/crypto/crypto_gnutls.o
@@ -959,7 +1045,9 @@ ifdef NEED_FIPS186_2_PRF
 SHA1OBJS += ../src/crypto/fips_prf_internal.o
 endif
 endif
-ifndef CONFIG_NO_WPA_PASSPHRASE
+ifdef CONFIG_NO_WPA_PASSPHRASE
+CFLAGS += -DCONFIG_NO_PBKDF2
+else
 SHA1OBJS += ../src/crypto/sha1-pbkdf2.o
 endif
 ifdef NEED_T_PRF
@@ -1023,6 +1111,12 @@ OBJS += ../src/crypto/dh_group5.o
 endif
 endif
 
+ifdef CONFIG_NO_RANDOM_POOL
+CFLAGS += -DCONFIG_NO_RANDOM_POOL
+else
+OBJS += ../src/crypto/random.o
+endif
+
 ifdef CONFIG_CTRL_IFACE
 ifeq ($(CONFIG_CTRL_IFACE), y)
 ifdef CONFIG_NATIVE_WINDOWS
@@ -1053,22 +1147,11 @@ DBUS_OBJS += dbus/dbus_old_handlers_wps.o
 endif
 DBUS_OBJS += dbus/dbus_dict_helpers.o
 ifndef DBUS_LIBS
-DBUS_LIBS := $(shell pkg-config --libs dbus-1)
+DBUS_LIBS := $(shell $(PKG_CONFIG) --libs dbus-1)
 endif
 ifndef DBUS_INCLUDE
-DBUS_INCLUDE := $(shell pkg-config --cflags dbus-1)
-endif
-dbus_version=$(subst ., ,$(shell pkg-config --modversion dbus-1))
-DBUS_VERSION_MAJOR=$(word 1,$(dbus_version))
-DBUS_VERSION_MINOR=$(word 2,$(dbus_version))
-ifeq ($(DBUS_VERSION_MAJOR),)
-DBUS_VERSION_MAJOR=0
-endif
-ifeq ($(DBUS_VERSION_MINOR),)
-DBUS_VERSION_MINOR=0
+DBUS_INCLUDE := $(shell $(PKG_CONFIG) --cflags dbus-1)
 endif
-DBUS_INCLUDE += -DDBUS_VERSION_MAJOR=$(DBUS_VERSION_MAJOR)
-DBUS_INCLUDE += -DDBUS_VERSION_MINOR=$(DBUS_VERSION_MINOR)
 DBUS_CFLAGS += $(DBUS_INCLUDE)
 endif
 
@@ -1081,11 +1164,14 @@ DBUS_OBJS += dbus/dbus_new.o dbus/dbus_new_handlers.o
 ifdef CONFIG_WPS
 DBUS_OBJS += dbus/dbus_new_handlers_wps.o
 endif
+ifdef CONFIG_P2P
+DBUS_OBJS += dbus/dbus_new_handlers_p2p.o
+endif
 ifndef DBUS_LIBS
-DBUS_LIBS := $(shell pkg-config --libs dbus-1)
+DBUS_LIBS := $(shell $(PKG_CONFIG) --libs dbus-1)
 endif
 ifndef DBUS_INCLUDE
-DBUS_INCLUDE := $(shell pkg-config --cflags dbus-1)
+DBUS_INCLUDE := $(shell $(PKG_CONFIG) --cflags dbus-1)
 endif
 ifdef CONFIG_CTRL_IFACE_DBUS_INTRO
 DBUS_OBJS += dbus/dbus_new_introspect.o
@@ -1104,8 +1190,14 @@ CFLAGS += $(DBUS_CFLAGS)
 LIBS += $(DBUS_LIBS)
 
 ifdef CONFIG_READLINE
-CFLAGS += -DCONFIG_READLINE
+OBJS_c += ../src/utils/edit_readline.o
 LIBS_c += -lncurses -lreadline
+else
+ifdef CONFIG_WPA_CLI_EDIT
+OBJS_c += ../src/utils/edit.o
+else
+OBJS_c += ../src/utils/edit_simple.o
+endif
 endif
 
 ifdef CONFIG_NATIVE_WINDOWS
@@ -1140,12 +1232,6 @@ OBJS += sme.o
 CFLAGS += -DCONFIG_SME
 endif
 
-ifdef CONFIG_CLIENT_MLME
-OBJS += mlme.o
-CFLAGS += -DCONFIG_CLIENT_MLME
-NEED_80211_COMMON=y
-endif
-
 ifdef NEED_80211_COMMON
 OBJS += ../src/common/ieee802_11_common.o
 endif
@@ -1160,6 +1246,9 @@ endif
 
 ifdef CONFIG_DEBUG_SYSLOG
 CFLAGS += -DCONFIG_DEBUG_SYSLOG
+ifdef CONFIG_DEBUG_SYSLOG_FACILITY
+CFLAGS += -DLOG_HOSTAPD="$(CONFIG_DEBUG_SYSLOG_FACILITY)"
+endif
 endif
 
 ifdef CONFIG_DEBUG_FILE
@@ -1184,12 +1273,32 @@ OBJS += bgscan_simple.o
 NEED_BGSCAN=y
 endif
 
+ifdef CONFIG_BGSCAN_LEARN
+CFLAGS += -DCONFIG_BGSCAN_LEARN
+OBJS += bgscan_learn.o
+NEED_BGSCAN=y
+endif
+
 ifdef NEED_BGSCAN
 CFLAGS += -DCONFIG_BGSCAN
 OBJS += bgscan.o
 endif
 
-OBJS_wpa_rm := ctrl_iface.o mlme.o ctrl_iface_unix.o
+ifdef NEED_GAS
+OBJS += ../src/common/gas.o
+OBJS += gas_query.o
+CFLAGS += -DCONFIG_GAS
+NEED_OFFCHANNEL=y
+endif
+
+ifdef NEED_OFFCHANNEL
+OBJS += offchannel.o
+CFLAGS += -DCONFIG_OFFCHANNEL
+endif
+
+OBJS += ../src/drivers/driver_common.o
+
+OBJS_wpa_rm := ctrl_iface.o ctrl_iface_unix.o
 OBJS_wpa := $(filter-out $(OBJS_wpa_rm),$(OBJS)) $(OBJS_h) tests/test_wpa.o
 ifdef CONFIG_AUTHENTICATOR
 OBJS_wpa += tests/link_test.o
@@ -1214,6 +1323,9 @@ OBJS_priv += ../src/utils/common.o
 OBJS_priv += ../src/utils/wpa_debug.o
 OBJS_priv += ../src/utils/wpabuf.o
 OBJS_priv += wpa_priv.o
+ifdef CONFIG_DRIVER_NL80211
+OBJS_priv += ../src/common/ieee802_11_common.o
+endif
 ifdef CONFIG_DRIVER_TEST
 OBJS_priv += $(SHA1OBJS)
 OBJS_priv += $(MD5OBJS)
@@ -1257,6 +1369,13 @@ ifndef LDO
 LDO=$(CC)
 endif
 
+Q=@
+E=echo
+ifeq ($(V), 1)
+Q=
+E=true
+endif
+
 dynamic_eap_methods: $(EAPDYN)
 
 ../src/drivers/build.wpa_supplicant:
@@ -1268,31 +1387,40 @@ dynamic_eap_methods: $(EAPDYN)
 BCHECK=../src/drivers/build.wpa_supplicant
 
 wpa_priv: $(BCHECK) $(OBJS_priv)
-       $(LDO) $(LDFLAGS) -o wpa_priv $(OBJS_priv) $(LIBS)
+       $(Q)$(LDO) $(LDFLAGS) -o wpa_priv $(OBJS_priv) $(LIBS)
+       @$(E) "  LD " $@
 
 wpa_supplicant: .config $(BCHECK) $(OBJS) $(EXTRA_progs)
-       $(LDO) $(LDFLAGS) -o wpa_supplicant $(OBJS) $(LIBS) $(EXTRALIBS)
+       $(Q)$(LDO) $(LDFLAGS) -o wpa_supplicant $(OBJS) $(LIBS) $(EXTRALIBS)
+       @$(E) "  LD " $@
 
 eapol_test: .config $(OBJS_t)
-       $(LDO) $(LDFLAGS) -o eapol_test $(OBJS_t) $(LIBS)
+       $(Q)$(LDO) $(LDFLAGS) -o eapol_test $(OBJS_t) $(LIBS)
+       @$(E) "  LD " $@
 
 preauth_test: .config $(OBJS_t2) 
-       $(LDO) $(LDFLAGS) -o preauth_test $(OBJS_t2) $(LIBS)
+       $(Q)$(LDO) $(LDFLAGS) -o preauth_test $(OBJS_t2) $(LIBS)
+       @$(E) "  LD " $@
 
 wpa_passphrase: $(OBJS_p)
-       $(LDO) $(LDFLAGS) -o wpa_passphrase $(OBJS_p) $(LIBS_p)
+       $(Q)$(LDO) $(LDFLAGS) -o wpa_passphrase $(OBJS_p) $(LIBS_p)
+       @$(E) "  LD " $@
 
 wpa_cli: $(OBJS_c)
-       $(LDO) $(LDFLAGS) -o wpa_cli $(OBJS_c) $(LIBS_c)
+       $(Q)$(LDO) $(LDFLAGS) -o wpa_cli $(OBJS_c) $(LIBS_c)
+       @$(E) "  LD " $@
 
 link_test: $(OBJS) $(OBJS_h) tests/link_test.o
-       $(LDO) $(LDFLAGS) -o link_test $(OBJS) $(OBJS_h) tests/link_test.o $(LIBS)
+       $(Q)$(LDO) $(LDFLAGS) -o link_test $(OBJS) $(OBJS_h) tests/link_test.o $(LIBS)
+       @$(E) "  LD " $@
 
 test_wpa: $(OBJS_wpa) $(OBJS_h)
-       $(LDO) $(LDFLAGS) -o test_wpa $(OBJS_wpa) $(LIBS)
+       $(Q)$(LDO) $(LDFLAGS) -o test_wpa $(OBJS_wpa) $(LIBS)
+       @$(E) "  LD " $@
 
 win_if_list: win_if_list.c
-       $(LDO) $(LDFLAGS) -o $@ win_if_list.c $(CFLAGS) $(LIBS_w)
+       $(Q)$(LDO) $(LDFLAGS) -o $@ win_if_list.c $(CFLAGS) $(LIBS_w)
+       @$(E) "  LD " $@
 
 eap_psk.so: ../src/eap_peer/eap_psk.c ../src/eap_common/eap_psk_common.c
        $(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \
@@ -1318,17 +1446,13 @@ eap_ikev2.so: ../src/eap_peer/eap_ikev2.c ../src/eap_peer/ikev2.c ../src/eap_com
        $(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $< \
                -D$(*F:eap_%=eap_peer_%)_register=eap_peer_method_dynamic_init
 
-Q=@
-E=echo
-ifeq ($(V), 1)
-Q=
-E=true
-endif
-
 %.o: %.c
        $(Q)$(CC) -c -o $@ $(CFLAGS) $<
        @$(E) "  CC " $<
 
+%.service: %.service.in
+       sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@
+
 wpa_supplicant.exe: wpa_supplicant
        mv -f $< $@
 wpa_cli.exe: wpa_cli
index 45c8bae..f324192 100644 (file)
@@ -1,7 +1,7 @@
 WPA Supplicant
 ==============
 
-Copyright (c) 2003-2010, Jouni Malinen <j@w1.fi> and contributors
+Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi> and contributors
 All Rights Reserved.
 
 This program is dual-licensed under both the GPL version 2 and BSD
@@ -147,22 +147,12 @@ Current hardware/software requirements:
        Linuxant DriverLoader (http://www.linuxant.com/driverloader/)
        with Windows NDIS driver for your wlan card supporting WPA.
 
-       Agere Systems Inc. Linux Driver
-       (http://www.agere.com/support/drivers/)
-       Please note that the driver interface file (driver_hermes.c) and
-       hardware specific include files are not included in the
-       wpa_supplicant distribution. You will need to copy these from the
-       source package of the Agere driver.
-
        madwifi driver for cards based on Atheros chip set (ar521x)
        (http://sourceforge.net/projects/madwifi/)
        Please note that you will need to modify the wpa_supplicant .config
        file to use the correct path for the madwifi driver root directory
        (CFLAGS += -I../madwifi/wpa line in example defconfig).
 
-       ATMEL AT76C5XXx driver for USB and PCMCIA cards
-       (http://atmelwlandriver.sourceforge.net/).
-
        Linux ndiswrapper (http://ndiswrapper.sourceforge.net/) with
        Windows NDIS driver.
 
@@ -181,12 +171,6 @@ Current hardware/software requirements:
        not need (or even work) with the specific driver wrapper. Use -Dwext
        with that driver.
 
-       Intel ipw2100 driver
-       (http://sourceforge.net/projects/ipw2100/)
-
-       Intel ipw2200 driver
-       (http://sourceforge.net/projects/ipw2200/)
-
        In theory, any driver that supports Linux wireless extensions can be
        used with IEEE 802.1X (i.e., not WPA) when using ap_scan=0 option in
        configuration file.
@@ -396,18 +380,13 @@ authentication algorithm (for EAP-SIM/EAP-AKA). This requires pcsc-lite
 CONFIG_PCSC=y
 
 Following options can be added to .config to select which driver
-interfaces are included. Hermes driver interface needs to be downloaded
-from Agere (see above).
+interfaces are included.
 
 CONFIG_DRIVER_HOSTAP=y
-CONFIG_DRIVER_HERMES=y
 CONFIG_DRIVER_MADWIFI=y
-CONFIG_DRIVER_ATMEL=y
 CONFIG_DRIVER_WEXT=y
 CONFIG_DRIVER_RALINK=y
-CONFIG_DRIVER_NDISWRAPPER=y
 CONFIG_DRIVER_BROADCOM=y
-CONFIG_DRIVER_IPW=y
 CONFIG_DRIVER_BSD=y
 CONFIG_DRIVER_NDIS=y
 
@@ -415,13 +394,9 @@ Following example includes all features and driver interfaces that are
 included in the wpa_supplicant package:
 
 CONFIG_DRIVER_HOSTAP=y
-CONFIG_DRIVER_HERMES=y
 CONFIG_DRIVER_MADWIFI=y
-CONFIG_DRIVER_ATMEL=y
 CONFIG_DRIVER_WEXT=y
-CONFIG_DRIVER_NDISWRAPPER=y
 CONFIG_DRIVER_BROADCOM=y
-CONFIG_DRIVER_IPW=y
 CONFIG_DRIVER_BSD=y
 CONFIG_DRIVER_NDIS=y
 CONFIG_IEEE8021X_EAPOL=y
@@ -516,14 +491,10 @@ options:
 drivers:
   hostap = Host AP driver (Intersil Prism2/2.5/3) [default]
        (this can also be used with Linuxant DriverLoader)
-  hermes = Agere Systems Inc. driver (Hermes-I/Hermes-II)
   madwifi = MADWIFI 802.11 support (Atheros, etc.) (deprecated; use wext)
-  atmel = ATMEL AT76C5XXx (USB, PCMCIA)
   wext = Linux wireless extensions (generic)
   ralink = Ralink Client driver
-  ndiswrapper = Linux ndiswrapper (deprecated; use wext)
   broadcom = Broadcom wl.o driver
-  ipw = Intel ipw2100/2200 driver (old; use wext with Linux 2.6.13 or newer)
   wired = wpa_supplicant wired Ethernet driver
   roboswitch = wpa_supplicant Broadcom switch driver
   bsd = BSD 802.11 support (Atheros, etc.)
@@ -894,13 +865,14 @@ script:
 IFNAME=$1
 CMD=$2
 
-if [ "$CMD" == "CONNECTED" ]; then
+if [ "$CMD" = "CONNECTED" ]; then
     SSID=`wpa_cli -i$IFNAME status | grep ^ssid= | cut -f2- -d=`
     # configure network, signal DHCP client, etc.
 fi
 
-if [ "$CMD" == "DISCONNECTED" ]; then
+if [ "$CMD" = "DISCONNECTED" ]; then
     # remove network configuration, if needed
+    SSID=
 fi
 
 
diff --git a/wpa_supplicant/README-P2P b/wpa_supplicant/README-P2P
new file mode 100644 (file)
index 0000000..db6e4ae
--- /dev/null
@@ -0,0 +1,525 @@
+wpa_supplicant and Wi-Fi P2P
+============================
+
+This document describes how the Wi-Fi P2P implementation in
+wpa_supplicant can be configured and how an external component on the
+client (e.g., management GUI) is used to enable WPS enrollment and
+registrar registration.
+
+
+Introduction to Wi-Fi P2P
+-------------------------
+
+TODO
+
+More information about Wi-Fi P2P is available from Wi-Fi Alliance:
+http://www.wi-fi.org/Wi-Fi_Direct.php
+
+
+wpa_supplicant implementation
+-----------------------------
+
+TODO
+
+
+wpa_supplicant configuration
+----------------------------
+
+Wi-Fi P2P is an optional component that needs to be enabled in the
+wpa_supplicant build configuration (.config). Here is an example
+configuration that includes Wi-Fi P2P support and Linux nl80211
+-based driver interface:
+
+CONFIG_DRIVER_NL80211=y
+CONFIG_CTRL_IFACE=y
+CONFIG_P2P=y
+CONFIG_AP=y
+CONFIG_WPS=y
+
+
+In run-time configuration file (wpa_supplicant.conf), some parameters
+for P2P may be set. In order to make the devices easier to recognize,
+device_name and device_type should be specified. For example,
+something like this should be included:
+
+ctrl_interface=/var/run/wpa_supplicant
+device_name=My P2P Device
+device_type=1-0050F204-1
+
+
+wpa_cli
+-------
+
+Actual Wi-Fi P2P operations are requested during runtime. These can be
+done for example using wpa_cli (which is described below) or a GUI
+like wpa_gui-qt4.
+
+
+wpa_cli starts in interactive mode if no command string is included on
+the command line. By default, it will select the first network interface
+that it can find (and that wpa_supplicant controls). If more than one
+interface is in use, it may be necessary to select one of the explicitly
+by adding -i argument on the command line (e.g., 'wpa_cli -i wlan1').
+
+Most of the P2P operations are done on the main interface (e.g., the
+interface that is automatically added when the driver is loaded, e.g.,
+wlan0). When using a separate virtual interface for group operations
+(e.g., wlan1), the control interface for that group interface may need
+to be used for some operations (mainly WPS activation in GO). This may
+change in the future so that all the needed operations could be done
+over the main control interface.
+
+Device Discovery
+
+p2p_find [timeout in seconds] [type=<social|progressive>]
+
+The default behavior is to run a single full scan in the beginning and
+then scan only social channels. type=social will scan only social
+channels, i.e., it skips the initial full scan. type=progressive is
+like the default behavior, but it will scan through all the channels
+progressively one channel at the time in the Search state rounds. This
+will help in finding new groups or groups missed during the initial
+full scan.
+
+p2p_listen [timeout in seconds]
+
+Start Listen-only state (become discoverable without searching for
+other devices). Optional parameter can be used to specify the duration
+for the Listen operation in seconds. This command may not be of that
+much use during normal operations and is mainly designed for
+testing. It can also be used to keep the device discoverable without
+having to maintain a group.
+
+p2p_stop_find
+
+Stop ongoing P2P device discovery or other operation (connect, listen
+mode).
+
+p2p_flush
+
+Flush P2P peer table and state.
+
+Group Formation
+
+p2p_prov_disc <peer device address> <display|keypad|pbc> [join]
+
+Send P2P provision discovery request to the specified peer. The
+parameters for this command are the P2P device address of the peer and
+the desired configuration method. For example, "p2p_prov_disc
+02:01:02:03:04:05 display" would request the peer to display a PIN for
+us and "p2p_prov_disc 02:01:02:03:04:05 keypad" would request the peer
+to enter a PIN that we display.
+
+The optional "join" parameter can be used to indicate that this command
+is requesting an already running GO to prepare for a new client. This is
+mainly used with "display" to request it to display a PIN.
+
+p2p_connect <peer device address> <pbc|pin|PIN#> [display|keypad]
+       [persistent] [join|auth] [go_intent=<0..15>] [freq=<in MHz>]
+
+Start P2P group formation with a discovered P2P peer. This includes
+optional group owner negotiation, group interface setup, provisioning,
+and establishing data connection.
+
+The <pbc|pin|PIN#> parameter specifies the WPS provisioning
+method. "pbc" string starts pushbutton method, "pin" string start PIN
+method using an automatically generated PIN (which will be returned as
+the command return code), PIN# means that a pre-selected PIN can be
+used (e.g., 12345670). [display|keypad] is used with PIN method
+to specify which PIN is used (display=dynamically generated random PIN
+from local display, keypad=PIN entered from peer display). "persistent"
+parameter can be used to request a persistent group to be formed.
+
+"join" indicates that this is a command to join an existing group as a
+client. It skips the GO Negotiation part. This will send a Provision
+Discovery Request message to the target GO before associating for WPS
+provisioning.
+
+"auth" indicates that the WPS parameters are authorized for the peer
+device without actually starting GO Negotiation (i.e., the peer is
+expected to initiate GO Negotiation). This is mainly for testing
+purposes.
+
+"go_intent" can be used to override the default GO Intent for this GO
+Negotiation.
+
+"freq" can be used to set a forced operating channel (e.g., freq=2412
+to select 2.4 GHz channel 1).
+
+p2p_group_add [persistent|persistent=<network id>] [freq=<freq in MHz>]
+
+Set up a P2P group owner manually (i.e., without group owner
+negotiation with a specific peer). This is also known as autonomous
+GO. Optional persistent=<network id> can be used to specify restart of
+a persistent group. Optional freq=<freq in MHz> can be used to force
+the GO to be started on a specific frequency. Special freq=2 or freq=5
+options can be used to request the best 2.4 GHz or 5 GHz band channel
+to be selected automatically.
+
+p2p_reject <peer device address>
+
+Reject connection attempt from a peer (specified with a device
+address). This is a mechanism to reject a pending GO Negotiation with
+a peer and request to automatically block any further connection or
+discovery of the peer.
+
+p2p_group_remove <group interface>
+
+Terminate a P2P group. If a new virtual network interface was used for
+the group, it will also be removed. The network interface name of the
+group interface is used as a parameter for this command.
+
+p2p_cancel
+
+Cancel an ongoing P2P group formation related operation.
+
+Service Discovery
+
+p2p_serv_disc_req
+
+Schedule a P2P service discovery request. The parameters for this
+command are the device address of the peer device (or 00:00:00:00:00:00
+for wildcard query that is sent to every discovered P2P peer that
+supports service discovery) and P2P Service Query TLV(s) as hexdump. For
+example,
+
+p2p_serv_disc_req 00:00:00:00:00:00 02000001
+
+schedules a request for listing all available services of all service
+discovery protocols and requests this to be sent to all discovered
+peers (note: this can result in long response frames). The pending
+requests are sent during device discovery (see p2p_find).
+
+Only a single pending wildcard query is supported, but there can be
+multiple pending peer device specific queries (each will be sent in
+sequence whenever the peer is found).
+
+This command returns an identifier for the pending query (e.g.,
+"1f77628") that can be used to cancel the request. Directed requests
+will be automatically removed when the specified peer has replied to
+it.
+
+For UPnP, an alternative command format can be used to specify a
+single query TLV (i.e., a service discovery for a specific UPnP
+service):
+
+p2p_serv_disc_req 00:00:00:00:00:00 upnp <version hex> <ST: from M-SEARCH>
+
+For example:
+
+p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 urn:schemas-upnp-org:device:InternetGatewayDevice:1
+
+Additional examples for queries:
+
+# list of all Bonjour services
+p2p_serv_disc_req 00:00:00:00:00:00 02000101
+
+# list of all UPnP services
+p2p_serv_disc_req 00:00:00:00:00:00 02000201
+
+# list of all WS-Discovery services
+p2p_serv_disc_req 00:00:00:00:00:00 02000301
+
+# list of all Bonjour and UPnP services
+p2p_serv_disc_req 00:00:00:00:00:00 0200010102000202
+
+# Apple File Sharing over TCP
+p2p_serv_disc_req 00:00:00:00:00:00 130001010b5f6166706f766572746370c00c000c01
+
+# Bonjour SSTH (supported service type hash)
+p2p_serv_disc_req 00:00:00:00:00:00 05000101000000
+
+# UPnP examples
+p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 ssdp:all
+p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 upnp:rootdevice
+p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 urn:schemas-upnp-org:service:ContentDirectory:2
+p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 uuid:6859dede-8574-59ab-9332-123456789012
+p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 urn:schemas-upnp-org:device:InternetGatewayDevice:1
+
+p2p_serv_disc_cancel_req <query identifier>
+
+Cancel a pending P2P service discovery request. This command takes a
+single parameter: identifier for the pending query (the value returned
+by p2p_serv_disc_req, e.g., "p2p_serv_disc_cancel_req 1f77628".
+
+p2p_serv_disc_resp
+
+Reply to a service discovery query. This command takes following
+parameters: frequency in MHz, destination address, dialog token,
+response TLV(s). The first three parameters are copied from the
+request event. For example, "p2p_serv_disc_resp 2437 02:40:61:c2:f3:b7
+1 0300000101". This command is used only if external program is used
+to process the request (see p2p_serv_disc_external).
+
+p2p_service_update
+
+Indicate that local services have changed. This is used to increment
+the P2P service indicator value so that peers know when previously
+cached information may have changed. This is only needed when external
+service discovery processing is enabled since the commands to
+pre-configure services for internal processing will increment the
+indicator automatically.
+
+p2p_serv_disc_external <0|1>
+
+Configure external processing of P2P service requests: 0 (default) =
+no external processing of requests (i.e., internal code will process
+each request based on pre-configured services), 1 = external
+processing of requests (external program is responsible for replying
+to service discovery requests with p2p_serv_disc_resp). Please note
+that there is quite strict limit on how quickly the response needs to
+be transmitted, so use of the internal processing is strongly
+recommended.
+
+p2p_service_add bonjour <query hexdump> <RDATA hexdump>
+
+Add a local Bonjour service for internal SD query processing.
+
+Examples:
+
+# AFP Over TCP (PTR)
+p2p_service_add bonjour 0b5f6166706f766572746370c00c000c01 074578616d706c65c027
+# AFP Over TCP (TXT) (RDATA=null)
+p2p_service_add bonjour 076578616d706c650b5f6166706f766572746370c00c001001 00
+
+# IP Printing over TCP (PTR) (RDATA=MyPrinter._ipp._tcp.local.)
+p2p_service_add bonjour 045f697070c00c000c01 094d795072696e746572c027
+# IP Printing over TCP (TXT) (RDATA=txtvers=1,pdl=application/postscript)
+p2p_service_add bonjour 096d797072696e746572045f697070c00c001001 09747874766572733d311a70646c3d6170706c69636174696f6e2f706f7374736372797074
+
+# Supported Service Type Hash (SSTH)
+p2p_service_add bonjour 000000 <32-byte bitfield as hexdump>
+(note: see P2P spec Annex E.4 for information on how to construct the bitfield)
+
+p2p_service_del bonjour <query hexdump>
+
+Remove a local Bonjour service from internal SD query processing.
+
+p2p_service_add upnp <version hex> <service>
+
+Add a local UPnP service for internal SD query processing.
+
+Examples:
+
+p2p_service_add upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::upnp:rootdevice
+p2p_service_add upnp 10 uuid:5566d33e-9774-09ab-4822-333456785632::upnp:rootdevice
+p2p_service_add upnp 10 uuid:1122de4e-8574-59ab-9322-333456789044::urn:schemas-upnp-org:service:ContentDirectory:2
+p2p_service_add upnp 10 uuid:5566d33e-9774-09ab-4822-333456785632::urn:schemas-upnp-org:service:ContentDirectory:2
+p2p_service_add upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::urn:schemas-upnp-org:device:InternetGatewayDevice:1
+
+p2p_service_del upnp <version hex> <service>
+
+Remove a local UPnP service from internal SD query processing.
+
+p2p_service_flush
+
+Remove all local services from internal SD query processing.
+
+Invitation
+
+p2p_invite [persistent=<network id>|group=<group ifname>] [peer=address]
+       [go_dev_addr=address]
+
+Invite a peer to join a group (e.g., group=wlan1) or to reinvoke a
+persistent group (e.g., persistent=4). If the peer device is the GO of
+the persisten group, the peer parameter is not needed. Otherwise it is
+used to specify which device to invite. go_dev_addr parameter can be
+used to override the GO device address for Invitation Request should
+it be not known for some reason (this should not be needed in most
+cases).
+
+Group Operations
+
+(These are used on the group interface.)
+
+wps_pin <any|address> <PIN>
+
+Start WPS PIN method. This allows a single WPS Enrollee to connect to
+the AP/GO. This is used on the GO when a P2P client joins an existing
+group. The second parameter is the address of the Enrollee or a string
+"any" to allow any station to use the entered PIN (which will restrict
+the PIN for one-time-use). PIN is the Enrollee PIN read either from a
+label or display on the P2P Client/WPS Enrollee.
+
+wps_pbc
+
+Start WPS PBC method (i.e., push the button). This allows a single WPS
+Enrollee to connect to the AP/GO. This is used on the GO when a P2P
+client joins an existing group.
+
+p2p_get_passphrase
+
+Get the passphrase for a group (only available when acting as a GO).
+
+p2p_presence_req [<duration> <interval>] [<duration> <interval>]
+
+Send a P2P Presence Request to the GO (this is only available when
+acting as a P2P client). If no duration/interval pairs are given, the
+request indicates that this client has no special needs for GO
+presence. the first parameter pair gives the preferred duration and
+interval values in microseconds. If the second pair is included, that
+indicates which value would be acceptable.
+
+Parameters
+
+p2p_ext_listen [<period> <interval>]
+
+Configure Extended Listen Timing. If the parameters are omitted, this
+feature is disabled. If the parameters are included, Listen State will
+be entered every interval msec for at least period msec. Both values
+have acceptable range of 1-65535 (with interval obviously having to be
+larger than or equal to duration). If the P2P module is not idle at
+the time the Extended Listen Timing timeout occurs, the Listen State
+operation will be skipped.
+
+The configured values will also be advertised to other P2P Devices. The
+received values are available in the p2p_peer command output:
+
+ext_listen_period=100 ext_listen_interval=5000
+
+p2p_set <field> <value>
+
+Change dynamic P2P parameters
+
+p2p_set discoverability <0/1>
+
+Disable/enable advertisement of client discoverability. This is
+enabled by default and this parameter is mainly used to allow testing
+of device discoverability.
+
+p2p_set managed <0/1>
+
+Disable/enable managed P2P Device operations. This is disabled by
+default.
+
+p2p_set listen_channel <1/6/11>
+
+Set P2P Listen channel. This is mainly meant for testing purposes and
+changing the Listen channel during normal operations can result in
+protocol failures.
+
+p2p_set ssid_postfix <postfix>
+
+Set postfix string to be added to the automatically generated P2P SSID
+(DIRECT-<two random characters>). For example, postfix of "-testing"
+could result in the SSID becoming DIRECT-ab-testing.
+
+set <field> <value>
+
+Set global configuration parameters which may also affect P2P
+operations. The format on these parameters is same as is used in
+wpa_supplicant.conf. Only the parameters listen here should be
+changed. Modifying other parameters may result in incorrect behavior
+since not all existing users of the parameters are updated.
+
+set uuid <UUID>
+
+Set WPS UUID (by default, this is generated based on the MAC address).
+
+set device_name <device name>
+
+Set WPS Device Name (also included in some P2P messages).
+
+set manufacturer <manufacturer>
+
+Set WPS Manufacturer.
+
+set model_name <model name>
+
+Set WPS Model Name.
+
+set model_number <model number>
+
+Set WPS Model Number.
+
+set serial_number <serial number>
+
+Set WPS Serial Number.
+
+set device_type <device type>
+
+Set WPS Device Type.
+
+set os_version <OS version>
+
+Set WPS OS Version.
+
+set config_methods <config methods>
+
+Set WPS Configuration Methods.
+
+set sec_device_type <device type>
+
+Add a new Secondary Device Type.
+
+set p2p_go_intent <GO intent>
+
+Set the default P2P GO Intent. Note: This value can be overridden in
+p2p_connect command and as such, there should be no need to change the
+default value here during normal operations.
+
+set p2p_ssid_postfix <P2P SSID postfix>
+
+Set P2P SSID postfix.
+
+set persistent_reconnect <0/1>
+
+Disable/enabled persistent reconnect for reinvocation of persistent
+groups. If enabled, invitations to reinvoke a persistent group will be
+accepted without separate authorization (e.g., user interaction).
+
+set country <two character country code>
+
+Set country code (this is included in some P2P messages).
+
+Status
+
+p2p_peers [discovered]
+
+List P2P Device Addresses of all the P2P peers we know. The optional
+"discovered" parameter filters out the peers that we have not fully
+discovered, i.e., which we have only seen in a received Probe Request
+frame.
+
+p2p_peer <P2P Device Address>
+
+Fetch information about a known P2P peer.
+
+Group Status
+
+(These are used on the group interface.)
+
+status
+
+Show status information (connection state, role, use encryption
+parameters, IP address, etc.).
+
+sta
+
+Show information about an associated station (when acting in AP/GO role).
+
+all_sta
+
+Lists the currently associated stations.
+
+Configuration data
+
+list_networks
+
+Lists the configured networks, including stored information for
+persistent groups. The identifier in this list is used with
+p2p_group_add and p2p_invite to indicate which persistent group is to
+be reinvoked.
+
+remove_network <network id>
+
+Remove a network entry from configuration. 
+
+
+wpa_cli action script
+---------------------
+
+See examples/p2p-action.sh
+
+TODO: describe DHCP/DNS setup
+TODO: cross-connection
index 8f0d0d6..bf75cb4 100644 (file)
@@ -47,9 +47,7 @@ wpa_supplicant implementation
 
 wpa_supplicant includes an optional WPS component that can be used as
 an Enrollee to enroll new network credential or as a Registrar to
-configure an AP. The current version of wpa_supplicant does not
-support operation as an external WLAN Management Registrar for adding
-new client devices or configuring the AP over UPnP.
+configure an AP.
 
 
 wpa_supplicant configuration
@@ -57,11 +55,17 @@ wpa_supplicant configuration
 
 WPS is an optional component that needs to be enabled in
 wpa_supplicant build configuration (.config). Here is an example
-configuration that includes WPS support and Linux wireless extensions
--based driver interface:
+configuration that includes WPS support and Linux nl80211 -based
+driver interface:
 
-CONFIG_DRIVER_WEXT=y
+CONFIG_DRIVER_NL80211=y
 CONFIG_WPS=y
+CONFIG_WPS2=y
+
+If you want to enable WPS external registrar (ER) functionality, you
+will also need to add following line:
+
+CONFIG_WPS_ER=y
 
 
 WPS needs the Universally Unique IDentifier (UUID; see RFC 4122) for
@@ -93,6 +97,13 @@ pushbutton event (for PBC) to allow a new WPS Enrollee to join the
 network. wpa_supplicant uses the control interface as an input channel
 for these events.
 
+The PIN value used in the commands must be processed by an UI to
+remove non-digit characters and potentially, to verify the checksum
+digit. "wpa_cli wps_check_pin <PIN>" can be used to do such processing.
+It returns FAIL if the PIN is invalid, or FAIL-CHECKSUM if the checksum
+digit is incorrect, or the processed PIN (non-digit characters removed)
+if the PIN is valid.
+
 If the client device has a display, a random PIN has to be generated
 for each WPS registration session. wpa_supplicant can do this with a
 control interface request, e.g., by calling wpa_cli:
@@ -116,6 +127,11 @@ This starts the WPS negotiation in the same way as above with the
 generated PIN.
 
 
+If a random PIN is needed for a user interface, "wpa_cli wps_pin get"
+can be used to generate a new PIN without starting WPS negotiation.
+This random PIN can then be passed as an argument to another wps_pin
+call when the actual operation should be started.
+
 If the client design wants to support optional WPS PBC mode, this can
 be enabled by either a physical button in the client device or a
 virtual button in the user interface. The PBC operation requires that
@@ -198,3 +214,92 @@ Following control interface messages are sent out for external programs:
 WPS-CRED-RECEIVED  <hexdump of Credential attribute(s)>
 For example:
 <2>WPS-CRED-RECEIVED 100e006f10260001011045000c6a6b6d2d7770732d74657374100300020020100f000200081027004030653462303435366332363666653064333961643135353461316634626637313234333761636664623766333939653534663166316230323061643434386235102000060266a0ee1727
+
+
+wpa_supplicant as WPS External Registrar (ER)
+---------------------------------------------
+
+wpa_supplicant can be used as a WPS ER to configure an AP or enroll
+new Enrollee to join the network. This functionality uses UPnP and
+requires that a working IP connectivity is available with the AP (this
+can be either over a wired or wireless connection).
+
+Separate wpa_supplicant process can be started for WPS ER
+operations. A special "none" driver can be used in such a case to
+indicate that no local network interface is actually controlled. For
+example, following command could be used to start the ER:
+
+wpa_supplicant -Dnone -c er.conf -ieth0
+
+Sample er.conf:
+
+ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=admin
+device_name=WPS External Registrar
+
+
+wpa_cli commands for ER functionality:
+
+wps_er_start [IP address]
+- start WPS ER functionality
+- the optional IP address parameter can be used to filter operations only
+  to include a single AP
+- if run again while ER is active, the stored information (discovered APs
+  and Enrollees) are shown again
+
+wps_er_stop
+- stop WPS ER functionality
+
+wps_er_learn <UUID> <AP PIN>
+- learn AP configuration
+
+wps_er_set_config <UUID> <network id>
+- use AP configuration from a locally configured network (e.g., from
+  wps_reg command); this does not change the AP's configuration, but
+  only prepares a configuration to be used when enrolling a new device
+  to the AP
+
+wps_er_config <UUID> <AP PIN> <new SSID> <auth> <encr> <new key>
+- examples:
+  wps_er_config 87654321-9abc-def0-1234-56789abc0002 12345670 testing WPA2PSK CCMP 12345678
+  wpa_er_config 87654321-9abc-def0-1234-56789abc0002 12345670 clear OPEN NONE ""
+
+<auth> must be one of the following: OPEN WPAPSK WPA2PSK
+<encr> must be one of the following: NONE WEP TKIP CCMP
+
+
+wps_er_pbc <Enrollee UUID>
+- accept an Enrollee PBC using External Registrar
+
+wps_er_pin <Enrollee UUID> <PIN> [Enrollee MAC address]
+- add an Enrollee PIN to External Registrar
+- if Enrollee UUID is not known, "any" can be used to add a wildcard PIN
+- if the MAC address of the enrollee is known, it should be configured
+  to allow the AP to advertise list of authorized enrollees
+
+
+WPS ER events:
+
+WPS_EVENT_ER_AP_ADD
+- WPS ER discovered an AP
+
+WPS-ER-AP-ADD 87654321-9abc-def0-1234-56789abc0002 02:11:22:33:44:55 pri_dev_type=6-0050F204-1 wps_state=1 |Very friendly name|Company|Long description of the model|WAP|http://w1.fi/|http://w1.fi/hostapd/
+
+WPS_EVENT_ER_AP_REMOVE
+- WPS ER removed an AP entry
+
+WPS-ER-AP-REMOVE 87654321-9abc-def0-1234-56789abc0002
+
+WPS_EVENT_ER_ENROLLEE_ADD
+- WPS ER discovered a new Enrollee
+
+WPS-ER-ENROLLEE-ADD 2b7093f1-d6fb-5108-adbb-bea66bb87333 02:66:a0:ee:17:27 M1=1 config_methods=0x14d dev_passwd_id=0 pri_dev_type=1-0050F204-1 |Wireless Client|Company|cmodel|123|12345|
+
+WPS_EVENT_ER_ENROLLEE_REMOVE
+- WPS ER removed an Enrollee entry
+
+WPS-ER-ENROLLEE-REMOVE 2b7093f1-d6fb-5108-adbb-bea66bb87333 02:66:a0:ee:17:27
+
+WPS-ER-AP-SETTINGS
+- WPS ER learned AP settings
+
+WPS-ER-AP-SETTINGS uuid=fd91b4ec-e3fa-5891-a57d-8c59efeed1d2 ssid=test-wps auth_type=0x0020 encr_type=0x0008 key=12345678
index 2b93984..75b1393 100644 (file)
 #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/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 "eap_server/eap_methods.h"
 #include "eap_common/eap_wsc_common.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 */
 
 
 static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s,
@@ -64,9 +78,59 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s,
                return -1;
        }
 
-       /* TODO: enable HT if driver supports it;
+       /* 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.
+        * Using default config settings for: conf->ht_op_mode_fixed,
+        * conf->ht_capab, conf->secondary_channel, conf->require_ht
+        */
+       if (wpa_s->hw.modes) {
+               struct hostapd_hw_modes *mode = NULL;
+               int i;
+               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;
+                       }
+               }
+               if (mode && mode->ht_capab)
+                       conf->ieee80211n = 1;
+       }
+#endif /* CONFIG_IEEE80211N */
+
+#ifdef CONFIG_P2P
+       if (conf->hw_mode == HOSTAPD_MODE_IEEE80211G) {
+               /* 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;
+#endif /* CONFIG_P2P */
+
        if (ssid->ssid_len == 0) {
                wpa_printf(MSG_ERROR, "No SSID configured for AP mode");
                return -1;
@@ -76,6 +140,9 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s,
        bss->ssid.ssid_len = ssid->ssid_len;
        bss->ssid.ssid_set = 1;
 
+       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;
@@ -89,6 +156,22 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s,
                        return -1;
                os_memcpy(bss->ssid.wpa_psk->psk, ssid->psk, PMK_LEN);
                bss->ssid.wpa_psk->group = 1;
+       } 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;
        }
 
        /* Select group cipher based on the enabled pairwise cipher suites */
@@ -110,46 +193,187 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s,
        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;
-       } else if (bss->ssid.wep.keys_set)
+               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;
-       else
+               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;
+       }
 
 #ifdef CONFIG_WPS
        /*
-        * Enable WPS by default, but require user interaction to actually use
-        * it. Only the internal Registrar is supported.
+        * 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;
+#ifdef CONFIG_WPS2
+       if (bss->ssid.security_policy == SECURITY_WPA_PSK &&
+           (!(pairwise & WPA_CIPHER_CCMP) || !(bss->wpa & 2)))
+               goto no_wps; /* WPS2 does not allow WPA/TKIP-only
+                             * configuration */
+#endif /* CONFIG_WPS2 */
        bss->eap_server = 1;
        bss->wps_state = 2;
-       bss->ap_setup_locked = 1;
+       bss->ap_setup_locked = 2;
        if (wpa_s->conf->config_methods)
                bss->config_methods = os_strdup(wpa_s->conf->config_methods);
-       if (wpa_s->conf->device_type)
-               bss->device_type = os_strdup(wpa_s->conf->device_type);
+       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);
+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;
+
        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;
+       size_t hdr_len;
+
+       mgmt = (const struct ieee80211_mgmt *) buf;
+       hdr_len = (const u8 *) &mgmt->u.action.u.vs_public_action.action - buf;
+       if (hdr_len > len)
+               return;
+       wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid,
+                          mgmt->u.action.category,
+                          &mgmt->u.action.u.vs_public_action.action,
+                          len - hdr_len, freq);
+#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);
+}
+
+
+static int ap_vendor_action_rx(void *ctx, const u8 *buf, size_t len, int freq)
+{
+#ifdef CONFIG_P2P
+       struct wpa_supplicant *wpa_s = ctx;
+       const struct ieee80211_mgmt *mgmt;
+       size_t hdr_len;
+
+       mgmt = (const struct ieee80211_mgmt *) buf;
+       hdr_len = (const u8 *) &mgmt->u.action.u.vs_public_action.action - buf;
+       if (hdr_len > len)
+               return -1;
+       wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid,
+                          mgmt->u.action.category,
+                          &mgmt->u.action.u.vs_public_action.action,
+                          len - hdr_len, freq);
+#endif /* CONFIG_P2P */
+       return 0;
 }
 
 
-static int ap_probe_req_rx(void *ctx, const u8 *addr, const u8 *ie,
-                          size_t ie_len)
+static int ap_probe_req_rx(void *ctx, const u8 *sa, const u8 *da,
+                          const u8 *bssid, const u8 *ie, size_t ie_len)
 {
+#ifdef CONFIG_P2P
+       struct wpa_supplicant *wpa_s = ctx;
+       return wpas_p2p_probe_req_rx(wpa_s, sa, da, bssid, ie, ie_len);
+#else /* CONFIG_P2P */
        return 0;
+#endif /* CONFIG_P2P */
 }
 
 
 static void ap_wps_reg_success_cb(void *ctx, const u8 *mac_addr,
                                  const u8 *uuid_e)
 {
+#ifdef CONFIG_P2P
+       struct wpa_supplicant *wpa_s = ctx;
+       wpas_p2p_wps_success(wpa_s, mac_addr, 1);
+#endif /* CONFIG_P2P */
+}
+
+
+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);
 }
 
 
@@ -182,11 +406,14 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s,
                params.mode = IEEE80211_MODE_IBSS;
                break;
        case WPAS_MODE_AP:
+       case WPAS_MODE_P2P_GO:
+       case WPAS_MODE_P2P_GROUP_FORMATION:
                params.mode = IEEE80211_MODE_AP;
                break;
        }
        params.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
@@ -207,6 +434,17 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s,
        params.pairwise_suite = cipher_suite2driver(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
+               params.uapsd = -1;
+
        if (wpa_drv_associate(wpa_s, &params) < 0) {
                wpa_msg(wpa_s, MSG_INFO, "Failed to start AP functionality");
                return -1;
@@ -216,6 +454,7 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s,
        if (hapd_iface == NULL)
                return -1;
        hapd_iface->owner = wpa_s;
+       hapd_iface->drv_flags = wpa_s->drv_flags;
 
        wpa_s->ap_iface->conf = conf = hostapd_config_defaults();
        if (conf == NULL) {
@@ -223,12 +462,25 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s,
                return -1;
        }
 
+       if (params.uapsd > 0) {
+               conf->bss->wmm_enabled = 1;
+               conf->bss->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_zalloc(conf->num_bss *
                                    sizeof(struct hostapd_data *));
@@ -247,42 +499,64 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s,
                }
 
                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]->p2p = wpa_s->global->p2p;
+               hapd_iface->bss[i]->p2p_group = wpas_p2p_group_init(
+                       wpa_s, ssid->p2p_persistent_group,
+                       ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION);
+#endif /* CONFIG_P2P */
+               hapd_iface->bss[i]->setup_complete_cb = wpas_ap_configured_cb;
+               hapd_iface->bss[i]->setup_complete_cb_ctx = wpa_s;
        }
 
        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;
+       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;
        }
 
-       wpa_s->current_ssid = ssid;
-       os_memcpy(wpa_s->bssid, wpa_s->own_addr, ETH_ALEN);
-       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);
-
        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;
+       wpa_s->assoc_freq = 0;
+       wpa_s->reassociated_connection = 0;
+#ifdef CONFIG_P2P
+       if (wpa_s->ap_iface->bss)
+               wpa_s->ap_iface->bss[0]->p2p_group = NULL;
+       wpas_p2p_group_deinit(wpa_s);
+#endif /* CONFIG_P2P */
        hostapd_interface_deinit(wpa_s->ap_iface);
        hostapd_interface_free(wpa_s->ap_iface);
        wpa_s->ap_iface = NULL;
@@ -300,16 +574,21 @@ void ap_tx_status(void *ctx, const u8 *addr,
 }
 
 
-void ap_rx_from_unknown_sta(void *ctx, const u8 *frame, size_t len)
+void ap_client_poll_ok(void *ctx, const u8 *addr)
 {
 #ifdef NEED_AP_MLME
        struct wpa_supplicant *wpa_s = ctx;
-       const struct ieee80211_hdr *hdr =
-               (const struct ieee80211_hdr *) frame;
-       u16 fc = le_to_host16(hdr->frame_control);
-       ieee802_11_rx_from_unknown(wpa_s->ap_iface->bss[0], hdr->addr2,
-                                  (fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) ==
-                                  (WLAN_FC_TODS | WLAN_FC_FROMDS));
+       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 */
 }
 
@@ -346,11 +625,58 @@ void wpa_supplicant_ap_rx_eapol(struct wpa_supplicant *wpa_s,
 
 #ifdef CONFIG_WPS
 
-int wpa_supplicant_ap_wps_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid)
+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]);
+       return hostapd_wps_button_pushed(wpa_s->ap_iface->bss[0],
+                                        p2p_dev_addr);
+}
+
+
+static int wpa_supplicant_ap_wps_sta_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;
+}
+
+
+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],
+                                 wpa_supplicant_ap_wps_sta_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;
 }
 
 
@@ -364,16 +690,135 @@ int wpa_supplicant_ap_wps_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
 
        if (pin == NULL) {
                unsigned int rpin = wps_generate_pin();
-               ret_len = os_snprintf(buf, buflen, "%d", rpin);
+               ret_len = os_snprintf(buf, buflen, "%08d", rpin);
                pin = buf;
-       }
+       } else
+               ret_len = os_snprintf(buf, buflen, "%s", pin);
 
-       ret = hostapd_wps_add_pin(wpa_s->ap_iface->bss[0], "any", pin, 0);
+       ret = hostapd_wps_add_pin(wpa_s->ap_iface->bss[0], bssid, "any", pin,
+                                 0);
        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 (ret < 0 || ret >= (int) sizeof(pin_txt))
+               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;
+}
+
 #endif /* CONFIG_WPS */
 
 
@@ -440,6 +885,35 @@ int ap_ctrl_iface_wpa_get_status(struct wpa_supplicant *wpa_s, char *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 wpa_supplicant_ap_mac_addr_filter(struct wpa_supplicant *wpa_s,
                                      const u8 *addr)
 {
index 381a432..567e784 100644 (file)
@@ -21,9 +21,16 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s,
 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);
+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 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,
@@ -34,10 +41,13 @@ 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_rx_from_unknown_sta(void *ctx, const u8 *frame, size_t len);
+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);
 
 #endif /* AP_H */
index 31b5d27..5661830 100644 (file)
 #ifdef CONFIG_BGSCAN_SIMPLE
 extern const struct bgscan_ops bgscan_simple_ops;
 #endif /* CONFIG_BGSCAN_SIMPLE */
+#ifdef CONFIG_BGSCAN_LEARN
+extern const struct bgscan_ops bgscan_learn_ops;
+#endif /* CONFIG_BGSCAN_LEARN */
 
 static const struct bgscan_ops * bgscan_modules[] = {
 #ifdef CONFIG_BGSCAN_SIMPLE
        &bgscan_simple_ops,
 #endif /* CONFIG_BGSCAN_SIMPLE */
+#ifdef CONFIG_BGSCAN_LEARN
+       &bgscan_learn_ops,
+#endif /* CONFIG_BGSCAN_LEARN */
        NULL
 };
 
@@ -88,10 +94,12 @@ void bgscan_deinit(struct wpa_supplicant *wpa_s)
 }
 
 
-int bgscan_notify_scan(struct wpa_supplicant *wpa_s)
+int bgscan_notify_scan(struct wpa_supplicant *wpa_s,
+                      struct wpa_scan_results *scan_res)
 {
        if (wpa_s->bgscan && wpa_s->bgscan_priv)
-               return wpa_s->bgscan->notify_scan(wpa_s->bgscan_priv);
+               return wpa_s->bgscan->notify_scan(wpa_s->bgscan_priv,
+                                                 scan_res);
        return 0;
 }
 
@@ -103,8 +111,13 @@ void bgscan_notify_beacon_loss(struct wpa_supplicant *wpa_s)
 }
 
 
-void bgscan_notify_signal_change(struct wpa_supplicant *wpa_s, int above)
+void bgscan_notify_signal_change(struct wpa_supplicant *wpa_s, int above,
+                                int current_signal, int current_noise,
+                                int current_txrate)
 {
        if (wpa_s->bgscan && wpa_s->bgscan_priv)
-               wpa_s->bgscan->notify_signal_change(wpa_s->bgscan_priv, above);
+               wpa_s->bgscan->notify_signal_change(wpa_s->bgscan_priv, above,
+                                                   current_signal,
+                                                   current_noise,
+                                                   current_txrate);
 }
index 69e99b6..ae94a48 100644 (file)
@@ -25,18 +25,24 @@ struct bgscan_ops {
                       const struct wpa_ssid *ssid);
        void (*deinit)(void *priv);
 
-       int (*notify_scan)(void *priv);
+       int (*notify_scan)(void *priv, struct wpa_scan_results *scan_res);
        void (*notify_beacon_loss)(void *priv);
-       void (*notify_signal_change)(void *priv, int above);
+       void (*notify_signal_change)(void *priv, int above,
+                                    int current_signal,
+                                    int current_noise,
+                                    int current_txrate);
 };
 
 #ifdef CONFIG_BGSCAN
 
 int bgscan_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
 void bgscan_deinit(struct wpa_supplicant *wpa_s);
-int bgscan_notify_scan(struct wpa_supplicant *wpa_s);
+int bgscan_notify_scan(struct wpa_supplicant *wpa_s,
+                      struct wpa_scan_results *scan_res);
 void bgscan_notify_beacon_loss(struct wpa_supplicant *wpa_s);
-void bgscan_notify_signal_change(struct wpa_supplicant *wpa_s, int above);
+void bgscan_notify_signal_change(struct wpa_supplicant *wpa_s, int above,
+                                int current_signal, int current_noise,
+                                int current_txrate);
 
 #else /* CONFIG_BGSCAN */
 
@@ -50,7 +56,8 @@ static inline void bgscan_deinit(struct wpa_supplicant *wpa_s)
 {
 }
 
-static inline int bgscan_notify_scan(struct wpa_supplicant *wpa_s)
+static inline int bgscan_notify_scan(struct wpa_supplicant *wpa_s,
+                                    struct wpa_scan_results *scan_res)
 {
        return 0;
 }
@@ -60,7 +67,9 @@ static inline void bgscan_notify_beacon_loss(struct wpa_supplicant *wpa_s)
 }
 
 static inline void bgscan_notify_signal_change(struct wpa_supplicant *wpa_s,
-                                              int above)
+                                              int above, int current_signal,
+                                              int current_noise,
+                                              int current_txrate)
 {
 }
 
diff --git a/wpa_supplicant/bgscan_learn.c b/wpa_supplicant/bgscan_learn.c
new file mode 100644 (file)
index 0000000..5385cce
--- /dev/null
@@ -0,0 +1,610 @@
+/*
+ * WPA Supplicant - background scan and roaming module: learn
+ * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "list.h"
+#include "common/ieee802_11_defs.h"
+#include "drivers/driver.h"
+#include "config_ssid.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "scan.h"
+#include "bgscan.h"
+
+struct bgscan_learn_bss {
+       struct dl_list list;
+       u8 bssid[ETH_ALEN];
+       int freq;
+       u8 *neigh; /* num_neigh * ETH_ALEN buffer */
+       size_t num_neigh;
+};
+
+struct bgscan_learn_data {
+       struct wpa_supplicant *wpa_s;
+       const struct wpa_ssid *ssid;
+       int scan_interval;
+       int signal_threshold;
+       int short_interval; /* use if signal < threshold */
+       int long_interval; /* use if signal > threshold */
+       struct os_time last_bgscan;
+       char *fname;
+       struct dl_list bss;
+       int *supp_freqs;
+       int probe_idx;
+};
+
+
+static void bss_free(struct bgscan_learn_bss *bss)
+{
+       os_free(bss->neigh);
+       os_free(bss);
+}
+
+
+static int bssid_in_array(u8 *array, size_t array_len, const u8 *bssid)
+{
+       size_t i;
+
+       if (array == NULL || array_len == 0)
+               return 0;
+
+       for (i = 0; i < array_len; i++) {
+               if (os_memcmp(array + i * ETH_ALEN, bssid, ETH_ALEN) == 0)
+                       return 1;
+       }
+
+       return 0;
+}
+
+
+static void bgscan_learn_add_neighbor(struct bgscan_learn_bss *bss,
+                                     const u8 *bssid)
+{
+       u8 *n;
+
+       if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0)
+               return;
+       if (bssid_in_array(bss->neigh, bss->num_neigh, bssid))
+               return;
+
+       n = os_realloc(bss->neigh, (bss->num_neigh + 1) * ETH_ALEN);
+       if (n == NULL)
+               return;
+
+       os_memcpy(n + bss->num_neigh * ETH_ALEN, bssid, ETH_ALEN);
+       bss->neigh = n;
+       bss->num_neigh++;
+}
+
+
+static struct bgscan_learn_bss * bgscan_learn_get_bss(
+       struct bgscan_learn_data *data, const u8 *bssid)
+{
+       struct bgscan_learn_bss *bss;
+
+       dl_list_for_each(bss, &data->bss, struct bgscan_learn_bss, list) {
+               if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0)
+                       return bss;
+       }
+       return NULL;
+}
+
+
+static int bgscan_learn_load(struct bgscan_learn_data *data)
+{
+       FILE *f;
+       char buf[128];
+       struct bgscan_learn_bss *bss;
+
+       if (data->fname == NULL)
+               return 0;
+
+       f = fopen(data->fname, "r");
+       if (f == NULL)
+               return 0;
+
+       wpa_printf(MSG_DEBUG, "bgscan learn: Loading data from %s",
+                  data->fname);
+
+       if (fgets(buf, sizeof(buf), f) == NULL ||
+           os_strncmp(buf, "wpa_supplicant-bgscan-learn\n", 28) != 0) {
+               wpa_printf(MSG_INFO, "bgscan learn: Invalid data file %s",
+                          data->fname);
+               fclose(f);
+               return -1;
+       }
+
+       while (fgets(buf, sizeof(buf), f)) {
+               if (os_strncmp(buf, "BSS ", 4) == 0) {
+                       bss = os_zalloc(sizeof(*bss));
+                       if (!bss)
+                               continue;
+                       if (hwaddr_aton(buf + 4, bss->bssid) < 0) {
+                               bss_free(bss);
+                               continue;
+                       }
+                       bss->freq = atoi(buf + 4 + 18);
+                       dl_list_add(&data->bss, &bss->list);
+                       wpa_printf(MSG_DEBUG, "bgscan learn: Loaded BSS "
+                                  "entry: " MACSTR " freq=%d",
+                                  MAC2STR(bss->bssid), bss->freq);
+               }
+
+               if (os_strncmp(buf, "NEIGHBOR ", 9) == 0) {
+                       u8 addr[ETH_ALEN];
+
+                       if (hwaddr_aton(buf + 9, addr) < 0)
+                               continue;
+                       bss = bgscan_learn_get_bss(data, addr);
+                       if (bss == NULL)
+                               continue;
+                       if (hwaddr_aton(buf + 9 + 18, addr) < 0)
+                               continue;
+
+                       bgscan_learn_add_neighbor(bss, addr);
+               }
+       }
+
+       fclose(f);
+       return 0;
+}
+
+
+static void bgscan_learn_save(struct bgscan_learn_data *data)
+{
+       FILE *f;
+       struct bgscan_learn_bss *bss;
+
+       if (data->fname == NULL)
+               return;
+
+       wpa_printf(MSG_DEBUG, "bgscan learn: Saving data to %s",
+                  data->fname);
+
+       f = fopen(data->fname, "w");
+       if (f == NULL)
+               return;
+       fprintf(f, "wpa_supplicant-bgscan-learn\n");
+
+       dl_list_for_each(bss, &data->bss, struct bgscan_learn_bss, list) {
+               fprintf(f, "BSS " MACSTR " %d\n",
+                       MAC2STR(bss->bssid), bss->freq);
+       }
+
+       dl_list_for_each(bss, &data->bss, struct bgscan_learn_bss, list) {
+               size_t i;
+               for (i = 0; i < bss->num_neigh; i++) {
+                       fprintf(f, "NEIGHBOR " MACSTR " " MACSTR "\n",
+                               MAC2STR(bss->bssid),
+                               MAC2STR(bss->neigh + i * ETH_ALEN));
+               }
+       }
+
+       fclose(f);
+}
+
+
+static int in_array(int *array, int val)
+{
+       int i;
+
+       if (array == NULL)
+               return 0;
+
+       for (i = 0; array[i]; i++) {
+               if (array[i] == val)
+                       return 1;
+       }
+
+       return 0;
+}
+
+
+static int * bgscan_learn_get_freqs(struct bgscan_learn_data *data,
+                                   size_t *count)
+{
+       struct bgscan_learn_bss *bss;
+       int *freqs = NULL, *n;
+
+       *count = 0;
+
+       dl_list_for_each(bss, &data->bss, struct bgscan_learn_bss, list) {
+               if (in_array(freqs, bss->freq))
+                       continue;
+               n = os_realloc(freqs, (*count + 2) * sizeof(int));
+               if (n == NULL)
+                       return freqs;
+               freqs = n;
+               freqs[*count] = bss->freq;
+               (*count)++;
+               freqs[*count] = 0;
+       }
+
+       return freqs;
+}
+
+
+static int * bgscan_learn_get_probe_freq(struct bgscan_learn_data *data,
+                                        int *freqs, size_t count)
+{
+       int idx, *n;
+
+       if (data->supp_freqs == NULL)
+               return freqs;
+
+       idx = data->probe_idx + 1;
+       while (idx != data->probe_idx) {
+               if (data->supp_freqs[idx] == 0)
+                       idx = 0;
+               if (!in_array(freqs, data->supp_freqs[idx])) {
+                       wpa_printf(MSG_DEBUG, "bgscan learn: Probe new freq "
+                                  "%u", data->supp_freqs[idx]);
+                       data->probe_idx = idx;
+                       n = os_realloc(freqs, (count + 2) * sizeof(int));
+                       if (n == NULL)
+                               return freqs;
+                       freqs = n;
+                       freqs[count] = data->supp_freqs[idx];
+                       count++;
+                       freqs[count] = 0;
+                       break;
+               }
+
+               idx++;
+       }
+
+       return freqs;
+}
+
+
+static void bgscan_learn_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+       struct bgscan_learn_data *data = eloop_ctx;
+       struct wpa_supplicant *wpa_s = data->wpa_s;
+       struct wpa_driver_scan_params params;
+       int *freqs = NULL;
+       size_t count, i;
+       char msg[100], *pos;
+
+       os_memset(&params, 0, sizeof(params));
+       params.num_ssids = 1;
+       params.ssids[0].ssid = data->ssid->ssid;
+       params.ssids[0].ssid_len = data->ssid->ssid_len;
+       if (data->ssid->scan_freq)
+               params.freqs = data->ssid->scan_freq;
+       else {
+               freqs = bgscan_learn_get_freqs(data, &count);
+               wpa_printf(MSG_DEBUG, "bgscan learn: BSSes in this ESS have "
+                          "been seen on %u channels", (unsigned int) count);
+               freqs = bgscan_learn_get_probe_freq(data, freqs, count);
+
+               msg[0] = '\0';
+               pos = msg;
+               for (i = 0; freqs && freqs[i]; i++) {
+                       int ret;
+                       ret = os_snprintf(pos, msg + sizeof(msg) - pos, " %d",
+                                         freqs[i]);
+                       if (ret < 0 || ret >= msg + sizeof(msg) - pos)
+                               break;
+                       pos += ret;
+               }
+               pos[0] = '\0';
+               wpa_printf(MSG_DEBUG, "bgscan learn: Scanning frequencies:%s",
+                          msg);
+               params.freqs = freqs;
+       }
+
+       wpa_printf(MSG_DEBUG, "bgscan learn: Request a background scan");
+       if (wpa_supplicant_trigger_scan(wpa_s, &params)) {
+               wpa_printf(MSG_DEBUG, "bgscan learn: Failed to trigger scan");
+               eloop_register_timeout(data->scan_interval, 0,
+                                      bgscan_learn_timeout, data, NULL);
+       } else
+               os_get_time(&data->last_bgscan);
+       os_free(freqs);
+}
+
+
+static int bgscan_learn_get_params(struct bgscan_learn_data *data,
+                                  const char *params)
+{
+       const char *pos;
+
+       if (params == NULL)
+               return 0;
+
+       data->short_interval = atoi(params);
+
+       pos = os_strchr(params, ':');
+       if (pos == NULL)
+               return 0;
+       pos++;
+       data->signal_threshold = atoi(pos);
+       pos = os_strchr(pos, ':');
+       if (pos == NULL) {
+               wpa_printf(MSG_ERROR, "bgscan learn: Missing scan interval "
+                          "for high signal");
+               return -1;
+       }
+       pos++;
+       data->long_interval = atoi(pos);
+       pos = os_strchr(pos, ':');
+       if (pos) {
+               pos++;
+               data->fname = os_strdup(pos);
+       }
+
+       return 0;
+}
+
+
+static int * bgscan_learn_get_supp_freqs(struct wpa_supplicant *wpa_s)
+{
+       struct hostapd_hw_modes *modes;
+       int i, j, *freqs = NULL, *n;
+       size_t count = 0;
+
+       modes = wpa_s->hw.modes;
+       if (modes == NULL)
+               return NULL;
+
+       for (i = 0; i < wpa_s->hw.num_modes; i++) {
+               for (j = 0; j < modes[i].num_channels; j++) {
+                       if (modes[i].channels[j].flag & HOSTAPD_CHAN_DISABLED)
+                               continue;
+                       n = os_realloc(freqs, (count + 2) * sizeof(int));
+                       if (n == NULL)
+                               continue;
+
+                       freqs = n;
+                       freqs[count] = modes[i].channels[j].freq;
+                       count++;
+                       freqs[count] = 0;
+               }
+       }
+
+       return freqs;
+}
+
+
+static void * bgscan_learn_init(struct wpa_supplicant *wpa_s,
+                               const char *params,
+                               const struct wpa_ssid *ssid)
+{
+       struct bgscan_learn_data *data;
+
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+       dl_list_init(&data->bss);
+       data->wpa_s = wpa_s;
+       data->ssid = ssid;
+       if (bgscan_learn_get_params(data, params) < 0) {
+               os_free(data->fname);
+               os_free(data);
+               return NULL;
+       }
+       if (data->short_interval <= 0)
+               data->short_interval = 30;
+       if (data->long_interval <= 0)
+               data->long_interval = 30;
+
+       if (bgscan_learn_load(data) < 0) {
+               os_free(data->fname);
+               os_free(data);
+               return NULL;
+       }
+
+       wpa_printf(MSG_DEBUG, "bgscan learn: Signal strength threshold %d  "
+                  "Short bgscan interval %d  Long bgscan interval %d",
+                  data->signal_threshold, data->short_interval,
+                  data->long_interval);
+
+       if (data->signal_threshold &&
+           wpa_drv_signal_monitor(wpa_s, data->signal_threshold, 4) < 0) {
+               wpa_printf(MSG_ERROR, "bgscan learn: Failed to enable "
+                          "signal strength monitoring");
+       }
+
+       data->supp_freqs = bgscan_learn_get_supp_freqs(wpa_s);
+       data->scan_interval = data->short_interval;
+       eloop_register_timeout(data->scan_interval, 0, bgscan_learn_timeout,
+                              data, NULL);
+
+       /*
+        * This function is called immediately after an association, so it is
+        * reasonable to assume that a scan was completed recently. This makes
+        * us skip an immediate new scan in cases where the current signal
+        * level is below the bgscan threshold.
+        */
+       os_get_time(&data->last_bgscan);
+
+       return data;
+}
+
+
+static void bgscan_learn_deinit(void *priv)
+{
+       struct bgscan_learn_data *data = priv;
+       struct bgscan_learn_bss *bss, *n;
+
+       bgscan_learn_save(data);
+       eloop_cancel_timeout(bgscan_learn_timeout, data, NULL);
+       if (data->signal_threshold)
+               wpa_drv_signal_monitor(data->wpa_s, 0, 0);
+       os_free(data->fname);
+       dl_list_for_each_safe(bss, n, &data->bss, struct bgscan_learn_bss,
+                             list) {
+               dl_list_del(&bss->list);
+               bss_free(bss);
+       }
+       os_free(data->supp_freqs);
+       os_free(data);
+}
+
+
+static int bgscan_learn_bss_match(struct bgscan_learn_data *data,
+                                 struct wpa_scan_res *bss)
+{
+       const u8 *ie;
+
+       ie = wpa_scan_get_ie(bss, WLAN_EID_SSID);
+       if (ie == NULL)
+               return 0;
+
+       if (data->ssid->ssid_len != ie[1] ||
+           os_memcmp(data->ssid->ssid, ie + 2, ie[1]) != 0)
+               return 0; /* SSID mismatch */
+
+       return 1;
+}
+
+
+static int bgscan_learn_notify_scan(void *priv,
+                                   struct wpa_scan_results *scan_res)
+{
+       struct bgscan_learn_data *data = priv;
+       size_t i, j;
+#define MAX_BSS 50
+       u8 bssid[MAX_BSS * ETH_ALEN];
+       size_t num_bssid = 0;
+
+       wpa_printf(MSG_DEBUG, "bgscan learn: scan result notification");
+
+       eloop_cancel_timeout(bgscan_learn_timeout, data, NULL);
+       eloop_register_timeout(data->scan_interval, 0, bgscan_learn_timeout,
+                              data, NULL);
+
+       for (i = 0; i < scan_res->num; i++) {
+               struct wpa_scan_res *res = scan_res->res[i];
+               if (!bgscan_learn_bss_match(data, res))
+                       continue;
+
+               if (num_bssid < MAX_BSS) {
+                       os_memcpy(bssid + num_bssid * ETH_ALEN, res->bssid,
+                                 ETH_ALEN);
+                       num_bssid++;
+               }
+       }
+       wpa_printf(MSG_DEBUG, "bgscan learn: %u matching BSSes in scan "
+                  "results", (unsigned int) num_bssid);
+
+       for (i = 0; i < scan_res->num; i++) {
+               struct wpa_scan_res *res = scan_res->res[i];
+               struct bgscan_learn_bss *bss;
+
+               if (!bgscan_learn_bss_match(data, res))
+                       continue;
+
+               bss = bgscan_learn_get_bss(data, res->bssid);
+               if (bss && bss->freq != res->freq) {
+                       wpa_printf(MSG_DEBUG, "bgscan learn: Update BSS "
+                          MACSTR " freq %d -> %d",
+                                  MAC2STR(res->bssid), bss->freq, res->freq);
+                       bss->freq = res->freq;
+               } else if (!bss) {
+                       wpa_printf(MSG_DEBUG, "bgscan learn: Add BSS " MACSTR
+                                  " freq=%d", MAC2STR(res->bssid), res->freq);
+                       bss = os_zalloc(sizeof(*bss));
+                       if (!bss)
+                               continue;
+                       os_memcpy(bss->bssid, res->bssid, ETH_ALEN);
+                       bss->freq = res->freq;
+                       dl_list_add(&data->bss, &bss->list);
+               }
+
+               for (j = 0; j < num_bssid; j++) {
+                       u8 *addr = bssid + j * ETH_ALEN;
+                       bgscan_learn_add_neighbor(bss, addr);
+               }
+       }
+
+       /*
+        * A more advanced bgscan could process scan results internally, select
+        * the BSS and request roam if needed. This sample uses the existing
+        * BSS/ESS selection routine. Change this to return 1 if selection is
+        * done inside the bgscan module.
+        */
+
+       return 0;
+}
+
+
+static void bgscan_learn_notify_beacon_loss(void *priv)
+{
+       wpa_printf(MSG_DEBUG, "bgscan learn: beacon loss");
+       /* TODO: speed up background scanning */
+}
+
+
+static void bgscan_learn_notify_signal_change(void *priv, int above,
+                                             int current_signal,
+                                             int current_noise,
+                                             int current_txrate)
+{
+       struct bgscan_learn_data *data = priv;
+       int scan = 0;
+       struct os_time now;
+
+       if (data->short_interval == data->long_interval ||
+           data->signal_threshold == 0)
+               return;
+
+       wpa_printf(MSG_DEBUG, "bgscan learn: signal level changed "
+                  "(above=%d current_signal=%d current_noise=%d "
+                  "current_txrate=%d)", above, current_signal,
+                  current_noise, current_txrate);
+       if (data->scan_interval == data->long_interval && !above) {
+               wpa_printf(MSG_DEBUG, "bgscan learn: Start using short bgscan "
+                          "interval");
+               data->scan_interval = data->short_interval;
+               os_get_time(&now);
+               if (now.sec > data->last_bgscan.sec + 1)
+                       scan = 1;
+       } else if (data->scan_interval == data->short_interval && above) {
+               wpa_printf(MSG_DEBUG, "bgscan learn: Start using long bgscan "
+                          "interval");
+               data->scan_interval = data->long_interval;
+               eloop_cancel_timeout(bgscan_learn_timeout, data, NULL);
+               eloop_register_timeout(data->scan_interval, 0,
+                                      bgscan_learn_timeout, data, NULL);
+       } else if (!above) {
+               /*
+                * Signal dropped further 4 dB. Request a new scan if we have
+                * not yet scanned in a while.
+                */
+               os_get_time(&now);
+               if (now.sec > data->last_bgscan.sec + 10)
+                       scan = 1;
+       }
+
+       if (scan) {
+               wpa_printf(MSG_DEBUG, "bgscan learn: Trigger immediate scan");
+               eloop_cancel_timeout(bgscan_learn_timeout, data, NULL);
+               eloop_register_timeout(0, 0, bgscan_learn_timeout, data, NULL);
+       }
+}
+
+
+const struct bgscan_ops bgscan_learn_ops = {
+       .name = "learn",
+       .init = bgscan_learn_init,
+       .deinit = bgscan_learn_deinit,
+       .notify_scan = bgscan_learn_notify_scan,
+       .notify_beacon_loss = bgscan_learn_notify_beacon_loss,
+       .notify_signal_change = bgscan_learn_notify_signal_change,
+};
index 8e80b12..eedc961 100644 (file)
@@ -28,6 +28,7 @@ struct bgscan_simple_data {
        const struct wpa_ssid *ssid;
        int scan_interval;
        int signal_threshold;
+       int short_scan_count; /* counter for scans using short scan interval */
        int short_interval; /* use if signal < threshold */
        int long_interval; /* use if signal > threshold */
        struct os_time last_bgscan;
@@ -57,8 +58,23 @@ static void bgscan_simple_timeout(void *eloop_ctx, void *timeout_ctx)
                wpa_printf(MSG_DEBUG, "bgscan simple: Failed to trigger scan");
                eloop_register_timeout(data->scan_interval, 0,
                                       bgscan_simple_timeout, data, NULL);
-       } else
+       } else {
+               if (data->scan_interval == data->short_interval) {
+                       data->short_scan_count++;
+                       /*
+                        * Spend at most the duration of a long scan interval
+                        * scanning at the short scan interval. After that,
+                        * revert to the long scan interval.
+                        */
+                       if (data->short_scan_count >
+                           data->long_interval / data->short_interval + 1) {
+                               data->scan_interval = data->long_interval;
+                               wpa_printf(MSG_DEBUG, "bgscan simple: Backing "
+                                          "off to long scan interval");
+                       }
+               }
                os_get_time(&data->last_bgscan);
+       }
 }
 
 
@@ -122,6 +138,15 @@ static void * bgscan_simple_init(struct wpa_supplicant *wpa_s,
        }
 
        data->scan_interval = data->short_interval;
+       if (data->signal_threshold) {
+               /* Poll for signal info to set initial scan interval */
+               struct wpa_signal_info siginfo;
+               if (wpa_drv_signal_poll(wpa_s, &siginfo) == 0 &&
+                   siginfo.current_signal >= data->signal_threshold)
+                       data->scan_interval = data->long_interval;
+       }
+       wpa_printf(MSG_DEBUG, "bgscan simple: Init scan interval: %d",
+                  data->scan_interval);
        eloop_register_timeout(data->scan_interval, 0, bgscan_simple_timeout,
                               data, NULL);
 
@@ -147,7 +172,8 @@ static void bgscan_simple_deinit(void *priv)
 }
 
 
-static int bgscan_simple_notify_scan(void *priv)
+static int bgscan_simple_notify_scan(void *priv,
+                                    struct wpa_scan_results *scan_res)
 {
        struct bgscan_simple_data *data = priv;
 
@@ -175,7 +201,10 @@ static void bgscan_simple_notify_beacon_loss(void *priv)
 }
 
 
-static void bgscan_simple_notify_signal_change(void *priv, int above)
+static void bgscan_simple_notify_signal_change(void *priv, int above,
+                                              int current_signal,
+                                              int current_noise,
+                                              int current_txrate)
 {
        struct bgscan_simple_data *data = priv;
        int scan = 0;
@@ -186,14 +215,29 @@ static void bgscan_simple_notify_signal_change(void *priv, int above)
                return;
 
        wpa_printf(MSG_DEBUG, "bgscan simple: signal level changed "
-                  "(above=%d)", above);
+                  "(above=%d current_signal=%d current_noise=%d "
+                  "current_txrate=%d))", above, current_signal,
+                  current_noise, current_txrate);
        if (data->scan_interval == data->long_interval && !above) {
                wpa_printf(MSG_DEBUG, "bgscan simple: Start using short "
                           "bgscan interval");
                data->scan_interval = data->short_interval;
+               data->short_scan_count = 0;
                os_get_time(&now);
                if (now.sec > data->last_bgscan.sec + 1)
                        scan = 1;
+               else if (data->last_bgscan.sec + data->long_interval >
+                        now.sec + data->scan_interval) {
+                       /*
+                        * Restart scan interval timer if currently scheduled
+                        * scan is too far in the future.
+                        */
+                       eloop_cancel_timeout(bgscan_simple_timeout, data,
+                                            NULL);
+                       eloop_register_timeout(data->scan_interval, 0,
+                                              bgscan_simple_timeout, data,
+                                              NULL);
+               }
        } else if (data->scan_interval == data->short_interval && above) {
                wpa_printf(MSG_DEBUG, "bgscan simple: Start using long bgscan "
                           "interval");
index 4ffb220..8f12ac9 100644 (file)
@@ -44,7 +44,7 @@ struct wpa_blacklist * wpa_blacklist_get(struct wpa_supplicant *wpa_s,
  * wpa_blacklist_add - Add an BSSID to the blacklist
  * @wpa_s: Pointer to wpa_supplicant data
  * @bssid: BSSID to be added to the blacklist
- * Returns: 0 on success, -1 on failure
+ * Returns: Current blacklist count on success, -1 on failure
  *
  * This function adds the specified BSSID to the blacklist or increases the
  * blacklist count if the BSSID was already listed. It should be called when
@@ -66,7 +66,7 @@ int wpa_blacklist_add(struct wpa_supplicant *wpa_s, const u8 *bssid)
                wpa_printf(MSG_DEBUG, "BSSID " MACSTR " blacklist count "
                           "incremented to %d",
                           MAC2STR(bssid), e->count);
-               return 0;
+               return e->count;
        }
 
        e = os_zalloc(sizeof(*e));
@@ -79,7 +79,7 @@ int wpa_blacklist_add(struct wpa_supplicant *wpa_s, const u8 *bssid)
        wpa_printf(MSG_DEBUG, "Added BSSID " MACSTR " into blacklist",
                   MAC2STR(bssid));
 
-       return 0;
+       return e->count;
 }
 
 
index e2ac230..1b27440 100644 (file)
  */
 #define WPA_BSS_EXPIRATION_PERIOD 10
 
-/**
- * WPA_BSS_EXPIRATION_AGE - BSS entry age after which it can be expired
- *
- * This value control the time in seconds after which a BSS entry gets removed
- * if it has not been updated or is not in use.
- */
-#define WPA_BSS_EXPIRATION_AGE 180
-
-/**
- * WPA_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.
- * Value 1 means that the entry is removed after the first scan without the
- * BSSID being 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).
- */
-#define WPA_BSS_EXPIRATION_SCAN_COUNT 2
-
 #define WPA_BSS_FREQ_CHANGED_FLAG      BIT(0)
 #define WPA_BSS_SIGNAL_CHANGED_FLAG    BIT(1)
 #define WPA_BSS_PRIVACY_CHANGED_FLAG   BIT(2)
@@ -65,10 +46,19 @@ static void wpa_bss_remove(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
        dl_list_del(&bss->list);
        dl_list_del(&bss->list_id);
        wpa_s->num_bss--;
-       wpa_printf(MSG_DEBUG, "BSS: Remove id %u BSSID " MACSTR " SSID '%s'",
-                  bss->id, MAC2STR(bss->bssid),
-                  wpa_ssid_txt(bss->ssid, bss->ssid_len));
+       wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Remove id %u BSSID " MACSTR
+               " SSID '%s'", bss->id, MAC2STR(bss->bssid),
+               wpa_ssid_txt(bss->ssid, bss->ssid_len));
        wpas_notify_bss_removed(wpa_s, bss->bssid, bss->id);
+#ifdef CONFIG_INTERWORKING
+       wpabuf_free(bss->anqp_venue_name);
+       wpabuf_free(bss->anqp_network_auth_type);
+       wpabuf_free(bss->anqp_roaming_consortium);
+       wpabuf_free(bss->anqp_ip_addr_type_availability);
+       wpabuf_free(bss->anqp_nai_realm);
+       wpabuf_free(bss->anqp_3gpp);
+       wpabuf_free(bss->anqp_domain_name);
+#endif /* CONFIG_INTERWORKING */
        os_free(bss);
 }
 
@@ -112,6 +102,55 @@ static void wpa_bss_copy_res(struct wpa_bss *dst, struct wpa_scan_res *src)
 }
 
 
+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_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);
+                       return 0;
+               }
+       }
+
+       return -1;
+}
+
+
+static void wpa_bss_remove_oldest(struct wpa_supplicant *wpa_s)
+{
+       /*
+        * Remove the oldest entry that does not match with any configured
+        * network.
+        */
+       if (wpa_bss_remove_oldest_unknown(wpa_s) == 0)
+               return;
+
+       /*
+        * Remove the oldest entry since no better candidate for removal was
+        * found.
+        */
+       wpa_bss_remove(wpa_s, dl_list_first(&wpa_s->bss,
+                                           struct wpa_bss, list));
+}
+
+
 static void wpa_bss_add(struct wpa_supplicant *wpa_s,
                        const u8 *ssid, size_t ssid_len,
                        struct wpa_scan_res *res)
@@ -133,14 +172,12 @@ static void wpa_bss_add(struct wpa_supplicant *wpa_s,
        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_printf(MSG_DEBUG, "BSS: Add new id %u BSSID " MACSTR " SSID '%s'",
-                  bss->id, MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len));
+       wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Add new id %u BSSID " MACSTR
+               " SSID '%s'",
+               bss->id, MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len));
        wpas_notify_bss_added(wpa_s, bss->bssid, bss->id);
-       if (wpa_s->num_bss > wpa_s->conf->bss_max_count) {
-               /* Remove the oldest entry */
-               wpa_bss_remove(wpa_s, dl_list_first(&wpa_s->bss,
-                                                   struct wpa_bss, list));
-       }
+       if (wpa_s->num_bss > wpa_s->conf->bss_max_count)
+               wpa_bss_remove_oldest(wpa_s);
 }
 
 
@@ -187,8 +224,11 @@ static int are_ies_equal(const struct wpa_bss *old,
                new_ie_len = new_ie ? new_ie[1] + 2 : 0;
        }
 
-       ret = (old_ie_len == new_ie_len &&
-              os_memcmp(old_ie, new_ie, old_ie_len) == 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);
@@ -292,6 +332,8 @@ static void wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
                nbss = os_realloc(bss, sizeof(*bss) + res->ie_len +
                                  res->beacon_ie_len);
                if (nbss) {
+                       if (wpa_s->current_bss == bss)
+                               wpa_s->current_bss = nbss;
                        bss = nbss;
                        os_memcpy(bss + 1, res + 1,
                                  res->ie_len + res->beacon_ie_len);
@@ -317,29 +359,34 @@ static int wpa_bss_in_use(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
 void wpa_bss_update_start(struct wpa_supplicant *wpa_s)
 {
        wpa_s->bss_update_idx++;
-       wpa_printf(MSG_DEBUG, "BSS: Start scan result update %u",
-                  wpa_s->bss_update_idx);
+       wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Start scan result update %u",
+               wpa_s->bss_update_idx);
 }
 
 
 void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s,
                             struct wpa_scan_res *res)
 {
-       const u8 *ssid;
+       const u8 *ssid, *p2p;
        struct wpa_bss *bss;
 
        ssid = wpa_scan_get_ie(res, WLAN_EID_SSID);
        if (ssid == NULL) {
-               wpa_printf(MSG_DEBUG, "BSS: No SSID IE included for " MACSTR,
-                          MAC2STR(res->bssid));
+               wpa_dbg(wpa_s, MSG_DEBUG, "BSS: No SSID IE included for "
+                       MACSTR, MAC2STR(res->bssid));
                return;
        }
        if (ssid[1] > 32) {
-               wpa_printf(MSG_DEBUG, "BSS: Too long SSID IE included for "
-                          MACSTR, MAC2STR(res->bssid));
+               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);
+       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) */
        bss = wpa_bss_get(wpa_s, res->bssid, ssid + 2, ssid[1]);
@@ -406,18 +453,18 @@ void wpa_bss_update_end(struct wpa_supplicant *wpa_s, struct scan_info *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_BSS_EXPIRATION_SCAN_COUNT) {
-                       wpa_printf(MSG_DEBUG, "BSS: Expire BSS %u due to no "
-                                  "match in scan", bss->id);
+               if (bss->scan_miss_count >=
+                   wpa_s->conf->bss_expiration_scan_count) {
+                       wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Expire BSS %u due to "
+                               "no match in scan", bss->id);
                        wpa_bss_remove(wpa_s, bss);
                }
        }
 }
 
 
-static void wpa_bss_timeout(void *eloop_ctx, void *timeout_ctx)
+void wpa_bss_flush_by_age(struct wpa_supplicant *wpa_s, int age)
 {
-       struct wpa_supplicant *wpa_s = eloop_ctx;
        struct wpa_bss *bss, *n;
        struct os_time t;
 
@@ -425,19 +472,27 @@ static void wpa_bss_timeout(void *eloop_ctx, void *timeout_ctx)
                return;
 
        os_get_time(&t);
-       t.sec -= WPA_BSS_EXPIRATION_AGE;
+       t.sec -= age;
 
        dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) {
                if (wpa_bss_in_use(wpa_s, bss))
                        continue;
 
                if (os_time_before(&bss->last_update, &t)) {
-                       wpa_printf(MSG_DEBUG, "BSS: Expire BSS %u due to age",
-                                  bss->id);
+                       wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Expire BSS %u due to "
+                               "age", bss->id);
                        wpa_bss_remove(wpa_s, bss);
                } else
                        break;
        }
+}
+
+
+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);
 }
@@ -453,14 +508,25 @@ 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)
 {
        struct wpa_bss *bss, *n;
-       eloop_cancel_timeout(wpa_bss_timeout, wpa_s, NULL);
+
        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)
+
+       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);
+       }
+}
+
+
+void wpa_bss_deinit(struct wpa_supplicant *wpa_s)
+{
+       eloop_cancel_timeout(wpa_bss_timeout, wpa_s, NULL);
+       wpa_bss_flush(wpa_s);
 }
 
 
@@ -468,7 +534,7 @@ struct wpa_bss * wpa_bss_get_bssid(struct wpa_supplicant *wpa_s,
                                   const u8 *bssid)
 {
        struct wpa_bss *bss;
-       dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+       dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) {
                if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0)
                        return bss;
        }
@@ -476,6 +542,23 @@ struct wpa_bss * wpa_bss_get_bssid(struct wpa_supplicant *wpa_s,
 }
 
 
+#ifdef CONFIG_P2P
+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 */
+
+
 struct wpa_bss * wpa_bss_get_id(struct wpa_supplicant *wpa_s, unsigned int id)
 {
        struct wpa_bss *bss;
index 1de4722..9f13298 100644 (file)
@@ -23,6 +23,7 @@ struct wpa_scan_res;
 #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 - BSS table
@@ -65,6 +66,15 @@ struct wpa_bss {
        int level;
        u64 tsf;
        struct os_time last_update;
+#ifdef CONFIG_INTERWORKING
+       struct wpabuf *anqp_venue_name;
+       struct wpabuf *anqp_network_auth_type;
+       struct wpabuf *anqp_roaming_consortium;
+       struct wpabuf *anqp_ip_addr_type_availability;
+       struct wpabuf *anqp_nai_realm;
+       struct wpabuf *anqp_3gpp;
+       struct wpabuf *anqp_domain_name;
+#endif /* CONFIG_INTERWORKING */
        size_t ie_len;
        size_t beacon_ie_len;
        /* followed by ie_len octets of IEs */
@@ -78,10 +88,14 @@ 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_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);
 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);
index 7e2a5b4..600e32b 100644 (file)
 #include "includes.h"
 
 #include "common.h"
+#include "utils/uuid.h"
 #include "crypto/sha1.h"
 #include "rsn_supp/wpa.h"
 #include "eap_peer/eap.h"
+#include "p2p/p2p.h"
 #include "config.h"
 
 
@@ -283,6 +285,12 @@ 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);
@@ -1109,6 +1117,7 @@ static int wpa_config_parse_eap(const struct parse_data *data,
 
        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;
 }
@@ -1344,6 +1353,90 @@ static char * wpa_config_write_wep_key3(const struct parse_data *data,
 #endif /* NO_CONFIG_WRITE */
 
 
+#ifdef CONFIG_P2P
+
+static int wpa_config_parse_p2p_client_list(const struct parse_data *data,
+                                           struct wpa_ssid *ssid, int line,
+                                           const char *value)
+{
+       const char *pos;
+       u8 *buf, *n, addr[ETH_ALEN];
+       size_t count;
+
+       buf = NULL;
+       count = 0;
+
+       pos = value;
+       while (pos && *pos) {
+               while (*pos == ' ')
+                       pos++;
+
+               if (hwaddr_aton(pos, addr)) {
+                       wpa_printf(MSG_ERROR, "Line %d: Invalid "
+                                  "p2p_client_list address '%s'.",
+                                  line, value);
+                       /* continue anyway */
+               } else {
+                       n = os_realloc(buf, (count + 1) * ETH_ALEN);
+                       if (n == NULL) {
+                               os_free(buf);
+                               return -1;
+                       }
+                       buf = n;
+                       os_memcpy(buf + count * ETH_ALEN, addr, ETH_ALEN);
+                       count++;
+                       wpa_hexdump(MSG_MSGDUMP, "p2p_client_list",
+                                   addr, ETH_ALEN);
+               }
+
+               pos = os_strchr(pos, ' ');
+       }
+
+       os_free(ssid->p2p_client_list);
+       ssid->p2p_client_list = buf;
+       ssid->num_p2p_clients = count;
+
+       return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_p2p_client_list(const struct parse_data *data,
+                                              struct wpa_ssid *ssid)
+{
+       char *value, *end, *pos;
+       int res;
+       size_t i;
+
+       if (ssid->p2p_client_list == NULL || ssid->num_p2p_clients == 0)
+               return NULL;
+
+       value = os_malloc(20 * ssid->num_p2p_clients);
+       if (value == NULL)
+               return NULL;
+       pos = value;
+       end = value + 20 * ssid->num_p2p_clients;
+
+       for (i = 0; i < ssid->num_p2p_clients; i++) {
+               res = os_snprintf(pos, end - pos, MACSTR " ",
+                                 MAC2STR(ssid->p2p_client_list +
+                                         i * ETH_ALEN));
+               if (res < 0 || res >= end - pos) {
+                       os_free(value);
+                       return NULL;
+               }
+               pos += res;
+       }
+
+       if (pos > value)
+               pos[-1] = '\0';
+
+       return value;
+}
+#endif /* NO_CONFIG_WRITE */
+
+#endif /* CONFIG_P2P */
+
 /* Helper macros for network block parser */
 
 #ifdef OFFSET
@@ -1492,9 +1585,9 @@ static const struct parse_data ssid_fields[] = {
        { STRe(pac_file) },
        { INTe(fragment_size) },
 #endif /* IEEE8021X_EAPOL */
-       { INT_RANGE(mode, 0, 2) },
+       { INT_RANGE(mode, 0, 4) },
        { INT_RANGE(proactive_key_caching, 0, 1) },
-       { INT_RANGE(disabled, 0, 1) },
+       { INT_RANGE(disabled, 0, 2) },
        { STR(id_str) },
 #ifdef CONFIG_IEEE80211W
        { INT_RANGE(ieee80211w, 0, 2) },
@@ -1504,6 +1597,9 @@ static const struct parse_data ssid_fields[] = {
        { INT_RANGE(frequency, 0, 10000) },
        { INT(wpa_ptk_rekey) },
        { STR(bgscan) },
+#ifdef CONFIG_P2P
+       { FUNC(p2p_client_list) },
+#endif /* CONFIG_P2P */
 };
 
 #undef OFFSET
@@ -1670,6 +1766,7 @@ void wpa_config_free_ssid(struct wpa_ssid *ssid)
        os_free(ssid->scan_freq);
        os_free(ssid->freq_list);
        os_free(ssid->bgscan);
+       os_free(ssid->p2p_client_list);
        os_free(ssid);
 }
 
@@ -1687,6 +1784,7 @@ void wpa_config_free(struct wpa_config *config)
        struct wpa_config_blob *blob, *prevblob;
 #endif /* CONFIG_NO_CONFIG_BLOBS */
        struct wpa_ssid *ssid, *prev = NULL;
+
        ssid = config->ssid;
        while (ssid) {
                prev = ssid;
@@ -1715,14 +1813,46 @@ void wpa_config_free(struct wpa_config *config)
        os_free(config->model_name);
        os_free(config->model_number);
        os_free(config->serial_number);
-       os_free(config->device_type);
        os_free(config->config_methods);
+       os_free(config->p2p_ssid_postfix);
        os_free(config->pssid);
+       os_free(config->home_realm);
+       os_free(config->home_username);
+       os_free(config->home_password);
+       os_free(config->home_ca_cert);
+       os_free(config->home_imsi);
+       os_free(config->home_milenage);
+       os_free(config->p2p_pref_chan);
        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
@@ -1876,10 +2006,32 @@ int wpa_config_set(struct wpa_ssid *ssid, const char *var, const char *value,
 }
 
 
+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]
  *
@@ -1895,6 +2047,8 @@ char ** wpa_config_get_all(struct wpa_ssid *ssid, int get_keys)
        char **props;
        int fields_num;
 
+       get_keys = get_keys && ssid->export_keys;
+
        props = os_zalloc(sizeof(char *) * ((2 * NUM_SSID_FIELDS) + 1));
        if (!props)
                return NULL;
@@ -2131,7 +2285,13 @@ struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface,
        config->eapol_version = DEFAULT_EAPOL_VERSION;
        config->ap_scan = DEFAULT_AP_SCAN;
        config->fast_reauth = DEFAULT_FAST_REAUTH;
+       config->p2p_go_intent = DEFAULT_P2P_GO_INTENT;
+       config->p2p_intra_bss = DEFAULT_P2P_INTRA_BSS;
        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;
 
        if (ctrl_interface)
                config->ctrl_interface = os_strdup(ctrl_interface);
@@ -2165,3 +2325,358 @@ void wpa_config_debug_dump_networks(struct wpa_config *config)
        }
 }
 #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);
+       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 *dst;
+       dst = (int *) (((u8 *) config) + (long) data->param1);
+       *dst = atoi(pos);
+       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_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;
+}
+
+#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(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;
+}
+#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;
+}
+
+
+#ifdef OFFSET
+#undef OFFSET
+#endif /* OFFSET */
+/* OFFSET: Get offset of a variable within the wpa_config structure */
+#define OFFSET(v) ((void *) &((struct wpa_config *) 0)->v)
+
+#define FUNC(f) #f, wpa_config_process_ ## f, OFFSET(f), NULL, NULL
+#define FUNC_NO_VAR(f) #f, wpa_config_process_ ## f, NULL, NULL, NULL
+#define _INT(f) #f, wpa_global_config_parse_int, OFFSET(f)
+#define INT(f) _INT(f), NULL, NULL
+#define INT_RANGE(f, min, max) _INT(f), (void *) min, (void *) max
+#define _STR(f) #f, wpa_global_config_parse_str, OFFSET(f)
+#define STR(f) _STR(f), NULL, NULL
+#define STR_RANGE(f, min, max) _STR(f), (void *) min, (void *) max
+
+static const struct global_parse_data global_fields[] = {
+#ifdef CONFIG_CTRL_IFACE
+       { STR(ctrl_interface), 0 },
+       { STR(ctrl_interface_group), 0 } /* deprecated */,
+#endif /* CONFIG_CTRL_IFACE */
+       { INT_RANGE(eapol_version, 1, 2), 0 },
+       { INT(ap_scan), 0 },
+       { INT(fast_reauth), 0 },
+       { STR(opensc_engine_path), 0 },
+       { STR(pkcs11_engine_path), 0 },
+       { STR(pkcs11_module_path), 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 },
+       { STR_RANGE(device_name, 0, 32), 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 },
+#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+       { FUNC(sec_device_type), CFG_CHANGED_SEC_DEVICE_TYPE },
+       { INT(p2p_listen_reg_class), 0 },
+       { INT(p2p_listen_channel), 0 },
+       { INT(p2p_oper_reg_class), 0 },
+       { INT(p2p_oper_channel), 0 },
+       { INT_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 },
+       { FUNC(p2p_pref_chan), CFG_CHANGED_P2P_PREF_CHAN },
+#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(max_num_sta), 0 },
+       { INT_RANGE(disassoc_low_ack, 0, 1), 0 },
+       { STR(home_realm), 0 },
+       { STR(home_username), 0 },
+       { STR(home_password), 0 },
+       { STR(home_ca_cert), 0 },
+       { STR(home_imsi), 0 },
+       { STR(home_milenage), 0 },
+       { INT_RANGE(interworking, 0, 1), 0 },
+       { FUNC(hessid), 0 },
+       { INT_RANGE(access_network_type, 0, 15), 0 }
+};
+
+#undef FUNC
+#undef _INT
+#undef INT
+#undef INT_RANGE
+#undef _STR
+#undef STR
+#undef STR_RANGE
+#define NUM_GLOBAL_FIELDS (sizeof(global_fields) / sizeof(global_fields[0]))
+
+
+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;
+               }
+               config->changed_parameters |= field->changed_flag;
+               break;
+       }
+       if (i == NUM_GLOBAL_FIELDS) {
+               if (line < 0)
+                       return -1;
+               wpa_printf(MSG_ERROR, "Line %d: unknown global field '%s'.",
+                          line, pos);
+               ret = -1;
+       }
+
+       return ret;
+}
index 754e4be..18317f5 100644 (file)
 #define DEFAULT_AP_SCAN 1
 #endif /* CONFIG_NO_SCAN_PROCESSING */
 #define DEFAULT_FAST_REAUTH 1
+#define DEFAULT_P2P_GO_INTENT 7
+#define DEFAULT_P2P_INTRA_BSS 1
 #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
 
 #include "config_ssid.h"
-
+#include "wps/wps.h"
+
+
+#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)
 
 /**
  * struct wpa_config - wpa_supplicant configuration data
@@ -103,7 +125,7 @@ struct wpa_config {
         * 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 existance of this parameter
+        * mechanism is used. For all cases, the existence of this parameter
         * in configuration is used to determine whether the control interface
         * is enabled.
         *
@@ -285,26 +307,19 @@ struct wpa_config {
 
        /**
         * device_type - Primary Device Type (WPS)
-        * 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)
         */
-       char *device_type;
+       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 display push_button keypad".
+        * 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.
+        * int_nfc_token nfc_interface push_button keypad
+        * virtual_display physical_display
+        * virtual_push_button physical_push_button.
         */
        char *config_methods;
 
@@ -333,18 +348,145 @@ struct wpa_config {
         */
        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;
+
+#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.
+        */
+       unsigned int p2p_group_idle;
+
        /**
         * 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;
+
+       /**
+        * max_num_sta - Maximum number of STAs in an AP/P2P GO
+        */
+       unsigned int max_num_sta;
+
+       /**
+        * 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];
+
+       /**
+        * home_realm - Home Realm for Interworking
+        */
+       char *home_realm;
+
+       /**
+        * home_username - Username for Interworking network selection
+        */
+       char *home_username;
+
+       /**
+        * home_password - Password for Interworking network selection
+        */
+       char *home_password;
+
+       /**
+        * home_ca_cert - CA certificate for Interworking network selection
+        */
+       char *home_ca_cert;
+
+       /**
+        * home_imsi - IMSI in <MCC> | <MNC> | '-' | <MSIN> format
+        */
+       char *home_imsi;
+
+       /**
+        * home_milenage - Milenage parameters for SIM/USIM simulator in
+        *      <Ki>:<OPc>:<SQN> format
+        */
+       char *home_milenage;
 };
 
 
@@ -352,12 +494,17 @@ struct wpa_config {
 
 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);
 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);
@@ -381,6 +528,10 @@ void wpa_config_debug_dump_networks(struct wpa_config *config);
 #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 */
 
 /**
index 5f07045..c1075e3 100644 (file)
@@ -22,7 +22,7 @@
 #include "config.h"
 #include "base64.h"
 #include "uuid.h"
-#include "eap_peer/eap_methods.h"
+#include "p2p/p2p.h"
 
 
 /**
@@ -270,238 +270,6 @@ static int wpa_config_process_blob(struct wpa_config *config, FILE *f,
 #endif /* CONFIG_NO_CONFIG_BLOBS */
 
 
-struct global_parse_data {
-       char *name;
-       int (*parser)(const struct global_parse_data *data,
-                     struct wpa_config *config, int line, const char *value);
-       void *param1, *param2, *param3;
-};
-
-
-static int wpa_config_parse_int(const struct global_parse_data *data,
-                               struct wpa_config *config, int line,
-                               const char *pos)
-{
-       int *dst;
-       dst = (int *) (((u8 *) config) + (long) data->param1);
-       *dst = atoi(pos);
-       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_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_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_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;
-}
-
-#endif /* CONFIG_WPS */
-
-
-#ifdef OFFSET
-#undef OFFSET
-#endif /* OFFSET */
-/* OFFSET: Get offset of a variable within the wpa_config structure */
-#define OFFSET(v) ((void *) &((struct wpa_config *) 0)->v)
-
-#define FUNC(f) #f, wpa_config_process_ ## f, OFFSET(f), NULL, NULL
-#define FUNC_NO_VAR(f) #f, wpa_config_process_ ## f, NULL, NULL, NULL
-#define _INT(f) #f, wpa_config_parse_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_config_parse_str, OFFSET(f)
-#define STR(f) _STR(f), NULL, NULL
-#define STR_RANGE(f, min, max) _STR(f), (void *) min, (void *) max
-
-static const struct global_parse_data global_fields[] = {
-#ifdef CONFIG_CTRL_IFACE
-       { STR(ctrl_interface) },
-       { STR(ctrl_interface_group) } /* deprecated */,
-#endif /* CONFIG_CTRL_IFACE */
-       { INT_RANGE(eapol_version, 1, 2) },
-       { INT(ap_scan) },
-       { INT(fast_reauth) },
-       { STR(opensc_engine_path) },
-       { STR(pkcs11_engine_path) },
-       { STR(pkcs11_module_path) },
-       { STR(driver_param) },
-       { INT(dot11RSNAConfigPMKLifetime) },
-       { INT(dot11RSNAConfigPMKReauthThreshold) },
-       { INT(dot11RSNAConfigSATimeout) },
-#ifndef CONFIG_NO_CONFIG_WRITE
-       { INT(update_config) },
-#endif /* CONFIG_NO_CONFIG_WRITE */
-       { FUNC_NO_VAR(load_dynamic_eap) },
-#ifdef CONFIG_WPS
-       { FUNC(uuid) },
-       { STR_RANGE(device_name, 0, 32) },
-       { STR_RANGE(manufacturer, 0, 64) },
-       { STR_RANGE(model_name, 0, 32) },
-       { STR_RANGE(model_number, 0, 32) },
-       { STR_RANGE(serial_number, 0, 32) },
-       { STR(device_type) },
-       { FUNC(os_version) },
-       { STR(config_methods) },
-       { INT_RANGE(wps_cred_processing, 0, 2) },
-#endif /* CONFIG_WPS */
-       { FUNC(country) },
-       { INT(bss_max_count) },
-       { INT_RANGE(filter_ssids, 0, 1) }
-};
-
-#undef FUNC
-#undef _INT
-#undef INT
-#undef INT_RANGE
-#undef _STR
-#undef STR
-#undef STR_RANGE
-#define NUM_GLOBAL_FIELDS (sizeof(global_fields) / sizeof(global_fields[0]))
-
-
-static 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;
-               }
-               break;
-       }
-       if (i == NUM_GLOBAL_FIELDS) {
-               wpa_printf(MSG_ERROR, "Line %d: unknown global field '%s'.",
-                          line, pos);
-               ret = -1;
-       }
-
-       return ret;
-}
-
-
 struct wpa_config * wpa_config_read(const char *name)
 {
        FILE *f;
@@ -564,11 +332,13 @@ struct wpa_config * wpa_config_read(const char *name)
        config->ssid = head;
        wpa_config_debug_dump_networks(config);
 
+#ifndef WPA_IGNORE_CONFIG_ERRORS
        if (errors) {
                wpa_config_free(config);
                config = NULL;
                head = NULL;
        }
+#endif /* WPA_IGNORE_CONFIG_ERRORS */
 
        return config;
 }
@@ -726,6 +496,18 @@ static void write_wep_key(FILE *f, int idx, struct wpa_ssid *ssid)
 }
 
 
+#ifdef CONFIG_P2P
+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);
+}
+#endif /* CONFIG_P2P */
+
+
 static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
 {
        int i;
@@ -745,6 +527,7 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
        write_pairwise(f, ssid);
        write_group(f, ssid);
        write_auth_alg(f, ssid);
+       STR(bgscan);
 #ifdef IEEE8021X_EAPOL
        write_eap(f, ssid);
        STR(identity);
@@ -800,6 +583,9 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
        INT(ieee80211w);
 #endif /* CONFIG_IEEE80211W */
        STR(id_str);
+#ifdef CONFIG_P2P
+       write_p2p_client_list(f, ssid);
+#endif /* CONFIG_P2P */
 
 #undef STR
 #undef INT
@@ -876,8 +662,13 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config)
                fprintf(f, "model_number=%s\n", config->model_number);
        if (config->serial_number)
                fprintf(f, "serial_number=%s\n", config->serial_number);
-       if (config->device_type)
-               fprintf(f, "device_type=%s\n", config->device_type);
+       {
+               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));
@@ -887,14 +678,80 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config)
                fprintf(f, "wps_cred_processing=%d\n",
                        config->wps_cred_processing);
 #endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+       if (config->p2p_listen_reg_class)
+               fprintf(f, "p2p_listen_reg_class=%u\n",
+                       config->p2p_listen_reg_class);
+       if (config->p2p_listen_channel)
+               fprintf(f, "p2p_listen_channel=%u\n",
+                       config->p2p_listen_channel);
+       if (config->p2p_oper_reg_class)
+               fprintf(f, "p2p_oper_reg_class=%u\n",
+                       config->p2p_oper_reg_class);
+       if (config->p2p_oper_channel)
+               fprintf(f, "p2p_oper_channel=%u\n", config->p2p_oper_channel);
+       if (config->p2p_go_intent != DEFAULT_P2P_GO_INTENT)
+               fprintf(f, "p2p_go_intent=%u\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, "persistent_reconnect=%u\n",
+                       config->persistent_reconnect);
+       if (config->p2p_intra_bss != DEFAULT_P2P_INTRA_BSS)
+               fprintf(f, "p2p_intra_bss=%u\n", config->p2p_intra_bss);
+       if (config->p2p_group_idle)
+               fprintf(f, "p2p_group_idle=%u\n", config->p2p_group_idle);
+       if (config->p2p_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");
+       }
+#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, "disassoc_low_ack=%u\n", config->disassoc_low_ack);
+#ifdef CONFIG_INTERWORKING
+       if (config->home_realm)
+               fprintf(f, "home_realm=%s\n", config->home_realm);
+       if (config->home_username)
+               fprintf(f, "home_username=%s\n", config->home_username);
+       if (config->home_password)
+               fprintf(f, "home_password=%s\n", config->home_password);
+       if (config->home_ca_cert)
+               fprintf(f, "home_ca_cert=%s\n", config->home_ca_cert);
+       if (config->home_imsi)
+               fprintf(f, "home_imsi=%s\n", config->home_imsi);
+       if (config->home_milenage)
+               fprintf(f, "home_milenage=%s\n", config->home_milenage);
+       if (config->interworking)
+               fprintf(f, "interworking=%u\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 */
 }
 
 #endif /* CONFIG_NO_CONFIG_WRITE */
@@ -921,8 +778,11 @@ int wpa_config_write(const char *name, struct wpa_config *config)
        wpa_config_write_global(f, config);
 
        for (ssid = config->ssid; ssid; ssid = ssid->next) {
-               if (ssid->key_mgmt == WPA_KEY_MGMT_WPS)
-                       continue; /* do not save temporary WPS networks */
+               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");
index 25e87aa..8a47c0b 100644 (file)
@@ -109,6 +109,9 @@ struct wpa_ssid {
         *
         * 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];
 
@@ -273,6 +276,11 @@ struct wpa_ssid {
         *
         * 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)
+        *
         * Note: IBSS can only be used with key_mgmt NONE (plaintext and
         * static WEP) and key_mgmt=WPA-NONE (fixed group key TKIP/CCMP). In
         * addition, ap_scan has to be set to 2 for IBSS. WPA-None requires
@@ -284,6 +292,8 @@ struct wpa_ssid {
                WPAS_MODE_INFRA = 0,
                WPAS_MODE_IBSS = 1,
                WPAS_MODE_AP = 2,
+               WPAS_MODE_P2P_GO = 3,
+               WPAS_MODE_P2P_GROUP_FORMATION = 4,
        } mode;
 
        /**
@@ -292,6 +302,8 @@ struct wpa_ssid {
         * 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;
 
@@ -373,6 +385,43 @@ struct wpa_ssid {
         * 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;
+
+       /**
+        * 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;
 };
 
 #endif /* CONFIG_SSID_H */
index 9a7825a..ea3a2ac 100644 (file)
@@ -247,8 +247,13 @@ static int wpa_config_read_global(struct wpa_config *config, HKEY hk)
                hk, TEXT("model_name"));
        config->serial_number = wpa_config_read_reg_string(
                hk, TEXT("serial_number"));
-       config->device_type = wpa_config_read_reg_string(
-               hk, TEXT("device_type"));
+       {
+               char *t = wpa_config_read_reg_string(
+                       hk, TEXT("device_type"));
+               if (t && wps_dev_type_str2bin(t, config->device_type))
+                       errors++;
+               os_free(t);
+       }
        config->config_methods = wpa_config_read_reg_string(
                hk, TEXT("config_methods"));
        if (wpa_config_read_global_os_version(config, hk))
@@ -256,11 +261,21 @@ static int wpa_config_read_global(struct wpa_config *config, HKEY hk)
        wpa_config_read_reg_dword(hk, TEXT("wps_cred_processing"),
                                  &config->wps_cred_processing);
 #endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+       config->p2p_ssid_postfix = wpa_config_read_reg_string(
+               hk, TEXT("p2p_ssid_postfix"));
+       wpa_config_read_reg_dword(hk, TEXT("p2p_group_idle"),
+                                 (int *) &config->p2p_group_idle);
+#endif /* CONFIG_P2P */
 
        wpa_config_read_reg_dword(hk, TEXT("bss_max_count"),
                                  (int *) &config->bss_max_count);
        wpa_config_read_reg_dword(hk, TEXT("filter_ssids"),
                                  &config->filter_ssids);
+       wpa_config_read_reg_dword(hk, TEXT("max_num_sta"),
+                                 (int *) &config->max_num_sta);
+       wpa_config_read_reg_dword(hk, TEXT("disassoc_low_ack"),
+                                 (int *) &config->disassoc_low_ack);
 
        return errors ? -1 : 0;
 }
@@ -575,7 +590,12 @@ static int wpa_config_write_global(struct wpa_config *config, HKEY hk)
        wpa_config_write_reg_string(hk, "model_number", config->model_number);
        wpa_config_write_reg_string(hk, "serial_number",
                                    config->serial_number);
-       wpa_config_write_reg_string(hk, "device_type", config->device_type);
+       {
+               char _buf[WPS_DEV_TYPE_BUFSIZE], *buf;
+               buf = wps_dev_type_bin2str(config->device_type,
+                                          _buf, sizeof(_buf));
+               wpa_config_write_reg_string(hk, "device_type", buf);
+       }
        wpa_config_write_reg_string(hk, "config_methods",
                                    config->config_methods);
        if (WPA_GET_BE32(config->os_version)) {
@@ -587,12 +607,22 @@ static int wpa_config_write_global(struct wpa_config *config, HKEY hk)
        wpa_config_write_reg_dword(hk, TEXT("wps_cred_processing"),
                                   config->wps_cred_processing, 0);
 #endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+       wpa_config_write_reg_string(hk, "p2p_ssid_postfix",
+                                   config->p2p_ssid_postfix);
+       wpa_config_write_reg_dword(hk, TEXT("p2p_group_idle"),
+                                  config->p2p_group_idle, 0);
+#endif /* CONFIG_P2P */
 
        wpa_config_write_reg_dword(hk, TEXT("bss_max_count"),
                                   config->bss_max_count,
                                   DEFAULT_BSS_MAX_COUNT);
        wpa_config_write_reg_dword(hk, TEXT("filter_ssids"),
                                   config->filter_ssids, 0);
+       wpa_config_write_reg_dword(hk, TEXT("max_num_sta"),
+                                  config->max_num_sta, DEFAULT_MAX_NUM_STA);
+       wpa_config_write_reg_dword(hk, TEXT("disassoc_low_ack"),
+                                  config->disassoc_low_ack, 0);
 
        return 0;
 }
index 19fea29..5ff6766 100644 (file)
@@ -16,6 +16,7 @@
 
 #include "utils/common.h"
 #include "utils/eloop.h"
+#include "common/version.h"
 #include "common/ieee802_11_defs.h"
 #include "common/wpa_ctrl.h"
 #include "eap_peer/eap.h"
 #include "wps_supplicant.h"
 #include "ibss_rsn.h"
 #include "ap.h"
+#include "p2p_supplicant.h"
+#include "p2p/p2p.h"
 #include "notify.h"
 #include "bss.h"
 #include "scan.h"
 #include "ctrl_iface.h"
+#include "interworking.h"
+#include "blacklist.h"
+#include "wpas_glue.h"
 
 extern struct wpa_driver_ops *wpa_drivers[];
 
@@ -80,13 +86,77 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s,
        } else if (os_strcasecmp(cmd, "dot11RSNAConfigSATimeout") == 0) {
                if (wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT, atoi(value)))
                        ret = -1;
-       } else
-               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);
+#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_TESTING
+       } else if (os_strcasecmp(cmd, "tdls_testing") == 0) {
+               extern unsigned int tdls_testing;
+               tdls_testing = strtol(value, NULL, 0);
+               wpa_printf(MSG_DEBUG, "TDLS: tdls_testing=0x%x", tdls_testing);
+#endif /* CONFIG_TDLS_TESTING */
+#ifdef CONFIG_TDLS
+       } else if (os_strcasecmp(cmd, "tdls_disabled") == 0) {
+               int disabled = atoi(value);
+               wpa_printf(MSG_DEBUG, "TDLS: tdls_disabled=%d", disabled);
+               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 {
+               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;
+
+       wpa_printf(MSG_DEBUG, "CTRL_IFACE GET '%s'", cmd);
+
+       if (os_strcmp(cmd, "version") == 0) {
+               res = os_snprintf(buf, buflen, "%s", VERSION_STR);
+               if (res < 0 || (unsigned int) res >= buflen)
+                       return -1;
+               return res;
+       }
+
+       return -1;
+}
+
+
 #ifdef IEEE8021X_EAPOL
 static int wpa_supplicant_ctrl_iface_preauth(struct wpa_supplicant *wpa_s,
                                             char *addr)
@@ -131,6 +201,80 @@ static int wpa_supplicant_ctrl_iface_stkstart(
 #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));
+
+       ret = wpa_tdls_reneg(wpa_s->wpa, peer);
+       if (ret) {
+               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];
+
+       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));
+
+       return wpa_tdls_teardown_link(wpa_s->wpa, peer,
+                                     WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
+}
+
+#endif /* CONFIG_TDLS */
+
+
 #ifdef CONFIG_IEEE80211R
 static int wpa_supplicant_ctrl_iface_ft_ds(
        struct wpa_supplicant *wpa_s, char *addr)
@@ -163,10 +307,26 @@ 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)
+       if (cmd == NULL || os_strcmp(cmd, "any") == 0) {
                _bssid = NULL;
-       else if (hwaddr_aton(cmd, bssid)) {
+#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;
@@ -174,10 +334,10 @@ static int wpa_supplicant_ctrl_iface_wps_pbc(struct wpa_supplicant *wpa_s,
 
 #ifdef CONFIG_AP
        if (wpa_s->ap_iface)
-               return wpa_supplicant_ap_wps_pbc(wpa_s, _bssid);
+               return wpa_supplicant_ap_wps_pbc(wpa_s, _bssid, _p2p_dev_addr);
 #endif /* CONFIG_AP */
 
-       return wpas_wps_start_pbc(wpa_s, _bssid);
+       return wpas_wps_start_pbc(wpa_s, _bssid, 0);
 }
 
 
@@ -195,7 +355,10 @@ static int wpa_supplicant_ctrl_iface_wps_pin(struct wpa_supplicant *wpa_s,
 
        if (os_strcmp(cmd, "any") == 0)
                _bssid = NULL;
-       else if (hwaddr_aton(cmd, bssid)) {
+       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;
@@ -208,7 +371,8 @@ static int wpa_supplicant_ctrl_iface_wps_pin(struct wpa_supplicant *wpa_s,
 #endif /* CONFIG_AP */
 
        if (pin) {
-               ret = wpas_wps_start_pin(wpa_s, _bssid, 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);
@@ -217,10 +381,11 @@ static int wpa_supplicant_ctrl_iface_wps_pin(struct wpa_supplicant *wpa_s,
                return ret;
        }
 
-       ret = wpas_wps_start_pin(wpa_s, _bssid, NULL);
+       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 (ret < 0 || (size_t) ret >= buflen)
@@ -229,6 +394,51 @@ static int wpa_supplicant_ctrl_iface_wps_pin(struct wpa_supplicant *wpa_s,
 }
 
 
+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 (ret < 0 || (size_t) ret >= buflen)
+                               return -1;
+                       return ret;
+               }
+       }
+
+       ret = os_snprintf(buf, buflen, "%s", pin);
+       if (ret < 0 || (size_t) ret >= buflen)
+               return -1;
+
+       return ret;
+}
+
+
 #ifdef CONFIG_WPS_OOB
 static int wpa_supplicant_ctrl_iface_wps_oob(struct wpa_supplicant *wpa_s,
                                             char *cmd)
@@ -257,7 +467,7 @@ static int wpa_supplicant_ctrl_iface_wps_oob(struct wpa_supplicant *wpa_s,
 static int wpa_supplicant_ctrl_iface_wps_reg(struct wpa_supplicant *wpa_s,
                                             char *cmd)
 {
-       u8 bssid[ETH_ALEN], *_bssid = bssid;
+       u8 bssid[ETH_ALEN];
        char *pin;
        char *new_ssid;
        char *new_auth;
@@ -270,9 +480,7 @@ static int wpa_supplicant_ctrl_iface_wps_reg(struct wpa_supplicant *wpa_s,
                return -1;
        *pin++ = '\0';
 
-       if (os_strcmp(cmd, "any") == 0)
-               _bssid = NULL;
-       else if (hwaddr_aton(cmd, bssid)) {
+       if (hwaddr_aton(cmd, bssid)) {
                wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_REG: invalid BSSID '%s'",
                           cmd);
                return -1;
@@ -280,7 +488,7 @@ static int wpa_supplicant_ctrl_iface_wps_reg(struct wpa_supplicant *wpa_s,
 
        new_ssid = os_strchr(pin, ' ');
        if (new_ssid == NULL)
-               return wpas_wps_start_reg(wpa_s, _bssid, pin, NULL);
+               return wpas_wps_start_reg(wpa_s, bssid, pin, NULL);
        *new_ssid++ = '\0';
 
        new_auth = os_strchr(new_ssid, ' ');
@@ -303,20 +511,86 @@ static int wpa_supplicant_ctrl_iface_wps_reg(struct wpa_supplicant *wpa_s,
        ap.auth = new_auth;
        ap.encr = new_encr;
        ap.key_hex = new_key;
-       return wpas_wps_start_reg(wpa_s, _bssid, pin, &ap);
+       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;
+       char *uuid = cmd, *pin, *pos;
+       u8 addr_buf[ETH_ALEN], *addr = NULL;
        pin = os_strchr(uuid, ' ');
        if (pin == NULL)
                return -1;
        *pin++ = '\0';
-       return wpas_wps_er_add_pin(wpa_s, uuid, pin);
+       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);
 }
 
 
@@ -330,6 +604,62 @@ static int wpa_supplicant_ctrl_iface_wps_er_learn(struct wpa_supplicant *wpa_s,
        *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);
+}
 #endif /* CONFIG_WPS_ER */
 
 #endif /* CONFIG_WPS */
@@ -355,84 +685,110 @@ static int wpa_supplicant_ctrl_iface_ibss_rsn(
 #endif /* CONFIG_IBSS_RSN */
 
 
-static int wpa_supplicant_ctrl_iface_ctrl_rsp(struct wpa_supplicant *wpa_s,
-                                             char *rsp)
+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
-       char *pos, *id_pos;
-       int id;
-       struct wpa_ssid *ssid;
-       struct eap_peer_config *eap;
-
-       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));
+       struct eap_peer_config *eap = &ssid->eap;
 
-       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;
-       }
-       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));
 
-       if (os_strcmp(rsp, "IDENTITY") == 0) {
+       switch (wpa_supplicant_ctrl_req_from_string(field)) {
+       case WPA_CTRL_REQ_EAP_IDENTITY:
                os_free(eap->identity);
-               eap->identity = (u8 *) os_strdup(pos);
-               eap->identity_len = os_strlen(pos);
+               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;
-       } else if (os_strcmp(rsp, "PASSWORD") == 0) {
+               break;
+       case WPA_CTRL_REQ_EAP_PASSWORD:
                os_free(eap->password);
-               eap->password = (u8 *) os_strdup(pos);
-               eap->password_len = os_strlen(pos);
+               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;
-       } else if (os_strcmp(rsp, "NEW_PASSWORD") == 0) {
+               break;
+       case WPA_CTRL_REQ_EAP_NEW_PASSWORD:
                os_free(eap->new_password);
-               eap->new_password = (u8 *) os_strdup(pos);
-               eap->new_password_len = os_strlen(pos);
+               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;
-       } else if (os_strcmp(rsp, "PIN") == 0) {
+               break;
+       case WPA_CTRL_REQ_EAP_PIN:
                os_free(eap->pin);
-               eap->pin = os_strdup(pos);
+               eap->pin = os_strdup(value);
                eap->pending_req_pin = 0;
                if (ssid == wpa_s->current_ssid)
                        wpa_s->reassociate = 1;
-       } else if (os_strcmp(rsp, "OTP") == 0) {
+               break;
+       case WPA_CTRL_REQ_EAP_OTP:
                os_free(eap->otp);
-               eap->otp = (u8 *) os_strdup(pos);
-               eap->otp_len = os_strlen(pos);
+               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;
-       } else if (os_strcmp(rsp, "PASSPHRASE") == 0) {
+               break;
+       case WPA_CTRL_REQ_EAP_PASSPHRASE:
                os_free(eap->private_key_passwd);
-               eap->private_key_passwd = (u8 *) os_strdup(pos);
+               eap->private_key_passwd = (u8 *) os_strdup(value);
                eap->pending_req_passphrase = 0;
                if (ssid == wpa_s->current_ssid)
                        wpa_s->reassociate = 1;
-       } else {
-               wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown field '%s'", rsp);
+               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 */
+}
+
+
+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 */
@@ -497,6 +853,15 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s,
                                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;
@@ -529,14 +894,30 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s,
                pos += ret;
        }
 
-       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;
-       }
-
+#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 (ret < 0 || ret >= end - pos)
+                       return pos - buf;
+               pos += ret;
+       }
+#endif /* CONFIG_P2P */
+
+       ret = os_snprintf(pos, end - pos, "address=" MACSTR "\n",
+                         MAC2STR(wpa_s->own_addr));
+       if (ret < 0 || ret >= end - pos)
+               return pos - buf;
+       pos += ret;
+
+       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;
+       }
+
        res = rsn_preauth_get_status(wpa_s->wpa, pos, end - pos, verbose);
        if (res >= 0)
                pos += res;
@@ -579,6 +960,152 @@ static int wpa_supplicant_ctrl_iface_bssid(struct wpa_supplicant *wpa_s,
 }
 
 
+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 (ret < 0 || ret >= end - pos)
+                               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;
+}
+
+
+extern int wpa_debug_level;
+extern int wpa_debug_timestamp;
+
+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;
+}
+
+
+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;
+
+       if (cmd == NULL) {
+               return -1;
+       }
+
+       /* 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 (ret < 0 || ret >= end - pos)
+                       ret = 0;
+
+               return ret;
+       }
+
+       while (*cmd == ' ')
+               cmd++;
+
+       stamp = os_strchr(cmd, ' ');
+       if (stamp) {
+               *stamp++ = '\0';
+               while (*stamp == ' ') {
+                       stamp++;
+               }
+       }
+
+       if (cmd && 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 *buf, size_t buflen)
 {
@@ -611,10 +1138,12 @@ static int wpa_supplicant_ctrl_iface_list_networks(
                if (ret < 0 || ret >= end - pos)
                        return pos - buf;
                pos += ret;
-               ret = os_snprintf(pos, end - pos, "\t%s%s",
+               ret = os_snprintf(pos, end - pos, "\t%s%s%s",
                                  ssid == wpa_s->current_ssid ?
                                  "[CURRENT]" : "",
-                                 ssid->disabled ? "[DISABLED]" : "");
+                                 ssid->disabled ? "[DISABLED]" : "",
+                                 ssid->disabled == 2 ? "[P2P-PERSISTENT]" :
+                                 "");
                if (ret < 0 || ret >= end - pos)
                        return pos - buf;
                pos += ret;
@@ -774,7 +1303,8 @@ static char * wpa_supplicant_ie_txt(char *pos, char *end, const char *proto,
 
 
 #ifdef CONFIG_WPS
-static char * wpa_supplicant_wps_ie_txt_buf(char *pos, char *end,
+static char * wpa_supplicant_wps_ie_txt_buf(struct wpa_supplicant *wpa_s,
+                                           char *pos, char *end,
                                            struct wpabuf *wps_ie)
 {
        int ret;
@@ -784,6 +1314,10 @@ static char * wpa_supplicant_wps_ie_txt_buf(char *pos, char *end,
                return pos;
        if (wps_is_selected_pbc_registrar(wps_ie))
                txt = "[WPS-PBC]";
+#ifdef CONFIG_WPS2
+       else if (wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 0))
+               txt = "[WPS-AUTH]";
+#endif /* CONFIG_WPS2 */
        else if (wps_is_selected_pin_registrar(wps_ie))
                txt = "[WPS-PIN]";
        else
@@ -798,13 +1332,14 @@ static char * wpa_supplicant_wps_ie_txt_buf(char *pos, char *end,
 #endif /* CONFIG_WPS */
 
 
-static char * wpa_supplicant_wps_ie_txt(char *pos, char *end,
+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(pos, end, wps_ie);
+       return wpa_supplicant_wps_ie_txt_buf(wpa_s, pos, end, wps_ie);
 #else /* CONFIG_WPS */
        return pos;
 #endif /* CONFIG_WPS */
@@ -813,11 +1348,18 @@ static char * wpa_supplicant_wps_ie_txt(char *pos, char *end,
 
 /* 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;
-       const u8 *ie, *ie2;
+       const u8 *ie, *ie2, *p2p;
+
+       p2p = wpa_bss_get_vendor_ie(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;
@@ -825,7 +1367,7 @@ static int wpa_supplicant_ctrl_iface_scan_result(
        ret = os_snprintf(pos, end - pos, MACSTR "\t%d\t%d\t",
                          MAC2STR(bss->bssid), bss->freq, bss->level);
        if (ret < 0 || ret >= end - pos)
-               return pos - buf;
+               return -1;
        pos += ret;
        ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
        if (ie)
@@ -833,35 +1375,41 @@ static int wpa_supplicant_ctrl_iface_scan_result(
        ie2 = wpa_bss_get_ie(bss, WLAN_EID_RSN);
        if (ie2)
                pos = wpa_supplicant_ie_txt(pos, end, "WPA2", ie2, 2 + ie2[1]);
-       pos = wpa_supplicant_wps_ie_txt(pos, end, bss);
+       pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss);
        if (!ie && !ie2 && bss->caps & IEEE80211_CAP_PRIVACY) {
                ret = os_snprintf(pos, end - pos, "[WEP]");
                if (ret < 0 || ret >= end - pos)
-                       return pos - buf;
+                       return -1;
                pos += ret;
        }
        if (bss->caps & IEEE80211_CAP_IBSS) {
                ret = os_snprintf(pos, end - pos, "[IBSS]");
                if (ret < 0 || ret >= end - pos)
-                       return pos - buf;
+                       return -1;
                pos += ret;
        }
        if (bss->caps & IEEE80211_CAP_ESS) {
                ret = os_snprintf(pos, end - pos, "[ESS]");
                if (ret < 0 || ret >= end - pos)
-                       return pos - buf;
+                       return -1;
+               pos += ret;
+       }
+       if (p2p) {
+               ret = os_snprintf(pos, end - pos, "[P2P]");
+               if (ret < 0 || ret >= end - pos)
+                       return -1;
                pos += ret;
        }
 
        ret = os_snprintf(pos, end - pos, "\t%s",
                          wpa_ssid_txt(bss->ssid, bss->ssid_len));
        if (ret < 0 || ret >= end - pos)
-               return pos - buf;
+               return -1;
        pos += ret;
 
        ret = os_snprintf(pos, end - pos, "\n");
        if (ret < 0 || ret >= end - pos)
-               return pos - buf;
+               return -1;
        pos += ret;
 
        return pos - buf;
@@ -884,7 +1432,7 @@ static int wpa_supplicant_ctrl_iface_scan_results(
        pos += ret;
 
        dl_list_for_each(bss, &wpa_s->bss_id, struct wpa_bss, list_id) {
-               ret = wpa_supplicant_ctrl_iface_scan_result(bss, pos,
+               ret = wpa_supplicant_ctrl_iface_scan_result(wpa_s, bss, pos,
                                                            end - pos);
                if (ret < 0 || ret >= end - pos)
                        return pos - buf;
@@ -915,6 +1463,11 @@ static int wpa_supplicant_ctrl_iface_select_network(
                                   "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;
+               }
        }
 
        wpa_supplicant_select_network(wpa_s, ssid);
@@ -943,6 +1496,11 @@ static int wpa_supplicant_ctrl_iface_enable_network(
                                   "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;
+               }
        }
        wpa_supplicant_enable_network(wpa_s, ssid);
 
@@ -970,6 +1528,12 @@ static int wpa_supplicant_ctrl_iface_disable_network(
                                   "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);
 
@@ -1018,8 +1582,13 @@ static int wpa_supplicant_ctrl_iface_remove_network(
                        wpas_notify_network_removed(wpa_s, remove_ssid);
                        wpa_config_remove_network(wpa_s->conf, id);
                }
+               eapol_sm_invalidate_cached_session(wpa_s->eapol);
                if (wpa_s->current_ssid) {
-                       eapol_sm_invalidate_cached_session(wpa_s->eapol);
+#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_supplicant_disassociate(wpa_s,
                                                    WLAN_REASON_DEAUTH_LEAVING);
                }
@@ -1030,6 +1599,8 @@ static int wpa_supplicant_ctrl_iface_remove_network(
        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_config_remove_network(wpa_s->conf, id) < 0) {
                wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find network "
@@ -1037,12 +1608,20 @@ static int wpa_supplicant_ctrl_iface_remove_network(
                return -1;
        }
 
-       if (ssid == wpa_s->current_ssid) {
+       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 network is
-                * removed.
+                * 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_supplicant_disassociate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
        }
@@ -1088,10 +1667,12 @@ static int wpa_supplicant_ctrl_iface_set_network(
                return -1;
        }
 
-       if (wpa_s->current_ssid == ssid) {
+       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
-                * configuration changes.
+                * or previously used configuration changes.
                 */
                eapol_sm_invalidate_cached_session(wpa_s->eapol);
        }
@@ -1482,6 +2063,41 @@ static int wpa_supplicant_ctrl_iface_get_capability(
 }
 
 
+#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 (ret < 0 || ret >= end - pos)
+               return start;
+       pos += ret;
+
+       d = wpabuf_head_u8(data);
+       for (i = 0; i < wpabuf_len(data); i++) {
+               ret = os_snprintf(pos, end - pos, "%02x", *d++);
+               if (ret < 0 || ret >= end - pos)
+                       return start;
+               pos += ret;
+       }
+
+       ret = os_snprintf(pos, end - pos, "\n");
+       if (ret < 0 || ret >= end - pos)
+               return start;
+       pos += ret;
+
+       return pos;
+}
+#endif /* CONFIG_INTERWORKING */
+
+
 static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s,
                                         const char *cmd, char *buf,
                                         size_t buflen)
@@ -1509,6 +2125,13 @@ static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s,
                                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 {
@@ -1572,7 +2195,7 @@ static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s,
        ie2 = wpa_bss_get_ie(bss, WLAN_EID_RSN);
        if (ie2)
                pos = wpa_supplicant_ie_txt(pos, end, "WPA2", ie2, 2 + ie2[1]);
-       pos = wpa_supplicant_wps_ie_txt(pos, end, bss);
+       pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss);
        if (!ie && !ie2 && bss->caps & IEEE80211_CAP_PRIVACY) {
                ret = os_snprintf(pos, end - pos, "[WEP]");
                if (ret < 0 || ret >= end - pos)
@@ -1591,6 +2214,12 @@ static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s,
                        return pos - buf;
                pos += ret;
        }
+       if (wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE)) {
+               ret = os_snprintf(pos, end - pos, "[P2P]");
+               if (ret < 0 || ret >= end - pos)
+                       return pos - buf;
+               pos += ret;
+       }
 
        ret = os_snprintf(pos, end - pos, "\n");
        if (ret < 0 || ret >= end - pos)
@@ -1611,6 +2240,28 @@ static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s,
        pos += ret;
 #endif /* CONFIG_WPS */
 
+#ifdef CONFIG_P2P
+       ie = (const u8 *) (bss + 1);
+       ret = wpas_p2p_scan_result_text(ie, bss->ie_len, pos, end);
+       if (ret < 0 || ret >= end - pos)
+               return pos - buf;
+       pos += ret;
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_INTERWORKING
+       pos = anqp_add_hex(pos, end, "anqp_venue_name", bss->anqp_venue_name);
+       pos = anqp_add_hex(pos, end, "anqp_network_auth_type",
+                          bss->anqp_network_auth_type);
+       pos = anqp_add_hex(pos, end, "anqp_roaming_consortium",
+                          bss->anqp_roaming_consortium);
+       pos = anqp_add_hex(pos, end, "anqp_ip_addr_type_availability",
+                          bss->anqp_ip_addr_type_availability);
+       pos = anqp_add_hex(pos, end, "anqp_nai_realm", bss->anqp_nai_realm);
+       pos = anqp_add_hex(pos, end, "anqp_3gpp", bss->anqp_3gpp);
+       pos = anqp_add_hex(pos, end, "anqp_domain_name",
+                          bss->anqp_domain_name);
+#endif /* CONFIG_INTERWORKING */
+
        return pos - buf;
 }
 
@@ -1623,19 +2274,44 @@ static int wpa_supplicant_ctrl_iface_ap_scan(
 }
 
 
-static void wpa_supplicant_ctrl_iface_drop_sa(struct wpa_supplicant *wpa_s)
+static int wpa_supplicant_ctrl_iface_scan_interval(
+       struct wpa_supplicant *wpa_s, char *cmd)
+{
+       int scan_int = atoi(cmd);
+       if (scan_int < 0)
+               return -1;
+       wpa_s->scan_interval = scan_int;
+       return 0;
+}
+
+
+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)
 {
-       u8 *bcast = (u8 *) "\xff\xff\xff\xff\xff\xff";
+       int expire_count = atoi(cmd);
+       return wpa_supplicant_set_bss_expiration_count(wpa_s, expire_count);
+}
+
 
+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, bcast, 0, 0, NULL, 0, NULL, 0);
-       wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 1, 0, NULL, 0, NULL, 0);
-       wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 2, 0, NULL, 0, NULL, 0);
-       wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 3, 0, NULL, 0, NULL, 0);
+       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, bcast, 4, 0, NULL, 0, NULL, 0);
-       wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 5, 0, NULL, 0, NULL, 0);
+       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,
@@ -1651,6 +2327,9 @@ static void wpa_supplicant_ctrl_iface_drop_sa(struct wpa_supplicant *wpa_s)
 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;
@@ -1685,68 +2364,995 @@ static int wpa_supplicant_ctrl_iface_roam(struct wpa_supplicant *wpa_s,
        wpa_supplicant_connect(wpa_s, bss, ssid);
 
        return 0;
+#endif /* CONFIG_NO_SCAN_PROCESSING */
 }
 
 
-char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
-                                        char *buf, size_t *resp_len)
+#ifdef CONFIG_P2P
+static int p2p_ctrl_find(struct wpa_supplicant *wpa_s, char *cmd)
 {
-       char *reply;
-       const int reply_size = 2048;
-       int ctrl_rsp = 0;
-       int reply_len;
+       unsigned int timeout = atoi(cmd);
+       enum p2p_discovery_type type = P2P_FIND_START_WITH_FULL;
+       u8 dev_id[ETH_ALEN], *_dev_id = NULL;
+       char *pos;
 
-       if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0 ||
-           os_strncmp(buf, "SET_NETWORK ", 12) == 0) {
-               wpa_hexdump_ascii_key(MSG_DEBUG, "RX ctrl_iface",
-                                     (const u8 *) buf, os_strlen(buf));
+       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;
+       }
+
+       return wpas_p2p_find(wpa_s, timeout, type, 0, NULL, _dev_id);
+}
+
+
+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;
+       int join;
+       int auth;
+       int go_intent = -1;
+       int freq = 0;
+
+       /* <addr> <"pbc" | "pin" | PIN> [label|display|keypad] [persistent]
+        * [join] [auth] [go_intent=<0..15>] [freq=<in MHz>] */
+
+       if (hwaddr_aton(cmd, addr))
+               return -1;
+
+       pos = cmd + 17;
+       if (*pos != ' ')
+               return -1;
+       pos++;
+
+       persistent_group = os_strstr(pos, " persistent") != NULL;
+       join = os_strstr(pos, " join") != NULL;
+       auth = os_strstr(pos, " auth") != NULL;
+
+       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 {
-               wpa_hexdump_ascii(MSG_DEBUG, "RX ctrl_iface",
-                                 (const u8 *) buf, os_strlen(buf));
+               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;
+               }
        }
 
-       reply = os_malloc(reply_size);
-       if (reply == NULL) {
-               *resp_len = 1;
-               return NULL;
+       new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method,
+                                  persistent_group, join, auth, go_intent,
+                                  freq);
+       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 (ret < 0 || (size_t) ret >= buflen)
+                       return -1;
+               return ret;
        }
 
-       os_memcpy(reply, "OK\n", 3);
-       reply_len = 3;
+       os_memcpy(buf, "OK\n", 3);
+       return 3;
+}
 
-       if (os_strcmp(buf, "PING") == 0) {
-               os_memcpy(reply, "PONG\n", 5);
-               reply_len = 5;
-       } else if (os_strcmp(buf, "MIB") == 0) {
-               reply_len = wpa_sm_get_mib(wpa_s->wpa, reply, reply_size);
-               if (reply_len >= 0) {
-                       int res;
-                       res = eapol_sm_get_mib(wpa_s->eapol, reply + reply_len,
-                                              reply_size - reply_len);
-                       if (res < 0)
-                               reply_len = -1;
-                       else
-                               reply_len += res;
+
+static int p2p_ctrl_listen(struct wpa_supplicant *wpa_s, char *cmd)
+{
+       unsigned int timeout = atoi(cmd);
+       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;
+
+       /* <addr> <config method> [join] */
+
+       if (hwaddr_aton(cmd, addr))
+               return -1;
+
+       pos = cmd + 17;
+       if (*pos != ' ')
+               return -1;
+       pos++;
+
+       return wpas_p2p_prov_disc(wpa_s, addr, pos,
+                                 os_strstr(pos, "join") != 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 = (unsigned long) wpas_p2p_sd_request_upnp(wpa_s, dst,
+                                                              version, pos);
+       } 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;
                }
-       } else if (os_strncmp(buf, "STATUS", 6) == 0) {
-               reply_len = wpa_supplicant_ctrl_iface_status(
-                       wpa_s, buf + 6, reply, reply_size);
+
+               ref = (unsigned long) wpas_p2p_sd_request(wpa_s, dst, tlvs);
+               wpabuf_free(tlvs);
+       }
+       res = os_snprintf(buf, buflen, "%llx", (long long unsigned) ref);
+       if (res < 0 || (unsigned) res >= buflen)
+               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, (void *) (unsigned long) 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(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);
+       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(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);
+       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[ETH_ALEN];
+
+       id = atoi(cmd);
+       pos = os_strstr(cmd, " peer=");
+       if (pos) {
+               pos += 6;
+               if (hwaddr_aton(pos, peer))
+                       return -1;
+       }
+       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_invite(wpa_s, pos ? peer : NULL, ssid, NULL);
+}
+
+
+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,
+                                        char *cmd, int freq)
+{
+       int id;
+       struct wpa_ssid *ssid;
+
+       id = atoi(cmd);
+       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);
+}
+
+
+static int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd)
+{
+       int freq = 0;
+       char *pos;
+
+       pos = os_strstr(cmd, "freq=");
+       if (pos)
+               freq = atoi(pos + 5);
+
+       if (os_strncmp(cmd, "persistent=", 11) == 0)
+               return p2p_ctrl_group_add_persistent(wpa_s, cmd + 11, freq);
+       if (os_strcmp(cmd, "persistent") == 0 ||
+           os_strncmp(cmd, "persistent ", 11) == 0)
+               return wpas_p2p_group_add(wpa_s, 1, freq);
+       if (os_strncmp(cmd, "freq=", 5) == 0)
+               return wpas_p2p_group_add(wpa_s, 0, freq);
+
+       wpa_printf(MSG_DEBUG, "CTRL: Invalid P2P_GROUP_ADD parameters '%s'",
+                  cmd);
+       return -1;
+}
+
+
+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;
+
+       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 (res < 0 || res >= end - pos)
+               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 (res < 0 || res >= end - pos)
+                       return pos - buf;
+               pos += res;
+       }
+
+       res = p2p_get_peer_info_txt(info, pos, end - pos);
+       if (res < 0)
+               return pos - buf;
+       pos += res;
+
+       return pos - buf;
+}
+
+
+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));
+       }
+
+       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, "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;
+       }
+
+       wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown P2P_SET field value '%s'",
+                  cmd);
+
+       return -1;
+}
+
+
+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);
+}
+
+#endif /* CONFIG_P2P */
+
+
+#ifdef CONFIG_INTERWORKING
+static int ctrl_interworking_connect(struct wpa_supplicant *wpa_s, char *dst)
+{
+       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;
+       }
+
+       return interworking_connect(wpa_s, bss);
+}
+
+
+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;
+
+       used = hwaddr_aton2(dst, dst_addr);
+       if (used < 0)
+               return -1;
+       pos = dst + used;
+       while (num_id < MAX_ANQP_INFO_ID) {
+               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);
+}
+#endif /* CONFIG_INTERWORKING */
+
+
+static int wpa_supplicant_ctrl_iface_sta_autoconnect(
+       struct wpa_supplicant *wpa_s, char *cmd)
+{
+       wpa_s->auto_reconnect_disabled = atoi(cmd) == 0 ? 1 : 0;
+       return 0;
+}
+
+
+static int wpa_supplicant_signal_poll(struct wpa_supplicant *wpa_s, char *buf,
+                                     size_t buflen)
+{
+       struct wpa_signal_info si;
+       int ret;
+
+       ret = wpa_drv_signal_poll(wpa_s, &si);
+       if (ret)
+               return -1;
+
+       ret = os_snprintf(buf, buflen, "RSSI=%d\nLINKSPEED=%d\n"
+                         "NOISE=%d\nFREQUENCY=%u\n",
+                         si.current_signal, si.current_txrate / 1000,
+                         si.current_noise, si.frequency);
+       if (ret < 0 || (unsigned int) ret > buflen)
+               return -1;
+       return ret;
+}
+
+
+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 ctrl_rsp = 0;
+       int reply_len;
+
+       if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0 ||
+           os_strncmp(buf, "SET_NETWORK ", 12) == 0) {
+               wpa_hexdump_ascii_key(MSG_DEBUG, "RX ctrl_iface",
+                                     (const u8 *) buf, os_strlen(buf));
+       } else {
+               int level = MSG_DEBUG;
+               if (os_strcmp(buf, "PING") == 0)
+                       level = MSG_EXCESSIVE;
+               wpa_hexdump_ascii(level, "RX ctrl_iface",
+                                 (const u8 *) buf, os_strlen(buf));
+       }
+
+       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, "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) {
+                       int res;
+                       res = eapol_sm_get_mib(wpa_s->eapol, reply + reply_len,
+                                              reply_size - reply_len);
+                       if (res < 0)
+                               reply_len = -1;
+                       else
+                               reply_len += res;
+               }
+       } 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_strncmp(buf, "SET ", 4) == 0) {
                if (wpa_supplicant_ctrl_iface_set(wpa_s, buf + 4))
                        reply_len = -1;
+       } 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) {
-               wpa_s->disconnected = 0;
-               wpa_s->reassociate = 1;
-               wpa_supplicant_req_scan(wpa_s, 0, 0);
+               if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
+                       reply_len = -1;
+               else {
+                       wpa_s->disconnected = 0;
+                       wpa_s->reassociate = 1;
+                       wpa_supplicant_req_scan(wpa_s, 0, 0);
+               }
        } else if (os_strcmp(buf, "RECONNECT") == 0) {
-               if (wpa_s->disconnected) {
+               if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
+                       reply_len = -1;
+               else if (wpa_s->disconnected) {
                        wpa_s->disconnected = 0;
                        wpa_s->reassociate = 1;
                        wpa_supplicant_req_scan(wpa_s, 0, 0);
@@ -1768,15 +3374,29 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
 #endif /* CONFIG_IEEE80211R */
 #ifdef CONFIG_WPS
        } else if (os_strcmp(buf, "WPS_PBC") == 0) {
-               if (wpa_supplicant_ctrl_iface_wps_pbc(wpa_s, NULL))
+               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) {
-               if (wpa_supplicant_ctrl_iface_wps_pbc(wpa_s, buf + 8))
+               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_OOB
        } else if (os_strncmp(buf, "WPS_OOB ", 8) == 0) {
                if (wpa_supplicant_ctrl_iface_wps_oob(wpa_s, buf + 8))
@@ -1785,9 +3405,17 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
        } 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))
+               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) {
                if (wpas_wps_er_stop(wpa_s))
@@ -1796,11 +3424,28 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
                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) {
-               if (wpas_wps_er_pbc(wpa_s, buf + 11))
+               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;
 #endif /* CONFIG_WPS_ER */
 #endif /* CONFIG_WPS */
 #ifdef CONFIG_IBSS_RSN
@@ -1808,6 +3453,113 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
                if (wpa_supplicant_ctrl_iface_ibss_rsn(wpa_s, buf + 9))
                        reply_len = -1;
 #endif /* CONFIG_IBSS_RSN */
+#ifdef CONFIG_P2P
+       } else if (os_strncmp(buf, "P2P_FIND ", 9) == 0) {
+               if (p2p_ctrl_find(wpa_s, buf + 9))
+                       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_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 (wpas_p2p_group_add(wpa_s, 0, 0))
+                       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_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) {
+               os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN);
+               wpa_s->force_long_sd = 0;
+               if (wpa_s->global->p2p)
+                       p2p_flush(wpa_s->global->p2p);
+       } 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;
+#endif /* CONFIG_P2P */
+#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_strncmp(buf, "INTERWORKING_SELECT", 19) == 0) {
+               if (interworking_select(wpa_s, os_strstr(buf + 19, "auto") !=
+                                       NULL) < 0)
+                       reply_len = -1;
+       } else if (os_strncmp(buf, "INTERWORKING_CONNECT ", 21) == 0) {
+               if (ctrl_interworking_connect(wpa_s, buf + 21) < 0)
+                       reply_len = -1;
+       } else if (os_strncmp(buf, "ANQP_GET ", 9) == 0) {
+               if (get_anqp(wpa_s, buf + 9) < 0)
+                       reply_len = -1;
+#endif /* CONFIG_INTERWORKING */
        } else if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0)
        {
                if (wpa_supplicant_ctrl_iface_ctrl_rsp(
@@ -1823,17 +3575,39 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
        } 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_strcmp(buf, "LIST_NETWORKS") == 0) {
                reply_len = wpa_supplicant_ctrl_iface_list_networks(
                        wpa_s, 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_deauthenticate(wpa_s,
                                              WLAN_REASON_DEAUTH_LEAVING);
        } else if (os_strcmp(buf, "SCAN") == 0) {
-               wpa_s->scan_req = 2;
-               wpa_supplicant_req_scan(wpa_s, 0, 0);
+               if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
+                       reply_len = -1;
+               else {
+                       if (!wpa_s->scanning &&
+                           ((wpa_s->wpa_state <= WPA_SCANNING) ||
+                            (wpa_s->wpa_state == WPA_COMPLETED))) {
+                               wpa_s->scan_req = 2;
+                               wpa_supplicant_req_scan(wpa_s, 0, 0);
+                       } else {
+                               wpa_printf(MSG_DEBUG, "Ongoing scan action - "
+                                          "reject new request");
+                               reply_len = os_snprintf(reply, reply_size,
+                                                       "FAIL-BUSY\n");
+                       }
+               }
        } else if (os_strcmp(buf, "SCAN_RESULTS") == 0) {
                reply_len = wpa_supplicant_ctrl_iface_scan_results(
                        wpa_s, reply, reply_size);
@@ -1869,6 +3643,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
        } 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);
@@ -1897,6 +3674,30 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
        } else if (os_strncmp(buf, "ROAM ", 5) == 0) {
                if (wpa_supplicant_ctrl_iface_roam(wpa_s, buf + 5))
                        reply_len = -1;
+       } else if (os_strncmp(buf, "STA_AUTOCONNECT ", 16) == 0) {
+               if (wpa_supplicant_ctrl_iface_sta_autoconnect(wpa_s, buf + 16))
+                       reply_len = -1;
+       } 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;
+#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;
+#endif /* CONFIG_TDLS */
+       } else if (os_strncmp(buf, "SIGNAL_POLL", 11) == 0) {
+               reply_len = wpa_supplicant_signal_poll(wpa_s, reply,
+                                                      reply_size);
        } else {
                os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
                reply_len = 16;
@@ -2093,8 +3894,11 @@ char * wpa_supplicant_global_ctrl_iface_process(struct wpa_global *global,
        char *reply;
        const int reply_size = 2048;
        int reply_len;
+       int level = MSG_DEBUG;
 
-       wpa_hexdump_ascii(MSG_DEBUG, "RX global ctrl_iface",
+       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);
index 051d99a..88ae6b7 100644 (file)
@@ -95,6 +95,21 @@ void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv);
 void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv);
 
 /**
+ * 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);
+
+/**
  * wpa_supplicant_global_ctrl_iface_init - Initialize global control interface
  * @global: Pointer to global data from wpa_supplicant_init()
  * Returns: Pointer to private data on success, %NULL on failure
index 84ac760..306a222 100644 (file)
@@ -17,6 +17,9 @@
 #include <sys/stat.h>
 #include <grp.h>
 #include <stddef.h>
+#ifdef ANDROID
+#include <cutils/sockets.h>
+#endif /* ANDROID */
 
 #include "utils/common.h"
 #include "utils/eloop.h"
@@ -132,7 +135,7 @@ static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx,
 {
        struct wpa_supplicant *wpa_s = eloop_ctx;
        struct ctrl_iface_priv *priv = sock_ctx;
-       char buf[256];
+       char buf[4096];
        int res;
        struct sockaddr_un from;
        socklen_t fromlen = sizeof(from);
@@ -276,6 +279,13 @@ wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
        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)
+               goto havesock;
+#endif /* ANDROID */
        if (os_strncmp(buf, "DIR=", 4) == 0) {
                dir = buf + 4;
                gid_str = os_strstr(dir, " GROUP=");
@@ -398,6 +408,9 @@ wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
        }
        os_free(fname);
 
+#ifdef ANDROID
+havesock:
+#endif /* ANDROID */
        eloop_register_read_sock(priv->sock, wpa_supplicant_ctrl_iface_receive,
                                 wpa_s, priv);
        wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);
@@ -637,6 +650,12 @@ wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global)
        if (global->params.ctrl_interface == NULL)
                return priv;
 
+#ifdef ANDROID
+       priv->sock = android_get_control_socket(global->params.ctrl_interface);
+       if (priv->sock >= 0)
+               goto havesock;
+#endif /* ANDROID */
+
        wpa_printf(MSG_DEBUG, "Global control interface '%s'",
                   global->params.ctrl_interface);
 
@@ -685,6 +704,9 @@ wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global)
                }
        }
 
+#ifdef ANDROID
+havesock:
+#endif /* ANDROID */
        eloop_register_read_sock(priv->sock,
                                 wpa_supplicant_global_ctrl_iface_receive,
                                 global, NULL);
index cfaf58d..d64c65c 100644 (file)
@@ -15,6 +15,7 @@ ifndef CFLAGS
 CFLAGS = -MMD -O2 -Wall -g
 endif
 
+PKG_CONFIG ?= pkg-config
 CFLAGS += -I../../src -I../../src/utils
 
 
@@ -38,10 +39,10 @@ CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_NEW
 CFLAGS += -DCONFIG_CTRL_IFACE_DBUS
 
 ifndef DBUS_LIBS
-DBUS_LIBS := $(shell pkg-config --libs dbus-1)
+DBUS_LIBS := $(shell $(PKG_CONFIG) --libs dbus-1)
 endif
 ifndef DBUS_INCLUDE
-DBUS_INCLUDE := $(shell pkg-config --cflags dbus-1)
+DBUS_INCLUDE := $(shell $(PKG_CONFIG) --cflags dbus-1)
 endif
 ifdef CONFIG_CTRL_IFACE_DBUS_INTRO
 CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_INTRO
@@ -49,18 +50,6 @@ DBUS_INCLUDE += $(shell xml2-config --cflags)
 DBUS_LIBS += $(shell xml2-config --libs)
 endif
 
-dbus_version=$(subst ., ,$(shell pkg-config --modversion dbus-1))
-DBUS_VERSION_MAJOR=$(word 1,$(dbus_version))
-DBUS_VERSION_MINOR=$(word 2,$(dbus_version))
-ifeq ($(DBUS_VERSION_MAJOR),)
-DBUS_VERSION_MAJOR=0
-endif
-ifeq ($(DBUS_VERSION_MINOR),)
-DBUS_VERSION_MINOR=0
-endif
-DBUS_INCLUDE += -DDBUS_VERSION_MAJOR=$(DBUS_VERSION_MAJOR)
-DBUS_INCLUDE += -DDBUS_VERSION_MINOR=$(DBUS_VERSION_MINOR)
-
 CFLAGS += $(DBUS_INCLUDE)
 
 LIB_OBJS= \
index b3aff40..5f9e64a 100644 (file)
@@ -16,6 +16,7 @@
 #include <dbus/dbus.h>
 
 #include "common.h"
+#include "wpabuf.h"
 #include "dbus_dict_helpers.h"
 
 
@@ -443,11 +444,12 @@ dbus_bool_t wpa_dbus_dict_append_byte_array(DBusMessageIter *iter_dict,
 
 
 /**
- * Begin a string array entry in the dict
+ * Begin an array entry in the dict
  *
  * @param iter_dict A valid DBusMessageIter returned from
  *                  wpa_dbus_dict_open_write()
  * @param key The key of the dict item
+ * @param type The type of the contained data
  * @param iter_dict_entry A private DBusMessageIter provided by the caller to
  *                        be passed to wpa_dbus_dict_end_string_array()
  * @param iter_dict_val A private DBusMessageIter provided by the caller to
@@ -457,12 +459,21 @@ dbus_bool_t wpa_dbus_dict_append_byte_array(DBusMessageIter *iter_dict,
  * @return TRUE on success, FALSE on failure
  *
  */
-dbus_bool_t wpa_dbus_dict_begin_string_array(DBusMessageIter *iter_dict,
-                                            const char *key,
-                                            DBusMessageIter *iter_dict_entry,
-                                            DBusMessageIter *iter_dict_val,
-                                            DBusMessageIter *iter_array)
+dbus_bool_t wpa_dbus_dict_begin_array(DBusMessageIter *iter_dict,
+                                     const char *key, const char *type,
+                                     DBusMessageIter *iter_dict_entry,
+                                     DBusMessageIter *iter_dict_val,
+                                     DBusMessageIter *iter_array)
 {
+       char array_type[10];
+       int err;
+
+       err = os_snprintf(array_type, sizeof(array_type),
+                         DBUS_TYPE_ARRAY_AS_STRING "%s",
+                         type);
+       if (err < 0 || err > (int) sizeof(array_type))
+               return FALSE;
+
        if (!iter_dict || !iter_dict_entry || !iter_dict_val || !iter_array)
                return FALSE;
 
@@ -472,20 +483,31 @@ dbus_bool_t wpa_dbus_dict_begin_string_array(DBusMessageIter *iter_dict,
 
        if (!dbus_message_iter_open_container(iter_dict_entry,
                                              DBUS_TYPE_VARIANT,
-                                             DBUS_TYPE_ARRAY_AS_STRING
-                                             DBUS_TYPE_STRING_AS_STRING,
+                                             array_type,
                                              iter_dict_val))
                return FALSE;
 
        if (!dbus_message_iter_open_container(iter_dict_val, DBUS_TYPE_ARRAY,
-                                             DBUS_TYPE_BYTE_AS_STRING,
-                                             iter_array))
+                                             type, iter_array))
                return FALSE;
 
        return TRUE;
 }
 
 
+dbus_bool_t wpa_dbus_dict_begin_string_array(DBusMessageIter *iter_dict,
+                                            const char *key,
+                                            DBusMessageIter *iter_dict_entry,
+                                            DBusMessageIter *iter_dict_val,
+                                            DBusMessageIter *iter_array)
+{
+       return wpa_dbus_dict_begin_array(
+               iter_dict, key,
+               DBUS_TYPE_STRING_AS_STRING,
+               iter_dict_entry, iter_dict_val, iter_array);
+}
+
+
 /**
  * Add a single string element to a string array dict entry
  *
@@ -508,23 +530,67 @@ dbus_bool_t wpa_dbus_dict_string_array_add_element(DBusMessageIter *iter_array,
 
 
 /**
- * End a string array dict entry
+ * Add a single byte array element to a string array dict entry
+ *
+ * @param iter_array A valid DBusMessageIter returned from
+ *                   wpa_dbus_dict_begin_array()'s iter_array
+ *                   parameter -- note that wpa_dbus_dict_begin_array()
+ *                   must have been called with "ay" as the type
+ * @param value The data to be added to the dict entry's array
+ * @param value_len The length of the data
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_bin_array_add_element(DBusMessageIter *iter_array,
+                                               const u8 *value,
+                                               size_t value_len)
+{
+       DBusMessageIter iter_bytes;
+       size_t i;
+
+       if (!iter_array || !value)
+               return FALSE;
+
+       if (!dbus_message_iter_open_container(iter_array, DBUS_TYPE_ARRAY,
+                                             DBUS_TYPE_BYTE_AS_STRING,
+                                             &iter_bytes))
+               return FALSE;
+
+       for (i = 0; i < value_len; i++) {
+               if (!dbus_message_iter_append_basic(&iter_bytes,
+                                                   DBUS_TYPE_BYTE,
+                                                   &(value[i])))
+                       return FALSE;
+       }
+
+       if (!dbus_message_iter_close_container(iter_array, &iter_bytes))
+               return FALSE;
+
+       return TRUE;
+}
+
+
+/**
+ * End an array dict entry
  *
  * @param iter_dict A valid DBusMessageIter returned from
  *                  wpa_dbus_dict_open_write()
  * @param iter_dict_entry A private DBusMessageIter returned from
- *                        wpa_dbus_dict_end_string_array()
+ *                        wpa_dbus_dict_begin_string_array() or
+ *                       wpa_dbus_dict_begin_array()
  * @param iter_dict_val A private DBusMessageIter returned from
- *                      wpa_dbus_dict_end_string_array()
+ *                      wpa_dbus_dict_begin_string_array() or
+ *                     wpa_dbus_dict_begin_array()
  * @param iter_array A DBusMessageIter returned from
- *                   wpa_dbus_dict_end_string_array()
+ *                   wpa_dbus_dict_begin_string_array() or
+ *                  wpa_dbus_dict_begin_array()
  * @return TRUE on success, FALSE on failure
  *
  */
-dbus_bool_t wpa_dbus_dict_end_string_array(DBusMessageIter *iter_dict,
-                                          DBusMessageIter *iter_dict_entry,
-                                          DBusMessageIter *iter_dict_val,
-                                          DBusMessageIter *iter_array)
+dbus_bool_t wpa_dbus_dict_end_array(DBusMessageIter *iter_dict,
+                                   DBusMessageIter *iter_dict_entry,
+                                   DBusMessageIter *iter_dict_val,
+                                   DBusMessageIter *iter_array)
 {
        if (!iter_dict || !iter_dict_entry || !iter_dict_val || !iter_array)
                return FALSE;
@@ -583,6 +649,52 @@ dbus_bool_t wpa_dbus_dict_append_string_array(DBusMessageIter *iter_dict,
 }
 
 
+/**
+ * Convenience function to add an wpabuf binary array to the dict.
+ *
+ * @param iter_dict A valid DBusMessageIter returned from
+ *                  wpa_dbus_dict_open_write()
+ * @param key The key of the dict item
+ * @param items The array of wpabuf structures
+ * @param num_items The number of strings in the array
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_append_wpabuf_array(DBusMessageIter *iter_dict,
+                                             const char *key,
+                                             const struct wpabuf **items,
+                                             const dbus_uint32_t num_items)
+{
+       DBusMessageIter iter_dict_entry, iter_dict_val, iter_array;
+       dbus_uint32_t i;
+
+       if (!key)
+               return FALSE;
+       if (!items && (num_items != 0))
+               return FALSE;
+
+       if (!wpa_dbus_dict_begin_array(iter_dict, key,
+                                      DBUS_TYPE_ARRAY_AS_STRING
+                                      DBUS_TYPE_BYTE_AS_STRING,
+                                      &iter_dict_entry, &iter_dict_val,
+                                      &iter_array))
+               return FALSE;
+
+       for (i = 0; i < num_items; i++) {
+               if (!wpa_dbus_dict_bin_array_add_element(&iter_array,
+                                                        wpabuf_head(items[i]),
+                                                        wpabuf_len(items[i])))
+                       return FALSE;
+       }
+
+       if (!wpa_dbus_dict_end_array(iter_dict, &iter_dict_entry,
+                                    &iter_dict_val, &iter_array))
+               return FALSE;
+
+       return TRUE;
+}
+
+
 /*****************************************************/
 /* Stuff for reading dicts                           */
 /*****************************************************/
@@ -593,18 +705,26 @@ dbus_bool_t wpa_dbus_dict_append_string_array(DBusMessageIter *iter_dict,
  * @param iter A valid DBusMessageIter pointing to the start of the dict
  * @param iter_dict (out) A DBusMessageIter to be passed to
  *    wpa_dbus_dict_read_next_entry()
+ * @error on failure a descriptive error
  * @return TRUE on success, FALSE on failure
  *
  */
 dbus_bool_t wpa_dbus_dict_open_read(DBusMessageIter *iter,
-                                   DBusMessageIter *iter_dict)
+                                   DBusMessageIter *iter_dict,
+                                   DBusError *error)
 {
-       if (!iter || !iter_dict)
+       if (!iter || !iter_dict) {
+               dbus_set_error_const(error, DBUS_ERROR_FAILED,
+                                    "[internal] missing message iterators");
                return FALSE;
+       }
 
        if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY ||
-           dbus_message_iter_get_element_type(iter) != DBUS_TYPE_DICT_ENTRY)
+           dbus_message_iter_get_element_type(iter) != DBUS_TYPE_DICT_ENTRY) {
+               dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS,
+                                    "unexpected message argument types");
                return FALSE;
+       }
 
        dbus_message_iter_recurse(iter, iter_dict);
        return TRUE;
@@ -615,8 +735,7 @@ dbus_bool_t wpa_dbus_dict_open_read(DBusMessageIter *iter,
 #define BYTE_ARRAY_ITEM_SIZE (sizeof(char))
 
 static dbus_bool_t _wpa_dbus_dict_entry_get_byte_array(
-       DBusMessageIter *iter, int array_type,
-       struct wpa_dbus_dict_entry *entry)
+       DBusMessageIter *iter, struct wpa_dbus_dict_entry *entry)
 {
        dbus_uint32_t count = 0;
        dbus_bool_t success = FALSE;
@@ -733,6 +852,66 @@ done:
 }
 
 
+#define BIN_ARRAY_CHUNK_SIZE 10
+#define BIN_ARRAY_ITEM_SIZE (sizeof(struct wpabuf *))
+
+static dbus_bool_t _wpa_dbus_dict_entry_get_binarray(
+       DBusMessageIter *iter, struct wpa_dbus_dict_entry *entry)
+{
+       struct wpa_dbus_dict_entry tmpentry;
+       size_t buflen = 0;
+       int i;
+
+       if (dbus_message_iter_get_element_type(iter) != DBUS_TYPE_BYTE)
+               return FALSE;
+
+       entry->array_type = WPAS_DBUS_TYPE_BINARRAY;
+       entry->array_len = 0;
+       entry->binarray_value = NULL;
+
+       while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_ARRAY) {
+               DBusMessageIter iter_array;
+
+               if (entry->array_len == buflen) {
+                       struct wpabuf **newbuf;
+
+                       buflen += BIN_ARRAY_CHUNK_SIZE;
+
+                       newbuf = os_realloc(entry->binarray_value,
+                                           buflen * BIN_ARRAY_ITEM_SIZE);
+                       if (!newbuf)
+                               goto cleanup;
+                       entry->binarray_value = newbuf;
+               }
+
+               dbus_message_iter_recurse(iter, &iter_array);
+               if (_wpa_dbus_dict_entry_get_byte_array(&iter_array, &tmpentry)
+                                       == FALSE)
+                       goto cleanup;
+
+               entry->binarray_value[entry->array_len] =
+                       wpabuf_alloc_ext_data((u8 *) tmpentry.bytearray_value,
+                                             tmpentry.array_len);
+               if (entry->binarray_value[entry->array_len] == NULL) {
+                       wpa_dbus_dict_entry_clear(&tmpentry);
+                       goto cleanup;
+               }
+               entry->array_len++;
+               dbus_message_iter_next(iter);
+       }
+
+       return TRUE;
+
+ cleanup:
+       for (i = 0; i < (int) entry->array_len; i++)
+               wpabuf_free(entry->binarray_value[i]);
+       os_free(entry->binarray_value);
+       entry->array_len = 0;
+       entry->binarray_value = NULL;
+       return FALSE;
+}
+
+
 static dbus_bool_t _wpa_dbus_dict_entry_get_array(
        DBusMessageIter *iter_dict_val, struct wpa_dbus_dict_entry *entry)
 {
@@ -748,7 +927,6 @@ static dbus_bool_t _wpa_dbus_dict_entry_get_array(
        switch (array_type) {
        case DBUS_TYPE_BYTE:
                success = _wpa_dbus_dict_entry_get_byte_array(&iter_array,
-                                                             array_type,
                                                              entry);
                break;
        case DBUS_TYPE_STRING:
@@ -756,6 +934,8 @@ static dbus_bool_t _wpa_dbus_dict_entry_get_array(
                                                                array_type,
                                                                entry);
                break;
+       case DBUS_TYPE_ARRAY:
+               success = _wpa_dbus_dict_entry_get_binarray(&iter_array, entry);
        default:
                break;
        }
@@ -915,6 +1095,11 @@ void wpa_dbus_dict_entry_clear(struct wpa_dbus_dict_entry *entry)
                                os_free(entry->strarray_value[i]);
                        os_free(entry->strarray_value);
                        break;
+               case WPAS_DBUS_TYPE_BINARRAY:
+                       for (i = 0; i < entry->array_len; i++)
+                               wpabuf_free(entry->binarray_value[i]);
+                       os_free(entry->binarray_value);
+                       break;
                }
                break;
        }
index eb31575..2f6eb45 100644 (file)
@@ -15,6 +15,8 @@
 #ifndef DBUS_DICT_HELPERS_H
 #define DBUS_DICT_HELPERS_H
 
+#include "wpabuf.h"
+
 /*
  * Adding a dict to a DBusMessage
  */
@@ -74,7 +76,13 @@ dbus_bool_t wpa_dbus_dict_append_byte_array(DBusMessageIter *iter_dict,
                                            const char *value,
                                            const dbus_uint32_t value_len);
 
-/* Manual construction and addition of string array elements */
+/* Manual construction and addition of array elements */
+dbus_bool_t wpa_dbus_dict_begin_array(DBusMessageIter *iter_dict,
+                                      const char *key, const char *type,
+                                      DBusMessageIter *iter_dict_entry,
+                                      DBusMessageIter *iter_dict_val,
+                                      DBusMessageIter *iter_array);
+
 dbus_bool_t wpa_dbus_dict_begin_string_array(DBusMessageIter *iter_dict,
                                              const char *key,
                                              DBusMessageIter *iter_dict_entry,
@@ -84,10 +92,24 @@ dbus_bool_t wpa_dbus_dict_begin_string_array(DBusMessageIter *iter_dict,
 dbus_bool_t wpa_dbus_dict_string_array_add_element(DBusMessageIter *iter_array,
                                              const char *elem);
 
-dbus_bool_t wpa_dbus_dict_end_string_array(DBusMessageIter *iter_dict,
-                                           DBusMessageIter *iter_dict_entry,
-                                           DBusMessageIter *iter_dict_val,
-                                           DBusMessageIter *iter_array);
+dbus_bool_t wpa_dbus_dict_bin_array_add_element(DBusMessageIter *iter_array,
+                                               const u8 *value,
+                                               size_t value_len);
+
+dbus_bool_t wpa_dbus_dict_end_array(DBusMessageIter *iter_dict,
+                                    DBusMessageIter *iter_dict_entry,
+                                    DBusMessageIter *iter_dict_val,
+                                    DBusMessageIter *iter_array);
+
+static inline dbus_bool_t
+wpa_dbus_dict_end_string_array(DBusMessageIter *iter_dict,
+                              DBusMessageIter *iter_dict_entry,
+                              DBusMessageIter *iter_dict_val,
+                              DBusMessageIter *iter_array)
+{
+       return wpa_dbus_dict_end_array(iter_dict, iter_dict_entry,
+                                      iter_dict_val, iter_array);
+}
 
 /* Convenience function to add a whole string list */
 dbus_bool_t wpa_dbus_dict_append_string_array(DBusMessageIter *iter_dict,
@@ -95,14 +117,22 @@ dbus_bool_t wpa_dbus_dict_append_string_array(DBusMessageIter *iter_dict,
                                              const char **items,
                                              const dbus_uint32_t num_items);
 
+dbus_bool_t wpa_dbus_dict_append_wpabuf_array(DBusMessageIter *iter_dict,
+                                             const char *key,
+                                             const struct wpabuf **items,
+                                             const dbus_uint32_t num_items);
+
 /*
  * Reading a dict from a DBusMessage
  */
 
+#define WPAS_DBUS_TYPE_BINARRAY (DBUS_NUMBER_OF_TYPES + 100)
+
 struct wpa_dbus_dict_entry {
        int type;         /** the dbus type of the dict entry's value */
        int array_type;   /** the dbus type of the array elements if the dict
-                             entry value contains an array */
+                             entry value contains an array, or the special
+                             WPAS_DBUS_TYPE_BINARRAY */
        const char *key;  /** key of the dict entry */
 
        /** Possible values of the property */
@@ -119,13 +149,15 @@ struct wpa_dbus_dict_entry {
                double double_value;
                char *bytearray_value;
                char **strarray_value;
+               struct wpabuf **binarray_value;
        };
        dbus_uint32_t array_len; /** length of the array if the dict entry's
                                     value contains an array */
 };
 
 dbus_bool_t wpa_dbus_dict_open_read(DBusMessageIter *iter,
-                                   DBusMessageIter *iter_dict);
+                                   DBusMessageIter *iter_dict,
+                                   DBusError *error);
 
 dbus_bool_t wpa_dbus_dict_get_entry(DBusMessageIter *iter_dict,
                                    struct wpa_dbus_dict_entry *entry);
index bdfbbac..a871f41 100644 (file)
 #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.h"
 #include "dbus_common_i.h"
+#include "dbus_new_handlers_p2p.h"
+#include "p2p/p2p.h"
 
 
 /**
@@ -42,7 +46,7 @@ static void wpas_dbus_signal_interface(struct wpa_supplicant *wpa_s,
 {
        struct wpas_dbus_priv *iface;
        DBusMessage *msg;
-       DBusMessageIter iter, iter_dict;
+       DBusMessageIter iter;
 
        iface = wpa_s->global->dbus;
 
@@ -61,14 +65,9 @@ static void wpas_dbus_signal_interface(struct wpa_supplicant *wpa_s,
                goto err;
 
        if (properties) {
-               if (!wpa_dbus_dict_open_write(&iter, &iter_dict))
-                       goto err;
-
-               wpa_dbus_get_object_properties(iface, wpa_s->dbus_new_path,
-                                              WPAS_DBUS_NEW_IFACE_INTERFACE,
-                                              &iter_dict);
-
-               if (!wpa_dbus_dict_close_write(&iter, &iter_dict))
+               if (!wpa_dbus_get_object_properties(
+                           iface, wpa_s->dbus_new_path,
+                           WPAS_DBUS_NEW_IFACE_INTERFACE, &iter))
                        goto err;
        }
 
@@ -157,7 +156,7 @@ static void wpas_dbus_signal_bss(struct wpa_supplicant *wpa_s,
 {
        struct wpas_dbus_priv *iface;
        DBusMessage *msg;
-       DBusMessageIter iter, iter_dict;
+       DBusMessageIter iter;
 
        iface = wpa_s->global->dbus;
 
@@ -177,14 +176,9 @@ static void wpas_dbus_signal_bss(struct wpa_supplicant *wpa_s,
                goto err;
 
        if (properties) {
-               if (!wpa_dbus_dict_open_write(&iter, &iter_dict))
-                       goto err;
-
-               wpa_dbus_get_object_properties(iface, bss_obj_path,
-                                              WPAS_DBUS_NEW_IFACE_BSS,
-                                              &iter_dict);
-
-               if (!wpa_dbus_dict_close_write(&iter, &iter_dict))
+               if (!wpa_dbus_get_object_properties(iface, bss_obj_path,
+                                                   WPAS_DBUS_NEW_IFACE_BSS,
+                                                   &iter))
                        goto err;
        }
 
@@ -304,7 +298,7 @@ static void wpas_dbus_signal_network(struct wpa_supplicant *wpa_s,
 {
        struct wpas_dbus_priv *iface;
        DBusMessage *msg;
-       DBusMessageIter iter, iter_dict;
+       DBusMessageIter iter;
        char net_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
 
        iface = wpa_s->global->dbus;
@@ -330,14 +324,9 @@ static void wpas_dbus_signal_network(struct wpa_supplicant *wpa_s,
                goto err;
 
        if (properties) {
-               if (!wpa_dbus_dict_open_write(&iter, &iter_dict))
-                       goto err;
-
-               wpa_dbus_get_object_properties(iface, net_obj_path,
-                                              WPAS_DBUS_NEW_IFACE_NETWORK,
-                                              &iter_dict);
-
-               if (!wpa_dbus_dict_close_write(&iter, &iter_dict))
+               if (!wpa_dbus_get_object_properties(
+                           iface, net_obj_path, WPAS_DBUS_NEW_IFACE_NETWORK,
+                           &iter))
                        goto err;
        }
 
@@ -394,6 +383,67 @@ void wpas_dbus_signal_network_selected(struct wpa_supplicant *wpa_s, int id)
 
 
 /**
+ * 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)
+               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))
+               goto err;
+       if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &field))
+               goto err;
+       if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &txt))
+               goto err;
+
+       dbus_connection_send(iface->con, msg, NULL);
+       dbus_message_unref(msg);
+       return;
+
+err:
+       wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+       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
@@ -650,381 +700,2146 @@ nomem:
 
 #endif /* CONFIG_WPS */
 
+void wpas_dbus_signal_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 *msg;
+       DBusMessageIter iter, dict_iter;
+
+       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_INTERFACE,
+                                     "Certification");
+       if (msg == NULL)
+               return;
+
+       dbus_message_iter_init_append(msg, &iter);
+       if (!wpa_dbus_dict_open_write(&iter, &dict_iter))
+               goto nomem;
+
+       if (!wpa_dbus_dict_append_uint32(&dict_iter, "depth", depth) ||
+           !wpa_dbus_dict_append_string(&dict_iter, "subject", subject))
+               goto nomem;
+
+       if (cert_hash &&
+           !wpa_dbus_dict_append_string(&dict_iter, "cert_hash", cert_hash))
+               goto nomem;
+
+       if (cert &&
+           !wpa_dbus_dict_append_byte_array(&dict_iter, "cert",
+                                            wpabuf_head(cert),
+                                            wpabuf_len(cert)))
+               goto nomem;
+
+       if (!wpa_dbus_dict_close_write(&iter, &dict_iter))
+               goto nomem;
+
+       dbus_connection_send(iface->con, msg, NULL);
+
+nomem:
+       dbus_message_unref(msg);
+}
+
+#ifdef CONFIG_P2P
 
 /**
- * wpas_dbus_signal_prop_changed - Signals change of property
+ * wpas_dbus_signal_p2p_group_removed - Signals P2P group was removed
  * @wpa_s: %wpa_supplicant network interface data
- * @property: indicates which property has changed
- *
- * Sends ProertyChanged signals with path, interface and arguments
- * depending on which property has changed.
+ * @role: role of this device (client or GO)
+ * Sends signal with i/f name and role as string arguments
  */
-void wpas_dbus_signal_prop_changed(struct wpa_supplicant *wpa_s,
-                                  enum wpas_dbus_prop property)
+void wpas_dbus_signal_p2p_group_removed(struct wpa_supplicant *wpa_s,
+                                       const char *role)
 {
-       WPADBusPropertyAccessor getter;
-       char *prop;
 
-       if (wpa_s->dbus_new_path == NULL)
-               return; /* Skip signal since D-Bus setup is not yet ready */
+       DBusMessage *msg;
+       DBusMessageIter iter;
+       struct wpas_dbus_priv *iface = wpa_s->global->dbus;
+       char *ifname = wpa_s->ifname;
 
-       switch (property) {
-       case WPAS_DBUS_PROP_AP_SCAN:
-               getter = (WPADBusPropertyAccessor) wpas_dbus_getter_ap_scan;
-               prop = "ApScan";
-               break;
-       case WPAS_DBUS_PROP_SCANNING:
-               getter = (WPADBusPropertyAccessor) wpas_dbus_getter_scanning;
-               prop = "Scanning";
-               break;
-       case WPAS_DBUS_PROP_STATE:
-               getter = (WPADBusPropertyAccessor) wpas_dbus_getter_state;
-               prop = "State";
-               break;
-       case WPAS_DBUS_PROP_CURRENT_BSS:
-               getter = (WPADBusPropertyAccessor)
-                       wpas_dbus_getter_current_bss;
-               prop = "CurrentBSS";
-               break;
-       case WPAS_DBUS_PROP_CURRENT_NETWORK:
-               getter = (WPADBusPropertyAccessor)
-                       wpas_dbus_getter_current_network;
-               prop = "CurrentNetwork";
-               break;
-       default:
-               wpa_printf(MSG_ERROR, "dbus: %s: Unknown Property value %d",
-                          __func__, property);
+       /* 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,
+                                     "GroupFinished");
+       if (msg == NULL)
                return;
+
+       dbus_message_iter_init_append(msg, &iter);
+
+       if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &ifname)) {
+               wpa_printf(MSG_ERROR, "dbus: Failed to construct GroupFinished"
+                                     "signal -not enough memory for ifname ");
+               goto err;
        }
 
-       wpa_dbus_mark_property_changed(wpa_s->global->dbus,
-                                      wpa_s->dbus_new_path,
-                                      WPAS_DBUS_NEW_IFACE_INTERFACE, prop);
+       if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &role))
+               wpa_printf(MSG_ERROR, "dbus: Failed to construct GroupFinished"
+                                     "signal -not enough memory for role ");
+       else
+               dbus_connection_send(iface->con, msg, NULL);
+
+err:
+       dbus_message_unref(msg);
 }
 
 
 /**
- * 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
+ * wpas_dbus_signal_p2p_provision_discovery - Signals various PD events
  *
- * Sends PropertyChanged signals with path, interface, and arguments depending
- * on which property has changed.
+ * @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_bss_signal_prop_changed(struct wpa_supplicant *wpa_s,
-                                      enum wpas_dbus_bss_prop property,
-                                      unsigned int id)
+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)
 {
-       char path[WPAS_DBUS_OBJECT_PATH_MAX];
-       char *prop;
+       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;
 
-       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_IES:
-               prop = "IEs";
-               break;
-       default:
-               wpa_printf(MSG_ERROR, "dbus: %s: Unknown Property value %d",
-                          __func__, property);
+       iface = wpa_s->global->dbus;
+
+       /* Do nothing if the control interface is not turned on */
+       if (iface == NULL)
                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 if (!request && status)
+               /* 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;
        }
 
-       os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX,
-                   "%s/" WPAS_DBUS_NEW_BSSIDS_PART "/%u",
-                   wpa_s->dbus_new_path, id);
+       msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+                                     WPAS_DBUS_NEW_IFACE_P2PDEVICE, _signal);
+       if (msg == NULL)
+               return;
 
-       wpa_dbus_mark_property_changed(wpa_s->global->dbus, path,
-                                      WPAS_DBUS_NEW_IFACE_BSS, prop);
+       /* 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_debug_level_changed - Signals change of debug param
- * @global: wpa_global structure
- *
- * Sends ProertyChanged signals informing that debug level has changed.
- */
-void wpas_dbus_signal_debug_level_changed(struct wpa_global *global)
+void wpas_dbus_signal_p2p_go_neg_req(struct wpa_supplicant *wpa_s,
+                                    const u8 *src, u16 dev_passwd_id)
 {
-       wpa_dbus_mark_property_changed(global->dbus, WPAS_DBUS_NEW_PATH,
-                                      WPAS_DBUS_NEW_INTERFACE,
-                                      "DebugLevel");
+       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;
+
+       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,
+                                           &dev_passwd_id))
+               wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+       else
+               dbus_connection_send(iface->con, msg, NULL);
+
+       dbus_message_unref(msg);
 }
 
 
-/**
- * wpas_dbus_signal_debug_timestamp_changed - Signals change of debug param
- * @global: wpa_global structure
- *
- * Sends ProertyChanged signals informing that debug timestamp has changed.
- */
-void wpas_dbus_signal_debug_timestamp_changed(struct wpa_global *global)
+static int wpas_dbus_get_group_obj_path(struct wpa_supplicant *wpa_s,
+                                       const struct wpa_ssid *ssid,
+                                       char *group_obj_path)
 {
-       wpa_dbus_mark_property_changed(global->dbus, WPAS_DBUS_NEW_PATH,
-                                      WPAS_DBUS_NEW_INTERFACE,
-                                      "DebugTimestamp");
+       char group_name[3];
+
+       if (os_memcmp(ssid->ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN))
+               return -1;
+
+       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;
 }
 
 
 /**
- * wpas_dbus_signal_debug_show_keys_changed - Signals change of debug param
- * @global: wpa_global structure
+ * 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
  *
- * Sends ProertyChanged signals informing that debug show_keys has changed.
+ * @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_debug_show_keys_changed(struct wpa_global *global)
+void wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s,
+                                       const struct wpa_ssid *ssid,
+                                       int client, int network_id)
 {
-       wpa_dbus_mark_property_changed(global->dbus, WPAS_DBUS_NEW_PATH,
-                                      WPAS_DBUS_NEW_INTERFACE,
-                                      "DebugShowKeys");
-}
+       DBusMessage *msg;
+       DBusMessageIter iter, dict_iter;
+       struct wpas_dbus_priv *iface;
+       char net_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
+       char group_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
 
+       iface = wpa_s->parent->global->dbus;
 
-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)
+       /* Do nothing if the control interface is not turned on */
+       if (iface == NULL)
+               return;
+
+       if (wpas_dbus_get_group_obj_path(wpa_s, ssid, group_obj_path) < 0)
+               return;
+
+       /* New interface has been created for this group */
+       msg = dbus_message_new_signal(wpa_s->parent->dbus_new_path,
+                                     WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+                                     "GroupStarted");
+
+       if (msg == NULL)
+               return;
+
+       dbus_message_iter_init_append(msg, &iter);
+       if (!wpa_dbus_dict_open_write(&iter, &dict_iter))
+               goto nomem;
+
+       /*
+        * In case the device supports creating a separate interface the
+        * DBus client will need to know the object path for the interface
+        * object this group was created on, so include it here.
+        */
+       if (!wpa_dbus_dict_append_object_path(&dict_iter,
+                                       "interface_object",
+                                       wpa_s->dbus_new_path))
+               goto nomem;
+
+       if (!wpa_dbus_dict_append_string(&dict_iter, "role",
+                                        client ? "client" : "GO"))
+               goto nomem;
+
+       os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+                   "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%u",
+                   wpa_s->parent->dbus_new_path, network_id);
+
+       if (!wpa_dbus_dict_append_object_path(&dict_iter, "group_object",
+                                            group_obj_path) ||
+          !wpa_dbus_dict_append_object_path(&dict_iter, "network_object",
+                                            net_obj_path) ||
+          !wpa_dbus_dict_close_write(&iter, &dict_iter))
+               goto nomem;
+
+       dbus_connection_send(iface->con, msg, NULL);
+
+nomem:
+       dbus_message_unref(msg);
+}
+
+
+/**
+ *
+ * Method to emit GONeogtiation Success or Failure signals based
+ * on status.
+ * @status: Status of the GO neg request. 0 for success, other for errors.
+ */
+void wpas_dbus_signal_p2p_go_neg_resp(struct wpa_supplicant *wpa_s,
+                                     struct p2p_go_neg_results *res)
 {
-       int n;
+       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;
 
-       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++;
+       iface = wpa_s->global->dbus;
 
-       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__);
+       os_memset(freqs, 0, sizeof(freqs));
+       /* Do nothing if the control interface is not turned on */
+       if (iface == 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(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))
+               goto err;
+       if (!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) {
+                       if (!wpa_dbus_dict_append_byte_array(
+                                   &dict_iter, "passphrase",
+                                   (const char *) res->passphrase,
+                                   sizeof(res->passphrase)))
+                               goto err;
+               }
+
+               if (!wpa_dbus_dict_append_string(&dict_iter, "role_go",
+                                                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))
+                       goto err;
+
+               if (!dbus_message_iter_append_fixed_array(&iter_dict_array,
+                                                         DBUS_TYPE_INT32,
+                                                         &f_array,
+                                                         freq_list_num))
+                       goto err;
+
+               if (!wpa_dbus_dict_end_array(&dict_iter,
+                                            &iter_dict_entry,
+                                            &iter_dict_val,
+                                            &iter_dict_array))
+                       goto err;
+
+               if (!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);
 }
 
 
-static const struct wpa_dbus_method_desc wpas_dbus_global_methods[] = {
-       { "CreateInterface", WPAS_DBUS_NEW_INTERFACE,
-         (WPADBusMethodHandler) &wpas_dbus_handler_create_interface,
+/**
+ *
+ * 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
+ */
+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_INFO, "%s\n", __func__);
+
+       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,
+                                     "InvitationResult");
+
+       if (msg == NULL)
+               return;
+
+       dbus_message_iter_init_append(msg, &iter);
+       if (!wpa_dbus_dict_open_write(&iter, &dict_iter))
+               goto nomem;
+
+       if (!wpa_dbus_dict_append_int32(&dict_iter, "status", status))
+               goto nomem;
+       if (bssid) {
+               if (!wpa_dbus_dict_append_byte_array(&dict_iter, "BSSID",
+                                                    (const char *) bssid,
+                                                    ETH_ALEN))
+                       goto nomem;
+       }
+       if (!wpa_dbus_dict_close_write(&iter, &dict_iter))
+               goto nomem;
+
+       dbus_connection_send(iface->con, msg, NULL);
+
+nomem:
+       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
+ * @member_addr: addr (p2p i/f) of the peer joining the group
+ */
+void wpas_dbus_signal_p2p_peer_joined(struct wpa_supplicant *wpa_s,
+                                     const u8 *member)
+{
+       struct wpas_dbus_priv *iface;
+       DBusMessage *msg;
+       DBusMessageIter iter;
+       char groupmember_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->dbus_groupobj_path)
+               return;
+
+       os_snprintf(groupmember_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+                       "%s/"  WPAS_DBUS_NEW_P2P_GROUPMEMBERS_PART "/"
+                       COMPACT_MACSTR,
+                       wpa_s->dbus_groupobj_path, MAC2STR(member));
+
+       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 = groupmember_obj_path;
+       if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+                                           &path))
+               goto err;
+
+       dbus_connection_send(iface->con, msg, NULL);
+
+       dbus_message_unref(msg);
+       return;
+
+err:
+       wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+       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 p2p i/f addr used for connecting.
+ *
+ * @wpa_s: %wpa_supplicant network interface data
+ * @member_addr: addr (p2p i/f) of the peer joining the group
+ */
+void wpas_dbus_signal_p2p_peer_disconnected(struct wpa_supplicant *wpa_s,
+                                     const u8 *member)
+{
+       struct wpas_dbus_priv *iface;
+       DBusMessage *msg;
+       DBusMessageIter iter;
+       char groupmember_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->dbus_groupobj_path)
+               return;
+
+       os_snprintf(groupmember_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+                       "%s/"  WPAS_DBUS_NEW_P2P_GROUPMEMBERS_PART "/"
+                       COMPACT_MACSTR,
+                       wpa_s->dbus_groupobj_path, MAC2STR(member));
+
+       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 = groupmember_obj_path;
+       if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+                                           &path))
+               goto err;
+
+       dbus_connection_send(iface->con, msg, NULL);
+
+       dbus_message_unref(msg);
+       return;
+
+err:
+       wpa_printf(MSG_ERROR, "dbus: Failed to construct PeerDisconnected "
+                             "signal");
+       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;
+
+       msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+                                     WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+                                     "ServiceDiscoveryRequest");
+       if (msg == NULL)
+               return;
+
+       /* Check if this is a known peer */
+       if (!p2p_peer_known(wpa_s->global->p2p, sa))
+               goto error;
+
+       os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+                   "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/"
+                   COMPACT_MACSTR, wpa_s->dbus_new_path, MAC2STR(sa));
+
+       path = peer_obj_path;
+
+       dbus_message_iter_init_append(msg, &iter);
+       if (!wpa_dbus_dict_open_write(&iter, &dict_iter))
+               goto error;
+
+
+       if (!wpa_dbus_dict_append_object_path(&dict_iter, "peer_object",
+                                             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))
+               goto error;
+
+       dbus_connection_send(iface->con, msg, NULL);
+       dbus_message_unref(msg);
+       return;
+error:
+       wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+       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;
+
+       msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+                                     WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+                               "ServiceDiscoveryResponse");
+       if (msg == NULL)
+               return;
+
+       /* Check if this is a known peer */
+       if (!p2p_peer_known(wpa_s->global->p2p, sa))
+               goto error;
+
+       os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+                   "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/"
+                   COMPACT_MACSTR, wpa_s->dbus_new_path, MAC2STR(sa));
+
+       path = peer_obj_path;
+
+       dbus_message_iter_init_append(msg, &iter);
+       if (!wpa_dbus_dict_open_write(&iter, &dict_iter))
+               goto error;
+
+       if (!wpa_dbus_dict_append_object_path(&dict_iter, "peer_object",
+                                             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))
+               goto error;
+
+
+       dbus_connection_send(iface->con, msg, NULL);
+       dbus_message_unref(msg);
+       return;
+error:
+       wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+       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;
+
+       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))
+               goto err;
+
+       if (properties) {
+               if (!wpa_dbus_get_object_properties(
+                           iface, pgrp_obj_path,
+                           WPAS_DBUS_NEW_IFACE_PERSISTENT_GROUP, &iter))
+                       goto err;
+       }
+
+       dbus_connection_send(iface->con, msg, NULL);
+
+       dbus_message_unref(msg);
+       return;
+
+err:
+       wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+       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
+ *
+ * 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;
+
+       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);
+}
+
+#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;
+
+       if (wpa_s->dbus_new_path == NULL)
+               return; /* Skip signal since D-Bus setup is not yet ready */
+
+       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;
+       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);
+}
+
+
+/**
+ * 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;
+
+       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_IES:
+               prop = "IEs";
+               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
+       },
+       { 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
+         }
+       },
+       { "NetworkRequest", WPAS_DBUS_NEW_IFACE_INTERFACE,
+         {
+                 { "path", "o", ARG_OUT },
+                 { "field", "s", ARG_OUT },
+                 { "text", "s", ARG_OUT },
+                 END_ARGS
+         }
+       },
+       /* Deprecated: use org.freedesktop.DBus.Properties.PropertiesChanged */
+       { "PropertiesChanged", WPAS_DBUS_NEW_INTERFACE,
+         {
+                 { "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)
+               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;
+       struct wpa_ssid *ssid;
+
+       ssid = wpa_config_get_network(wpa_s->conf, nid);
+
+#ifdef CONFIG_P2P
+       /* 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
+       },
+       { "IEs", WPAS_DBUS_NEW_IFACE_BSS, "ay",
+         wpas_dbus_getter_bss_ies,
+         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)
+               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 */
+       if (wpa_s == NULL || wpa_s->global == NULL)
+               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
+         }
+       },
+       { "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
+         }
+       },
+       { "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
+         }
+       },
+       { "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
+         }
+       },
+#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
+         }
+       },
+#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
+         }
+       },
+       { "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
+         }
+       },
+       { "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 },
+                 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
+         }
+       },
+       { "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
+         }
+       },
+       { 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
+       },
+#ifdef CONFIG_WPS
+       { "ProcessCredentials", WPAS_DBUS_NEW_IFACE_WPS, "b",
+         wpas_dbus_getter_process_credentials,
+         wpas_dbus_setter_process_credentials
+       },
+#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 */
+       { 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,
          {
-                 { "args", "a{sv}", ARG_IN },
-                 { "path", "o", ARG_OUT },
+                 { "properties", "a{sv}", ARG_OUT },
                  END_ARGS
          }
        },
-       { "RemoveInterface", WPAS_DBUS_NEW_INTERFACE,
-         (WPADBusMethodHandler) &wpas_dbus_handler_remove_interface,
+#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+       { "P2PStateChanged", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
          {
-                 { "path", "o", ARG_IN },
+                 { "states", "a{ss}", ARG_OUT },
                  END_ARGS
          }
        },
-       { "GetInterface", WPAS_DBUS_NEW_INTERFACE,
-         (WPADBusMethodHandler) &wpas_dbus_handler_get_interface,
+       { "DeviceFound", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
          {
-                 { "ifname", "s", ARG_IN },
                  { "path", "o", ARG_OUT },
+                 { "properties", "a{sv}", 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",
-         (WPADBusPropertyAccessor) wpas_dbus_getter_debug_level,
-         (WPADBusPropertyAccessor) wpas_dbus_setter_debug_level,
-         RW
+       { "DeviceLost", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+         {
+                 { "path", "o", ARG_OUT },
+                 END_ARGS
+         }
        },
-       { "DebugTimestamp", WPAS_DBUS_NEW_INTERFACE, "b",
-         (WPADBusPropertyAccessor) wpas_dbus_getter_debug_timestamp,
-         (WPADBusPropertyAccessor) wpas_dbus_setter_debug_timestamp,
-         RW
+       { "ProvisionDiscoveryRequestDisplayPin", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+         {
+                 { "peer_object", "o", ARG_OUT },
+                 { "pin", "s", ARG_OUT },
+                 END_ARGS
+         }
        },
-       { "DebugShowKeys", WPAS_DBUS_NEW_INTERFACE, "b",
-         (WPADBusPropertyAccessor) wpas_dbus_getter_debug_show_keys,
-         (WPADBusPropertyAccessor) wpas_dbus_setter_debug_show_keys,
-         RW
+       { "ProvisionDiscoveryResponseDisplayPin", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+         {
+                 { "peer_object", "o", ARG_OUT },
+                 { "pin", "s", ARG_OUT },
+                 END_ARGS
+         }
        },
-       { "Interfaces", WPAS_DBUS_NEW_INTERFACE, "ao",
-         (WPADBusPropertyAccessor) &wpas_dbus_getter_interfaces,
-         NULL,
-         R
+       { "ProvisionDiscoveryRequestEnterPin", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+         {
+                 { "peer_object", "o", ARG_OUT },
+                 END_ARGS
+         }
        },
-       { "EapMethods", WPAS_DBUS_NEW_INTERFACE, "as",
-         (WPADBusPropertyAccessor) wpas_dbus_getter_eap_methods,
-         NULL,
-         R
+       { "ProvisionDiscoveryResponseEnterPin", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+         {
+                 { "peer_object", "o", ARG_OUT },
+                 END_ARGS
+         }
        },
-       { NULL, NULL, NULL, NULL, NULL, 0 }
-};
-
-static const struct wpa_dbus_signal_desc wpas_dbus_global_signals[] = {
-       { "InterfaceAdded", WPAS_DBUS_NEW_INTERFACE,
+       { "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,
          {
-                 { "path", "o", ARG_OUT },
                  { "properties", "a{sv}", ARG_OUT },
                  END_ARGS
          }
        },
-       { "InterfaceRemoved", WPAS_DBUS_NEW_INTERFACE,
+       { "GONegotiationSuccess", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+         {
+                 END_ARGS
+         }
+       },
+       { "GONegotiationFailure", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+         {
+                 { "status", "i", ARG_OUT },
+                 END_ARGS
+         }
+       },
+       { "GONegotiationRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
          {
                  { "path", "o", ARG_OUT },
+                 { "dev_passwd_id", "i", ARG_OUT },
                  END_ARGS
          }
        },
-       { "PropertiesChanged", WPAS_DBUS_NEW_INTERFACE,
+       { "InvitationResult", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
          {
-                 { "properties", "a{sv}", ARG_OUT },
+                 { "invite_result", "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}",
-         (WPADBusPropertyAccessor) wpas_dbus_getter_network_properties,
-         (WPADBusPropertyAccessor) wpas_dbus_setter_network_properties,
-         RW
+       { "GroupFinished", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+         {
+                 { "ifname", "s", ARG_OUT },
+                 { "role", "s", ARG_OUT },
+                 END_ARGS
+         }
        },
-       { "Enabled", WPAS_DBUS_NEW_IFACE_NETWORK, "b",
-         (WPADBusPropertyAccessor) wpas_dbus_getter_enabled,
-         (WPADBusPropertyAccessor) wpas_dbus_setter_enabled,
-         RW
+       { "ServiceDiscoveryRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+         {
+                 { "sd_request", "a{sv}", ARG_OUT },
+                 END_ARGS
+         }
        },
-       { NULL, NULL, NULL, NULL, NULL, 0 }
-};
-
-
-static const struct wpa_dbus_signal_desc wpas_dbus_network_signals[] = {
-       { "PropertiesChanged", WPAS_DBUS_NEW_IFACE_NETWORK,
+       { "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
+         }
+       },
+#endif /* CONFIG_P2P */
+       { "Certification", WPAS_DBUS_NEW_IFACE_INTERFACE,
+         {
+                 { "certification", "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)
+int wpas_dbus_register_interface(struct wpa_supplicant *wpa_s)
 {
-       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];
+
+       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 (wpa_s == NULL || wpa_s->global == 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, ssid->id);
+       /* 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);
 
-       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 "
@@ -1032,206 +2847,212 @@ int wpas_dbus_register_network(struct wpa_supplicant *wpa_s,
                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);
+       wpas_dbus_register(obj_desc, wpa_s, NULL, wpas_dbus_interface_methods,
+                          wpas_dbus_interface_properties,
+                          wpas_dbus_interface_signals);
 
-       if (wpa_dbus_register_object_per_iface(ctrl_iface, net_obj_path,
+       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_network_added(wpa_s, ssid->id);
+       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_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)
+int wpas_dbus_unregister_interface(struct wpa_supplicant *wpa_s)
 {
        struct wpas_dbus_priv *ctrl_iface;
-       char net_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
-       int ret;
 
        /* Do nothing if the control interface is not turned on */
-       if (wpa_s == NULL || wpa_s->global == NULL ||
-           wpa_s->dbus_new_path == NULL)
+       if (wpa_s == NULL || wpa_s->global == 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 interface object '%s'",
+                  wpa_s->dbus_new_path);
+       if (wpa_dbus_unregister_object_per_iface(ctrl_iface,
+                                                wpa_s->dbus_new_path))
+               return -1;
 
-       wpa_printf(MSG_DEBUG, "dbus: Unregister network object '%s'",
-                  net_obj_path);
-       ret = wpa_dbus_unregister_object_per_iface(ctrl_iface, net_obj_path);
+       wpas_dbus_signal_interface_removed(wpa_s);
 
-       if (!ret)
-               wpas_dbus_signal_network_removed(wpa_s, nid);
+       os_free(wpa_s->dbus_new_path);
+       wpa_s->dbus_new_path = NULL;
 
-       return ret;
+       return 0;
 }
 
+#ifdef CONFIG_P2P
 
-static const struct wpa_dbus_property_desc wpas_dbus_bss_properties[] = {
-       { "SSID", WPAS_DBUS_NEW_IFACE_BSS, "ay",
-         (WPADBusPropertyAccessor) wpas_dbus_getter_bss_ssid,
-         NULL,
-         R
+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
        },
-       { "BSSID", WPAS_DBUS_NEW_IFACE_BSS, "ay",
-         (WPADBusPropertyAccessor) wpas_dbus_getter_bss_bssid,
-         NULL,
-         R
-       },
-       { "Privacy", WPAS_DBUS_NEW_IFACE_BSS, "b",
-         (WPADBusPropertyAccessor) wpas_dbus_getter_bss_privacy,
-         NULL,
-         R
+       { "PrimaryDeviceType", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ay",
+         wpas_dbus_getter_p2p_peer_primary_device_type,
+         NULL
        },
-       { "Mode", WPAS_DBUS_NEW_IFACE_BSS, "s",
-         (WPADBusPropertyAccessor) wpas_dbus_getter_bss_mode,
-         NULL,
-         R
+       { "config_method", WPAS_DBUS_NEW_IFACE_P2P_PEER, "q",
+         wpas_dbus_getter_p2p_peer_config_method,
+         NULL
        },
-       { "Signal", WPAS_DBUS_NEW_IFACE_BSS, "n",
-         (WPADBusPropertyAccessor) wpas_dbus_getter_bss_signal,
-         NULL,
-         R
+       { "level", WPAS_DBUS_NEW_IFACE_P2P_PEER, "i",
+         wpas_dbus_getter_p2p_peer_level,
+         NULL
        },
-       { "Frequency", WPAS_DBUS_NEW_IFACE_BSS, "q",
-         (WPADBusPropertyAccessor) wpas_dbus_getter_bss_frequency,
-         NULL,
-         R
+       { "devicecapability", WPAS_DBUS_NEW_IFACE_P2P_PEER, "y",
+         wpas_dbus_getter_p2p_peer_device_capability,
+         NULL
        },
-       { "Rates", WPAS_DBUS_NEW_IFACE_BSS, "au",
-         (WPADBusPropertyAccessor) wpas_dbus_getter_bss_rates,
-         NULL,
-         R
+       { "groupcapability", WPAS_DBUS_NEW_IFACE_P2P_PEER, "y",
+         wpas_dbus_getter_p2p_peer_group_capability,
+         NULL
        },
-       { "WPA", WPAS_DBUS_NEW_IFACE_BSS, "a{sv}",
-         (WPADBusPropertyAccessor) wpas_dbus_getter_bss_wpa,
-         NULL,
-         R
+       { "SecondaryDeviceTypes", WPAS_DBUS_NEW_IFACE_P2P_PEER, "aay",
+         wpas_dbus_getter_p2p_peer_secondary_device_types,
+         NULL
        },
-       { "RSN", WPAS_DBUS_NEW_IFACE_BSS, "a{sv}",
-         (WPADBusPropertyAccessor) wpas_dbus_getter_bss_rsn,
-         NULL,
-         R
+       { "VendorExtension", WPAS_DBUS_NEW_IFACE_P2P_PEER, "aay",
+         wpas_dbus_getter_p2p_peer_vendor_extension,
+         NULL
        },
-       { "IEs", WPAS_DBUS_NEW_IFACE_BSS, "ay",
-         (WPADBusPropertyAccessor) wpas_dbus_getter_bss_ies,
-         NULL,
-         R
+       { "IEs", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ay",
+         wpas_dbus_getter_p2p_peer_ies,
+         NULL
        },
-       { NULL, NULL, NULL, NULL, NULL, 0 }
+       { NULL, NULL, NULL, NULL, NULL }
 };
 
+static const struct wpa_dbus_signal_desc wpas_dbus_p2p_peer_signals[] = {
 
-static const struct wpa_dbus_signal_desc wpas_dbus_bss_signals[] = {
-       { "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
+ * 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
  *
- * Unregisters BSS representing object from dbus
+ * Notify listeners about event related with newly found p2p peer device
  */
-int wpas_dbus_unregister_bss(struct wpa_supplicant *wpa_s,
-                            u8 bssid[ETH_ALEN], unsigned int id)
+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 *ctrl_iface;
-       char bss_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
+       struct wpas_dbus_priv *iface;
+       DBusMessage *msg;
+       DBusMessageIter iter;
+       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 (wpa_s == NULL || wpa_s->global == NULL)
-               return 0;
-       ctrl_iface = wpa_s->global->dbus;
-       if (ctrl_iface == NULL)
-               return 0;
+       if (iface == NULL)
+               return;
 
-       os_snprintf(bss_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
-                   "%s/" WPAS_DBUS_NEW_BSSIDS_PART "/%u",
-                   wpa_s->dbus_new_path, id);
+       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_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;
-       }
+       msg = dbus_message_new_signal(wpa_s->dbus_new_path, interface,
+                                     sig_name);
+       if (msg == NULL)
+               return;
 
-       wpas_dbus_signal_bss_removed(wpa_s, bss_obj_path);
+       dbus_message_iter_init_append(msg, &iter);
+       path = peer_obj_path;
+       if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+                                           &path))
+               goto err;
 
-       return 0;
+       dbus_connection_send(iface->con, msg, NULL);
+
+       dbus_message_unref(msg);
+       return;
+
+err:
+       wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+       dbus_message_unref(msg);
 }
 
 
 /**
- * wpas_dbus_register_bss - Register a scanned BSS with dbus
+ * wpas_dbus_signal_peer_found - Send a peer found signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @dev: peer device object
+ *
+ * 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
+ * @dev: peer device object
+ *
+ * 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
- * @bssid: scanned network bssid
- * @id: unique BSS identifier
+ * @ssid: network configuration data
  * Returns: 0 on success, -1 on failure
  *
- * Registers BSS representing object with dbus
+ * Registers network representing object with dbus
  */
-int wpas_dbus_register_bss(struct wpa_supplicant *wpa_s,
-                          u8 bssid[ETH_ALEN], unsigned int id)
+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;
-       char bss_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
-       struct bss_handler_args *arg;
+       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;
 
-       os_snprintf(bss_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
-                   "%s/" WPAS_DBUS_NEW_BSSIDS_PART "/%u",
-                   wpa_s->dbus_new_path, id);
+       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 "
@@ -1239,30 +3060,25 @@ int wpas_dbus_register_bss(struct wpa_supplicant *wpa_s,
                goto err;
        }
 
-       arg = os_zalloc(sizeof(struct bss_handler_args));
+       /* 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 handler");
+                          "to create arguments for method");
                goto err;
        }
+
        arg->wpa_s = wpa_s;
-       arg->id = id;
+       os_memcpy(arg->p2p_device_addr, dev_addr, ETH_ALEN);
 
-       wpas_dbus_register(obj_desc, arg, wpa_dbus_free, NULL,
-                          wpas_dbus_bss_properties,
-                          wpas_dbus_bss_signals);
+       wpas_dbus_register(obj_desc, arg, wpa_dbus_free,
+                          NULL,
+                          wpas_dbus_p2p_peer_properties,
+                          wpas_dbus_p2p_peer_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);
+       if (wpa_dbus_register_object_per_iface(ctrl_iface, peer_obj_path,
+                                              wpa_s->ifname, obj_desc))
                goto err;
-       }
-
-       wpas_dbus_signal_bss_added(wpa_s, bss_obj_path);
 
        return 0;
 
@@ -1271,240 +3087,231 @@ err:
        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 == NULL || wpa_s->global == NULL ||
+           wpa_s->dbus_new_path == NULL)
+               return 0;
+       ctrl_iface = wpa_s->global->dbus;
+       if (ctrl_iface == NULL)
+               return 0;
 
-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
-         }
-       },
-       { "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
-         }
-       },
-       { "RemoveNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE,
-         (WPADBusMethodHandler) &wpas_dbus_handler_remove_network,
-         {
-                 { "path", "o", ARG_IN },
-                 END_ARGS
-         }
-       },
-       { "SelectNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE,
-         (WPADBusMethodHandler) &wpas_dbus_handler_select_network,
-         {
-                 { "path", "o", ARG_IN },
-                 END_ARGS
-         }
-       },
-       { "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
-         }
-       },
-#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
-         }
-       },
-#endif /* CONFIG_WPS */
-       { NULL, NULL, NULL, { END_ARGS } }
-};
+       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));
 
-static const struct wpa_dbus_property_desc wpas_dbus_interface_properties[] = {
-       { "Capabilities", WPAS_DBUS_NEW_IFACE_INTERFACE, "a{sv}",
-         (WPADBusPropertyAccessor) wpas_dbus_getter_capabilities,
-         NULL, R
-       },
-       { "State", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
-         (WPADBusPropertyAccessor) wpas_dbus_getter_state,
-         NULL, R
-       },
-       { "Scanning", WPAS_DBUS_NEW_IFACE_INTERFACE, "b",
-         (WPADBusPropertyAccessor) wpas_dbus_getter_scanning,
-         NULL, R
-       },
-       { "ApScan", WPAS_DBUS_NEW_IFACE_INTERFACE, "u",
-         (WPADBusPropertyAccessor) wpas_dbus_getter_ap_scan,
-         (WPADBusPropertyAccessor) wpas_dbus_setter_ap_scan,
-         RW
-       },
-       { "Ifname", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
-         (WPADBusPropertyAccessor) wpas_dbus_getter_ifname,
-         NULL, R
-       },
-       { "Driver", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
-         (WPADBusPropertyAccessor) wpas_dbus_getter_driver,
-         NULL, R
-       },
-       { "BridgeIfname", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
-         (WPADBusPropertyAccessor) wpas_dbus_getter_bridge_ifname,
-         NULL, R
-       },
-       { "CurrentBSS", WPAS_DBUS_NEW_IFACE_INTERFACE, "o",
-         (WPADBusPropertyAccessor) wpas_dbus_getter_current_bss,
-         NULL, R
-       },
-       { "CurrentNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE, "o",
-         (WPADBusPropertyAccessor) wpas_dbus_getter_current_network,
-         NULL, R
-       },
-       { "Blobs", WPAS_DBUS_NEW_IFACE_INTERFACE, "a{say}",
-         (WPADBusPropertyAccessor) wpas_dbus_getter_blobs,
-         NULL, R
-       },
-       { "BSSs", WPAS_DBUS_NEW_IFACE_INTERFACE, "ao",
-         (WPADBusPropertyAccessor) wpas_dbus_getter_bsss,
-         NULL, R
-       },
-       { "Networks", WPAS_DBUS_NEW_IFACE_INTERFACE, "ao",
-         (WPADBusPropertyAccessor) wpas_dbus_getter_networks,
-         NULL, R
-       },
-#ifdef CONFIG_WPS
-       { "ProcessCredentials", WPAS_DBUS_NEW_IFACE_WPS, "b",
-         (WPADBusPropertyAccessor) wpas_dbus_getter_process_credentials,
-         (WPADBusPropertyAccessor) wpas_dbus_setter_process_credentials,
-         RW
-       },
-#endif /* CONFIG_WPS */
-       { NULL, NULL, NULL, NULL, NULL, 0 }
-};
+       wpa_printf(MSG_INFO, "dbus: Unregister peer object '%s'",
+                  peer_obj_path);
+       ret = wpa_dbus_unregister_object_per_iface(ctrl_iface, peer_obj_path);
 
-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
-         }
+       return ret;
+}
+
+
+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
        },
-       { "BSSRemoved", WPAS_DBUS_NEW_IFACE_INTERFACE,
-         {
-                 { "path", "o", ARG_OUT },
-                 END_ARGS
-         }
+       { "Group", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "o",
+         wpas_dbus_getter_p2p_group,
+         NULL
        },
-       { "BlobAdded", WPAS_DBUS_NEW_IFACE_INTERFACE,
-         {
-                 { "name", "s", ARG_OUT },
-                 END_ARGS
-         }
+       { "Role", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "s",
+         wpas_dbus_getter_p2p_role,
+         NULL
        },
-       { "BlobRemoved", WPAS_DBUS_NEW_IFACE_INTERFACE,
-         {
-                 { "name", "s", ARG_OUT },
-                 END_ARGS
-         }
+       { "SSID", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ay",
+         wpas_dbus_getter_p2p_group_ssid,
+         NULL
        },
-       { "NetworkAdded", WPAS_DBUS_NEW_IFACE_INTERFACE,
-         {
-                 { "path", "o", ARG_OUT },
-                 { "properties", "a{sv}", ARG_OUT },
-                 END_ARGS
-         }
+       { "BSSID", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ay",
+         wpas_dbus_getter_p2p_group_bssid,
+         NULL
        },
-       { "NetworkRemoved", WPAS_DBUS_NEW_IFACE_INTERFACE,
-         {
-                 { "path", "o", ARG_OUT },
-                 END_ARGS
-         }
+       { "Frequency", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "q",
+         wpas_dbus_getter_p2p_group_frequency,
+         NULL
        },
-       { "NetworkSelected", WPAS_DBUS_NEW_IFACE_INTERFACE,
-         {
-                 { "path", "o", ARG_OUT },
-                 END_ARGS
-         }
+       { "Passphrase", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "s",
+         wpas_dbus_getter_p2p_group_passphrase,
+         NULL
        },
-       { "PropertiesChanged", WPAS_DBUS_NEW_IFACE_INTERFACE,
-         {
-                 { "properties", "a{sv}", ARG_OUT },
-                 END_ARGS
-         }
+       { "PSK", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ay",
+         wpas_dbus_getter_p2p_group_psk,
+         NULL
        },
-#ifdef CONFIG_WPS
-       { "Event", WPAS_DBUS_NEW_IFACE_WPS,
-         {
-                 { "name", "s", ARG_OUT },
-                 { "args", "a{sv}", ARG_OUT },
-                 END_ARGS
-         }
+       { "WPSVendorExtensions", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "aay",
+         wpas_dbus_getter_p2p_group_vendor_ext,
+         wpas_dbus_setter_p2p_group_vendor_ext
        },
-       { "Credentials", WPAS_DBUS_NEW_IFACE_WPS,
+       { NULL, NULL, NULL, NULL, NULL }
+};
+
+static const struct wpa_dbus_signal_desc wpas_dbus_p2p_group_signals[] = {
+       { "PeerJoined", WPAS_DBUS_NEW_IFACE_P2P_GROUP,
          {
-                 { "credentials", "a{sv}", ARG_OUT },
+                 { "peer", "o", ARG_OUT },
                  END_ARGS
          }
        },
-       { "PropertiesChanged", WPAS_DBUS_NEW_IFACE_WPS,
+       { "PeerDisconnected", WPAS_DBUS_NEW_IFACE_P2P_GROUP,
          {
-                 { "properties", "a{sv}", ARG_OUT },
+                 { "peer", "o", ARG_OUT },
                  END_ARGS
          }
        },
-#endif /* CONFIG_WPS */
        { 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];
 
-int wpas_dbus_register_interface(struct wpa_supplicant *wpa_s)
+       /* 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;
+
+       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;
+       }
+
+       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_p2p_groupmember_properties[] = {
+       { NULL, NULL, NULL, NULL, NULL }
+};
+
+/**
+ * wpas_dbus_register_p2p_groupmember - Register a p2p groupmember
+ * object with dbus
+ * @wpa_s: wpa_supplicant interface structure
+ * @p2p_if_addr: i/f addr of the device joining this group
+ *
+ * Registers p2p groupmember representing object with dbus
+ */
+void wpas_dbus_register_p2p_groupmember(struct wpa_supplicant *wpa_s,
+                                       const u8 *p2p_if_addr)
+{
+       struct wpas_dbus_priv *ctrl_iface;
        struct wpa_dbus_object_desc *obj_desc = NULL;
-       struct wpas_dbus_priv *ctrl_iface = wpa_s->global->dbus;
-       int next;
+       struct groupmember_handler_args *arg;
+       char groupmember_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
 
        /* Do nothing if the control interface is not turned on */
+       if (wpa_s == NULL || wpa_s->global == NULL)
+               return;
+
+       ctrl_iface = wpa_s->global->dbus;
        if (ctrl_iface == NULL)
-               return 0;
+               return;
 
-       /* 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);
+       if (!wpa_s->dbus_groupobj_path)
+               return;
+
+       os_snprintf(groupmember_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+               "%s/" WPAS_DBUS_NEW_P2P_GROUPMEMBERS_PART "/" COMPACT_MACSTR,
+               wpa_s->dbus_groupobj_path, MAC2STR(p2p_if_addr));
 
        obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc));
        if (!obj_desc) {
@@ -1513,50 +3320,198 @@ int wpas_dbus_register_interface(struct wpa_supplicant *wpa_s)
                goto err;
        }
 
-       wpas_dbus_register(obj_desc, wpa_s, NULL, wpas_dbus_interface_methods,
-                          wpas_dbus_interface_properties,
-                          wpas_dbus_interface_signals);
+       /* allocate memory for handlers arguments */
+       arg = os_zalloc(sizeof(struct groupmember_handler_args));
+       if (!arg) {
+               wpa_printf(MSG_ERROR, "Not enough memory "
+                          "to create arguments for method");
+               goto err;
+       }
 
-       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,
+       arg->wpa_s = wpa_s;
+       os_memcpy(arg->member_addr, p2p_if_addr, ETH_ALEN);
+
+       wpas_dbus_register(obj_desc, arg, wpa_dbus_free, NULL,
+                          wpas_dbus_p2p_groupmember_properties, NULL);
+
+       if (wpa_dbus_register_object_per_iface(ctrl_iface, groupmember_obj_path,
                                               wpa_s->ifname, obj_desc))
                goto err;
 
-       wpas_dbus_signal_interface_added(wpa_s);
+       wpa_printf(MSG_INFO,
+                  "dbus: Registered group member object '%s' successfully",
+                  groupmember_obj_path);
+       return;
+
+err:
+       free_dbus_object_desc(obj_desc);
+}
+
+/**
+ * wpas_dbus_unregister_p2p_groupmember - Unregister a p2p groupmember
+ * object with dbus
+ * @wpa_s: wpa_supplicant interface structure
+ * @p2p_if_addr: i/f addr of the device joining this group
+ *
+ * Unregisters p2p groupmember representing object with dbus
+ */
+void wpas_dbus_unregister_p2p_groupmember(struct wpa_supplicant *wpa_s,
+                                         const u8 *p2p_if_addr)
+{
+       struct wpas_dbus_priv *ctrl_iface;
+       char groupmember_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
+
+       /* Do nothing if the control interface is not turned on */
+       if (wpa_s == NULL || wpa_s->global == NULL)
+               return;
+
+       ctrl_iface = wpa_s->global->dbus;
+       if (ctrl_iface == NULL)
+               return;
+
+       if (!wpa_s->dbus_groupobj_path)
+               return;
+
+       os_snprintf(groupmember_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+               "%s/" WPAS_DBUS_NEW_P2P_GROUPMEMBERS_PART "/" COMPACT_MACSTR,
+               wpa_s->dbus_groupobj_path, MAC2STR(p2p_if_addr));
+
+       wpa_dbus_unregister_object_per_iface(ctrl_iface, groupmember_obj_path);
+}
+
+
+static const struct wpa_dbus_property_desc
+       wpas_dbus_persistent_group_properties[] = {
+       { "Properties", WPAS_DBUS_NEW_IFACE_PERSISTENT_GROUP, "a{sv}",
+         wpas_dbus_getter_persistent_group_properties,
+         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;
+
+       /* Make sure ssid is a persistent group */
+       if (ssid->disabled != 2 && !ssid->p2p_persistent_group)
+               return -1; /* should we return w/o complaining? */
+
+       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:
-       os_free(wpa_s->dbus_new_path);
-       wpa_s->dbus_new_path = NULL;
        free_dbus_object_desc(obj_desc);
        return -1;
 }
 
 
-int wpas_dbus_unregister_interface(struct wpa_supplicant *wpa_s)
+/**
+ * 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 == NULL || wpa_s->global == NULL)
+       if (wpa_s == NULL || wpa_s->global == NULL ||
+           wpa_s->dbus_new_path == NULL)
                return 0;
        ctrl_iface = wpa_s->global->dbus;
        if (ctrl_iface == NULL)
                return 0;
 
-       wpa_printf(MSG_DEBUG, "dbus: Unregister interface object '%s'",
-                  wpa_s->dbus_new_path);
-       if (wpa_dbus_unregister_object_per_iface(ctrl_iface,
-                                                wpa_s->dbus_new_path))
-               return -1;
+       os_snprintf(pgrp_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+                   "%s/" WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/%u",
+                   wpa_s->dbus_new_path, nid);
 
-       wpas_dbus_signal_interface_removed(wpa_s);
+       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);
 
-       os_free(wpa_s->dbus_new_path);
-       wpa_s->dbus_new_path = NULL;
+       if (!ret)
+               wpas_dbus_signal_persistent_group_removed(wpa_s, nid);
 
-       return 0;
+       return ret;
 }
+
+#endif /* CONFIG_P2P */
index 80ea98c..93ce722 100644 (file)
 #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 wpa_states;
 
 enum wpas_dbus_prop {
        WPAS_DBUS_PROP_AP_SCAN,
@@ -30,6 +32,8 @@ enum wpas_dbus_prop {
        WPAS_DBUS_PROP_STATE,
        WPAS_DBUS_PROP_CURRENT_BSS,
        WPAS_DBUS_PROP_CURRENT_NETWORK,
+       WPAS_DBUS_PROP_CURRENT_AUTH_MODE,
+       WPAS_DBUS_PROP_BSSS,
 };
 
 enum wpas_dbus_bss_prop {
@@ -59,6 +63,29 @@ enum wpas_dbus_bss_prop {
 #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"
+
+#define WPAS_DBUS_NEW_P2P_GROUPMEMBERS_PART    "Members"
+#define        WPAS_DBUS_NEW_IFACE_P2P_GROUPMEMBER \
+       WPAS_DBUS_NEW_INTERFACE ".GroupMember"
 
 /* Errors */
 #define WPAS_DBUS_ERROR_UNKNOWN_ERROR \
@@ -76,6 +103,13 @@ enum wpas_dbus_bss_prop {
 #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 \
@@ -97,6 +131,10 @@ void wpas_dbus_bss_signal_prop_changed(struct wpa_supplicant *wpa_s,
 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);
@@ -120,6 +158,59 @@ 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_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_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,
+                                    const u8 *src, u16 dev_passwd_id);
+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_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_register_p2p_groupmember(struct wpa_supplicant *wpa_s,
+                                       const u8 *p2p_if_addr);
+void wpas_dbus_unregister_p2p_groupmember(struct wpa_supplicant *wpa_s,
+                                         const u8 *p2p_if_addr);
+void wpas_dbus_signal_p2p_peer_disconnected(struct wpa_supplicant *wpa_s,
+                                           const u8 *member);
+void wpas_dbus_signal_p2p_sd_request(struct wpa_supplicant *wpa_s,
+                                    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 *cert_hash,
+                                   const struct wpabuf *cert);
+
 #else /* CONFIG_CTRL_IFACE_DBUS_NEW */
 
 static inline int wpas_dbus_register_interface(struct wpa_supplicant *wpa_s)
@@ -155,6 +246,12 @@ static inline void wpas_dbus_signal_network_selected(
 {
 }
 
+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)
 {
@@ -229,6 +326,147 @@ static inline void wpas_dbus_signal_debug_show_keys_changed(
 {
 }
 
+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_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)
+{
+}
+
+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_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_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 *cert_hash,
+                                                 const struct wpabuf *cert)
+{
+}
+
 #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
 
 #endif /* CTRL_IFACE_DBUS_H_NEW */
index e2b5e50..b662fa4 100644 (file)
@@ -28,6 +28,7 @@
 #include "../wpas_glue.h"
 #include "../bss.h"
 #include "../scan.h"
+#include "../ctrl_iface.h"
 #include "dbus_new_helpers.h"
 #include "dbus_new.h"
 #include "dbus_new_handlers.h"
@@ -38,75 +39,11 @@ extern int wpa_debug_show_keys;
 extern int wpa_debug_timestamp;
 
 static const char *debug_strings[] = {
-       "msgdump", "debug", "info", "warning", "error", NULL
+       "excessive", "msgdump", "debug", "info", "warning", "error", NULL
 };
 
 
 /**
- * wpas_dbus_new_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.
- */
-static char * wpas_dbus_new_decompose_object_path(const char *path,
-                                                 char **network,
-                                                 char **bssid)
-{
-       const unsigned int dev_path_prefix_len =
-               strlen(WPAS_DBUS_NEW_PATH_INTERFACES "/");
-       char *obj_path_only;
-       char *next_sep;
-
-       /* Be a bit paranoid about path */
-       if (!path || os_strncmp(path, WPAS_DBUS_NEW_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 = os_strchr(obj_path_only + dev_path_prefix_len, '/');
-       if (next_sep != NULL) {
-               const char *net_part = os_strstr(
-                       next_sep, WPAS_DBUS_NEW_NETWORKS_PART "/");
-               const char *bssid_part = os_strstr(
-                       next_sep, WPAS_DBUS_NEW_BSSIDS_PART "/");
-
-               if (network && net_part) {
-                       /* Deal with a request for a configured network */
-                       const char *net_name = net_part +
-                               os_strlen(WPAS_DBUS_NEW_NETWORKS_PART "/");
-                       *network = NULL;
-                       if (os_strlen(net_name))
-                               *network = os_strdup(net_name);
-               } else if (bssid && bssid_part) {
-                       /* Deal with a request for a scanned BSSID */
-                       const char *bssid_name = bssid_part +
-                               os_strlen(WPAS_DBUS_NEW_BSSIDS_PART "/");
-                       if (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_error_unknown_error - Return a new InvalidArgs error message
  * @message: Pointer to incoming dbus message this error refers to
  * @arg: Optional string appended to error message
@@ -117,6 +54,20 @@ static char * wpas_dbus_new_decompose_object_path(const char *path,
 DBusMessage * wpas_dbus_error_unknown_error(DBusMessage *message,
                                            const char *arg)
 {
+       /*
+        * This function can be called as a result of a failure
+        * within internal getter calls, which will call this function
+        * with a NULL message parameter.  However, dbus_message_new_error
+        * looks very unkindly (i.e, abort()) on a NULL message, so
+        * in this case, we should not call it.
+        */
+       if (message == NULL) {
+               wpa_printf(MSG_INFO, "dbus: wpas_dbus_error_unknown_error "
+                          "called with NULL message (arg=%s)",
+                          arg ? arg : "N/A");
+               return NULL;
+       }
+
        return dbus_message_new_error(message, WPAS_DBUS_ERROR_UNKNOWN_ERROR,
                                      arg);
 }
@@ -213,36 +164,35 @@ static struct wpa_supplicant * get_iface_by_dbus_path(
 
 /**
  * set_network_properties - Set properties of 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
  * @iter: DBus message iterator containing dictionary of network
  * properties to set.
- * Returns: NULL when succeed or DBus error on failure
+ * @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
  */
-static DBusMessage * set_network_properties(DBusMessage *message,
-                                           struct wpa_supplicant *wpa_s,
-                                           struct wpa_ssid *ssid,
-                                           DBusMessageIter *iter)
+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 };
-       DBusMessage *reply = NULL;
        DBusMessageIter iter_dict;
+       char *value = NULL;
 
-       if (!wpa_dbus_dict_open_read(iter, &iter_dict))
-               return wpas_dbus_error_invalid_args(message, NULL);
+       if (!wpa_dbus_dict_open_read(iter, &iter_dict, error))
+               return FALSE;
 
        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_error_invalid_args(message, NULL);
-                       break;
-               }
+
+               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)
@@ -311,71 +261,59 @@ static DBusMessage * set_network_properties(DBusMessage *message,
 
                os_free(value);
                wpa_dbus_dict_entry_clear(&entry);
-               continue;
-
-       error:
-               os_free(value);
-               reply = wpas_dbus_error_invalid_args(message, entry.key);
-               wpa_dbus_dict_entry_clear(&entry);
-               break;
        }
 
-       return reply;
+       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
- * @message: Pointer to incoming dbus message
+ * @iter: Message iter to use when appending arguments
  * @type: DBus type of property (must be basic type)
  * @val: pointer to place holding property value
- * Returns: The DBus message containing response for Properties.Get call
- * or DBus error message if error occurred.
+ * @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.
  */
-DBusMessage * wpas_dbus_simple_property_getter(DBusMessage *message,
-                                              const int type, const void *val)
+dbus_bool_t wpas_dbus_simple_property_getter(DBusMessageIter *iter,
+                                            const int type,
+                                            const void *val,
+                                            DBusError *error)
 {
-       DBusMessage *reply = NULL;
-       DBusMessageIter iter, variant_iter;
+       DBusMessageIter variant_iter;
 
        if (!dbus_type_is_basic(type)) {
-               wpa_printf(MSG_ERROR, "dbus: wpas_dbus_simple_property_getter:"
-                          " given type is not basic");
-               return wpas_dbus_error_unknown_error(message, NULL);
+               dbus_set_error(error, DBUS_ERROR_FAILED,
+                              "%s: given type is not basic", __func__);
+               return FALSE;
        }
 
-       if (message == NULL)
-               reply = dbus_message_new(DBUS_MESSAGE_TYPE_SIGNAL);
-       else
-               reply = dbus_message_new_method_return(message);
-
-       if (reply != NULL) {
-               dbus_message_iter_init_append(reply, &iter);
-               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)) {
-                       wpa_printf(MSG_ERROR, "dbus: "
-                                  "wpas_dbus_simple_property_getter: out of "
-                                  "memory to put property value into "
-                                  "message");
-                       dbus_message_unref(reply);
-                       reply = dbus_message_new_error(message,
-                                                      DBUS_ERROR_NO_MEMORY,
-                                                      NULL);
-               }
-       } else {
-               wpa_printf(MSG_ERROR, "dbus: wpas_dbus_simple_property_getter:"
-                          " out of memory to return property value");
-               reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-                                              NULL);
-       }
+       if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+                                             wpa_dbus_type_as_string(type),
+                                             &variant_iter))
+               goto error;
 
-       return reply;
+       if (!dbus_message_iter_append_basic(&variant_iter, type, val))
+               goto error;
+
+       if (!dbus_message_iter_close_container(iter, &variant_iter))
+               goto error;
+
+       return TRUE;
+
+error:
+       dbus_set_error(error, DBUS_ERROR_FAILED,
+                      "%s: error constructing reply", __func__);
+       return FALSE;
 }
 
 
@@ -384,102 +322,79 @@ DBusMessage * wpas_dbus_simple_property_getter(DBusMessage *message,
  * @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: NULL or DBus error message if error occurred.
+ * Returns: TRUE if the request was successful, FALSE if it failed
  *
  * Generic setter for basic type properties. Type is required to be basic.
  */
-DBusMessage * wpas_dbus_simple_property_setter(DBusMessage *message,
-                                              const int type, void *val)
+dbus_bool_t wpas_dbus_simple_property_setter(DBusMessageIter *iter,
+                                            DBusError *error,
+                                            const int type, void *val)
 {
-       DBusMessageIter iter, variant_iter;
+       DBusMessageIter variant_iter;
 
        if (!dbus_type_is_basic(type)) {
-               wpa_printf(MSG_ERROR, "dbus: wpas_dbus_simple_property_setter:"
-                          " given type is not basic");
-               return wpas_dbus_error_unknown_error(message, NULL);
-       }
-
-       if (!dbus_message_iter_init(message, &iter)) {
-               wpa_printf(MSG_ERROR, "dbus: wpas_dbus_simple_property_setter:"
-                          " out of memory to return scanning state");
-               return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-                                             NULL);
+               dbus_set_error(error, DBUS_ERROR_FAILED,
+                              "%s: given type is not basic", __func__);
+               return FALSE;
        }
 
-       /* omit first and second argument and get value from third */
-       dbus_message_iter_next(&iter);
-       dbus_message_iter_next(&iter);
-       dbus_message_iter_recurse(&iter, &variant_iter);
-
+       /* Look at the new value */
+       dbus_message_iter_recurse(iter, &variant_iter);
        if (dbus_message_iter_get_arg_type(&variant_iter) != type) {
-               wpa_printf(MSG_DEBUG, "dbus: wpas_dbus_simple_property_setter:"
-                          " wrong property type");
-               return wpas_dbus_error_invalid_args(message,
-                                                   "wrong property type");
+               dbus_set_error_const(error, DBUS_ERROR_FAILED,
+                                    "wrong property type");
+               return FALSE;
        }
        dbus_message_iter_get_basic(&variant_iter, val);
 
-       return NULL;
+       return TRUE;
 }
 
 
 /**
  * wpas_dbus_simple_array_property_getter - Get array type property
- * @message: Pointer to incoming dbus message
+ * @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
- * Returns: The DBus message containing response for Properties.Get call
- * or DBus error message if error occurred.
+ * @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.
  */
-DBusMessage * wpas_dbus_simple_array_property_getter(DBusMessage *message,
-                                                    const int type,
-                                                    const void *array,
-                                                    size_t array_len)
+dbus_bool_t wpas_dbus_simple_array_property_getter(DBusMessageIter *iter,
+                                                  const int type,
+                                                  const void *array,
+                                                  size_t array_len,
+                                                  DBusError *error)
 {
-       DBusMessage *reply = NULL;
-       DBusMessageIter iter, variant_iter, array_iter;
+       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)) {
-               wpa_printf(MSG_ERROR, "dbus: "
-                          "wpas_dbus_simple_array_property_getter: given "
-                          "type is not basic");
-               return wpas_dbus_error_unknown_error(message, NULL);
+               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 (message == NULL)
-               reply = dbus_message_new(DBUS_MESSAGE_TYPE_SIGNAL);
-       else
-               reply = dbus_message_new_method_return(message);
-       if (reply == NULL) {
-               wpa_printf(MSG_ERROR, "dbus: "
-                          "wpas_dbus_simple_array_property_getter: out of "
-                          "memory to create return message");
-               return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-                                             NULL);
+       if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+                                             type_str, &variant_iter)) {
+               dbus_set_error(error, DBUS_ERROR_FAILED,
+                              "%s: failed to construct message 1", __func__);
+               return FALSE;
        }
 
-       dbus_message_iter_init_append(reply, &iter);
-
-       if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
-                                             type_str, &variant_iter) ||
-           !dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY,
+       if (!dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY,
                                              sub_type_str, &array_iter)) {
-               wpa_printf(MSG_ERROR, "dbus: "
-                          "wpas_dbus_simple_array_property_getter: out of "
-                          "memory to open container");
-               dbus_message_unref(reply);
-               return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-                                             NULL);
+               dbus_set_error(error, DBUS_ERROR_FAILED,
+                              "%s: failed to construct message 2", __func__);
+               return FALSE;
        }
 
        switch(type) {
@@ -507,11 +422,9 @@ DBusMessage * wpas_dbus_simple_array_property_getter(DBusMessage *message,
                element_size = sizeof(char *);
                break;
        default:
-               wpa_printf(MSG_ERROR, "dbus: "
-                          "wpas_dbus_simple_array_property_getter: "
-                          "fatal: unknown element type");
-               element_size = 1;
-               break;
+               dbus_set_error(error, DBUS_ERROR_FAILED,
+                              "%s: unknown element type %d", __func__, type);
+               return FALSE;
        }
 
        for (i = 0; i < array_len; i++) {
@@ -519,17 +432,89 @@ DBusMessage * wpas_dbus_simple_array_property_getter(DBusMessage *message,
                                               array + i * element_size);
        }
 
-       if (!dbus_message_iter_close_container(&variant_iter, &array_iter) ||
-           !dbus_message_iter_close_container(&iter, &variant_iter)) {
-               wpa_printf(MSG_ERROR, "dbus: "
-                          "wpas_dbus_simple_array_property_getter: out of "
-                          "memory to close container");
-               dbus_message_unref(reply);
-               return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-                                             NULL);
+       if (!dbus_message_iter_close_container(&variant_iter, &array_iter)) {
+               dbus_set_error(error, DBUS_ERROR_FAILED,
+                              "%s: failed to construct message 3", __func__);
+               return FALSE;
        }
 
-       return reply;
+       if (!dbus_message_iter_close_container(iter, &variant_iter)) {
+               dbus_set_error(error, DBUS_ERROR_FAILED,
+                              "%s: failed to construct message 4", __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_set_error(error, DBUS_ERROR_FAILED,
+                              "%s: failed to construct message 1", __func__);
+               return FALSE;
+       }
+       if (!dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY,
+                                             inner_type_str, &array_iter)) {
+               dbus_set_error(error, DBUS_ERROR_FAILED,
+                              "%s: failed to construct message 2", __func__);
+               return FALSE;
+       }
+
+       for (i = 0; i < array_len; i++) {
+               wpa_dbus_dict_bin_array_add_element(&array_iter,
+                                                   wpabuf_head(array[i]),
+                                                   wpabuf_len(array[i]));
+
+       }
+
+       if (!dbus_message_iter_close_container(&variant_iter, &array_iter)) {
+               dbus_set_error(error, DBUS_ERROR_FAILED,
+                              "%s: failed to close message 2", __func__);
+               return FALSE;
+       }
+
+       if (!dbus_message_iter_close_container(iter, &variant_iter)) {
+               dbus_set_error(error, DBUS_ERROR_FAILED,
+                              "%s: failed to close message 1", __func__);
+               return FALSE;
+       }
+
+       return TRUE;
 }
 
 
@@ -553,11 +538,12 @@ DBusMessage * wpas_dbus_handler_create_interface(DBusMessage *message,
        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))
+       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))
@@ -574,6 +560,12 @@ DBusMessage * wpas_dbus_handler_create_interface(DBusMessage *message,
                        wpa_dbus_dict_entry_clear(&entry);
                        if (ifname == NULL)
                                goto error;
+               } else if (!strcmp(entry.key, "ConfigFile") &&
+                          (entry.type == DBUS_TYPE_STRING)) {
+                       confname = os_strdup(entry.str_value);
+                       wpa_dbus_dict_entry_clear(&entry);
+                       if (confname == NULL)
+                               goto error;
                } else if (!strcmp(entry.key, "BridgeIfname") &&
                           (entry.type == DBUS_TYPE_STRING)) {
                        bridge_ifname = os_strdup(entry.str_value);
@@ -604,6 +596,7 @@ DBusMessage * wpas_dbus_handler_create_interface(DBusMessage *message,
                os_memset(&iface, 0, sizeof(iface));
                iface.driver = driver;
                iface.ifname = ifname;
+               iface.confname = confname;
                iface.bridge_ifname = bridge_ifname;
                /* Otherwise, have wpa_supplicant attach to it. */
                if ((wpa_s = wpa_supplicant_add_iface(global, &iface))) {
@@ -706,79 +699,86 @@ DBusMessage * wpas_dbus_handler_get_interface(DBusMessage *message,
 
 /**
  * wpas_dbus_getter_debug_level - Get debug level
- * @message: Pointer to incoming dbus message
- * @global: %wpa_supplicant global data structure
- * Returns: DBus message with value of 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.
  */
-DBusMessage * wpas_dbus_getter_debug_level(DBusMessage *message,
-                                          struct wpa_global *global)
+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 > 4)
-               idx = 4;
+       if (idx > 5)
+               idx = 5;
        str = debug_strings[idx];
-       return wpas_dbus_simple_property_getter(message, DBUS_TYPE_STRING,
-                                               &str);
+       return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
+                                               &str, error);
 }
 
 
 /**
  * wpas_dbus_getter_debug_timestamp - Get debug timestamp
- * @message: Pointer to incoming dbus message
- * @global: %wpa_supplicant global data structure
- * Returns: DBus message with value of 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.
  */
-DBusMessage * wpas_dbus_getter_debug_timestamp(DBusMessage *message,
-                                              struct wpa_global *global)
+dbus_bool_t wpas_dbus_getter_debug_timestamp(DBusMessageIter *iter,
+                                             DBusError *error,
+                                             void *user_data)
 {
-       return wpas_dbus_simple_property_getter(message, DBUS_TYPE_BOOLEAN,
-                                               &wpa_debug_timestamp);
+       return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
+                                               &wpa_debug_timestamp, error);
 
 }
 
 
 /**
  * wpas_dbus_getter_debug_show_keys - Get debug show keys
- * @message: Pointer to incoming dbus message
- * @global: %wpa_supplicant global data structure
- * Returns: DBus message with value of 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.
  */
-DBusMessage * wpas_dbus_getter_debug_show_keys(DBusMessage *message,
-                                              struct wpa_global *global)
+dbus_bool_t wpas_dbus_getter_debug_show_keys(DBusMessageIter *iter,
+                                            DBusError *error,
+                                            void *user_data)
 {
-       return wpas_dbus_simple_property_getter(message, DBUS_TYPE_BOOLEAN,
-                                               &wpa_debug_show_keys);
+       return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
+                                               &wpa_debug_show_keys, error);
 
 }
 
 /**
  * wpas_dbus_setter_debug_level - Set debug level
- * @message: Pointer to incoming dbus message
- * @global: %wpa_supplicant global data structure
- * Returns: %NULL or DBus error message
+ * @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.
  */
-DBusMessage * wpas_dbus_setter_debug_level(DBusMessage *message,
-                                          struct wpa_global *global)
+dbus_bool_t wpas_dbus_setter_debug_level(DBusMessageIter *iter,
+                                        DBusError *error, void *user_data)
 {
-       DBusMessage *reply;
+       struct wpa_global *global = user_data;
        const char *str = NULL;
        int i, val = -1;
 
-       reply = wpas_dbus_simple_property_setter(message, DBUS_TYPE_STRING,
-                                                &str);
-       if (reply)
-               return reply;
+       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) {
@@ -789,138 +789,142 @@ DBusMessage * wpas_dbus_setter_debug_level(DBusMessage *message,
        if (val < 0 ||
            wpa_supplicant_set_debug_params(global, val, wpa_debug_timestamp,
                                            wpa_debug_show_keys)) {
-               dbus_message_unref(reply);
-               return wpas_dbus_error_invalid_args(
-                       message, "Wrong debug level value");
+               dbus_set_error_const(error, DBUS_ERROR_FAILED, "wrong debug "
+                                    "level value");
+               return FALSE;
        }
 
-       return NULL;
+       return TRUE;
 }
 
 
 /**
  * wpas_dbus_setter_debug_timestamp - Set debug timestamp
- * @message: Pointer to incoming dbus message
- * @global: %wpa_supplicant global data structure
- * Returns: %NULL or DBus error message
+ * @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.
  */
-DBusMessage * wpas_dbus_setter_debug_timestamp(DBusMessage *message,
-                                              struct wpa_global *global)
+dbus_bool_t wpas_dbus_setter_debug_timestamp(DBusMessageIter *iter,
+                                            DBusError *error,
+                                            void *user_data)
 {
-       DBusMessage *reply;
+       struct wpa_global *global = user_data;
        dbus_bool_t val;
 
-       reply = wpas_dbus_simple_property_setter(message, DBUS_TYPE_BOOLEAN,
-                                                &val);
-       if (reply)
-               return reply;
+       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 NULL;
+       return TRUE;
 }
 
 
 /**
  * wpas_dbus_setter_debug_show_keys - Set debug show keys
- * @message: Pointer to incoming dbus message
- * @global: %wpa_supplicant global data structure
- * Returns: %NULL or DBus error message
+ * @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.
  */
-DBusMessage * wpas_dbus_setter_debug_show_keys(DBusMessage *message,
-                                              struct wpa_global *global)
+dbus_bool_t wpas_dbus_setter_debug_show_keys(DBusMessageIter *iter,
+                                            DBusError *error,
+                                            void *user_data)
 {
-       DBusMessage *reply;
+       struct wpa_global *global = user_data;
        dbus_bool_t val;
 
-       reply = wpas_dbus_simple_property_setter(message, DBUS_TYPE_BOOLEAN,
-                                                &val);
-       if (reply)
-               return reply;
+       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 NULL;
+       return TRUE;
 }
 
 
 /**
  * wpas_dbus_getter_interfaces - Request registered interfaces list
- * @message: Pointer to incoming dbus message
- * @global: %wpa_supplicant global data structure
- * Returns: The object paths array containing registered interfaces
- * objects paths or DBus error on failure
+ * @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
  */
-DBusMessage * wpas_dbus_getter_interfaces(DBusMessage *message,
-                                         struct wpa_global *global)
+dbus_bool_t wpas_dbus_getter_interfaces(DBusMessageIter *iter,
+                                       DBusError *error,
+                                       void *user_data)
 {
-       DBusMessage *reply = NULL;
+       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)
                num++;
 
        paths = os_zalloc(num * sizeof(char*));
        if (!paths) {
-               return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-                                             NULL);
+               dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+               return FALSE;
        }
 
        for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next)
-               paths[i] = wpa_s->dbus_new_path;
+               paths[i++] = wpa_s->dbus_new_path;
 
-       reply = wpas_dbus_simple_array_property_getter(message,
-                                                      DBUS_TYPE_OBJECT_PATH,
-                                                      paths, num);
+       success = wpas_dbus_simple_array_property_getter(iter,
+                                                        DBUS_TYPE_OBJECT_PATH,
+                                                        paths, num, error);
 
        os_free(paths);
-       return reply;
+       return success;
 }
 
 
 /**
  * wpas_dbus_getter_eap_methods - Request supported EAP methods list
- * @message: Pointer to incoming dbus message
- * @nothing: not used argument. may be NULL or anything else
- * Returns: The object paths array containing supported EAP methods
- * represented by strings or DBus error on failure
+ * @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
  */
-DBusMessage * wpas_dbus_getter_eap_methods(DBusMessage *message, void *nothing)
+dbus_bool_t wpas_dbus_getter_eap_methods(DBusMessageIter *iter,
+                                        DBusError *error, void *user_data)
 {
-       DBusMessage *reply = NULL;
        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) {
-               return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-                                             NULL);
+               dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+               return FALSE;
        }
 
-       reply = wpas_dbus_simple_array_property_getter(message,
-                                                      DBUS_TYPE_STRING,
-                                                      eap_methods, num_items);
+       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 reply;
+       return success;
 }
 
 
@@ -987,21 +991,34 @@ static int wpas_dbus_get_scan_ssids(DBusMessage *message, DBusMessageIter *var,
                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;
-               }
 
-               ssid = os_malloc(len);
-               if (ssid == NULL) {
-                       wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
-                                  "out of memory. Cannot allocate memory for "
-                                  "SSID");
-                       *reply = dbus_message_new_error(
-                               message, DBUS_ERROR_NO_MEMORY, NULL);
+               if (len > MAX_SSID_LEN) {
+                       wpa_printf(MSG_DEBUG,
+                                  "wpas_dbus_handler_scan[dbus]: "
+                                  "SSID too long (len=%d max_len=%d)",
+                                  len, MAX_SSID_LEN);
+                       *reply = wpas_dbus_error_invalid_args(
+                               message, "Invalid SSID: too long");
                        return -1;
                }
-               os_memcpy(ssid, val, len);
+
+               if (len != 0) {
+                       ssid = os_malloc(len);
+                       if (ssid == NULL) {
+                               wpa_printf(MSG_DEBUG,
+                                          "wpas_dbus_handler_scan[dbus]: "
+                                          "out of memory. Cannot allocate "
+                                          "memory for SSID");
+                               *reply = dbus_message_new_error(
+                                       message, DBUS_ERROR_NO_MEMORY, NULL);
+                               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;
 
@@ -1260,14 +1277,16 @@ DBusMessage * wpas_dbus_handler_scan(DBusMessage *message,
                                "passive scan");
                        goto out;
                } else if (params.freqs && params.freqs[0]) {
-                       /* wildcard ssid */
-                       params.num_ssids++;
                        wpa_supplicant_trigger_scan(wpa_s, &params);
                } else {
                        wpa_s->scan_req = 2;
                        wpa_supplicant_req_scan(wpa_s, 0, 0);
                }
        } else if (!os_strcmp(type, "active")) {
+               if (!params.num_ssids) {
+                       /* Add wildcard ssid */
+                       params.num_ssids++;
+               }
                wpa_supplicant_trigger_scan(wpa_s, &params);
        } else {
                wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
@@ -1326,6 +1345,7 @@ DBusMessage * wpas_dbus_handler_add_network(DBusMessage *message,
        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);
 
@@ -1343,11 +1363,15 @@ DBusMessage * wpas_dbus_handler_add_network(DBusMessage *message,
        ssid->disabled = 1;
        wpa_config_set_network_defaults(ssid);
 
-       reply = set_network_properties(message, wpa_s, ssid, &iter);
-       if (reply) {
+       dbus_error_init(&error);
+       if (!set_network_properties(wpa_s, ssid, &iter, &error)) {
                wpa_printf(MSG_DEBUG, "wpas_dbus_handler_add_network[dbus]:"
                           "control interface couldn't set network "
                           "properties");
+               reply = wpas_dbus_reply_new_from_error(message, &error,
+                                                      DBUS_ERROR_INVALID_ARGS,
+                                                      "Failed to add network");
+               dbus_error_free(&error);
                goto err;
        }
 
@@ -1403,7 +1427,7 @@ DBusMessage * wpas_dbus_handler_remove_network(DBusMessage *message,
 
        /* Extract the network ID and ensure the network */
        /* is actually a child of this interface */
-       iface = wpas_dbus_new_decompose_object_path(op, &net_id, NULL);
+       iface = wpas_dbus_new_decompose_object_path(op, 0, &net_id, NULL);
        if (iface == NULL || os_strcmp(iface, wpa_s->dbus_new_path) != 0) {
                reply = wpas_dbus_error_invalid_args(message, op);
                goto out;
@@ -1444,6 +1468,42 @@ out:
 }
 
 
+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,
+                          "wpas_dbus_handler_remove_all_networks[dbus]: "
+                          "error occurred when removing network %d",
+                          ssid->id);
+               return;
+       }
+
+       if (ssid == wpa_s->current_ssid)
+               wpa_supplicant_disassociate(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)
+{
+       /* 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
@@ -1466,7 +1526,7 @@ DBusMessage * wpas_dbus_handler_select_network(DBusMessage *message,
 
        /* Extract the network ID and ensure the network */
        /* is actually a child of this interface */
-       iface = wpas_dbus_new_decompose_object_path(op, &net_id, NULL);
+       iface = wpas_dbus_new_decompose_object_path(op, 0, &net_id, NULL);
        if (iface == NULL || os_strcmp(iface, wpa_s->dbus_new_path) != 0) {
                reply = wpas_dbus_error_invalid_args(message, op);
                goto out;
@@ -1495,6 +1555,70 @@ out:
 
 
 /**
+ * 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 = NULL, *net_id = NULL;
+       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, 0, &net_id, NULL);
+       if (iface == NULL || os_strcmp(iface, wpa_s->dbus_new_path) != 0) {
+               reply = wpas_dbus_error_invalid_args(message, op);
+               goto out;
+       }
+
+       id = strtoul(net_id, NULL, 10);
+       if (errno == EINVAL) {
+               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);
+       os_free(net_id);
+       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 */
+}
+
+
+/**
  * wpas_dbus_handler_add_blob - Store named binary blob (ie, for certificates)
  * @message: Pointer to incoming dbus message
  * @wpa_s: %wpa_supplicant data structure
@@ -1658,38 +1782,52 @@ DBusMessage * wpas_dbus_handler_remove_blob(DBusMessage *message,
 
 }
 
-
-/**
- * wpas_dbus_getter_capabilities - Return interface capabilities
+/*
+ * 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: A dbus message containing a dict of strings
+ * Returns: NULL
  *
- * Getter for "Capabilities" property of an interface.
+ * Handler function for "FlushBSS" method call of network interface.
  */
-DBusMessage * wpas_dbus_getter_capabilities(DBusMessage *message,
-                                           struct wpa_supplicant *wpa_s)
+DBusMessage * wpas_dbus_handler_flush_bss(DBusMessage *message,
+                                         struct wpa_supplicant *wpa_s)
 {
-       DBusMessage *reply = NULL;
-       struct wpa_driver_capa capa;
-       int res;
-       DBusMessageIter iter, iter_dict;
-       DBusMessageIter iter_dict_entry, iter_dict_val, iter_array,
-               variant_iter;
-       const char *scans[] = { "active", "passive", "ssid" };
-       const char *modes[] = { "infrastructure", "ad-hoc", "ap" };
-       int n = sizeof(modes) / sizeof(char *);
+       dbus_uint32_t age;
+
+       dbus_message_get_args(message, NULL, DBUS_TYPE_UINT32, &age,
+                             DBUS_TYPE_INVALID);
 
-       if (message == NULL)
-               reply = dbus_message_new(DBUS_MESSAGE_TYPE_SIGNAL);
+       if (age == 0)
+               wpa_bss_flush(wpa_s);
        else
-               reply = dbus_message_new_method_return(message);
-       if (!reply)
-               goto nomem;
+               wpa_bss_flush_by_age(wpa_s, age);
 
-       dbus_message_iter_init_append(reply, &iter);
-       if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
-                                             "a{sv}", &variant_iter))
+       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))
                goto nomem;
 
        if (!wpa_dbus_dict_open_write(&variant_iter, &iter_dict))
@@ -1949,41 +2087,78 @@ DBusMessage * wpas_dbus_getter_capabilities(DBusMessage *message,
                goto nomem;
 
        /***** Modes */
-       if (res < 0 || !(capa.flags & WPA_DRIVER_FLAGS_AP))
-               n--; /* exclude ap mode if it is not supported by the driver */
-       if (!wpa_dbus_dict_append_string_array(&iter_dict, "Modes", modes, n))
+       if (!wpa_dbus_dict_begin_string_array(&iter_dict, "Modes",
+                                             &iter_dict_entry,
+                                             &iter_dict_val,
+                                             &iter_array))
+               goto nomem;
+
+       if (!wpa_dbus_dict_string_array_add_element(
+                           &iter_array, "infrastructure"))
+               goto nomem;
+
+       if (!wpa_dbus_dict_string_array_add_element(
+                           &iter_array, "ad-hoc"))
                goto nomem;
 
+       if (res >= 0) {
+               if (capa.flags & (WPA_DRIVER_FLAGS_AP)) {
+                       if (!wpa_dbus_dict_string_array_add_element(
+                                   &iter_array, "ap"))
+                               goto nomem;
+               }
+
+               if (capa.flags & (WPA_DRIVER_FLAGS_P2P_CAPABLE)) {
+                       if (!wpa_dbus_dict_string_array_add_element(
+                                   &iter_array, "p2p"))
+                               goto nomem;
+               }
+       }
+
+       if (!wpa_dbus_dict_end_string_array(&iter_dict,
+                                           &iter_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))
                goto nomem;
-       if (!dbus_message_iter_close_container(&iter, &variant_iter))
+       if (!dbus_message_iter_close_container(iter, &variant_iter))
                goto nomem;
 
-       return reply;
+       return TRUE;
 
 nomem:
-       if (reply)
-               dbus_message_unref(reply);
-
-       return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, NULL);
+       dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+       return FALSE;
 }
 
 
 /**
  * wpas_dbus_getter_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
+ * @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.
  */
-DBusMessage * wpas_dbus_getter_state(DBusMessage *message,
-                                    struct wpa_supplicant *wpa_s)
+dbus_bool_t wpas_dbus_getter_state(DBusMessageIter *iter, DBusError *error,
+                                  void *user_data)
 {
-       DBusMessage *reply = NULL;
+       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);
 
@@ -1991,141 +2166,365 @@ DBusMessage * wpas_dbus_getter_state(DBusMessage *message,
         */
        state_ls = tmp = os_strdup(str_state);
        if (!tmp) {
-               return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-                                             NULL);
+               dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+               return FALSE;
        }
        while (*tmp) {
                *tmp = tolower(*tmp);
                tmp++;
        }
 
-       reply = wpas_dbus_simple_property_getter(message, DBUS_TYPE_STRING,
-                                                &state_ls);
+       success = wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
+                                                  &state_ls, error);
 
        os_free(state_ls);
 
-       return reply;
+       return success;
 }
 
 
 /**
  * wpas_dbus_new_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
+ * @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.
  */
-DBusMessage * wpas_dbus_getter_scanning(DBusMessage *message,
-                                       struct wpa_supplicant *wpa_s)
+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(message, DBUS_TYPE_BOOLEAN,
-                                               &scanning);
+
+       return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
+                                               &scanning, error);
 }
 
 
 /**
  * wpas_dbus_getter_ap_scan - Control roaming mode
- * @message: Pointer to incoming dbus message
- * @wpa_s: wpa_supplicant structure for a network interface
- * Returns: A message containong value of ap_scan variable
+ * @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.
  */
-DBusMessage * wpas_dbus_getter_ap_scan(DBusMessage *message,
-                                      struct wpa_supplicant *wpa_s)
+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(message, DBUS_TYPE_UINT32,
-                                               &ap_scan);
+
+       return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT32,
+                                               &ap_scan, error);
 }
 
 
 /**
  * wpas_dbus_setter_ap_scan - Control roaming mode
- * @message: Pointer to incoming dbus message
- * @wpa_s: wpa_supplicant structure for a network interface
- * Returns: NULL
+ * @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.
  */
-DBusMessage * wpas_dbus_setter_ap_scan(DBusMessage *message,
-                                      struct wpa_supplicant *wpa_s)
+dbus_bool_t wpas_dbus_setter_ap_scan(DBusMessageIter *iter, DBusError *error,
+                                    void *user_data)
 {
-       DBusMessage *reply = NULL;
+       struct wpa_supplicant *wpa_s = user_data;
        dbus_uint32_t ap_scan;
 
-       reply = wpas_dbus_simple_property_setter(message, DBUS_TYPE_UINT32,
-                                                &ap_scan);
-       if (reply)
-               return reply;
+       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)) {
-               return wpas_dbus_error_invalid_args(
-                       message, "ap_scan must equal 0, 1 or 2");
+               dbus_set_error_const(error, DBUS_ERROR_FAILED,
+                                    "ap_scan must be 0, 1, or 2");
+               return FALSE;
        }
-       return NULL;
+       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_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_age;
+
+       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_ifname - Get interface name
- * @message: Pointer to incoming dbus message
- * @wpa_s: wpa_supplicant structure for a network interface
- * Returns: A dbus message containing a name of network interface
- * associated with with wpa_s
+ * @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.
  */
-DBusMessage * wpas_dbus_getter_ifname(DBusMessage *message,
-                                     struct wpa_supplicant *wpa_s)
+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(message, DBUS_TYPE_STRING,
-                                               &ifname);
+
+       return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
+                                               &ifname, error);
 }
 
 
 /**
  * wpas_dbus_getter_driver - Get interface name
- * @message: Pointer to incoming dbus message
- * @wpa_s: wpa_supplicant structure for a network interface
- * Returns: A dbus message containing a name of network interface
- * driver associated with with wpa_s
+ * @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.
  */
-DBusMessage * wpas_dbus_getter_driver(DBusMessage *message,
-                                     struct wpa_supplicant *wpa_s)
+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, "wpas_dbus_getter_driver[dbus]: "
                           "wpa_s has no driver set");
-               return wpas_dbus_error_unknown_error(message, NULL);
+               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(message, DBUS_TYPE_STRING,
-                                               &driver);
+       return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
+                                               &driver, error);
 }
 
 
 /**
  * wpas_dbus_getter_current_bss - Get current bss object path
- * @message: Pointer to incoming dbus message
- * @wpa_s: wpa_supplicant structure for a network interface
- * Returns: A dbus message containing a DBus object path to
- * current 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 "CurrentBSS" property.
  */
-DBusMessage * wpas_dbus_getter_current_bss(DBusMessage *message,
-                                          struct wpa_supplicant *wpa_s)
+dbus_bool_t wpas_dbus_getter_current_bss(DBusMessageIter *iter,
+                                        DBusError *error,
+                                        void *user_data)
 {
-       DBusMessage *reply;
+       struct wpa_supplicant *wpa_s = user_data;
        char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *bss_obj_path = path_buf;
 
        if (wpa_s->current_bss)
@@ -2135,27 +2534,25 @@ DBusMessage * wpas_dbus_getter_current_bss(DBusMessage *message,
        else
                os_snprintf(bss_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "/");
 
-       reply = wpas_dbus_simple_property_getter(message,
-                                                DBUS_TYPE_OBJECT_PATH,
-                                                &bss_obj_path);
-
-       return reply;
+       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
- * @message: Pointer to incoming dbus message
- * @wpa_s: wpa_supplicant structure for a network interface
- * Returns: A dbus message containing a DBus object path to
- * current 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 "CurrentNetwork" property.
  */
-DBusMessage * wpas_dbus_getter_current_network(DBusMessage *message,
-                                              struct wpa_supplicant *wpa_s)
+dbus_bool_t wpas_dbus_getter_current_network(DBusMessageIter *iter,
+                                            DBusError *error,
+                                            void *user_data)
 {
-       DBusMessage *reply;
+       struct wpa_supplicant *wpa_s = user_data;
        char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *net_obj_path = path_buf;
 
        if (wpa_s->current_ssid)
@@ -2165,70 +2562,100 @@ DBusMessage * wpas_dbus_getter_current_network(DBusMessage *message,
        else
                os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "/");
 
-       reply = wpas_dbus_simple_property_getter(message,
-                                                DBUS_TYPE_OBJECT_PATH,
-                                                &net_obj_path);
+       return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_OBJECT_PATH,
+                                               &net_obj_path, error);
+}
 
-       return reply;
+
+/**
+ * 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
- * @message: Pointer to incoming dbus message
- * @wpa_s: wpa_supplicant structure for a network interface
- * Returns: A dbus message containing a name of bridge network
- * interface associated with with wpa_s
+ * @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.
  */
-DBusMessage * wpas_dbus_getter_bridge_ifname(DBusMessage *message,
-                                            struct wpa_supplicant *wpa_s)
+dbus_bool_t wpas_dbus_getter_bridge_ifname(DBusMessageIter *iter,
+                                          DBusError *error,
+                                          void *user_data)
 {
-       const char *bridge_ifname = NULL;
+       struct wpa_supplicant *wpa_s = user_data;
+       const char *bridge_ifname;
 
-       bridge_ifname = wpa_s->bridge_ifname;
-       if (bridge_ifname == NULL) {
-               wpa_printf(MSG_ERROR, "wpas_dbus_getter_bridge_ifname[dbus]: "
-                          "wpa_s has no bridge interface name set");
-               return wpas_dbus_error_unknown_error(message, NULL);
-       }
-
-       return wpas_dbus_simple_property_getter(message, DBUS_TYPE_STRING,
-                                               &bridge_ifname);
+       bridge_ifname = wpa_s->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
- * @message: Pointer to incoming dbus message
- * @wpa_s: wpa_supplicant structure for a network interface
- * Returns: a dbus message containing an array of all known BSS objects
- * dbus paths
+ * @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.
  */
-DBusMessage * wpas_dbus_getter_bsss(DBusMessage *message,
-                                   struct wpa_supplicant *wpa_s)
+dbus_bool_t wpas_dbus_getter_bsss(DBusMessageIter *iter, DBusError *error,
+                                 void *user_data)
 {
-       DBusMessage *reply = NULL;
+       struct wpa_supplicant *wpa_s = user_data;
        struct wpa_bss *bss;
        char **paths;
        unsigned int i = 0;
+       dbus_bool_t success = FALSE;
 
        paths = os_zalloc(wpa_s->num_bss * sizeof(char *));
        if (!paths) {
-               return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-                                             NULL);
+               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) {
-                       reply = dbus_message_new_error(message,
-                                                      DBUS_ERROR_NO_MEMORY,
-                                                      NULL);
+                       dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY,
+                                            "no memory");
                        goto out;
                }
                /* Construct the object path for this BSS. */
@@ -2237,57 +2664,62 @@ DBusMessage * wpas_dbus_getter_bsss(DBusMessage *message,
                            wpa_s->dbus_new_path, bss->id);
        }
 
-       reply = wpas_dbus_simple_array_property_getter(message,
-                                                      DBUS_TYPE_OBJECT_PATH,
-                                                      paths, wpa_s->num_bss);
+       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 reply;
+       return success;
 }
 
 
 /**
  * wpas_dbus_getter_networks - Get array of networks objects
- * @message: Pointer to incoming dbus message
- * @wpa_s: wpa_supplicant structure for a network interface
- * Returns: a dbus message containing an array of all configured
- * networks dbus object paths.
+ * @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.
  */
-DBusMessage * wpas_dbus_getter_networks(DBusMessage *message,
-                                       struct wpa_supplicant *wpa_s)
+dbus_bool_t wpas_dbus_getter_networks(DBusMessageIter *iter, DBusError *error,
+                                     void *user_data)
 {
-       DBusMessage *reply = NULL;
+       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->conf == NULL) {
-               wpa_printf(MSG_ERROR, "wpas_dbus_getter_networks[dbus]: "
-                          "An error occurred getting networks list.");
-               return wpas_dbus_error_unknown_error(message, NULL);
+               wpa_printf(MSG_ERROR, "%s[dbus]: An error occurred getting "
+                          "networks list.", __func__);
+               dbus_set_error(error, DBUS_ERROR_FAILED, "%s: an error "
+                              "occurred getting the networks list", __func__);
+               return FALSE;
        }
 
        for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next)
-               num++;
+               if (!network_is_persistent_group(ssid))
+                       num++;
 
        paths = os_zalloc(num * sizeof(char *));
        if (!paths) {
-               return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-                                             NULL);
+               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) {
-                       reply = dbus_message_new_error(message,
-                                                      DBUS_ERROR_NO_MEMORY,
-                                                      NULL);
+                       dbus_set_error(error, DBUS_ERROR_NO_MEMORY, "no memory");
                        goto out;
                }
 
@@ -2297,50 +2729,40 @@ DBusMessage * wpas_dbus_getter_networks(DBusMessage *message,
                            wpa_s->dbus_new_path, ssid->id);
        }
 
-       reply = wpas_dbus_simple_array_property_getter(message,
-                                                      DBUS_TYPE_OBJECT_PATH,
-                                                      paths, num);
+       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 reply;
+       return success;
 }
 
 
 /**
  * wpas_dbus_getter_blobs - Get all blobs defined for this interface
- * @message: Pointer to incoming dbus message
- * @wpa_s: wpa_supplicant structure for a network interface
- * Returns: a dbus message containing a dictionary of pairs (blob_name, blob)
+ * @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.
  */
-DBusMessage * wpas_dbus_getter_blobs(DBusMessage *message,
-                                    struct wpa_supplicant *wpa_s)
+dbus_bool_t wpas_dbus_getter_blobs(DBusMessageIter *iter, DBusError *error,
+                                  void *user_data)
 {
-       DBusMessage *reply = NULL;
-       DBusMessageIter iter, variant_iter, dict_iter, entry_iter, array_iter;
+       struct wpa_supplicant *wpa_s = user_data;
+       DBusMessageIter variant_iter, dict_iter, entry_iter, array_iter;
        struct wpa_config_blob *blob;
 
-       if (message == NULL)
-               reply = dbus_message_new(DBUS_MESSAGE_TYPE_SIGNAL);
-       else
-               reply = dbus_message_new_method_return(message);
-       if (!reply)
-               return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-                                             NULL);
-
-       dbus_message_iter_init_append(reply, &iter);
-
-       if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
+       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_message_unref(reply);
-               return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-                                             NULL);
+               dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+               return FALSE;
        }
 
        blob = wpa_s->conf->blobs;
@@ -2363,176 +2785,196 @@ DBusMessage * wpas_dbus_getter_blobs(DBusMessage *message,
                                                       &array_iter) ||
                    !dbus_message_iter_close_container(&dict_iter,
                                                       &entry_iter)) {
-                       dbus_message_unref(reply);
-                       return dbus_message_new_error(message,
-                                                     DBUS_ERROR_NO_MEMORY,
-                                                     NULL);
+                       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_message_unref(reply);
-               return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-                                             NULL);
+           !dbus_message_iter_close_container(iter, &variant_iter)) {
+               dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+               return FALSE;
        }
 
-       return reply;
+       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
- * @message: Pointer to incoming dbus message
- * @bss: a pair of interface describing structure and bss's id
- * Returns: a dbus message containing the bssid for the requested 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.
  */
-DBusMessage * wpas_dbus_getter_bss_bssid(DBusMessage *message,
-                                        struct bss_handler_args *bss)
+dbus_bool_t wpas_dbus_getter_bss_bssid(DBusMessageIter *iter, DBusError *error,
+                                      void *user_data)
 {
-       struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id);
+       struct bss_handler_args *args = user_data;
+       struct wpa_bss *res;
 
-       if (!res) {
-               wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_bssid[dbus]: no "
-                          "bss with id %d found", bss->id);
-               return NULL;
-       }
+       res = get_bss_helper(args, error, __func__);
+       if (!res)
+               return FALSE;
 
-       return wpas_dbus_simple_array_property_getter(message, DBUS_TYPE_BYTE,
-                                                     res->bssid, ETH_ALEN);
+       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
- * @message: Pointer to incoming dbus message
- * @bss: a pair of interface describing structure and bss's id
- * Returns: a dbus message containing the ssid for the requested 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.
  */
-DBusMessage * wpas_dbus_getter_bss_ssid(DBusMessage *message,
-                                             struct bss_handler_args *bss)
+dbus_bool_t wpas_dbus_getter_bss_ssid(DBusMessageIter *iter, DBusError *error,
+                                     void *user_data)
 {
-       struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id);
+       struct bss_handler_args *args = user_data;
+       struct wpa_bss *res;
 
-       if (!res) {
-               wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_ssid[dbus]: no "
-                          "bss with id %d found", bss->id);
-               return NULL;
-       }
+       res = get_bss_helper(args, error, __func__);
+       if (!res)
+               return FALSE;
 
-       return wpas_dbus_simple_array_property_getter(message, DBUS_TYPE_BYTE,
-                                                     res->ssid,
-                                                     res->ssid_len);
+       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
- * @message: Pointer to incoming dbus message
- * @bss: a pair of interface describing structure and bss's id
- * Returns: a dbus message containing the privacy flag value of requested 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.
  */
-DBusMessage * wpas_dbus_getter_bss_privacy(DBusMessage *message,
-                                          struct bss_handler_args *bss)
+dbus_bool_t wpas_dbus_getter_bss_privacy(DBusMessageIter *iter,
+                                        DBusError *error, void *user_data)
 {
-       struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id);
+       struct bss_handler_args *args = user_data;
+       struct wpa_bss *res;
        dbus_bool_t privacy;
 
-       if (!res) {
-               wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_privacy[dbus]: no "
-                          "bss with id %d found", bss->id);
-               return NULL;
-       }
+       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(message, DBUS_TYPE_BOOLEAN,
-                                               &privacy);
+       return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
+                                               &privacy, error);
 }
 
 
 /**
  * wpas_dbus_getter_bss_mode - Return the mode of a BSS
- * @message: Pointer to incoming dbus message
- * @bss: a pair of interface describing structure and bss's id
- * Returns: a dbus message containing the mode of requested 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.
  */
-DBusMessage * wpas_dbus_getter_bss_mode(DBusMessage *message,
-                                       struct bss_handler_args *bss)
+dbus_bool_t wpas_dbus_getter_bss_mode(DBusMessageIter *iter, DBusError *error,
+                                     void *user_data)
 {
-       struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id);
+       struct bss_handler_args *args = user_data;
+       struct wpa_bss *res;
        const char *mode;
 
-       if (!res) {
-               wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_mode[dbus]: no "
-                          "bss with id %d found", bss->id);
-               return NULL;
-       }
+       res = get_bss_helper(args, error, __func__);
+       if (!res)
+               return FALSE;
 
        if (res->caps & IEEE80211_CAP_IBSS)
                mode = "ad-hoc";
        else
                mode = "infrastructure";
 
-       return wpas_dbus_simple_property_getter(message, DBUS_TYPE_STRING,
-                                               &mode);
+       return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
+                                               &mode, error);
 }
 
 
 /**
  * wpas_dbus_getter_bss_level - Return the signal strength of a BSS
- * @message: Pointer to incoming dbus message
- * @bss: a pair of interface describing structure and bss's id
- * Returns: a dbus message containing the signal strength of requested 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.
  */
-DBusMessage * wpas_dbus_getter_bss_signal(DBusMessage *message,
-                                         struct bss_handler_args *bss)
+dbus_bool_t wpas_dbus_getter_bss_signal(DBusMessageIter *iter,
+                                       DBusError *error, void *user_data)
 {
-       struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id);
+       struct bss_handler_args *args = user_data;
+       struct wpa_bss *res;
+       s16 level;
 
-       if (!res) {
-               wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_signal[dbus]: no "
-                          "bss with id %d found", bss->id);
-               return NULL;
-       }
+       res = get_bss_helper(args, error, __func__);
+       if (!res)
+               return FALSE;
 
-       return wpas_dbus_simple_property_getter(message, DBUS_TYPE_INT16,
-                                               &res->level);
+       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
- * @message: Pointer to incoming dbus message
- * @bss: a pair of interface describing structure and bss's id
- * Returns: a dbus message containing the frequency of requested 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.
  */
-DBusMessage * wpas_dbus_getter_bss_frequency(DBusMessage *message,
-                                            struct bss_handler_args *bss)
+dbus_bool_t wpas_dbus_getter_bss_frequency(DBusMessageIter *iter,
+                                          DBusError *error, void *user_data)
 {
-       struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id);
+       struct bss_handler_args *args = user_data;
+       struct wpa_bss *res;
+       u16 freq;
 
-       if (!res) {
-               wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_frequency[dbus]: "
-                          "no bss with id %d found", bss->id);
-               return NULL;
-       }
+       res = get_bss_helper(args, error, __func__);
+       if (!res)
+               return FALSE;
 
-       return wpas_dbus_simple_property_getter(message, DBUS_TYPE_UINT16,
-                                               &res->freq);
+       freq = (u16) res->freq;
+       return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT16,
+                                               &freq, error);
 }
 
 
@@ -2544,72 +2986,64 @@ static int cmp_u8s_desc(const void *a, const void *b)
 
 /**
  * wpas_dbus_getter_bss_rates - Return available bit rates of a BSS
- * @message: Pointer to incoming dbus message
- * @bss: a pair of interface describing structure and bss's id
- * Returns: a dbus message containing sorted array of bit rates
+ * @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.
  */
-DBusMessage * wpas_dbus_getter_bss_rates(DBusMessage *message,
-                                           struct bss_handler_args *bss)
+dbus_bool_t wpas_dbus_getter_bss_rates(DBusMessageIter *iter,
+                                      DBusError *error, void *user_data)
 {
-       DBusMessage *reply;
-       struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id);
+       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;
 
-       if (!res) {
-               wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_rates[dbus]: "
-                          "no bss with id %d found", bss->id);
-               return NULL;
-       }
+       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 NULL;
+               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);
-               return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-                                             NULL);
+               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;
 
-       reply = wpas_dbus_simple_array_property_getter(message,
-                                                      DBUS_TYPE_UINT32,
-                                                      real_rates, rates_num);
+       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 reply;
+       return success;
 }
 
 
-static DBusMessage * wpas_dbus_get_bss_security_prop(
-       DBusMessage *message, struct wpa_ie_data *ie_data)
+static dbus_bool_t wpas_dbus_get_bss_security_prop(DBusMessageIter *iter,
+                                                  struct wpa_ie_data *ie_data,
+                                                  DBusError *error)
 {
-       DBusMessage *reply;
-       DBusMessageIter iter, iter_dict, variant_iter;
+       DBusMessageIter iter_dict, variant_iter;
        const char *group;
        const char *pairwise[2]; /* max 2 pairwise ciphers is supported */
        const char *key_mgmt[7]; /* max 7 key managements may be supported */
        int n;
 
-       if (message == NULL)
-               reply = dbus_message_new(DBUS_MESSAGE_TYPE_SIGNAL);
-       else
-               reply = dbus_message_new_method_return(message);
-       if (!reply)
-               goto nomem;
-
-       dbus_message_iter_init_append(reply, &iter);
-       if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
+       if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
                                              "a{sv}", &variant_iter))
                goto nomem;
 
@@ -2690,152 +3124,152 @@ static DBusMessage * wpas_dbus_get_bss_security_prop(
 
        if (!wpa_dbus_dict_close_write(&variant_iter, &iter_dict))
                goto nomem;
-       if (!dbus_message_iter_close_container(&iter, &variant_iter))
+       if (!dbus_message_iter_close_container(iter, &variant_iter))
                goto nomem;
 
-       return reply;
+       return TRUE;
 
 nomem:
-       if (reply)
-               dbus_message_unref(reply);
-
-       return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, NULL);
+       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
- * @message: Pointer to incoming dbus message
- * @bss: a pair of interface describing structure and bss's id
- * Returns: a dbus message containing the WPA options of requested 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.
  */
-DBusMessage * wpas_dbus_getter_bss_wpa(DBusMessage *message,
-                                      struct bss_handler_args *bss)
+dbus_bool_t wpas_dbus_getter_bss_wpa(DBusMessageIter *iter, DBusError *error,
+                                    void *user_data)
 {
-       struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id);
+       struct bss_handler_args *args = user_data;
+       struct wpa_bss *res;
        struct wpa_ie_data wpa_data;
        const u8 *ie;
 
-       if (!res) {
-               wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_wpa[dbus]: no "
-                          "bss with id %d found", bss->id);
-               return NULL;
-       }
+       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) {
-               if (wpa_parse_wpa_ie(ie, 2 + ie[1], &wpa_data) < 0)
-                       return wpas_dbus_error_unknown_error(message,
-                                                            "invalid WPA IE");
+               if (wpa_parse_wpa_ie(ie, 2 + ie[1], &wpa_data) < 0) {
+                       dbus_set_error_const(error, DBUS_ERROR_FAILED,
+                                            "failed to parse WPA IE");
+                       return FALSE;
+               }
        }
 
-       return wpas_dbus_get_bss_security_prop(message, &wpa_data);
+       return wpas_dbus_get_bss_security_prop(iter, &wpa_data, error);
 }
 
 
 /**
  * wpas_dbus_getter_bss_rsn - Return the RSN options of a BSS
- * @message: Pointer to incoming dbus message
- * @bss: a pair of interface describing structure and bss's id
- * Returns: a dbus message containing the RSN options of requested 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.
  */
-DBusMessage * wpas_dbus_getter_bss_rsn(DBusMessage *message,
-                                      struct bss_handler_args *bss)
+dbus_bool_t wpas_dbus_getter_bss_rsn(DBusMessageIter *iter, DBusError *error,
+                                    void *user_data)
 {
-       struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id);
+       struct bss_handler_args *args = user_data;
+       struct wpa_bss *res;
        struct wpa_ie_data wpa_data;
        const u8 *ie;
 
-       if (!res) {
-               wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_rsn[dbus]: no "
-                          "bss with id %d found", bss->id);
-               return NULL;
-       }
+       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) {
-               if (wpa_parse_wpa_ie(ie, 2 + ie[1], &wpa_data) < 0)
-                       return wpas_dbus_error_unknown_error(message,
-                                                            "invalid RSN IE");
+               if (wpa_parse_wpa_ie(ie, 2 + ie[1], &wpa_data) < 0) {
+                       dbus_set_error_const(error, DBUS_ERROR_FAILED,
+                                            "failed to parse RSN IE");
+                       return FALSE;
+               }
        }
 
-       return wpas_dbus_get_bss_security_prop(message, &wpa_data);
+       return wpas_dbus_get_bss_security_prop(iter, &wpa_data, error);
 }
 
 
 /**
  * wpas_dbus_getter_bss_ies - Return all IEs of a BSS
- * @message: Pointer to incoming dbus message
- * @bss: a pair of interface describing structure and bss's id
- * Returns: a dbus message containing IEs byte array
+ * @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.
  */
-DBusMessage * wpas_dbus_getter_bss_ies(DBusMessage *message,
-                                      struct bss_handler_args *bss)
+dbus_bool_t wpas_dbus_getter_bss_ies(DBusMessageIter *iter, DBusError *error,
+                                    void *user_data)
 {
-       struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id);
+       struct bss_handler_args *args = user_data;
+       struct wpa_bss *res;
 
-       if (!res) {
-               wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_ies[dbus]: no "
-                          "bss with id %d found", bss->id);
-               return NULL;
-       }
+       res = get_bss_helper(args, error, __func__);
+       if (!res)
+               return FALSE;
 
-       return wpas_dbus_simple_array_property_getter(message, DBUS_TYPE_BYTE,
-                                                     res + 1, res->ie_len);
+       return wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE,
+                                                     res + 1, res->ie_len,
+                                                     error);
 }
 
 
 /**
  * wpas_dbus_getter_enabled - Check whether network is enabled or disabled
- * @message: Pointer to incoming dbus message
- * @wpas_dbus_setter_enabled: wpa_supplicant structure for a network interface
- * and wpa_ssid structure for a configured network
- * Returns: DBus message with boolean indicating state of configured network
- * or DBus error on failure
+ * @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.
  */
-DBusMessage * wpas_dbus_getter_enabled(DBusMessage *message,
-                                      struct network_handler_args *net)
+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(message, DBUS_TYPE_BOOLEAN,
-                                               &enabled);
+
+       return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
+                                               &enabled, error);
 }
 
 
 /**
  * wpas_dbus_setter_enabled - Mark a configured network as enabled or disabled
- * @message: Pointer to incoming dbus message
- * @wpas_dbus_setter_enabled: wpa_supplicant structure for a network interface
- * and wpa_ssid structure for a configured network
- * Returns: NULL indicating success or DBus error on failure
+ * @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.
  */
-DBusMessage * wpas_dbus_setter_enabled(DBusMessage *message,
-                                      struct network_handler_args *net)
+dbus_bool_t wpas_dbus_setter_enabled(DBusMessageIter *iter, DBusError *error,
+                                    void *user_data)
 {
-       DBusMessage *reply = NULL;
-
+       struct network_handler_args *net = user_data;
        struct wpa_supplicant *wpa_s;
        struct wpa_ssid *ssid;
-
        dbus_bool_t enable;
 
-       reply = wpas_dbus_simple_property_setter(message, DBUS_TYPE_BOOLEAN,
-                                                &enable);
-
-       if (reply)
-               return reply;
+       if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_BOOLEAN,
+                                             &enable))
+               return FALSE;
 
        wpa_s = net->wpa_s;
        ssid = net->ssid;
@@ -2845,48 +3279,38 @@ DBusMessage * wpas_dbus_setter_enabled(DBusMessage *message,
        else
                wpa_supplicant_disable_network(wpa_s, ssid);
 
-       return NULL;
+       return TRUE;
 }
 
 
 /**
  * wpas_dbus_getter_network_properties - Get options for a configured network
- * @message: Pointer to incoming dbus message
- * @net: wpa_supplicant structure for a network interface and
- * wpa_ssid structure for a configured network
- * Returns: DBus message with network properties or DBus error on failure
+ * @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.
  */
-DBusMessage * wpas_dbus_getter_network_properties(
-       DBusMessage *message, struct network_handler_args *net)
+dbus_bool_t wpas_dbus_getter_network_properties(DBusMessageIter *iter,
+                                               DBusError *error,
+                                               void *user_data)
 {
-       DBusMessage *reply = NULL;
-       DBusMessageIter iter, variant_iter, dict_iter;
+       struct network_handler_args *net = user_data;
+       DBusMessageIter variant_iter, dict_iter;
        char **iterator;
-       char **props = wpa_config_get_all(net->ssid, 0);
-       if (!props)
-               return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-                                             NULL);
+       char **props = wpa_config_get_all(net->ssid, 1);
+       dbus_bool_t success = FALSE;
 
-       if (message == NULL)
-               reply = dbus_message_new(DBUS_MESSAGE_TYPE_SIGNAL);
-       else
-               reply = dbus_message_new_method_return(message);
-       if (!reply) {
-               reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-                                              NULL);
-               goto out;
+       if (!props) {
+               dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+               return FALSE;
        }
 
-       dbus_message_iter_init_append(reply, &iter);
-
-       if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
-                       "a{sv}", &variant_iter) ||
+       if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "a{sv}",
+                                             &variant_iter) ||
            !wpa_dbus_dict_open_write(&variant_iter, &dict_iter)) {
-               dbus_message_unref(reply);
-               reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-                                              NULL);
+               dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
                goto out;
        }
 
@@ -2894,10 +3318,8 @@ DBusMessage * wpas_dbus_getter_network_properties(
        while (*iterator) {
                if (!wpa_dbus_dict_append_string(&dict_iter, *iterator,
                                                 *(iterator + 1))) {
-                       dbus_message_unref(reply);
-                       reply = dbus_message_new_error(message,
-                                                      DBUS_ERROR_NO_MEMORY,
-                                                      NULL);
+                       dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY,
+                                            "no memory");
                        goto out;
                }
                iterator += 2;
@@ -2905,13 +3327,13 @@ DBusMessage * wpas_dbus_getter_network_properties(
 
 
        if (!wpa_dbus_dict_close_write(&variant_iter, &dict_iter) ||
-           !dbus_message_iter_close_container(&iter, &variant_iter)) {
-               dbus_message_unref(reply);
-               reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-                                              NULL);
+           !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) {
@@ -2919,39 +3341,27 @@ out:
                iterator++;
        }
        os_free(props);
-       return reply;
+       return success;
 }
 
 
 /**
  * wpas_dbus_setter_network_properties - Set options for a configured network
- * @message: Pointer to incoming dbus message
- * @net: wpa_supplicant structure for a network interface and
- * wpa_ssid structure for a configured network
- * Returns: NULL indicating success or DBus error on failure
+ * @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.
  */
-DBusMessage * wpas_dbus_setter_network_properties(
-       DBusMessage *message, struct network_handler_args *net)
+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;
 
-       DBusMessage *reply = NULL;
-       DBusMessageIter iter, variant_iter;
-
-       dbus_message_iter_init(message, &iter);
-
-       dbus_message_iter_next(&iter);
-       dbus_message_iter_next(&iter);
-
-       dbus_message_iter_recurse(&iter, &variant_iter);
-
-       reply = set_network_properties(message, net->wpa_s, ssid,
-                                      &variant_iter);
-       if (reply)
-               wpa_printf(MSG_DEBUG, "dbus control interface couldn't set "
-                          "network properties");
-
-       return reply;
+       dbus_message_iter_recurse(iter, &variant_iter);
+       return set_network_properties(net->wpa_s, ssid, &variant_iter, error);
 }
index 3cdf9cb..b46658f 100644 (file)
@@ -26,17 +26,26 @@ struct bss_handler_args {
        unsigned int id;
 };
 
-DBusMessage * wpas_dbus_simple_property_getter(DBusMessage *message,
-                                              const int type,
-                                              const void *val);
-
-DBusMessage * wpas_dbus_simple_property_setter(DBusMessage *message,
-                                              const int type, void *val);
-
-DBusMessage * wpas_dbus_simple_array_property_getter(DBusMessage *message,
-                                                    const int type,
-                                                    const void *array,
-                                                    size_t array_len);
+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);
@@ -47,29 +56,35 @@ DBusMessage * wpas_dbus_handler_remove_interface(DBusMessage *message,
 DBusMessage * wpas_dbus_handler_get_interface(DBusMessage *message,
                                              struct wpa_global *global);
 
-DBusMessage * wpas_dbus_getter_debug_level(DBusMessage *message,
-                                          struct wpa_global *global);
+dbus_bool_t wpas_dbus_getter_debug_level(DBusMessageIter *iter,
+                                        DBusError *error,
+                                        void *user_data);
 
-DBusMessage * wpas_dbus_getter_debug_timestamp(DBusMessage *message,
-                                              struct wpa_global *global);
+dbus_bool_t wpas_dbus_getter_debug_timestamp(DBusMessageIter *iter,
+                                             DBusError *error,
+                                             void *user_data);
 
-DBusMessage * wpas_dbus_getter_debug_show_keys(DBusMessage *message,
-                                              struct wpa_global *global);
+dbus_bool_t wpas_dbus_getter_debug_show_keys(DBusMessageIter *iter,
+                                            DBusError *error,
+                                            void *user_data);
 
-DBusMessage * wpas_dbus_setter_debug_level(DBusMessage *message,
-                                          struct wpa_global *global);
+dbus_bool_t wpas_dbus_setter_debug_level(DBusMessageIter *iter,
+                                        DBusError *error, void *user_data);
 
-DBusMessage * wpas_dbus_setter_debug_timestamp(DBusMessage *message,
-                                              struct wpa_global *global);
+dbus_bool_t wpas_dbus_setter_debug_timestamp(DBusMessageIter *iter,
+                                            DBusError *error,
+                                            void *user_data);
 
-DBusMessage * wpas_dbus_setter_debug_show_keys(DBusMessage *message,
-                                              struct wpa_global *global);
+dbus_bool_t wpas_dbus_setter_debug_show_keys(DBusMessageIter *iter,
+                                            DBusError *error,
+                                            void *user_data);
 
-DBusMessage * wpas_dbus_getter_interfaces(DBusMessage *message,
-                                         struct wpa_global *global);
+dbus_bool_t wpas_dbus_getter_interfaces(DBusMessageIter *iter,
+                                       DBusError *error,
+                                       void *user_data);
 
-DBusMessage * wpas_dbus_getter_eap_methods(DBusMessage *message,
-                                          void *nothing);
+dbus_bool_t wpas_dbus_getter_eap_methods(DBusMessageIter *iter,
+                                        DBusError *error, void *user_data);
 
 DBusMessage * wpas_dbus_handler_scan(DBusMessage *message,
                                     struct wpa_supplicant *wpa_s);
@@ -77,15 +92,26 @@ DBusMessage * wpas_dbus_handler_scan(DBusMessage *message,
 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_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);
 
@@ -95,98 +121,137 @@ DBusMessage * wpas_dbus_handler_get_blob(DBusMessage *message,
 DBusMessage * wpas_dbus_handler_remove_blob(DBusMessage *message,
                                            struct wpa_supplicant *wpa_s);
 
-DBusMessage * wpas_dbus_getter_capabilities(DBusMessage *message,
-                                           struct wpa_supplicant *wpa_s);
+DBusMessage * wpas_dbus_handler_flush_bss(DBusMessage *message,
+                                         struct wpa_supplicant *wpa_s);
 
-DBusMessage * wpas_dbus_getter_state(DBusMessage *message,
-                                    struct wpa_supplicant *wpa_s);
+dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter,
+                                         DBusError *error, void *user_data);
 
-DBusMessage * wpas_dbus_getter_scanning(DBusMessage *message,
-                                       struct wpa_supplicant *wpa_s);
+dbus_bool_t wpas_dbus_getter_state(DBusMessageIter *iter, DBusError *error,
+                                  void *user_data);
 
-DBusMessage * wpas_dbus_getter_ap_scan(DBusMessage *message,
-                                      struct wpa_supplicant *wpa_s);
+dbus_bool_t wpas_dbus_getter_scanning(DBusMessageIter *iter, DBusError *error,
+                                     void *user_data);
 
-DBusMessage * wpas_dbus_setter_ap_scan(DBusMessage *message,
-                                      struct wpa_supplicant *wpa_s);
+dbus_bool_t wpas_dbus_getter_ap_scan(DBusMessageIter *iter, DBusError *error,
+                                    void *user_data);
 
-DBusMessage * wpas_dbus_getter_ifname(DBusMessage *message,
-                                     struct wpa_supplicant *wpa_s);
+dbus_bool_t wpas_dbus_setter_ap_scan(DBusMessageIter *iter, DBusError *error,
+                                    void *user_data);
 
-DBusMessage * wpas_dbus_getter_driver(DBusMessage *message,
-                                     struct wpa_supplicant *wpa_s);
+dbus_bool_t wpas_dbus_getter_fast_reauth(DBusMessageIter *iter,
+                                        DBusError *error,
+                                        void *user_data);
 
-DBusMessage * wpas_dbus_getter_bridge_ifname(DBusMessage *message,
-                                            struct wpa_supplicant *wpa_s);
+dbus_bool_t wpas_dbus_setter_fast_reauth(DBusMessageIter *iter,
+                                        DBusError *error,
+                                        void *user_data);
 
-DBusMessage * wpas_dbus_getter_current_bss(DBusMessage *message,
-                                          struct wpa_supplicant *wpa_s);
+dbus_bool_t wpas_dbus_getter_bss_expire_age(DBusMessageIter *iter,
+                                           DBusError *error, void *user_data);
 
-DBusMessage * wpas_dbus_getter_current_network(DBusMessage *message,
-                                              struct wpa_supplicant *wpa_s);
+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);
 
-DBusMessage * wpas_dbus_getter_bsss(DBusMessage *message,
-                                   struct wpa_supplicant *wpa_s);
+dbus_bool_t wpas_dbus_getter_country(DBusMessageIter *iter, DBusError *error,
+                                    void *user_data);
 
-DBusMessage * wpas_dbus_getter_networks(DBusMessage *message,
-                                       struct wpa_supplicant *wpa_s);
+dbus_bool_t wpas_dbus_setter_country(DBusMessageIter *iter, DBusError *error,
+                                    void *user_data);
 
-DBusMessage * wpas_dbus_getter_blobs(DBusMessage *message,
-                                    struct wpa_supplicant *bss);
+dbus_bool_t wpas_dbus_getter_ifname(DBusMessageIter *iter, DBusError *error,
+                                   void *user_data);
 
-DBusMessage * wpas_dbus_getter_bss_bssid(DBusMessage *message,
-                                        struct bss_handler_args *bss);
+dbus_bool_t wpas_dbus_getter_driver(DBusMessageIter *iter, DBusError *error,
+                                   void *user_data);
 
-DBusMessage * wpas_dbus_getter_bss_ssid(DBusMessage *message,
-                                       struct bss_handler_args *bss);
+dbus_bool_t wpas_dbus_getter_bridge_ifname(DBusMessageIter *iter,
+                                          DBusError *error,
+                                          void *user_data);
 
-DBusMessage * wpas_dbus_getter_bss_privacy(DBusMessage *message,
-                                          struct bss_handler_args *bss);
+dbus_bool_t wpas_dbus_getter_current_bss(DBusMessageIter *iter,
+                                        DBusError *error,
+                                        void *user_data);
 
-DBusMessage * wpas_dbus_getter_bss_mode(DBusMessage *message,
-                                       struct bss_handler_args *bss);
+dbus_bool_t wpas_dbus_getter_current_network(DBusMessageIter *iter,
+                                            DBusError *error,
+                                            void *user_data);
 
-DBusMessage * wpas_dbus_getter_bss_signal(DBusMessage *message,
-                                         struct bss_handler_args *bss);
+dbus_bool_t wpas_dbus_getter_current_auth_mode(DBusMessageIter *iter,
+                                              DBusError *error,
+                                              void *user_data);
 
-DBusMessage * wpas_dbus_getter_bss_frequency(DBusMessage *message,
-                                            struct bss_handler_args *bss);
+dbus_bool_t wpas_dbus_getter_bsss(DBusMessageIter *iter, DBusError *error,
+                                 void *user_data);
 
-DBusMessage * wpas_dbus_getter_bss_rates(DBusMessage *message,
-                                        struct bss_handler_args *bss);
+dbus_bool_t wpas_dbus_getter_networks(DBusMessageIter *iter, DBusError *error,
+                                     void *user_data);
 
-DBusMessage * wpas_dbus_getter_bss_wpa(DBusMessage *message,
-                                      struct bss_handler_args *bss);
+dbus_bool_t wpas_dbus_getter_blobs(DBusMessageIter *iter, DBusError *error,
+                                  void *user_data);
 
-DBusMessage * wpas_dbus_getter_bss_rsn(DBusMessage *message,
-                                      struct bss_handler_args *bss);
+dbus_bool_t wpas_dbus_getter_bss_bssid(DBusMessageIter *iter, DBusError *error,
+                                      void *user_data);
 
-DBusMessage * wpas_dbus_getter_bss_ies(DBusMessage *message,
-                                      struct bss_handler_args *bss);
+dbus_bool_t wpas_dbus_getter_bss_ssid(DBusMessageIter *iter, DBusError *error,
+                                     void *user_data);
 
-DBusMessage * wpas_dbus_getter_enabled(DBusMessage *message,
-                                      struct network_handler_args *net);
+dbus_bool_t wpas_dbus_getter_bss_privacy(DBusMessageIter *iter,
+                                        DBusError *error, void *user_data);
 
-DBusMessage * wpas_dbus_setter_enabled(DBusMessage *message,
-                                      struct network_handler_args *net);
+dbus_bool_t wpas_dbus_getter_bss_mode(DBusMessageIter *iter, DBusError *error,
+                                     void *user_data);
 
-DBusMessage * wpas_dbus_getter_network_properties(
-       DBusMessage *message, struct network_handler_args *net);
+dbus_bool_t wpas_dbus_getter_bss_signal(DBusMessageIter *iter,
+                                       DBusError *error, void *user_data);
 
-DBusMessage * wpas_dbus_setter_network_properties(
-       DBusMessage *message, struct network_handler_args *net);
+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_ies(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_getter_process_credentials(
-       DBusMessage *message, struct wpa_supplicant *wpa_s);
-
-DBusMessage * wpas_dbus_setter_process_credentials(
-       DBusMessage *message, struct wpa_supplicant *wpa_s);
+dbus_bool_t wpas_dbus_getter_process_credentials(DBusMessageIter *iter,
+       DBusError *error, void *user_data);
 
-DBusMessage * wpas_dbus_getter_credentials(DBusMessage *message,
-                                          struct wpa_supplicant *wpa_s);
+dbus_bool_t wpas_dbus_setter_process_credentials(DBusMessageIter *iter,
+                                                DBusError *error,
+                                                void *user_data);
 
 DBusMessage * wpas_dbus_error_invalid_args(DBusMessage *message,
                                           const char *arg);
diff --git a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
new file mode 100644 (file)
index 0000000..aa05f58
--- /dev/null
@@ -0,0 +1,2456 @@
+/*
+ * WPA Supplicant / dbus-based control interface (P2P)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#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"
+
+/**
+ * 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(char *peer_path, u8 addr[ETH_ALEN])
+{
+       char *p;
+
+       if (!peer_path)
+               return -1;
+       p = 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_ONLY_SOCIAL;
+       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") &&
+                   (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") &&
+                          (entry.type == DBUS_TYPE_STRING)) {
+                       if (!os_strcmp(entry.str_value, "start_with_full"))
+                               type = P2P_FIND_START_WITH_FULL;
+                       else if (!os_strcmp(entry.str_value, "social"))
+                               type = P2P_FIND_ONLY_SOCIAL;
+                       else if (!os_strcmp(entry.str_value, "progressive"))
+                               type = P2P_FIND_PROGRESSIVE;
+                       else
+                               goto error_clear;
+               } else
+                       goto error_clear;
+               wpa_dbus_dict_entry_clear(&entry);
+       }
+
+       wpas_p2p_find(wpa_s, timeout, type, num_req_dev_types, req_dev_types,
+                     NULL);
+       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)
+{
+       wpas_p2p_stop_find(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 (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 dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+                                             NULL);
+
+       if (wpas_p2p_listen(wpa_s, (unsigned int)timeout))
+               return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+                                             NULL);
+
+       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") &&
+                   (entry.type == DBUS_TYPE_INT32))
+                       period = entry.uint32_value;
+               else if (!os_strcmp(entry.key, "interval") &&
+                        (entry.type == DBUS_TYPE_INT32))
+                       interval = entry.uint32_value;
+               else
+                       goto error_clear;
+               wpa_dbus_dict_entry_clear(&entry);
+       }
+
+       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") &&
+                   (entry.type == DBUS_TYPE_INT32))
+                       dur1 = entry.uint32_value;
+               else if (!os_strcmp(entry.key, "interval1") &&
+                        entry.type == DBUS_TYPE_INT32)
+                       int1 = entry.uint32_value;
+               else if (!os_strcmp(entry.key, "duration2") &&
+                        entry.type == DBUS_TYPE_INT32)
+                       dur2 = entry.uint32_value;
+               else if (!os_strcmp(entry.key, "interval2") &&
+                        entry.type == DBUS_TYPE_INT32)
+                       int2 = entry.uint32_value;
+               else
+                       goto error_clear;
+
+               wpa_dbus_dict_entry_clear(&entry);
+       }
+       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;
+       char *net_id_str = 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") &&
+                   (entry.type == DBUS_TYPE_BOOLEAN)) {
+                       persistent_group = (entry.bool_value == TRUE) ? 1 : 0;
+               } else if (!os_strcmp(entry.key, "frequency") &&
+                          (entry.type == DBUS_TYPE_INT32)) {
+                       freq = entry.int32_value;
+                       if (freq <= 0)
+                               goto inv_args_clear;
+               } else if (!os_strcmp(entry.key, "persistent_group_object") &&
+                          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);
+       }
+
+       if (pg_object_path != NULL) {
+               /*
+                * A persistent group Object Path is defined meaning we want
+                * to re-invoke a persistent group.
+                */
+
+               iface = wpas_dbus_new_decompose_object_path(pg_object_path, 1,
+                                                           &net_id_str, NULL);
+               if (iface == NULL ||
+                   os_strcmp(iface, wpa_s->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)) {
+                       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))
+               goto inv_args;
+
+out:
+       os_free(pg_object_path);
+       os_free(net_id_str);
+       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_flush(DBusMessage *message,
+                                         struct wpa_supplicant *wpa_s)
+{
+       DBusMessage *reply = NULL;
+
+       if (!wpa_dbus_p2p_check_enabled(wpa_s, message, &reply, NULL))
+               return reply;
+
+       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") &&
+                   (entry.type == DBUS_TYPE_OBJECT_PATH)) {
+                       peer_object_path = os_strdup(entry.str_value);
+               } else if (!os_strcmp(entry.key, "persistent") &&
+                          (entry.type == DBUS_TYPE_BOOLEAN)) {
+                       persistent_group = (entry.bool_value == TRUE) ? 1 : 0;
+               } else if (!os_strcmp(entry.key, "join") &&
+                          (entry.type == DBUS_TYPE_BOOLEAN)) {
+                       join = (entry.bool_value == TRUE) ? 1 : 0;
+               } else if (!os_strcmp(entry.key, "authorize_only") &&
+                          (entry.type == DBUS_TYPE_BOOLEAN)) {
+                       authorize_only = (entry.bool_value == TRUE) ? 1 : 0;
+               } else if (!os_strcmp(entry.key, "frequency") &&
+                          (entry.type == DBUS_TYPE_INT32)) {
+                       freq = entry.int32_value;
+                       if (freq <= 0)
+                               goto inv_args_clear;
+               } else if (!os_strcmp(entry.key, "go_intent") &&
+                          (entry.type == DBUS_TYPE_INT32)) {
+                       go_intent = entry.int32_value;
+                       if ((go_intent < 0) || (go_intent > 15))
+                               goto inv_args_clear;
+               } else if (!os_strcmp(entry.key, "wps_method") &&
+                          (entry.type == DBUS_TYPE_STRING)) {
+                       if (!os_strcmp(entry.str_value, "pbc"))
+                               wps_method = WPS_PBC;
+                       else if (!os_strcmp(entry.str_value, "pin"))
+                               wps_method = WPS_PIN_DISPLAY;
+                       else if (!os_strcmp(entry.str_value, "display"))
+                               wps_method = WPS_PIN_DISPLAY;
+                       else if (!os_strcmp(entry.str_value, "keypad"))
+                               wps_method = WPS_PIN_KEYPAD;
+                       else
+                               goto inv_args_clear;
+               } else if (!os_strcmp(entry.key, "pin") &&
+                          (entry.type == DBUS_TYPE_STRING)) {
+                       pin = os_strdup(entry.str_value);
+               } else
+                       goto inv_args_clear;
+
+               wpa_dbus_dict_entry_clear(&entry);
+       }
+
+       if (!peer_object_path || (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;
+
+       new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method,
+                                  persistent_group, join, authorize_only,
+                                  go_intent, freq);
+
+       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;
+}
+
+
+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;
+       char *net_id_str = 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") &&
+                   (entry.type == DBUS_TYPE_OBJECT_PATH)) {
+                       peer_object_path = os_strdup(entry.str_value);
+                       wpa_dbus_dict_entry_clear(&entry);
+               } else if (!os_strcmp(entry.key, "persistent_group_object") &&
+                          (entry.type == DBUS_TYPE_OBJECT_PATH)) {
+                       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 (!peer_object_path ||
+           (parse_peer_object_path(peer_object_path, peer_addr) < 0) ||
+           !p2p_peer_known(wpa_s->global->p2p, peer_addr)) {
+               goto err;
+       }
+
+       if (persistent) {
+               /*
+                * A group ID is defined meaning we want to re-invoke a
+                * persistent group
+                */
+
+               iface = wpas_dbus_new_decompose_object_path(pg_object_path, 1,
+                                                           &net_id_str, NULL);
+               if (iface == NULL ||
+                   os_strcmp(iface, wpa_s->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) {
+                       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(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 (wpas_p2p_prov_disc(wpa_s, peer_addr, config_method, 0) < 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 (!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))
+               goto err_no_mem;
+
+       /* GO Intent */
+       if (!wpa_dbus_dict_append_uint32(&dict_iter, "GOIntent",
+                                        wpa_s->conf->p2p_go_intent))
+               goto err_no_mem;
+
+       /* Persistent Reconnect */
+       if (!wpa_dbus_dict_append_bool(&dict_iter, "PersistentReconnect",
+                                      wpa_s->conf->persistent_reconnect))
+               goto err_no_mem;
+
+       /* Listen Reg Class */
+       if (!wpa_dbus_dict_append_uint32(&dict_iter, "ListenRegClass",
+                                        wpa_s->conf->p2p_listen_reg_class))
+               goto err_no_mem;
+
+       /* Listen Channel */
+       if (!wpa_dbus_dict_append_uint32(&dict_iter, "ListenChannel",
+                                        wpa_s->conf->p2p_listen_channel))
+               goto err_no_mem;
+
+       /* Oper Reg Class */
+       if (!wpa_dbus_dict_append_uint32(&dict_iter, "OperRegClass",
+                                        wpa_s->conf->p2p_oper_reg_class))
+               goto err_no_mem;
+
+       /* Oper Channel */
+       if (!wpa_dbus_dict_append_uint32(&dict_iter, "OperChannel",
+                                        wpa_s->conf->p2p_oper_channel))
+               goto err_no_mem;
+
+       /* SSID Postfix */
+       if (wpa_s->conf->p2p_ssid_postfix &&
+           !wpa_dbus_dict_append_string(&dict_iter, "SsidPostfix",
+                                        wpa_s->conf->p2p_ssid_postfix))
+               goto err_no_mem;
+
+       /* Intra Bss */
+       if (!wpa_dbus_dict_append_bool(&dict_iter, "IntraBss",
+                                      wpa_s->conf->p2p_intra_bss))
+               goto err_no_mem;
+
+       /* Group Idle */
+       if (!wpa_dbus_dict_append_uint32(&dict_iter, "GroupIdle",
+                                        wpa_s->conf->p2p_group_idle))
+               goto err_no_mem;
+
+       /* Dissasociation low ack */
+       if (!wpa_dbus_dict_append_uint32(&dict_iter, "disassoc_low_ack",
+                                        wpa_s->conf->disassoc_low_ack))
+               goto err_no_mem;
+
+       if (!wpa_dbus_dict_close_write(&variant_iter, &dict_iter) ||
+           !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;
+
+       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
+                       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;
+
+       if (!wpa_dbus_p2p_check_enabled(wpa_s, NULL, NULL, error))
+               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(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_zalloc(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";
+       }
+
+       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 (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,
+                           wpa_s->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_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_set_error(error, DBUS_ERROR_FAILED,
+                              "%s: failed to construct message 1", __func__);
+               return FALSE;
+       }
+
+       if (!dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY,
+                                             DBUS_TYPE_ARRAY_AS_STRING
+                                             DBUS_TYPE_BYTE_AS_STRING,
+                                             &array_iter)) {
+               dbus_set_error(error, DBUS_ERROR_FAILED,
+                              "%s: failed to construct message 2", __func__);
+               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_set_error(error, DBUS_ERROR_FAILED,
+                                              "%s: failed to construct "
+                                              "message 3 (%d)",
+                                              __func__, i);
+                               return FALSE;
+                       }
+
+                       if (!dbus_message_iter_append_fixed_array(
+                                   &inner_array_iter, DBUS_TYPE_BYTE,
+                                   &sec_dev_type_list, WPS_DEV_TYPE_LEN)) {
+                               dbus_set_error(error, DBUS_ERROR_FAILED,
+                                              "%s: failed to construct "
+                                              "message 4 (%d)",
+                                              __func__, i);
+                               return FALSE;
+                       }
+
+                       if (!dbus_message_iter_close_container(
+                                   &array_iter, &inner_array_iter)) {
+                               dbus_set_error(error, DBUS_ERROR_FAILED,
+                                              "%s: failed to construct "
+                                              "message 5 (%d)",
+                                              __func__, i);
+                               return FALSE;
+                       }
+
+                       sec_dev_type_list += WPS_DEV_TYPE_LEN;
+               }
+       }
+
+       if (!dbus_message_iter_close_container(&variant_iter, &array_iter)) {
+               dbus_set_error(error, DBUS_ERROR_FAILED,
+                              "%s: failed to construct message 6", __func__);
+               return FALSE;
+       }
+
+       if (!dbus_message_iter_close_container(iter, &variant_iter)) {
+               dbus_set_error(error, DBUS_ERROR_FAILED,
+                              "%s: failed to construct message 7", __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];
+       int i, num;
+       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 */
+       for (i = 0, num = 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)
+{
+       dbus_bool_t success;
+       /* struct peer_handler_args *peer_args = user_data; */
+
+       success = wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE,
+                                                        NULL, 0, error);
+       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;
+
+       if (wpa_s->conf == NULL) {
+               wpa_printf(MSG_ERROR, "dbus: %s: "
+                          "An error occurred getting persistent groups list",
+                          __func__);
+               dbus_set_error_const(error, DBUS_ERROR_FAILED, "an error "
+                                    "occurred getting persistent groups list");
+               return FALSE;
+       }
+
+       for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next)
+               if (network_is_persistent_group(ssid))
+                       num++;
+
+       paths = os_zalloc(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",
+                           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_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);
+}
+
+
+/**
+ * wpas_dbus_setter_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
+ *
+ * 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);
+
+       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",
+                   wpa_s->dbus_new_path, ssid->id);
+
+       reply = dbus_message_new_method_return(message);
+       if (reply == NULL) {
+               reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+                                              NULL);
+               goto err;
+       }
+       if (!dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path,
+                                     DBUS_TYPE_INVALID)) {
+               dbus_message_unref(reply);
+               reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+                                              NULL);
+               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 = NULL;
+       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, 1,
+                                                   &persistent_group_id,
+                                                   NULL);
+       if (iface == NULL || os_strcmp(iface, wpa_s->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);
+       os_free(persistent_group_id);
+       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;
+
+       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;
+
+       /* 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_zalloc(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_GROUPMEMBERS_PART
+                           "/" COMPACT_MACSTR,
+                           wpa_s->dbus_groupobj_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;
+       u8 role = wpas_get_p2p_role(wpa_s);
+       char *p_pass = NULL;
+
+       /* Verify correct role for this property */
+       if (role == WPAS_P2P_ROLE_GO) {
+               if (wpa_s->current_ssid == NULL)
+                       return FALSE;
+               p_pass = wpa_s->current_ssid->passphrase;
+       } else
+               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 role = wpas_get_p2p_role(wpa_s);
+       u8 *p_psk = NULL;
+       u8 psk_len = 0;
+
+       /* Verify correct role for this property */
+       if (role == WPAS_P2P_ROLE_CLIENT) {
+               if (wpa_s->current_ssid == NULL)
+                       return FALSE;
+               p_psk = wpa_s->current_ssid->psk;
+               psk_len = 32;
+       }
+
+       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];
+       int num_vendor_ext = 0;
+       int i;
+
+       /* 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)
+                               vendor_ext[i] = NULL;
+                       else {
+                               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;
+       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 (!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++) {
+                               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;
+
+       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") &&
+                   (entry.type == DBUS_TYPE_STRING)) {
+                       if (!os_strcmp(entry.str_value, "upnp"))
+                               upnp = 1;
+                       else if (!os_strcmp(entry.str_value, "bonjour"))
+                               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") &&
+                           entry.type == DBUS_TYPE_INT32)
+                               version = entry.uint32_value;
+                       else if (!os_strcmp(entry.key, "service") &&
+                                entry.type == DBUS_TYPE_STRING)
+                               service = os_strdup(entry.str_value);
+                       wpa_dbus_dict_entry_clear(&entry);
+               }
+               if (version <= 0 || service == NULL)
+                       goto error;
+
+               if (wpas_p2p_service_add_upnp(wpa_s, version, service) != 0)
+                       goto error;
+
+               os_free(service);
+       } 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")) {
+                               if ((entry.type != DBUS_TYPE_ARRAY) ||
+                                   (entry.array_type != DBUS_TYPE_BYTE))
+                                       goto error_clear;
+                               query = wpabuf_alloc_copy(
+                                       entry.bytearray_value,
+                                       entry.array_len);
+                       } else if (!os_strcmp(entry.key, "response")) {
+                               if ((entry.type != DBUS_TYPE_ARRAY) ||
+                                   (entry.array_type != DBUS_TYPE_BYTE))
+                                       goto error_clear;
+                               resp = wpabuf_alloc_copy(entry.bytearray_value,
+                                                        entry.array_len);
+                       }
+
+                       wpa_dbus_dict_entry_clear(&entry);
+               }
+
+               if (query == NULL || resp == NULL)
+                       goto error;
+
+               if (wpas_p2p_service_add_bonjour(wpa_s, query, resp) < 0) {
+                       wpabuf_free(query);
+                       wpabuf_free(resp);
+                       goto error;
+               }
+       } else
+               goto error;
+
+       return reply;
+error_clear:
+       wpa_dbus_dict_entry_clear(&entry);
+error:
+       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") &&
+                   (entry.type == DBUS_TYPE_STRING)) {
+                       if (!os_strcmp(entry.str_value, "upnp"))
+                               upnp = 1;
+                       else if (!os_strcmp(entry.str_value, "bonjour"))
+                               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") &&
+                           entry.type == DBUS_TYPE_INT32)
+                               version = entry.uint32_value;
+                       else if (!os_strcmp(entry.key, "service") &&
+                                entry.type == DBUS_TYPE_STRING)
+                               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);
+               os_free(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")) {
+                               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
+                               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;
+               wpabuf_free(query);
+       } else
+               goto error;
+
+       return reply;
+error_clear:
+       wpa_dbus_dict_entry_clear(&entry);
+error:
+       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") &&
+                   entry.type == DBUS_TYPE_OBJECT_PATH) {
+                       peer_object_path = os_strdup(entry.str_value);
+               } else if (!os_strcmp(entry.key, "service_type") &&
+                          entry.type == DBUS_TYPE_STRING) {
+                       if (!os_strcmp(entry.str_value, "upnp"))
+                               upnp = 1;
+                       else
+                               goto error_clear;
+               } else if (!os_strcmp(entry.key, "version") &&
+                          entry.type == DBUS_TYPE_INT32) {
+                       version = entry.uint32_value;
+               } else if (!os_strcmp(entry.key, "service") &&
+                          entry.type == DBUS_TYPE_STRING) {
+                       service = os_strdup(entry.str_value);
+               } else if (!os_strcmp(entry.key, "tlv")) {
+                       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 = (unsigned long) wpas_p2p_sd_request_upnp(wpa_s, addr,
+                                                              version,
+                                                              service);
+       } else {
+               if (tlv == NULL)
+                       goto error;
+               ref = (unsigned long)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") &&
+                   entry.type == DBUS_TYPE_OBJECT_PATH) {
+                       peer_object_path = os_strdup(entry.str_value);
+               } else if (!os_strcmp(entry.key, "frequency") &&
+                          entry.type == DBUS_TYPE_INT32) {
+                       freq = entry.uint32_value;
+               } else if (!os_strcmp(entry.key, "dialog_token") &&
+                          entry.type == DBUS_TYPE_UINT32) {
+                       dlg_tok = entry.uint32_value;
+               } else if (!os_strcmp(entry.key, "tlvs")) {
+                       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 ||
+           (parse_peer_object_path(peer_object_path, addr) < 0) ||
+           !p2p_peer_known(wpa_s->global->p2p, addr))
+               goto error;
+
+       if (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, (void *)(unsigned long) req))
+               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;
+
+}
diff --git a/wpa_supplicant/dbus/dbus_new_handlers_p2p.h b/wpa_supplicant/dbus/dbus_new_handlers_p2p.h
new file mode 100644 (file)
index 0000000..241dd75
--- /dev/null
@@ -0,0 +1,217 @@
+
+/*
+ * WPA Supplicant / dbus-based control interface for p2p
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef 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];
+};
+
+struct groupmember_handler_args {
+       struct wpa_supplicant *wpa_s;
+       u8 member_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_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_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_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);
+
+/*
+ * 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);
+
+
+#endif /* DBUS_NEW_HANDLERS_P2P_H */
index dc44a59..a72cfb3 100644 (file)
@@ -19,6 +19,8 @@
 #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"
@@ -30,6 +32,7 @@ struct wps_start_params {
        int type; /* 0 - not set, 1 - pin,      2 - pbc       */
        u8 *bssid;
        char *pin;
+       u8 *p2p_dev_addr;
 };
 
 
@@ -107,7 +110,7 @@ static int wpas_dbus_handler_wps_bssid(DBusMessage *message,
        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_ARRAY) {
+           DBUS_TYPE_BYTE) {
                wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Wrong Bssid type, "
                           "byte array required");
                *reply = wpas_dbus_error_invalid_args(
@@ -148,6 +151,41 @@ static int wpas_dbus_handler_wps_pin(DBusMessage *message,
 }
 
 
+#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,
@@ -165,6 +203,11 @@ static int wpas_dbus_handler_wps_start_entry(DBusMessage *message, char *key,
        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);
@@ -231,11 +274,31 @@ DBusMessage * wpas_dbus_handler_wps_start(DBusMessage *message,
                ret = wpas_wps_start_reg(wpa_s, params.bssid, params.pin,
                                         NULL);
        else if (params.type == 1) {
-               ret = wpas_wps_start_pin(wpa_s, params.bssid, params.pin);
-               if (ret > 0)
-                       os_snprintf(npin, sizeof(npin), "%08d", ret);
-       } else
-               ret = wpas_wps_start_pbc(wpa_s, params.bssid);
+#ifdef CONFIG_AP
+               if (wpa_s->ap_iface)
+                       ret = wpa_supplicant_ap_wps_pin(wpa_s,
+                                                       params.bssid,
+                                                       params.pin,
+                                                       npin, sizeof(npin));
+               else
+#endif /* CONFIG_AP */
+               {
+                       ret = wpas_wps_start_pin(wpa_s, params.bssid,
+                                                params.pin, 0,
+                                                DEV_PW_DEFAULT);
+                       if (ret > 0)
+                               os_snprintf(npin, sizeof(npin), "%08d", ret);
+               }
+       } else {
+#ifdef CONFIG_AP
+               if (wpa_s->ap_iface)
+                       ret = wpa_supplicant_ap_wps_pbc(wpa_s,
+                                                       params.bssid,
+                                                       params.p2p_dev_addr);
+               else
+#endif /* CONFIG_AP */
+               ret = wpas_wps_start_pbc(wpa_s, params.bssid, 0);
+       }
 
        if (ret < 0) {
                wpa_printf(MSG_DEBUG, "dbus: WPS.Start wpas_wps_failed in "
@@ -283,40 +346,43 @@ DBusMessage * wpas_dbus_handler_wps_start(DBusMessage *message,
  * wpas_dbus_getter_process_credentials - Check if credentials are processed
  * @message: Pointer to incoming dbus message
  * @wpa_s: %wpa_supplicant data structure
- * Returns: DBus message with a boolean on success or DBus error on failure
+ * 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.
  */
-DBusMessage * wpas_dbus_getter_process_credentials(
-       DBusMessage *message, struct wpa_supplicant *wpa_s)
+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(message, DBUS_TYPE_BOOLEAN,
-                                               &process);
+       return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
+                                               &process, error);
 }
 
 
 /**
  * wpas_dbus_setter_process_credentials - Set credentials_processed conf param
- * @message: Pointer to incoming dbus message
- * @wpa_s: %wpa_supplicant data structure
- * Returns: NULL on success or DBus error on failure
+ * @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.
  */
-DBusMessage * wpas_dbus_setter_process_credentials(
-       DBusMessage *message, struct wpa_supplicant *wpa_s)
+dbus_bool_t wpas_dbus_setter_process_credentials(DBusMessageIter *iter,
+                                                DBusError *error,
+                                                void *user_data)
 {
-       DBusMessage *reply = NULL;
+       struct wpa_supplicant *wpa_s = user_data;
        dbus_bool_t process_credentials, old_pc;
 
-       reply = wpas_dbus_simple_property_setter(message, DBUS_TYPE_BOOLEAN,
-                                                &process_credentials);
-       if (reply)
-               return reply;
+       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);
@@ -327,5 +393,5 @@ DBusMessage * wpas_dbus_setter_process_credentials(
                                               WPAS_DBUS_NEW_IFACE_WPS,
                                               "ProcessCredentials");
 
-       return NULL;
+       return TRUE;
 }
index 06749db..e254365 100644 (file)
 #include "dbus_common_i.h"
 #include "dbus_new.h"
 #include "dbus_new_helpers.h"
+#include "dbus_dict_helpers.h"
 
 
-/**
- * recursive_iter_copy - Reads arguments from one iterator and
- * writes to another recursively
- * @from: iterator to read from
- * @to: iterator to write to
- *
- * Copies one iterator's elements to another. If any element in
- * iterator is of container type, its content is copied recursively
- */
-static void recursive_iter_copy(DBusMessageIter *from, DBusMessageIter *to)
-{
-
-       char *subtype = NULL;
-       int type;
-
-       /* iterate over iterator to copy */
-       while ((type = dbus_message_iter_get_arg_type(from)) !=
-              DBUS_TYPE_INVALID) {
-
-               /* simply copy basic type entries */
-               if (dbus_type_is_basic(type)) {
-                       if (dbus_type_is_fixed(type)) {
-                               /*
-                                * According to DBus documentation all
-                                * fixed-length types are guaranteed to fit
-                                * 8 bytes
-                                */
-                               dbus_uint64_t v;
-                               dbus_message_iter_get_basic(from, &v);
-                               dbus_message_iter_append_basic(to, type, &v);
-                       } else {
-                               char *v;
-                               dbus_message_iter_get_basic(from, &v);
-                               dbus_message_iter_append_basic(to, type, &v);
-                       }
-               } else {
-                       /* recursively copy container type entries */
-                       DBusMessageIter write_subiter, read_subiter;
-
-                       dbus_message_iter_recurse(from, &read_subiter);
-
-                       if (type == DBUS_TYPE_VARIANT ||
-                           type == DBUS_TYPE_ARRAY) {
-                               subtype = dbus_message_iter_get_signature(
-                                       &read_subiter);
-                       }
-
-                       dbus_message_iter_open_container(to, type, subtype,
-                                                        &write_subiter);
-
-                       recursive_iter_copy(&read_subiter, &write_subiter);
-
-                       dbus_message_iter_close_container(to, &write_subiter);
-                       if (subtype)
-                               dbus_free(subtype);
-               }
-
-               dbus_message_iter_next(from);
-       }
-}
-
-
-static unsigned int fill_dict_with_properties(
-       DBusMessageIter *dict_iter, const struct wpa_dbus_property_desc *props,
-       const char *interface, const void *user_data)
+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)
 {
-       DBusMessage *reply;
-       DBusMessageIter entry_iter, ret_iter;
-       unsigned int counter = 0;
+       DBusMessageIter entry_iter;
        const struct wpa_dbus_property_desc *dsc;
 
        for (dsc = props; dsc && dsc->dbus_property; dsc++) {
-               if (!os_strncmp(dsc->dbus_interface, interface,
-                               WPAS_DBUS_INTERFACE_MAX) &&
-                   dsc->access != W && dsc->getter) {
-                       reply = dsc->getter(NULL, user_data);
-                       if (!reply)
-                               continue;
-
-                       if (dbus_message_get_type(reply) ==
-                           DBUS_MESSAGE_TYPE_ERROR) {
-                               dbus_message_unref(reply);
-                               continue;
-                       }
+               /* Only return properties for the requested D-Bus interface */
+               if (os_strncmp(dsc->dbus_interface, interface,
+                              WPAS_DBUS_INTERFACE_MAX) != 0)
+                       continue;
 
-                       dbus_message_iter_init(reply, &ret_iter);
+               /* Skip write-only properties */
+               if (dsc->getter == NULL)
+                       continue;
 
-                       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);
+               if (!dbus_message_iter_open_container(dict_iter,
+                                                     DBUS_TYPE_DICT_ENTRY,
+                                                     NULL, &entry_iter)) {
+                       dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY,
+                                            "no memory");
+                       return FALSE;
+               }
+               if (!dbus_message_iter_append_basic(&entry_iter,
+                                                   DBUS_TYPE_STRING,
+                                                   &dsc->dbus_property)) {
+                       dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY,
+                                            "no memory");
+                       return FALSE;
+               }
 
-                       recursive_iter_copy(&ret_iter, &entry_iter);
+               /* An error getting a property fails the request entirely */
+               if (!dsc->getter(&entry_iter, error, user_data))
+                       return FALSE;
 
-                       dbus_message_iter_close_container(dict_iter,
-                                                         &entry_iter);
-                       dbus_message_unref(reply);
-                       counter++;
-               }
+               dbus_message_iter_close_container(dict_iter, &entry_iter);
        }
 
-       return counter;
+       return TRUE;
 }
 
 
@@ -142,37 +80,44 @@ static unsigned int fill_dict_with_properties(
  * 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)
+static DBusMessage * get_all_properties(DBusMessage *message, char *interface,
+                                       struct wpa_dbus_object_desc *obj_dsc)
 {
-       /* Create and initialize the return message */
-       DBusMessage *reply = dbus_message_new_method_return(message);
+       DBusMessage *reply;
        DBusMessageIter iter, dict_iter;
-       int props_num;
-
-       dbus_message_iter_init_append(reply, &iter);
-
-       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
-                                        DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
-                                        DBUS_TYPE_STRING_AS_STRING
-                                        DBUS_TYPE_VARIANT_AS_STRING
-                                        DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
-                                        &dict_iter);
+       DBusError error;
 
-       props_num = fill_dict_with_properties(&dict_iter, obj_dsc->properties,
-                                             interface, obj_dsc->user_data);
+       reply = dbus_message_new_method_return(message);
+       if (reply == NULL) {
+               wpa_printf(MSG_ERROR, "%s: out of memory creating dbus reply",
+                          __func__);
+               return NULL;
+       }
 
-       dbus_message_iter_close_container(&iter, &dict_iter);
+       dbus_message_iter_init_append(reply, &iter);
+       if (!wpa_dbus_dict_open_write(&iter, &dict_iter)) {
+               wpa_printf(MSG_ERROR, "%s: out of memory creating reply",
+                          __func__);
+               dbus_message_unref(reply);
+               reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+                                              "out of memory");
+               return reply;
+       }
 
-       if (props_num == 0) {
+       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 = dbus_message_new_error(message,
-                                              DBUS_ERROR_INVALID_ARGS,
-                                              "No readable properties in "
-                                              "this interface");
+               reply = wpas_dbus_reply_new_from_error(message, &error,
+                                                      DBUS_ERROR_INVALID_ARGS,
+                                                      "No readable properties"
+                                                      " in this interface");
+               dbus_error_free(&error);
+               return reply;
        }
 
+       wpa_dbus_dict_close_write(&iter, &dict_iter);
        return reply;
 }
 
@@ -219,15 +164,33 @@ static DBusMessage * properties_get(DBusMessage *message,
                                    const struct wpa_dbus_property_desc *dsc,
                                    void *user_data)
 {
-       if (os_strcmp(dbus_message_get_signature(message), "ss"))
+       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");
+       }
 
-       if (dsc->access != W && dsc->getter)
-               return dsc->getter(message, user_data);
+       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 dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
-                                     "Property is write-only");
+       return reply;
 }
 
 
@@ -235,15 +198,38 @@ static DBusMessage * properties_set(DBusMessage *message,
                                    const struct wpa_dbus_property_desc *dsc,
                                    void *user_data)
 {
-       if (os_strcmp(dbus_message_get_signature(message), "ssv"))
+       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);
 
-       if (dsc->access != R && dsc->setter)
-               return dsc->setter(message, user_data);
+       /* 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 dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
-                                     "Property is read-only");
+       return reply;
 }
 
 
@@ -552,6 +538,7 @@ int wpa_dbus_register_object_per_iface(
        struct wpa_dbus_object_desc *obj_desc)
 {
        DBusConnection *con;
+       DBusError error;
 
        DBusObjectPathVTable vtable = {
                &free_dbus_object_desc_cb, &message_handler,
@@ -566,14 +553,24 @@ int wpa_dbus_register_object_per_iface(
        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_register_object_path(con, path, &vtable,
-                                                 obj_desc)) {
-               wpa_printf(MSG_ERROR, "dbus: Could not set up message "
-                          "handler for interface %s object %s", ifname, path);
+       if (!dbus_connection_try_register_object_path(con, path, &vtable,
+                                                     obj_desc, &error)) {
+               if (!os_strcmp(error.name, DBUS_ERROR_OBJECT_PATH_IN_USE)) {
+                       wpa_printf(MSG_DEBUG, "dbus: %s", error.message);
+               } else {
+                       wpa_printf(MSG_ERROR, "dbus: Could not set up message "
+                                  "handler for interface %s object %s",
+                                  ifname, path);
+                       wpa_printf(MSG_ERROR, "dbus error: %s", error.name);
+                       wpa_printf(MSG_ERROR, "dbus: %s", error.message);
+               }
+               dbus_error_free(&error);
                return -1;
        }
 
+       dbus_error_free(&error);
        return 0;
 }
 
@@ -611,14 +608,14 @@ int wpa_dbus_unregister_object_per_iface(
 }
 
 
-static void put_changed_properties(const struct wpa_dbus_object_desc *obj_dsc,
-                                  const char *interface,
-                                  DBusMessageIter *dict_iter)
+static dbus_bool_t put_changed_properties(
+       const struct wpa_dbus_object_desc *obj_dsc, const char *interface,
+       DBusMessageIter *dict_iter, int clear_changed)
 {
-       DBusMessage *getter_reply;
-       DBusMessageIter prop_iter, entry_iter;
+       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++) {
@@ -627,43 +624,94 @@ static void put_changed_properties(const struct wpa_dbus_object_desc *obj_dsc,
                        continue;
                if (os_strcmp(dsc->dbus_interface, interface) != 0)
                        continue;
-               obj_dsc->prop_changed_flags[i] = 0;
-
-               getter_reply = dsc->getter(NULL, obj_dsc->user_data);
-               if (!getter_reply ||
-                   dbus_message_get_type(getter_reply) ==
-                   DBUS_MESSAGE_TYPE_ERROR) {
-                       wpa_printf(MSG_ERROR, "dbus: %s: Cannot get new value "
-                                  "of property %s", __func__,
-                                  dsc->dbus_property);
-                       continue;
-               }
+               if (clear_changed)
+                       obj_dsc->prop_changed_flags[i] = 0;
 
-               if (!dbus_message_iter_init(getter_reply, &prop_iter) ||
-                   !dbus_message_iter_open_container(dict_iter,
+               if (!dbus_message_iter_open_container(dict_iter,
                                                      DBUS_TYPE_DICT_ENTRY,
-                                                     NULL, &entry_iter) ||
-                   !dbus_message_iter_append_basic(&entry_iter,
+                                                     NULL, &entry_iter))
+                       return FALSE;
+
+               if (!dbus_message_iter_append_basic(&entry_iter,
                                                    DBUS_TYPE_STRING,
                                                    &dsc->dbus_property))
-                       goto err;
-
-               recursive_iter_copy(&prop_iter, &entry_iter);
+                       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))
-                       goto err;
-
-               dbus_message_unref(getter_reply);
+                       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))
+               goto err;
+
+       /* Changed properties dict */
+       if (!dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY,
+                                             "{sv}", &dict_iter))
+               goto err;
+
+       if (!put_changed_properties(obj_dsc, interface, &dict_iter, 0))
+               goto err;
+
+       if (!dbus_message_iter_close_container(&signal_iter, &dict_iter))
+               goto err;
+
+       /* Invalidated properties array (empty) */
+       if (!dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY,
+                                             "s", &dict_iter))
+               goto err;
+
+       if (!dbus_message_iter_close_container(&signal_iter, &dict_iter))
+               goto err;
+
+       dbus_connection_send(con, msg, NULL);
+
+out:
+       dbus_message_unref(msg);
        return;
 
 err:
-       wpa_printf(MSG_ERROR, "dbus: %s: Cannot construct signal", __func__);
+       wpa_printf(MSG_DEBUG, "dbus: %s: Failed to construct signal",
+                  __func__);
+       goto out;
 }
 
 
-static void send_prop_changed_signal(
+static void do_send_deprecated_prop_changed_signal(
        DBusConnection *con, const char *path, const char *interface,
        const struct wpa_dbus_object_desc *obj_dsc)
 {
@@ -680,7 +728,8 @@ static void send_prop_changed_signal(
                                              "{sv}", &dict_iter))
                goto err;
 
-       put_changed_properties(obj_dsc, interface, &dict_iter);
+       if (!put_changed_properties(obj_dsc, interface, &dict_iter, 1))
+               goto err;
 
        if (!dbus_message_iter_close_container(&signal_iter, &dict_iter))
                goto err;
@@ -698,6 +747,29 @@ err:
 }
 
 
+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;
@@ -849,27 +921,147 @@ void wpa_dbus_mark_property_changed(struct wpas_dbus_priv *iface,
  * @iface: dbus priv struct
  * @path: path to DBus object which properties will be obtained
  * @interface: interface name which properties will be obtained
- * @dict_iter: correct, open DBus dictionary iterator.
+ * @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.
  */
-void wpa_dbus_get_object_properties(struct wpas_dbus_priv *iface,
-                                   const char *path, const char *interface,
-                                   DBusMessageIter *dict_iter)
+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: wpa_dbus_get_object_properties: "
-                          "could not obtain object's private data: %s", path);
-               return;
+               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;
        }
 
-       fill_dict_with_properties(dict_iter, obj_desc->properties,
-                                 interface, obj_desc->user_data);
+       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
+ * @p2p_persistent_group: indicates whether to parse the path as a P2P
+ *                        persistent group object
+ * @network: (out) the configured network this object path refers to, if any
+ * @bssid: (out) the scanned bssid this object path refers to, if any
+ * Returns: The object path of the network interface this path refers to
+ *
+ * For a given object path, decomposes the object path into object id, network,
+ * and BSSID parts, if those parts exist.
+ */
+char *wpas_dbus_new_decompose_object_path(const char *path,
+                                          int p2p_persistent_group,
+                                          char **network,
+                                          char **bssid)
+{
+       const unsigned int dev_path_prefix_len =
+               os_strlen(WPAS_DBUS_NEW_PATH_INTERFACES "/");
+       char *obj_path_only;
+       char *next_sep;
+
+       /* Be a bit paranoid about path */
+       if (!path || os_strncmp(path, WPAS_DBUS_NEW_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 = os_strchr(obj_path_only + dev_path_prefix_len, '/');
+       if (next_sep != NULL) {
+               const char *net_part = os_strstr(
+                       next_sep, p2p_persistent_group ?
+                       WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/" :
+                       WPAS_DBUS_NEW_NETWORKS_PART "/");
+               const char *bssid_part = os_strstr(
+                       next_sep, WPAS_DBUS_NEW_BSSIDS_PART "/");
+
+               if (network && net_part) {
+                       /* Deal with a request for a configured network */
+                       const char *net_name = net_part +
+                               os_strlen(p2p_persistent_group ?
+                                         WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART
+                                         "/" :
+                                         WPAS_DBUS_NEW_NETWORKS_PART "/");
+                       *network = NULL;
+                       if (os_strlen(net_name))
+                               *network = os_strdup(net_name);
+               } else if (bssid && bssid_part) {
+                       /* Deal with a request for a scanned BSSID */
+                       const char *bssid_name = bssid_part +
+                               os_strlen(WPAS_DBUS_NEW_BSSIDS_PART "/");
+                       if (os_strlen(bssid_name))
+                               *bssid = os_strdup(bssid_name);
+                       else
+                               *bssid = NULL;
+               }
+
+               /* Cut off interface object path before "/" */
+               *next_sep = '\0';
+       }
+
+       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 8db7a37..d6e7b48 100644 (file)
@@ -22,8 +22,9 @@ typedef DBusMessage * (* WPADBusMethodHandler)(DBusMessage *message,
                                               void *user_data);
 typedef void (* WPADBusArgumentFreeFunction)(void *handler_arg);
 
-typedef DBusMessage * (* WPADBusPropertyAccessor)(DBusMessage *message,
-                                                 const void *user_data);
+typedef dbus_bool_t (* WPADBusPropertyAccessor)(DBusMessageIter *iter,
+                                                DBusError *error,
+                                               void *user_data);
 
 struct wpa_dbus_object_desc {
        DBusConnection *connection;
@@ -44,8 +45,6 @@ struct wpa_dbus_object_desc {
        WPADBusArgumentFreeFunction user_data_free_func;
 };
 
-enum dbus_prop_access { R, W, RW };
-
 enum dbus_arg_direction { ARG_IN, ARG_OUT };
 
 struct wpa_dbus_argument {
@@ -67,7 +66,7 @@ struct wpa_dbus_method_desc {
        /* method handling function */
        WPADBusMethodHandler method_handler;
        /* array of arguments */
-       struct wpa_dbus_argument args[3];
+       struct wpa_dbus_argument args[4];
 };
 
 /**
@@ -79,7 +78,7 @@ struct wpa_dbus_signal_desc {
        /* signal interface */
        const char *dbus_interface;
        /* array of arguments */
-       struct wpa_dbus_argument args[3];
+       struct wpa_dbus_argument args[4];
 };
 
 /**
@@ -96,14 +95,13 @@ struct wpa_dbus_property_desc {
        WPADBusPropertyAccessor getter;
        /* property setter function */
        WPADBusPropertyAccessor setter;
-       /* property access permissions */
-       enum dbus_prop_access access;
 };
 
 
 #define WPAS_DBUS_OBJECT_PATH_MAX 150
 #define WPAS_DBUS_INTERFACE_MAX 150
 #define WPAS_DBUS_METHOD_SIGNAL_PROP_MAX 50
+#define WPAS_DBUS_AUTH_MODE_MAX 64
 
 #define WPA_DBUS_INTROSPECTION_INTERFACE "org.freedesktop.DBus.Introspectable"
 #define WPA_DBUS_INTROSPECTION_METHOD "Introspect"
@@ -127,9 +125,10 @@ int wpa_dbus_unregister_object_per_iface(
        struct wpas_dbus_priv *ctrl_iface,
        const char *path);
 
-void wpa_dbus_get_object_properties(struct wpas_dbus_priv *iface,
-                                   const char *path, const char *interface,
-                                   DBusMessageIter *dict_iter);
+dbus_bool_t wpa_dbus_get_object_properties(struct wpas_dbus_priv *iface,
+                                          const char *path,
+                                          const char *interface,
+                                          DBusMessageIter *iter);
 
 
 void wpa_dbus_flush_all_changed_properties(DBusConnection *con);
@@ -144,4 +143,14 @@ void wpa_dbus_mark_property_changed(struct wpas_dbus_priv *iface,
 DBusMessage * wpa_dbus_introspect(DBusMessage *message,
                                  struct wpa_dbus_object_desc *obj_dsc);
 
+char *wpas_dbus_new_decompose_object_path(const char *path,
+                                          int p2p_persistent_group,
+                                          char **network,
+                                          char **bssid);
+
+DBusMessage *wpas_dbus_reply_new_from_error(DBusMessage *message,
+                                           DBusError *error,
+                                           const char *fallback_name,
+                                           const char *fallback_string);
+
 #endif /* WPA_DBUS_CTRL_H */
index c660c04..d443269 100644 (file)
@@ -43,7 +43,7 @@ static struct interfaces * add_interface(struct dl_list *list,
        iface = os_zalloc(sizeof(struct interfaces));
        if (!iface)
                return NULL;
-       iface->xml = wpabuf_alloc(3000);
+       iface->xml = wpabuf_alloc(6000);
        if (iface->xml == NULL) {
                os_free(iface);
                return NULL;
@@ -89,10 +89,11 @@ static void add_entry(struct wpabuf *xml, const char *type, const char *name,
 static void add_property(struct wpabuf *xml,
                         const struct wpa_dbus_property_desc *dsc)
 {
-       wpabuf_printf(xml, "<property name=\"%s\" type=\"%s\" access=\"%s\"/>",
+       wpabuf_printf(xml, "<property name=\"%s\" type=\"%s\" "
+                     "access=\"%s%s\"/>",
                      dsc->dbus_property, dsc->type,
-                     (dsc->access == R ? "read" :
-                      (dsc->access == W ? "write" : "readwrite")));
+                     dsc->getter ? "read" : "",
+                     dsc->setter ? "write" : "");
 }
 
 
@@ -163,6 +164,12 @@ static void add_interfaces(struct dl_list *list, struct wpabuf *xml)
                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);
@@ -250,7 +257,7 @@ DBusMessage * wpa_dbus_introspect(DBusMessage *message,
        DBusMessage *reply;
        struct wpabuf *xml;
 
-       xml = wpabuf_alloc(4000);
+       xml = wpabuf_alloc(10000);
        if (xml == NULL)
                return NULL;
 
index 7f25bf0..d255e14 100644 (file)
@@ -287,6 +287,8 @@ static DBusHandlerResult wpas_iface_message_handler(DBusConnection *connection,
                else if (!os_strcmp(method, "wpsReg"))
                        reply = wpas_dbus_iface_wps_reg(message, wpa_s);
 #endif /* CONFIG_WPS */
+               else if (!os_strcmp(method, "flush"))
+                       reply = wpas_dbus_iface_flush(message, wpa_s);
        }
 
        /* If the message was handled, send back the reply */
@@ -547,6 +549,59 @@ void wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s,
 }
 #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 (iface == NULL)
+               return;
+
+       _signal = dbus_message_new_signal(wpa_s->dbus_path,
+                                         WPAS_DBUS_IFACE_INTERFACE,
+                                         "Certification");
+       if (_signal == NULL) {
+               wpa_printf(MSG_ERROR,
+                          "dbus: wpa_supplicant_dbus_notify_certification: "
+                          "Could not create dbus signal; likely out of "
+                          "memory");
+               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: wpa_supplicant_dbus_notify_certification: "
+                          "Not enough memory to construct signal");
+               goto out;
+       }
+
+       dbus_connection_send(iface->con, _signal, NULL);
+
+out:
+       dbus_message_unref(_signal);
+
+}
+
 
 /**
  * wpa_supplicant_dbus_ctrl_iface_init - Initialize dbus control interface
index a9840c2..9523867 100644 (file)
@@ -82,6 +82,10 @@ void wpa_supplicant_dbus_notify_state_change(struct wpa_supplicant *wpa_s,
                                             enum wpa_states old_state);
 void wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s,
                                         const struct wps_credential *cred);
+void wpa_supplicant_dbus_notify_certification(struct wpa_supplicant *wpa_s,
+                                             int depth, const char *subject,
+                                             const char *cert_hash,
+                                             const struct wpabuf *cert);
 
 char * wpas_dbus_decompose_object_path(const char *path, char **network,
                                        char **bssid);
@@ -114,6 +118,14 @@ wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s,
 {
 }
 
+static inline void
+wpa_supplicant_dbus_notify_certification(struct wpa_supplicant *wpa_s,
+                                             int depth, const char *subject,
+                                             const char *cert_hash,
+                                             const struct wpabuf *cert)
+{
+}
+
 static inline int
 wpas_dbus_register_iface(struct wpa_supplicant *wpa_s)
 {
index d914697..a7eabf3 100644 (file)
@@ -116,7 +116,7 @@ DBusMessage * wpas_dbus_global_add_interface(DBusMessage *message,
                DBusMessageIter iter_dict;
                struct wpa_dbus_dict_entry entry;
 
-               if (!wpa_dbus_dict_open_read(&iter, &iter_dict))
+               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))
@@ -922,7 +922,7 @@ DBusMessage * wpas_dbus_iface_set_network(DBusMessage *message,
 
        dbus_message_iter_init(message, &iter);
 
-       if (!wpa_dbus_dict_open_read(&iter, &iter_dict)) {
+       if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) {
                reply = wpas_dbus_new_invalid_opts_error(message, NULL);
                goto out;
        }
@@ -1202,7 +1202,7 @@ DBusMessage * wpas_dbus_iface_set_smartcard_modules(
        if (!dbus_message_iter_init(message, &iter))
                goto error;
 
-       if (!wpa_dbus_dict_open_read(&iter, &iter_dict))
+       if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
                goto error;
 
        while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
@@ -1324,7 +1324,7 @@ DBusMessage * wpas_dbus_iface_set_blobs(DBusMessage *message,
 
        dbus_message_iter_init(message, &iter);
 
-       if (!wpa_dbus_dict_open_read(&iter, &iter_dict))
+       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)) {
@@ -1434,3 +1434,35 @@ DBusMessage * wpas_dbus_iface_remove_blobs(DBusMessage *message,
 
        return wpas_dbus_new_success_reply(message);
 }
+
+
+/**
+ * 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 65e876f..009e807 100644 (file)
@@ -96,6 +96,9 @@ DBusMessage * wpas_dbus_iface_wps_pin(DBusMessage *message,
 DBusMessage * wpas_dbus_iface_wps_reg(DBusMessage *message,
                                      struct wpa_supplicant *wpa_s);
 
+DBusMessage * wpas_dbus_iface_flush(DBusMessage *message,
+                                   struct wpa_supplicant *wpa_s);
+
 DBusMessage * wpas_dbus_new_success_reply(DBusMessage *message);
 DBusMessage * wpas_dbus_new_invalid_opts_error(DBusMessage *message,
                                               const char *arg);
index b5879f3..c04b844 100644 (file)
@@ -43,9 +43,9 @@ DBusMessage * wpas_dbus_iface_wps_pbc(DBusMessage *message,
                return wpas_dbus_new_invalid_opts_error(message, NULL);
 
        if (!os_strcmp(arg_bssid, "any"))
-               ret = wpas_wps_start_pbc(wpa_s, NULL);
+               ret = wpas_wps_start_pbc(wpa_s, NULL, 0);
        else if (!hwaddr_aton(arg_bssid, bssid))
-               ret = wpas_wps_start_pbc(wpa_s, bssid);
+               ret = wpas_wps_start_pbc(wpa_s, bssid, 0);
        else {
                return wpas_dbus_new_invalid_opts_error(message,
                                                        "Invalid BSSID");
@@ -94,9 +94,11 @@ DBusMessage * wpas_dbus_iface_wps_pin(DBusMessage *message,
        }
 
        if (os_strlen(pin) > 0)
-               ret = wpas_wps_start_pin(wpa_s, _bssid, pin);
+               ret = wpas_wps_start_pin(wpa_s, _bssid, pin, 0,
+                                        DEV_PW_DEFAULT);
        else
-               ret = wpas_wps_start_pin(wpa_s, _bssid, NULL);
+               ret = wpas_wps_start_pin(wpa_s, _bssid, NULL, 0,
+                                        DEV_PW_DEFAULT);
 
        if (ret < 0) {
                return dbus_message_new_error(message,
diff --git a/wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service b/wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service
deleted file mode 100644 (file)
index a9ce1ec..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-[D-BUS Service]
-Name=fi.epitest.hostap.WPASupplicant
-Exec=/sbin/wpa_supplicant -u
-User=root
diff --git a/wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service.in b/wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service.in
new file mode 100644 (file)
index 0000000..a75918f
--- /dev/null
@@ -0,0 +1,5 @@
+[D-BUS Service]
+Name=fi.epitest.hostap.WPASupplicant
+Exec=@BINDIR@/wpa_supplicant -u
+User=root
+SystemdService=wpa_supplicant.service
diff --git a/wpa_supplicant/dbus/fi.w1.wpa_supplicant1.service b/wpa_supplicant/dbus/fi.w1.wpa_supplicant1.service
deleted file mode 100644 (file)
index df78471..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-[D-BUS Service]
-Name=fi.w1.wpa_supplicant1
-Exec=/sbin/wpa_supplicant -u
-User=root
diff --git a/wpa_supplicant/dbus/fi.w1.wpa_supplicant1.service.in b/wpa_supplicant/dbus/fi.w1.wpa_supplicant1.service.in
new file mode 100644 (file)
index 0000000..d97ff39
--- /dev/null
@@ -0,0 +1,5 @@
+[D-BUS Service]
+Name=fi.w1.wpa_supplicant1
+Exec=@BINDIR@/wpa_supplicant -u
+User=root
+SystemdService=wpa_supplicant.service
index 8c32cb3..0476bc3 100644 (file)
@@ -78,10 +78,15 @@ CONFIG_DRIVER_ATMEL=y
 #CONFIG_DRIVER_RALINK=y
 
 # Driver interface for generic Linux wireless extensions
+# Note: WEXT is deprecated in the current Linux kernel version and no new
+# functionality is added to it. nl80211-based interface is the new
+# replacement for WEXT and its use allows wpa_supplicant to properly control
+# the driver to improve existing functionality like roaming and to support new
+# functionality.
 CONFIG_DRIVER_WEXT=y
 
 # Driver interface for Linux drivers using the nl80211 kernel interface
-#CONFIG_DRIVER_NL80211=y
+CONFIG_DRIVER_NL80211=y
 
 # Driver interface for FreeBSD net80211 layer (e.g., Atheros driver)
 #CONFIG_DRIVER_BSD=y
@@ -109,11 +114,6 @@ CONFIG_DRIVER_WEXT=y
 # Driver interface for development testing
 #CONFIG_DRIVER_TEST=y
 
-# Include client MLME (management frame processing) for test driver
-# This can be used to test MLME operations in hostapd with the test interface.
-# space.
-#CONFIG_CLIENT_MLME=y
-
 # Driver interface for wired Ethernet drivers
 CONFIG_DRIVER_WIRED=y
 
@@ -123,6 +123,10 @@ CONFIG_DRIVER_WIRED=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
@@ -161,6 +165,9 @@ CONFIG_EAP_OTP=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
 
@@ -190,6 +197,13 @@ CONFIG_EAP_LEAP=y
 
 # Wi-Fi Protected Setup (WPS)
 #CONFIG_WPS=y
+# Enable WSC 2.0 support
+#CONFIG_WPS2=y
+# Enable WPS external registrar functionality
+#CONFIG_WPS_ER=y
+# Disable credentials for an open network by default when acting as a WPS
+# registrar.
+#CONFIG_WPS_REG_DISABLE_OPEN=y
 
 # EAP-IKEv2
 #CONFIG_EAP_IKEV2=y
@@ -224,6 +238,10 @@ CONFIG_CTRL_IFACE=y
 # 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%
@@ -305,18 +323,17 @@ CONFIG_PEERKEY=y
 
 # Select TLS implementation
 # openssl = OpenSSL (default)
-# gnutls = GnuTLS (needed for TLS/IA, see also CONFIG_GNUTLS_EXTRA)
+# gnutls = GnuTLS
 # internal = Internal TLSv1 implementation (experimental)
 # none = Empty template
 #CONFIG_TLS=openssl
 
-# Whether to enable TLS/IA support, which is required for EAP-TTLSv1.
-# You need CONFIG_TLS=gnutls for this to have any effect. Please note that
-# even though the core GnuTLS library is released under LGPL, this extra
-# library uses GPL and as such, the terms of GPL apply to the combination
-# of wpa_supplicant and GnuTLS if this option is enabled. BSD license may not
-# apply for distribution of the resulting binary.
-#CONFIG_GNUTLS_EXTRA=y
+# 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
 
 # If CONFIG_TLS=internal is used, additional library and include paths are
 # needed for LibTomMath. Alternatively, an integrated, minimal version of
@@ -378,6 +395,11 @@ CONFIG_PEERKEY=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
+
 # Enable privilege separation (see README 'Privilege separation' for details)
 #CONFIG_PRIVSEP=y
 
@@ -389,7 +411,7 @@ CONFIG_PEERKEY=y
 # This tracks use of memory allocations and other registrations and reports
 # incorrect use with a backtrace of call (or allocation) location.
 #CONFIG_WPA_TRACE=y
-# For BSD, comment out these.
+# For BSD, uncomment these.
 #LIBS += -lexecinfo
 #LIBS_p += -lexecinfo
 #LIBS_c += -lexecinfo
@@ -398,7 +420,47 @@ CONFIG_PEERKEY=y
 # This enables use of libbfd to get more detailed symbols for the backtraces
 # generated by CONFIG_WPA_TRACE=y.
 #CONFIG_WPA_TRACE_BFD=y
-# For BSD, comment out these.
+# For BSD, uncomment these.
 #LIBS += -lbfd -liberty -lz
 #LIBS_p += -lbfd -liberty -lz
 #LIBS_c += -lbfd -liberty -lz
+
+# 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
+
+# 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
index 3aae51b..0ab4e15 100644 (file)
       </varlistentry>
 
       <varlistentry>
-       <term>atmel</term>
-       <listitem>
-         <para>ATMEL AT76C5XXx (USB, PCMCIA).</para>
-       </listitem>
-      </varlistentry>
-
-      <varlistentry>
        <term>wext</term>
        <listitem>
          <para>Linux wireless extensions (generic).</para>
       </varlistentry>
 
       <varlistentry>
-       <term>ndiswrapper</term>
-       <listitem>
-         <para>Linux ndiswrapper.</para>
-       </listitem>
-      </varlistentry>
-
-      <varlistentry>
        <term>broadcom</term>
        <listitem>
          <para>Broadcom wl.o driver.</para>
       </varlistentry>
 
       <varlistentry>
-       <term>ipw</term>
-       <listitem>
-         <para>Intel ipw2100/2200 driver.</para>
-       </listitem>
-      </varlistentry>
-
-      <varlistentry>
        <term>wired</term>
        <listitem>
          <para>wpa_supplicant wired Ethernet driver</para>
@@ -599,13 +578,6 @@ wpa_supplicant \
       </varlistentry>
 
       <varlistentry>
-       <term>ATMEL AT76C5XXx driver for USB and PCMCIA cards</term>
-       <listitem>
-         <para> (http://atmelwlandriver.sourceforge.net/).</para>
-       </listitem>
-      </varlistentry>
-
-      <varlistentry>
        <term>Linux ndiswrapper</term>
        <listitem>
          <para> (http://ndiswrapper.sourceforge.net/) with Windows
index a70aa6a..9828aff 100644 (file)
@@ -79,6 +79,23 @@ static inline int wpa_drv_scan(struct wpa_supplicant *wpa_s,
        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)
 {
@@ -236,35 +253,6 @@ wpa_drv_get_hw_feature_data(struct wpa_supplicant *wpa_s, u16 *num_modes,
        return NULL;
 }
 
-static inline int wpa_drv_set_channel(struct wpa_supplicant *wpa_s,
-                                     enum hostapd_hw_mode phymode, int chan,
-                                     int freq)
-{
-       if (wpa_s->driver->set_channel)
-               return wpa_s->driver->set_channel(wpa_s->drv_priv, phymode,
-                                                 chan, freq);
-       return -1;
-}
-
-static inline int wpa_drv_set_ssid(struct wpa_supplicant *wpa_s,
-                                  const u8 *ssid, size_t ssid_len)
-{
-       if (wpa_s->driver->set_ssid) {
-               return wpa_s->driver->set_ssid(wpa_s->drv_priv, ssid,
-                                              ssid_len);
-       }
-       return -1;
-}
-
-static inline int wpa_drv_set_bssid(struct wpa_supplicant *wpa_s,
-                                   const u8 *bssid)
-{
-       if (wpa_s->driver->set_bssid) {
-               return wpa_s->driver->set_bssid(wpa_s->drv_priv, bssid);
-       }
-       return -1;
-}
-
 static inline int wpa_drv_set_country(struct wpa_supplicant *wpa_s,
                                      const char *alpha2)
 {
@@ -282,24 +270,6 @@ static inline int wpa_drv_send_mlme(struct wpa_supplicant *wpa_s,
        return -1;
 }
 
-static inline int wpa_drv_mlme_add_sta(struct wpa_supplicant *wpa_s,
-                                      const u8 *addr, const u8 *supp_rates,
-                                      size_t supp_rates_len)
-{
-       if (wpa_s->driver->mlme_add_sta)
-               return wpa_s->driver->mlme_add_sta(wpa_s->drv_priv, addr,
-                                                  supp_rates, supp_rates_len);
-       return -1;
-}
-
-static inline int wpa_drv_mlme_remove_sta(struct wpa_supplicant *wpa_s,
-                                         const u8 *addr)
-{
-       if (wpa_s->driver->mlme_remove_sta)
-               return wpa_s->driver->mlme_remove_sta(wpa_s->drv_priv, addr);
-       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)
@@ -320,15 +290,11 @@ static inline int wpa_drv_send_ft_action(struct wpa_supplicant *wpa_s,
        return -1;
 }
 
-static inline int wpa_drv_set_beacon(struct wpa_supplicant *wpa_s,
-                                    const u8 *head, size_t head_len,
-                                    const u8 *tail, size_t tail_len,
-                                    int dtim_period, int beacon_int)
+static inline int wpa_drv_set_ap(struct wpa_supplicant *wpa_s,
+                                struct wpa_driver_ap_params *params)
 {
-       if (wpa_s->driver->set_beacon)
-               return wpa_s->driver->set_beacon(wpa_s->drv_priv, head,
-                                                head_len, tail, tail_len,
-                                                dtim_period, beacon_int);
+       if (wpa_s->driver->set_ap)
+               return wpa_s->driver->set_ap(wpa_s->drv_priv, params);
        return -1;
 }
 
@@ -351,12 +317,12 @@ static inline int wpa_drv_sta_remove(struct wpa_supplicant *wpa_s,
 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)
+                                         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);
+                                                     own_addr, flags);
        return -1;
 }
 
@@ -383,14 +349,30 @@ static inline int wpa_drv_set_supp_port(struct wpa_supplicant *wpa_s,
 
 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)
+                                     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,
-                                                 dst, src, bssid, data,
-                                                 data_len);
+                                                 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;
 }
 
@@ -398,12 +380,12 @@ 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)
+                                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);
+                                            if_addr, bridge);
        return -1;
 }
 
@@ -444,15 +426,6 @@ static inline int wpa_drv_probe_req_report(struct wpa_supplicant *wpa_s,
        return -1;
 }
 
-static inline int wpa_drv_disable_11b_rates(struct wpa_supplicant *wpa_s,
-                                           int disabled)
-{
-       if (wpa_s->driver->disable_11b_rates)
-               return wpa_s->driver->disable_11b_rates(wpa_s->drv_priv,
-                                                       disabled);
-       return -1;
-}
-
 static inline int wpa_drv_deinit_ap(struct wpa_supplicant *wpa_s)
 {
        if (wpa_s->driver->deinit_ap)
@@ -460,6 +433,13 @@ static inline int wpa_drv_deinit_ap(struct wpa_supplicant *wpa_s)
        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)
@@ -481,14 +461,213 @@ static inline int wpa_drv_signal_monitor(struct wpa_supplicant *wpa_s,
        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_set_ap_wps_ie(struct wpa_supplicant *wpa_s,
                                        const struct wpabuf *beacon,
-                                       const struct wpabuf *proberesp)
+                                       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);
+                                           proberesp, assocresp);
+}
+
+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);
+}
+
+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_p2p_find(struct wpa_supplicant *wpa_s,
+                                  unsigned int timeout, int type)
+{
+       if (!wpa_s->driver->p2p_find)
+               return -1;
+       return wpa_s->driver->p2p_find(wpa_s->drv_priv, timeout, type);
+}
+
+static inline int wpa_drv_p2p_stop_find(struct wpa_supplicant *wpa_s)
+{
+       if (!wpa_s->driver->p2p_stop_find)
+               return -1;
+       return wpa_s->driver->p2p_stop_find(wpa_s->drv_priv);
+}
+
+static inline int wpa_drv_p2p_listen(struct wpa_supplicant *wpa_s,
+                                    unsigned int timeout)
+{
+       if (!wpa_s->driver->p2p_listen)
+               return -1;
+       return wpa_s->driver->p2p_listen(wpa_s->drv_priv, timeout);
+}
+
+static inline int wpa_drv_p2p_connect(struct wpa_supplicant *wpa_s,
+                                     const u8 *peer_addr, int wps_method,
+                                     int go_intent,
+                                     const u8 *own_interface_addr,
+                                     unsigned int force_freq,
+                                     int persistent_group)
+{
+       if (!wpa_s->driver->p2p_connect)
+               return -1;
+       return wpa_s->driver->p2p_connect(wpa_s->drv_priv, peer_addr,
+                                         wps_method, go_intent,
+                                         own_interface_addr, force_freq,
+                                         persistent_group);
+}
+
+static inline int wpa_drv_wps_success_cb(struct wpa_supplicant *wpa_s,
+                                        const u8 *peer_addr)
+{
+       if (!wpa_s->driver->wps_success_cb)
+               return -1;
+       return wpa_s->driver->wps_success_cb(wpa_s->drv_priv, peer_addr);
+}
+
+static inline int
+wpa_drv_p2p_group_formation_failed(struct wpa_supplicant *wpa_s)
+{
+       if (!wpa_s->driver->p2p_group_formation_failed)
+               return -1;
+       return wpa_s->driver->p2p_group_formation_failed(wpa_s->drv_priv);
+}
+
+static inline int wpa_drv_p2p_set_params(struct wpa_supplicant *wpa_s,
+                                        const struct p2p_params *params)
+{
+       if (!wpa_s->driver->p2p_set_params)
+               return -1;
+       return wpa_s->driver->p2p_set_params(wpa_s->drv_priv, params);
+}
+
+static inline int wpa_drv_p2p_prov_disc_req(struct wpa_supplicant *wpa_s,
+                                           const u8 *peer_addr,
+                                           u16 config_methods, int join)
+{
+       if (!wpa_s->driver->p2p_prov_disc_req)
+               return -1;
+       return wpa_s->driver->p2p_prov_disc_req(wpa_s->drv_priv, peer_addr,
+                                               config_methods, join);
+}
+
+static inline u64 wpa_drv_p2p_sd_request(struct wpa_supplicant *wpa_s,
+                                        const u8 *dst,
+                                        const struct wpabuf *tlvs)
+{
+       if (!wpa_s->driver->p2p_sd_request)
+               return 0;
+       return wpa_s->driver->p2p_sd_request(wpa_s->drv_priv, dst, tlvs);
+}
+
+static inline int wpa_drv_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s,
+                                               u64 req)
+{
+       if (!wpa_s->driver->p2p_sd_cancel_request)
+               return -1;
+       return wpa_s->driver->p2p_sd_cancel_request(wpa_s->drv_priv, req);
+}
+
+static inline int wpa_drv_p2p_sd_response(struct wpa_supplicant *wpa_s,
+                                         int freq, const u8 *dst,
+                                         u8 dialog_token,
+                                         const struct wpabuf *resp_tlvs)
+{
+       if (!wpa_s->driver->p2p_sd_response)
+               return -1;
+       return wpa_s->driver->p2p_sd_response(wpa_s->drv_priv, freq, dst,
+                                             dialog_token, resp_tlvs);
+}
+
+static inline int wpa_drv_p2p_service_update(struct wpa_supplicant *wpa_s)
+{
+       if (!wpa_s->driver->p2p_service_update)
+               return -1;
+       return wpa_s->driver->p2p_service_update(wpa_s->drv_priv);
+}
+
+static inline int wpa_drv_p2p_reject(struct wpa_supplicant *wpa_s,
+                                    const u8 *addr)
+{
+       if (!wpa_s->driver->p2p_reject)
+               return -1;
+       return wpa_s->driver->p2p_reject(wpa_s->drv_priv, addr);
+}
+
+static inline int wpa_drv_p2p_invite(struct wpa_supplicant *wpa_s,
+                                    const u8 *peer, int role, const u8 *bssid,
+                                    const u8 *ssid, size_t ssid_len,
+                                    const u8 *go_dev_addr,
+                                    int persistent_group)
+{
+       if (!wpa_s->driver->p2p_invite)
+               return -1;
+       return wpa_s->driver->p2p_invite(wpa_s->drv_priv, peer, role, bssid,
+                                        ssid, ssid_len, go_dev_addr,
+                                        persistent_group);
+}
+
+static inline int wpa_drv_send_tdls_mgmt(struct wpa_supplicant *wpa_s,
+                                        const u8 *dst, u8 action_code,
+                                        u8 dialog_token, u16 status_code,
+                                        const u8 *buf, size_t len)
+{
+       if (wpa_s->driver->send_tdls_mgmt) {
+               return wpa_s->driver->send_tdls_mgmt(wpa_s->drv_priv, dst,
+                                                    action_code, dialog_token,
+                                                    status_code, buf, len);
+       }
+       return -1;
+}
+
+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);
+}
+
+static inline void wpa_drv_set_rekey_info(struct wpa_supplicant *wpa_s,
+                                         const u8 *kek, const u8 *kck,
+                                         const u8 *replay_ctr)
+{
+       if (!wpa_s->driver->set_rekey_info)
+               return;
+       wpa_s->driver->set_rekey_info(wpa_s->drv_priv, kek, kck, replay_ctr);
 }
 
 #endif /* DRIVER_I_H */
index f668874..e5f43aa 100644 (file)
@@ -130,6 +130,10 @@ int eap_register_methods(void)
                ret = eap_peer_tnc_register();
 #endif /* EAP_TNC */
 
+#ifdef EAP_PWD
+       if (ret == 0)
+               ret = eap_peer_pwd_register();
+#endif /* EAP_PWD */
 
 #ifdef EAP_SERVER_IDENTITY
        if (ret == 0)
@@ -231,5 +235,10 @@ int eap_register_methods(void)
                ret = eap_server_tnc_register();
 #endif /* EAP_SERVER_TNC */
 
+#ifdef EAP_SERVER_PWD
+       if (ret == 0)
+               ret = eap_server_pwd_register();
+#endif /* EAP_SERVER_PWD */
+
        return ret;
 }
index 4eed854..024903c 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant - test code
- * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2011, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
 #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 "eap_peer/eap_i.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"
 
@@ -74,6 +77,8 @@ struct eapol_test_data {
        char *connect_info;
        u8 own_addr[ETH_ALEN];
        struct extra_radius_attr *extra_attrs;
+
+       FILE *server_cert_file;
 };
 
 static struct eapol_test_data eapol_test;
@@ -279,7 +284,9 @@ static void ieee802_1x_encapsulate_radius(struct eapol_test_data *e,
                }
        }
 
-       radius_client_send(e->radius, msg, RADIUS_AUTH, e->wpa_s->own_addr);
+       if (radius_client_send(e->radius, msg, RADIUS_AUTH, e->wpa_s->own_addr)
+           < 0)
+               goto fail;
        return;
 
  fail:
@@ -290,7 +297,6 @@ static void ieee802_1x_encapsulate_radius(struct eapol_test_data *e,
 static int eapol_test_eapol_send(void *ctx, int type, const u8 *buf,
                                 size_t len)
 {
-       /* struct wpa_supplicant *wpa_s = ctx; */
        printf("WPA: eapol_test_eapol_send(type=%d len=%lu)\n",
               type, (unsigned long) len);
        if (type == IEEE802_1X_TYPE_EAP_PACKET) {
@@ -304,16 +310,16 @@ static int eapol_test_eapol_send(void *ctx, int type, const u8 *buf,
 static void eapol_test_set_config_blob(void *ctx,
                                       struct wpa_config_blob *blob)
 {
-       struct wpa_supplicant *wpa_s = ctx;
-       wpa_config_set_blob(wpa_s->conf, 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 wpa_supplicant *wpa_s = ctx;
-       return wpa_config_get_blob(wpa_s->conf, name);
+       struct eapol_test_data *e = ctx;
+       return wpa_config_get_blob(e->wpa_s->conf, name);
 }
 
 
@@ -382,6 +388,53 @@ static void eapol_sm_cb(struct eapol_sm *eapol, int success, void *ctx)
 }
 
 
+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);
+}
+
+
+static void eapol_test_cert_cb(void *ctx, int depth, const char *subject,
+                              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);
+       }
+}
+
+
 static int test_eapol(struct eapol_test_data *e, struct wpa_supplicant *wpa_s,
                      struct wpa_ssid *ssid)
 {
@@ -393,7 +446,7 @@ static int test_eapol(struct eapol_test_data *e, struct wpa_supplicant *wpa_s,
                printf("Failed to allocate EAPOL context.\n");
                return -1;
        }
-       ctx->ctx = wpa_s;
+       ctx->ctx = e;
        ctx->msg_ctx = wpa_s;
        ctx->scard_ctx = wpa_s->scard;
        ctx->cb = eapol_sm_cb;
@@ -407,6 +460,8 @@ static int test_eapol(struct eapol_test_data *e, struct wpa_supplicant *wpa_s,
        ctx->opensc_engine_path = wpa_s->conf->opensc_engine_path;
        ctx->pkcs11_engine_path = wpa_s->conf->pkcs11_engine_path;
        ctx->pkcs11_module_path = wpa_s->conf->pkcs11_module_path;
+       ctx->cert_cb = eapol_test_cert_cb;
+       ctx->cert_in_cb = 1;
 
        wpa_s->eapol = eapol_sm_init(ctx);
        if (wpa_s->eapol == NULL) {
@@ -963,7 +1018,7 @@ static void usage(void)
               "eapol_test [-nWS] -c<conf> [-a<AS IP>] [-p<AS port>] "
               "[-s<AS secret>]\\\n"
               "           [-r<count>] [-t<timeout>] [-C<Connect-Info>] \\\n"
-              "           [-M<client MAC address>] \\\n"
+              "           [-M<client MAC address>] [-o<server cert file] \\\n"
               "           [-N<attr spec>] \\\n"
               "           [-A<client IP>]\n"
               "eapol_test scard\n"
@@ -989,6 +1044,8 @@ static void usage(void)
               "  -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"
@@ -1030,7 +1087,7 @@ int main(int argc, char *argv[])
        wpa_debug_show_keys = 1;
 
        for (;;) {
-               c = getopt(argc, argv, "a:A:c:C:M:nN:p:r:s:St:W");
+               c = getopt(argc, argv, "a:A:c:C:M:nN:o:p:r:s:St:W");
                if (c < 0)
                        break;
                switch (c) {
@@ -1055,6 +1112,16 @@ int main(int argc, char *argv[])
                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;
@@ -1191,9 +1258,15 @@ int main(int argc, char *argv[])
        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)
index 85dcfb2..1c3b9eb 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant - Driver event processing
- * Copyright (c) 2003-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2011, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
 #include "common/wpa_ctrl.h"
 #include "eap_peer/eap.h"
 #include "ap/hostapd.h"
+#include "p2p/p2p.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 "ap.h"
 #include "bss.h"
-#include "mlme.h"
 #include "scan.h"
+#include "offchannel.h"
 
 
 static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s)
@@ -49,22 +54,22 @@ static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s)
        if (wpa_s->conf->ap_scan == 1 && wpa_s->current_ssid)
                return 0;
 
-       wpa_printf(MSG_DEBUG, "Select network based on association "
-                  "information");
+       wpa_dbg(wpa_s, MSG_DEBUG, "Select network based on association "
+               "information");
        ssid = wpa_supplicant_get_ssid(wpa_s);
        if (ssid == NULL) {
-               wpa_printf(MSG_INFO, "No network configuration found for the "
-                          "current AP");
+               wpa_msg(wpa_s, MSG_INFO,
+                       "No network configuration found for the current AP");
                return -1;
        }
 
        if (ssid->disabled) {
-               wpa_printf(MSG_DEBUG, "Selected network is disabled");
+               wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is disabled");
                return -1;
        }
 
-       wpa_printf(MSG_DEBUG, "Network configuration found for the current "
-                  "AP");
+       wpa_dbg(wpa_s, MSG_DEBUG, "Network configuration found for the "
+               "current AP");
        if (ssid->key_mgmt & (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_IEEE8021X |
                              WPA_KEY_MGMT_WPA_NONE |
                              WPA_KEY_MGMT_FT_PSK | WPA_KEY_MGMT_FT_IEEE8021X |
@@ -91,8 +96,7 @@ static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s)
 }
 
 
-static void wpa_supplicant_stop_countermeasures(void *eloop_ctx,
-                                               void *sock_ctx)
+void wpa_supplicant_stop_countermeasures(void *eloop_ctx, void *sock_ctx)
 {
        struct wpa_supplicant *wpa_s = eloop_ctx;
 
@@ -109,11 +113,34 @@ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s)
 {
        int bssid_changed;
 
+#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);
+#ifdef CONFIG_P2P
+       os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN);
+#endif /* CONFIG_P2P */
        wpa_s->current_bss = NULL;
+       wpa_s->assoc_freq = 0;
+#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_SME
+       if (wpa_s->sme.ft_ies)
+               sme_update_ft_ies(wpa_s, NULL, NULL, 0);
+#endif /* CONFIG_SME */
+#endif /* CONFIG_IEEE80211R */
+
        if (bssid_changed)
                wpas_notify_bssid_changed(wpa_s);
 
@@ -145,8 +172,8 @@ static void wpa_find_assoc_pmkid(struct wpa_supplicant *wpa_s)
                }
        }
 
-       wpa_printf(MSG_DEBUG, "RSN: PMKID from assoc IE %sfound from PMKSA "
-                  "cache", pmksa_set == 0 ? "" : "not ");
+       wpa_dbg(wpa_s, MSG_DEBUG, "RSN: PMKID from assoc IE %sfound from "
+               "PMKSA cache", pmksa_set == 0 ? "" : "not ");
 }
 
 
@@ -154,14 +181,15 @@ static void wpa_supplicant_event_pmkid_candidate(struct wpa_supplicant *wpa_s,
                                                 union wpa_event_data *data)
 {
        if (data == NULL) {
-               wpa_printf(MSG_DEBUG, "RSN: No data in PMKID candidate event");
+               wpa_dbg(wpa_s, MSG_DEBUG, "RSN: No data in PMKID candidate "
+                       "event");
                return;
        }
-       wpa_printf(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);
+       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,
@@ -204,6 +232,7 @@ 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, type;
 
        if (ssid->eap.pcsc == NULL || wpa_s->scard != NULL)
@@ -232,13 +261,14 @@ int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s,
                aka = 0;
 
        if (!sim && !aka) {
-               wpa_printf(MSG_DEBUG, "Selected network is configured to use "
-                          "SIM, but neither EAP-SIM nor EAP-AKA are enabled");
+               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_printf(MSG_DEBUG, "Selected network is configured to use SIM "
-                  "(sim=%d aka=%d) - initialize PCSC", sim, aka);
+       wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is configured to use SIM "
+               "(sim=%d aka=%d) - initialize PCSC", sim, aka);
        if (sim && aka)
                type = SCARD_TRY_BOTH;
        else if (aka)
@@ -248,12 +278,13 @@ int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s,
 
        wpa_s->scard = scard_init(type);
        if (wpa_s->scard == NULL) {
-               wpa_printf(MSG_WARNING, "Failed to initialize SIM "
-                          "(pcsc-lite)");
+               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;
@@ -287,6 +318,9 @@ static int wpa_supplicant_match_privacy(struct wpa_scan_res *bss,
                privacy = 1;
 #endif /* IEEE8021X_EAPOL */
 
+       if (wpa_key_mgmt_wpa(ssid->key_mgmt))
+               privacy = 1;
+
        if (bss->caps & IEEE80211_CAP_PRIVACY)
                return privacy;
        return !privacy;
@@ -301,53 +335,70 @@ static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s,
        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_scan_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_printf(MSG_DEBUG, "   skip RSN IE - parse failed");
+                       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_printf(MSG_DEBUG, "   skip RSN IE - proto "
-                                  "mismatch");
+                       wpa_dbg(wpa_s, MSG_DEBUG, "   skip RSN IE - proto "
+                               "mismatch");
                        break;
                }
 
                if (!(ie.pairwise_cipher & ssid->pairwise_cipher)) {
-                       wpa_printf(MSG_DEBUG, "   skip RSN IE - PTK cipher "
-                                  "mismatch");
+                       wpa_dbg(wpa_s, MSG_DEBUG, "   skip RSN IE - PTK "
+                               "cipher mismatch");
                        break;
                }
 
                if (!(ie.group_cipher & ssid->group_cipher)) {
-                       wpa_printf(MSG_DEBUG, "   skip RSN IE - GTK cipher "
-                                  "mismatch");
+                       wpa_dbg(wpa_s, MSG_DEBUG, "   skip RSN IE - GTK "
+                               "cipher mismatch");
                        break;
                }
 
                if (!(ie.key_mgmt & ssid->key_mgmt)) {
-                       wpa_printf(MSG_DEBUG, "   skip RSN IE - key mgmt "
-                                  "mismatch");
+                       wpa_dbg(wpa_s, MSG_DEBUG, "   skip RSN IE - key mgmt "
+                               "mismatch");
                        break;
                }
 
 #ifdef CONFIG_IEEE80211W
                if (!(ie.capabilities & WPA_CAPABILITY_MFPC) &&
                    ssid->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED) {
-                       wpa_printf(MSG_DEBUG, "   skip RSN IE - no mgmt frame "
-                                  "protection");
+                       wpa_dbg(wpa_s, MSG_DEBUG, "   skip RSN IE - no mgmt "
+                               "frame protection");
                        break;
                }
 #endif /* CONFIG_IEEE80211W */
 
-               wpa_printf(MSG_DEBUG, "   selected based on RSN IE");
+               wpa_dbg(wpa_s, MSG_DEBUG, "   selected based on RSN IE");
                return 1;
        }
 
@@ -356,39 +407,60 @@ static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s,
                proto_match++;
 
                if (wpa_parse_wpa_ie(wpa_ie, 2 + wpa_ie[1], &ie)) {
-                       wpa_printf(MSG_DEBUG, "   skip WPA IE - parse failed");
+                       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_printf(MSG_DEBUG, "   skip WPA IE - proto "
-                                  "mismatch");
+                       wpa_dbg(wpa_s, MSG_DEBUG, "   skip WPA IE - proto "
+                               "mismatch");
                        break;
                }
 
                if (!(ie.pairwise_cipher & ssid->pairwise_cipher)) {
-                       wpa_printf(MSG_DEBUG, "   skip WPA IE - PTK cipher "
-                                  "mismatch");
+                       wpa_dbg(wpa_s, MSG_DEBUG, "   skip WPA IE - PTK "
+                               "cipher mismatch");
                        break;
                }
 
                if (!(ie.group_cipher & ssid->group_cipher)) {
-                       wpa_printf(MSG_DEBUG, "   skip WPA IE - GTK cipher "
-                                  "mismatch");
+                       wpa_dbg(wpa_s, MSG_DEBUG, "   skip WPA IE - GTK "
+                               "cipher mismatch");
                        break;
                }
 
                if (!(ie.key_mgmt & ssid->key_mgmt)) {
-                       wpa_printf(MSG_DEBUG, "   skip WPA IE - key mgmt "
-                                  "mismatch");
+                       wpa_dbg(wpa_s, MSG_DEBUG, "   skip WPA IE - key mgmt "
+                               "mismatch");
                        break;
                }
 
-               wpa_printf(MSG_DEBUG, "   selected based on WPA IE");
+               wpa_dbg(wpa_s, MSG_DEBUG, "   selected based on WPA IE");
                return 1;
        }
 
-       if (proto_match == 0)
-               wpa_printf(MSG_DEBUG, "   skip - no WPA/RSN proto match");
+       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 (!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;
 }
@@ -408,237 +480,266 @@ static int freq_allowed(int *freqs, int freq)
 }
 
 
-static struct wpa_bss *
-wpa_supplicant_select_bss_wpa(struct wpa_supplicant *wpa_s,
-                             struct wpa_scan_results *scan_res,
-                             struct wpa_ssid *group,
-                             struct wpa_ssid **selected_ssid)
+static int ht_supported(const struct hostapd_hw_modes *mode)
 {
-       struct wpa_ssid *ssid;
-       struct wpa_scan_res *bss;
-       size_t i;
-       struct wpa_blacklist *e;
-       const u8 *ie;
+       if (!(mode->flags & HOSTAPD_MODE_FLAG_HT_INFO_KNOWN)) {
+               /*
+                * The driver did not indicate whether it supports HT. Assume
+                * it does to avoid connection issues.
+                */
+               return 1;
+       }
 
-       wpa_printf(MSG_DEBUG, "Try to find WPA-enabled AP");
-       for (i = 0; i < scan_res->num; i++) {
-               const u8 *ssid_;
-               u8 wpa_ie_len, rsn_ie_len, ssid_len;
-               bss = scan_res->res[i];
+       /*
+        * IEEE Std 802.11n-2009 20.1.1:
+        * An HT non-AP STA shall support all EQM rates for one spatial stream.
+        */
+       return mode->mcs_set[0] == 0xff;
+}
 
-               ie = wpa_scan_get_ie(bss, WLAN_EID_SSID);
-               ssid_ = ie ? ie + 2 : (u8 *) "";
-               ssid_len = ie ? ie[1] : 0;
 
-               ie = wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
-               wpa_ie_len = ie ? ie[1] : 0;
+static int rate_match(struct wpa_supplicant *wpa_s, struct wpa_scan_res *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;
 
-               ie = wpa_scan_get_ie(bss, WLAN_EID_RSN);
-               rsn_ie_len = ie ? ie[1] : 0;
+       if (bss->freq == 0)
+               return 1; /* Cannot do matching without knowing band */
 
-               wpa_printf(MSG_DEBUG, "%d: " MACSTR " ssid='%s' "
-                          "wpa_ie_len=%u rsn_ie_len=%u caps=0x%x",
-                          (int) i, MAC2STR(bss->bssid),
-                          wpa_ssid_txt(ssid_, ssid_len),
-                          wpa_ie_len, rsn_ie_len, bss->caps);
+       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;
+       }
 
-               e = wpa_blacklist_get(wpa_s, bss->bssid);
-               if (e && e->count > 1) {
-                       wpa_printf(MSG_DEBUG, "   skip - blacklisted");
-                       continue;
+       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 (ssid_len == 0) {
-                       wpa_printf(MSG_DEBUG, "   skip - SSID not known");
-                       continue;
-               }
+       if (mode == NULL)
+               return 0;
 
-               if (wpa_ie_len == 0 && rsn_ie_len == 0) {
-                       wpa_printf(MSG_DEBUG, "   skip - no WPA/RSN IE");
+       for (i = 0; i < (int) sizeof(scan_ie); i++) {
+               rate_ie = wpa_scan_get_ie(bss, scan_ie[i]);
+               if (rate_ie == NULL)
                        continue;
-               }
 
-               for (ssid = group; ssid; ssid = ssid->pnext) {
-                       int check_ssid = 1;
+               for (j = 2; j < rate_ie[1] + 2; j++) {
+                       int flagged = !!(rate_ie[j] & 0x80);
+                       int r = (rate_ie[j] & 0x7f) * 5;
 
-                       if (ssid->disabled) {
-                               wpa_printf(MSG_DEBUG, "   skip - disabled");
+                       /*
+                        * 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;
                        }
 
-#ifdef CONFIG_WPS
-                       if (ssid->ssid_len == 0 &&
-                           wpas_wps_ssid_wildcard_ok(wpa_s, ssid, bss))
-                               check_ssid = 0;
-#endif /* CONFIG_WPS */
-
-                       if (check_ssid &&
-                           (ssid_len != ssid->ssid_len ||
-                            os_memcmp(ssid_, ssid->ssid, ssid_len) != 0)) {
-                               wpa_printf(MSG_DEBUG, "   skip - "
-                                          "SSID mismatch");
+                       if (!flagged)
                                continue;
-                       }
 
-                       if (ssid->bssid_set &&
-                           os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) != 0)
-                       {
-                               wpa_printf(MSG_DEBUG, "   skip - "
-                                          "BSSID mismatch");
-                               continue;
+                       /* check for legacy basic rates */
+                       for (k = 0; k < mode->num_rates; k++) {
+                               if (mode->rates[k] == r)
+                                       break;
                        }
-
-                       if (!wpa_supplicant_ssid_bss_match(wpa_s, ssid, bss))
-                               continue;
-
-                       if (!freq_allowed(ssid->freq_list, bss->freq)) {
-                               wpa_printf(MSG_DEBUG, "   skip - "
-                                          "frequency not allowed");
-                               continue;
+                       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",
+                                       r / 10, r % 10);
+                               return 0;
                        }
-
-                       wpa_printf(MSG_DEBUG, "   selected WPA AP "
-                                  MACSTR " ssid='%s'",
-                                  MAC2STR(bss->bssid),
-                                  wpa_ssid_txt(ssid_, ssid_len));
-                       *selected_ssid = ssid;
-                       return wpa_bss_get(wpa_s, bss->bssid, ssid_, ssid_len);
                }
        }
 
-       return NULL;
+       return 1;
 }
 
 
-static struct wpa_bss *
-wpa_supplicant_select_bss_non_wpa(struct wpa_supplicant *wpa_s,
-                                 struct wpa_scan_results *scan_res,
-                                 struct wpa_ssid *group,
-                                 struct wpa_ssid **selected_ssid)
+static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
+                                           int i, struct wpa_scan_res *bss,
+                                           struct wpa_ssid *group)
 {
-       struct wpa_ssid *ssid;
-       struct wpa_scan_res *bss;
-       size_t i;
+       const u8 *ssid_;
+       u8 wpa_ie_len, rsn_ie_len, ssid_len;
+       int wpa;
        struct wpa_blacklist *e;
        const u8 *ie;
+       struct wpa_ssid *ssid;
 
-       wpa_printf(MSG_DEBUG, "Try to find non-WPA AP");
-       for (i = 0; i < scan_res->num; i++) {
-               const u8 *ssid_;
-               u8 wpa_ie_len, rsn_ie_len, ssid_len;
-               bss = scan_res->res[i];
+       ie = wpa_scan_get_ie(bss, WLAN_EID_SSID);
+       ssid_ = ie ? ie + 2 : (u8 *) "";
+       ssid_len = ie ? ie[1] : 0;
 
-               ie = wpa_scan_get_ie(bss, WLAN_EID_SSID);
-               ssid_ = ie ? ie + 2 : (u8 *) "";
-               ssid_len = ie ? ie[1] : 0;
+       ie = wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
+       wpa_ie_len = ie ? ie[1] : 0;
 
-               ie = wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
-               wpa_ie_len = ie ? ie[1] : 0;
+       ie = wpa_scan_get_ie(bss, WLAN_EID_RSN);
+       rsn_ie_len = ie ? ie[1] : 0;
 
-               ie = wpa_scan_get_ie(bss, WLAN_EID_RSN);
-               rsn_ie_len = ie ? ie[1] : 0;
+       wpa_dbg(wpa_s, MSG_DEBUG, "%d: " MACSTR " ssid='%s' "
+               "wpa_ie_len=%u rsn_ie_len=%u caps=0x%x level=%d%s",
+               i, MAC2STR(bss->bssid), wpa_ssid_txt(ssid_, ssid_len),
+               wpa_ie_len, rsn_ie_len, bss->caps, bss->level,
+               wpa_scan_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE) ? " wps" : "");
 
-               wpa_printf(MSG_DEBUG, "%d: " MACSTR " ssid='%s' "
-                          "wpa_ie_len=%u rsn_ie_len=%u caps=0x%x",
-                          (int) i, MAC2STR(bss->bssid),
-                          wpa_ssid_txt(ssid_, ssid_len),
-                          wpa_ie_len, rsn_ie_len, bss->caps);
+       e = wpa_blacklist_get(wpa_s, bss->bssid);
+       if (e) {
+               int limit = 1;
+               if (wpa_supplicant_enabled_networks(wpa_s->conf) == 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 (ssid_len == 0) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "   skip - SSID not known");
+               return NULL;
+       }
 
-               e = wpa_blacklist_get(wpa_s, bss->bssid);
-               if (e && e->count > 1) {
-                       wpa_printf(MSG_DEBUG, "   skip - blacklisted");
+       wpa = wpa_ie_len > 0 || rsn_ie_len > 0;
+
+       for (ssid = group; ssid; ssid = ssid->pnext) {
+               int check_ssid = wpa ? 1 : (ssid->ssid_len != 0);
+
+               if (ssid->disabled) {
+                       wpa_dbg(wpa_s, MSG_DEBUG, "   skip - disabled");
                        continue;
                }
 
-               if (ssid_len == 0) {
-                       wpa_printf(MSG_DEBUG, "   skip - SSID not known");
+#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;
                }
 
-               for (ssid = group; ssid; ssid = ssid->pnext) {
-                       int check_ssid = ssid->ssid_len != 0;
+               if (wpa && ssid->ssid_len == 0 &&
+                   wpas_wps_ssid_wildcard_ok(wpa_s, ssid, bss))
+                       check_ssid = 0;
 
-                       if (ssid->disabled) {
-                               wpa_printf(MSG_DEBUG, "   skip - disabled");
-                               continue;
-                       }
-
-#ifdef CONFIG_WPS
-                       if (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;
-                       }
+               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 (check_ssid &&
-                           (ssid_len != ssid->ssid_len ||
-                            os_memcmp(ssid_, ssid->ssid, ssid_len) != 0)) {
-                               wpa_printf(MSG_DEBUG, "   skip - "
-                                          "SSID mismatch");
-                               continue;
-                       }
+               if (ssid->bssid_set && ssid->ssid_len == 0 &&
+                   os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) == 0)
+                       check_ssid = 0;
 
-                       if (ssid->bssid_set &&
-                           os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) != 0)
-                       {
-                               wpa_printf(MSG_DEBUG, "   skip - "
-                                          "BSSID mismatch");
-                               continue;
-                       }
+               if (check_ssid &&
+                   (ssid_len != ssid->ssid_len ||
+                    os_memcmp(ssid_, ssid->ssid, ssid_len) != 0)) {
+                       wpa_dbg(wpa_s, MSG_DEBUG, "   skip - SSID mismatch");
+                       continue;
+               }
 
-                       if (!(ssid->key_mgmt & WPA_KEY_MGMT_NONE) &&
-                           !(ssid->key_mgmt & WPA_KEY_MGMT_WPS) &&
-                           !(ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA))
-                       {
-                               wpa_printf(MSG_DEBUG, "   skip - "
-                                          "non-WPA network not allowed");
-                               continue;
-                       }
+               if (ssid->bssid_set &&
+                   os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) != 0) {
+                       wpa_dbg(wpa_s, MSG_DEBUG, "   skip - BSSID mismatch");
+                       continue;
+               }
 
-                       if ((ssid->key_mgmt &
-                            (WPA_KEY_MGMT_IEEE8021X | WPA_KEY_MGMT_PSK |
-                             WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_FT_PSK |
-                             WPA_KEY_MGMT_IEEE8021X_SHA256 |
-                             WPA_KEY_MGMT_PSK_SHA256)) &&
-                           (wpa_ie_len != 0 || rsn_ie_len != 0)) {
-                               wpa_printf(MSG_DEBUG, "   skip - "
-                                          "WPA network");
-                               continue;
-                       }
+               if (!wpa_supplicant_ssid_bss_match(wpa_s, ssid, bss))
+                       continue;
 
-                       if (!wpa_supplicant_match_privacy(bss, ssid)) {
-                               wpa_printf(MSG_DEBUG, "   skip - "
-                                          "privacy mismatch");
-                               continue;
-                       }
+               if (!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 (bss->caps & IEEE80211_CAP_IBSS) {
-                               wpa_printf(MSG_DEBUG, "   skip - "
-                                          "IBSS (adhoc) network");
-                               continue;
-                       }
+               if (!wpa_supplicant_match_privacy(bss, ssid)) {
+                       wpa_dbg(wpa_s, MSG_DEBUG, "   skip - privacy "
+                               "mismatch");
+                       continue;
+               }
 
-                       if (!freq_allowed(ssid->freq_list, bss->freq)) {
-                               wpa_printf(MSG_DEBUG, "   skip - "
-                                          "frequency not allowed");
-                               continue;
-                       }
+               if (bss->caps & IEEE80211_CAP_IBSS) {
+                       wpa_dbg(wpa_s, MSG_DEBUG, "   skip - IBSS (adhoc) "
+                               "network");
+                       continue;
+               }
 
-                       wpa_printf(MSG_DEBUG, "   selected non-WPA AP "
-                                  MACSTR " ssid='%s'",
-                                  MAC2STR(bss->bssid),
-                                  wpa_ssid_txt(ssid_, ssid_len));
-                       *selected_ssid = ssid;
-                       return wpa_bss_get(wpa_s, bss->bssid, ssid_, ssid_len);
+               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
+               /*
+                * 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 */
+
+               /* Matching configuration found */
+               return ssid;
        }
 
+       /* No matching configuration found */
        return NULL;
 }
 
@@ -649,21 +750,31 @@ wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s,
                          struct wpa_ssid *group,
                          struct wpa_ssid **selected_ssid)
 {
-       struct wpa_bss *selected;
+       size_t i;
 
-       wpa_printf(MSG_DEBUG, "Selecting BSS from priority group %d",
-                  group->priority);
+       wpa_dbg(wpa_s, MSG_DEBUG, "Selecting BSS from priority group %d",
+               group->priority);
 
-       /* First, try to find WPA-enabled AP */
-       selected = wpa_supplicant_select_bss_wpa(wpa_s, scan_res, group,
-                                                selected_ssid);
-       if (selected)
-               return selected;
+       for (i = 0; i < scan_res->num; i++) {
+               struct wpa_scan_res *bss = scan_res->res[i];
+               const u8 *ie, *ssid;
+               u8 ssid_len;
 
-       /* If no WPA-enabled AP found, try to find non-WPA AP, if configuration
-        * allows this. */
-       return wpa_supplicant_select_bss_non_wpa(wpa_s, scan_res, group,
-                                                selected_ssid);
+               *selected_ssid = wpa_scan_res_match(wpa_s, i, bss, group);
+               if (!*selected_ssid)
+                       continue;
+
+               ie = wpa_scan_get_ie(bss, WLAN_EID_SSID);
+               ssid = ie ? ie + 2 : (u8 *) "";
+               ssid_len = ie ? ie[1] : 0;
+
+               wpa_dbg(wpa_s, MSG_DEBUG, "   selected BSS " MACSTR
+                       " ssid='%s'",
+                       MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len));
+               return wpa_bss_get(wpa_s, bss->bssid, ssid, ssid_len);
+       }
+
+       return NULL;
 }
 
 
@@ -685,8 +796,8 @@ wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s,
                }
 
                if (selected == NULL && wpa_s->blacklist) {
-                       wpa_printf(MSG_DEBUG, "No APs found - clear blacklist "
-                                  "and try again");
+                       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)
@@ -713,15 +824,22 @@ static void wpa_supplicant_req_new_scan(struct wpa_supplicant *wpa_s,
 }
 
 
-void wpa_supplicant_connect(struct wpa_supplicant *wpa_s,
-                           struct wpa_bss *selected,
-                           struct wpa_ssid *ssid)
+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");
-               wpa_supplicant_req_new_scan(wpa_s, 10, 0);
-               return;
+#ifdef CONFIG_P2P
+               if (wpas_p2p_notif_pbc_overlap(wpa_s) == 1)
+                       return -1;
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_WPS
+               wpas_wps_cancel(wpa_s);
+#endif /* CONFIG_WPS */
+               return -1;
        }
 
        /*
@@ -731,18 +849,27 @@ void wpa_supplicant_connect(struct wpa_supplicant *wpa_s,
         */
        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_ASSOCIATING &&
+              wpa_s->wpa_state != WPA_AUTHENTICATING) ||
              os_memcmp(selected->bssid, wpa_s->pending_bssid, ETH_ALEN) !=
              0))) {
                if (wpa_supplicant_scard_init(wpa_s, ssid)) {
                        wpa_supplicant_req_new_scan(wpa_s, 10, 0);
-                       return;
+                       return 0;
                }
+               wpa_msg(wpa_s, MSG_DEBUG, "Request association: "
+                       "reassociate: %d  selected: "MACSTR "  bssid: " MACSTR
+                       "  pending: " MACSTR "  wpa_state: %s",
+                       wpa_s->reassociate, MAC2STR(selected->bssid),
+                       MAC2STR(wpa_s->bssid), MAC2STR(wpa_s->pending_bssid),
+                       wpa_supplicant_state_txt(wpa_s->wpa_state));
                wpa_supplicant_associate(wpa_s, selected, ssid);
        } else {
-               wpa_printf(MSG_DEBUG, "Already associated with the selected "
-                          "AP");
+               wpa_dbg(wpa_s, MSG_DEBUG, "Already associated with the "
+                       "selected AP");
        }
+
+       return 0;
 }
 
 
@@ -769,28 +896,25 @@ wpa_supplicant_pick_new_network(struct wpa_supplicant *wpa_s)
 /* 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_scan_results *scan_res)
+       struct wpa_supplicant *wpa_s)
 {
-       int i;
+       struct wpa_bss *bss;
 
        if (rsn_preauth_scan_results(wpa_s->wpa) < 0)
                return;
 
-       for (i = scan_res->num - 1; i >= 0; i--) {
+       dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
                const u8 *ssid, *rsn;
-               struct wpa_scan_res *r;
-
-               r = scan_res->res[i];
 
-               ssid = wpa_scan_get_ie(r, WLAN_EID_SSID);
+               ssid = wpa_bss_get_ie(bss, WLAN_EID_SSID);
                if (ssid == NULL)
                        continue;
 
-               rsn = wpa_scan_get_ie(r, WLAN_EID_RSN);
+               rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN);
                if (rsn == NULL)
                        continue;
 
-               rsn_preauth_scan_result(wpa_s->wpa, r->bssid, ssid, rsn);
+               rsn_preauth_scan_result(wpa_s->wpa, bss->bssid, ssid, rsn);
        }
 
 }
@@ -814,6 +938,9 @@ static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s,
        if (wpa_s->current_ssid != ssid)
                return 1; /* different network block */
 
+       if (wpas_driver_bss_selection(wpa_s))
+               return 0; /* Driver-based roaming */
+
        for (i = 0; i < scan_res->num; i++) {
                struct wpa_scan_res *res = scan_res->res[i];
                const u8 *ie;
@@ -833,17 +960,17 @@ static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s,
        if (!current_bss)
                return 1; /* current BSS not seen in scan results */
 
-       wpa_printf(MSG_DEBUG, "Considering within-ESS reassociation");
-       wpa_printf(MSG_DEBUG, "Current BSS: " MACSTR " level=%d",
-                  MAC2STR(current_bss->bssid), current_bss->level);
-       wpa_printf(MSG_DEBUG, "Selected BSS: " MACSTR " level=%d",
-                  MAC2STR(selected->bssid), selected->level);
+       wpa_dbg(wpa_s, MSG_DEBUG, "Considering within-ESS reassociation");
+       wpa_dbg(wpa_s, MSG_DEBUG, "Current BSS: " MACSTR " level=%d",
+               MAC2STR(current_bss->bssid), current_bss->level);
+       wpa_dbg(wpa_s, MSG_DEBUG, "Selected BSS: " MACSTR " level=%d",
+               MAC2STR(selected->bssid), selected->level);
 
        if (wpa_s->current_ssid->bssid_set &&
            os_memcmp(selected->bssid, wpa_s->current_ssid->bssid, ETH_ALEN) ==
            0) {
-               wpa_printf(MSG_DEBUG, "Allow reassociation - selected BSS has "
-                          "preferred BSSID");
+               wpa_dbg(wpa_s, MSG_DEBUG, "Allow reassociation - selected BSS "
+                       "has preferred BSSID");
                return 1;
        }
 
@@ -861,16 +988,16 @@ static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s,
                        min_diff = 5;
        }
        if (abs(current_bss->level - selected->level) < min_diff) {
-               wpa_printf(MSG_DEBUG, "Skip roam - too small difference in "
-                          "signal level");
+               wpa_dbg(wpa_s, MSG_DEBUG, "Skip roam - too small difference "
+                       "in signal level");
                return 0;
        }
 
        return 1;
 }
 
-
-static void wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
+/* Return < 0 if no scan results could be fetched. */
+static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
                                              union wpa_event_data *data)
 {
        struct wpa_bss *selected;
@@ -885,32 +1012,70 @@ static void wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
 
        wpa_supplicant_notify_scanning(wpa_s, 0);
 
+#ifdef CONFIG_P2P
+       if (wpa_s->p2p_cb_on_scan_complete && !wpa_s->global->p2p_disabled &&
+           wpa_s->global->p2p != NULL) {
+               wpa_s->p2p_cb_on_scan_complete = 0;
+               if (p2p_other_scan_completed(wpa_s->global->p2p) == 1) {
+                       wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Pending P2P operation "
+                               "stopped scan processing");
+                       return -1;
+               }
+       }
+#endif /* CONFIG_P2P */
+
        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)
-                       return;
-               wpa_printf(MSG_DEBUG, "Failed to get scan results - try "
-                          "scanning again");
+                       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);
-               return;
+               return -1;
        }
 
+#ifndef CONFIG_NO_RANDOM_POOL
+       size_t i, num;
+       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 (wpa_s->scan_res_handler) {
-               wpa_s->scan_res_handler(wpa_s, scan_res);
+               void (*scan_res_handler)(struct wpa_supplicant *wpa_s,
+                                        struct wpa_scan_results *scan_res);
+
+               scan_res_handler = wpa_s->scan_res_handler;
                wpa_s->scan_res_handler = NULL;
+               scan_res_handler(wpa_s, scan_res);
+
                wpa_scan_results_free(scan_res);
-               return;
+               return 0;
        }
 
        if (ap) {
-               wpa_printf(MSG_DEBUG, "Ignore scan results in AP mode");
+               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 */
                wpa_scan_results_free(scan_res);
-               return;
+               return 0;
        }
 
-       wpa_printf(MSG_DEBUG, "New scan results available");
+       wpa_dbg(wpa_s, MSG_DEBUG, "New scan results available");
        wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS);
        wpas_notify_scan_results(wpa_s);
 
@@ -918,22 +1083,21 @@ static void wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
 
        if ((wpa_s->conf->ap_scan == 2 && !wpas_wps_searching(wpa_s))) {
                wpa_scan_results_free(scan_res);
-               return;
+               return 0;
        }
 
        if (wpa_s->disconnected) {
                wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
                wpa_scan_results_free(scan_res);
-               return;
+               return 0;
        }
 
-       if (bgscan_notify_scan(wpa_s) == 1) {
+       if (!wpas_driver_bss_selection(wpa_s) &&
+           bgscan_notify_scan(wpa_s, scan_res) == 1) {
                wpa_scan_results_free(scan_res);
-               return;
+               return 0;
        }
 
-       wpa_supplicant_rsn_preauth_scan_results(wpa_s, scan_res);
-
        selected = wpa_supplicant_pick_network(wpa_s, scan_res, &ssid);
 
        if (selected) {
@@ -941,24 +1105,91 @@ static void wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
                skip = !wpa_supplicant_need_to_roam(wpa_s, selected, ssid,
                                                    scan_res);
                wpa_scan_results_free(scan_res);
-               if (skip)
-                       return;
-               wpa_supplicant_connect(wpa_s, selected, ssid);
+               if (skip) {
+                       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;
+               }
+               wpa_supplicant_rsn_preauth_scan_results(wpa_s);
        } else {
                wpa_scan_results_free(scan_res);
-               wpa_printf(MSG_DEBUG, "No suitable network found");
+               wpa_dbg(wpa_s, MSG_DEBUG, "No suitable network found");
                ssid = wpa_supplicant_pick_new_network(wpa_s);
                if (ssid) {
-                       wpa_printf(MSG_DEBUG, "Setup a new network");
+                       wpa_dbg(wpa_s, MSG_DEBUG, "Setup a new network");
                        wpa_supplicant_associate(wpa_s, NULL, ssid);
+                       wpa_supplicant_rsn_preauth_scan_results(wpa_s);
                } else {
-                       int timeout_sec = 5;
+                       int timeout_sec = wpa_s->scan_interval;
                        int timeout_usec = 0;
-                       wpa_supplicant_req_new_scan(wpa_s, timeout_sec,
-                                                   timeout_usec);
+#ifdef CONFIG_P2P
+                       if (wpa_s->p2p_in_provisioning) {
+                               /*
+                                * Use shorter wait during P2P Provisioning
+                                * state 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 */
+                       if (wpa_supplicant_req_sched_scan(wpa_s))
+                               wpa_supplicant_req_new_scan(wpa_s, timeout_sec,
+                                                           timeout_usec);
                }
        }
+       return 0;
 }
+
+
+static void wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
+                                             union wpa_event_data *data)
+{
+       const char *rn, *rn2;
+       struct wpa_supplicant *ifs;
+
+       if (_wpa_supplicant_event_scan_results(wpa_s, data) < 0) {
+               /*
+                * If no scan results could be fetched, then no need to
+                * notify those interfaces that did not actually request
+                * this scan.
+                */
+               return;
+       }
+
+       /*
+        * Check other interfaces to see if they have the same radio-name. If
+        * so, they get updated with this same scan info.
+        */
+       if (!wpa_s->driver->get_radio_name)
+               return;
+
+       rn = wpa_s->driver->get_radio_name(wpa_s->drv_priv);
+       if (rn == NULL || rn[0] == '\0')
+               return;
+
+       wpa_dbg(wpa_s, MSG_DEBUG, "Checking for other virtual interfaces "
+               "sharing same radio (%s) in event_scan_results", rn);
+
+       for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
+               if (ifs == wpa_s || !ifs->driver->get_radio_name)
+                       continue;
+
+               rn2 = ifs->driver->get_radio_name(ifs->drv_priv);
+               if (rn2 && os_strcmp(rn, rn2) == 0) {
+                       wpa_printf(MSG_DEBUG, "%s: Updating scan results from "
+                                  "sibling", ifs->ifname);
+                       _wpa_supplicant_event_scan_results(ifs, data);
+               }
+       }
+}
+
 #endif /* CONFIG_NO_SCAN_PROCESSING */
 
 
@@ -968,19 +1199,25 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s,
        int l, len, found = 0, wpa_found, rsn_found;
        const u8 *p;
 
-       wpa_printf(MSG_DEBUG, "Association info event");
+       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)
+       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 */
+       }
        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_printf(MSG_DEBUG, "freq=%u MHz", 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;
@@ -1017,8 +1254,8 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s,
                                                 data->assoc_info.resp_ies,
                                                 data->assoc_info.resp_ies_len,
                                                 bssid) < 0) {
-                       wpa_printf(MSG_DEBUG, "FT: Validation of "
-                                  "Reassociation Response failed");
+                       wpa_dbg(wpa_s, MSG_DEBUG, "FT: Validation of "
+                               "Reassociation Response failed");
                        wpa_supplicant_deauthenticate(
                                wpa_s, WLAN_REASON_INVALID_IE);
                        return -1;
@@ -1028,6 +1265,27 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s,
        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;
@@ -1090,6 +1348,14 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s,
        if (wpa_found || rsn_found)
                wpa_s->ap_ies_from_associnfo = 1;
 
+       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;
@@ -1109,7 +1375,8 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
                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.req_ies_len,
+                                   data->assoc_info.reassoc);
                return;
        }
 #endif /* CONFIG_AP */
@@ -1119,13 +1386,11 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
                return;
 
        wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATED);
-       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
-               os_memcpy(bssid, wpa_s->bssid, ETH_ALEN);
-       if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) ||
-           (wpa_drv_get_bssid(wpa_s, bssid) >= 0 &&
-            os_memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0)) {
-               wpa_msg(wpa_s, MSG_DEBUG, "Associated to a new BSS: BSSID="
+       if (wpa_drv_get_bssid(wpa_s, bssid) >= 0 &&
+           os_memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "Associated to a new BSS: BSSID="
                        MACSTR, MAC2STR(bssid));
+               random_add_randomness(bssid, ETH_ALEN);
                bssid_changed = os_memcmp(wpa_s->bssid, bssid, ETH_ALEN);
                os_memcpy(wpa_s->bssid, bssid, ETH_ALEN);
                os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
@@ -1209,6 +1474,23 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *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);
        }
 
        if (wpa_s->pending_eapol_rx) {
@@ -1218,9 +1500,9 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
                if (age.sec == 0 && age.usec < 100000 &&
                    os_memcmp(wpa_s->pending_eapol_rx_src, bssid, ETH_ALEN) ==
                    0) {
-                       wpa_printf(MSG_DEBUG, "Process pending EAPOL frame "
-                                  "that was received just before association "
-                                  "notification");
+                       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),
@@ -1230,25 +1512,6 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
                wpa_s->pending_eapol_rx = NULL;
        }
 
-#ifdef CONFIG_BGSCAN
-       if (wpa_s->current_ssid != wpa_s->bgscan_ssid) {
-               bgscan_deinit(wpa_s);
-               if (wpa_s->current_ssid && wpa_s->current_ssid->bgscan) {
-                       if (bgscan_init(wpa_s, wpa_s->current_ssid)) {
-                               wpa_printf(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
-                               wpa_s->bgscan_ssid = wpa_s->current_ssid;
-               } else
-                       wpa_s->bgscan_ssid = NULL;
-       }
-#endif /* CONFIG_BGSCAN */
-
        if ((wpa_s->key_mgmt == WPA_KEY_MGMT_NONE ||
             wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) &&
            wpa_s->current_ssid && wpa_drv_get_capa(wpa_s, &capa) == 0 &&
@@ -1256,6 +1519,24 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
                /* 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 */
 }
 
 
@@ -1263,13 +1544,11 @@ static void wpa_supplicant_event_disassoc(struct wpa_supplicant *wpa_s,
                                          u16 reason_code)
 {
        const u8 *bssid;
-#ifdef CONFIG_SME
        int authenticating;
        u8 prev_pending_bssid[ETH_ALEN];
 
        authenticating = wpa_s->wpa_state == WPA_AUTHENTICATING;
        os_memcpy(prev_pending_bssid, wpa_s->pending_bssid, ETH_ALEN);
-#endif /* CONFIG_SME */
 
        if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
                /*
@@ -1277,8 +1556,8 @@ static void wpa_supplicant_event_disassoc(struct wpa_supplicant *wpa_s,
                 * generating streams of disconnected events when configuring
                 * IBSS for WPA-None. Ignore them for now.
                 */
-               wpa_printf(MSG_DEBUG, "Disconnect event - ignore in "
-                          "IBSS/WPA-None mode");
+               wpa_dbg(wpa_s, MSG_DEBUG, "Disconnect event - ignore in "
+                       "IBSS/WPA-None mode");
                return;
        }
 
@@ -1287,51 +1566,49 @@ static void wpa_supplicant_event_disassoc(struct wpa_supplicant *wpa_s,
                wpa_msg(wpa_s, MSG_INFO, "WPA: 4-Way Handshake failed - "
                        "pre-shared key may be incorrect");
        }
-       if (wpa_s->wpa_state >= WPA_ASSOCIATED)
-               wpa_supplicant_req_scan(wpa_s, 0, 100000);
+       if (!wpa_s->auto_reconnect_disabled ||
+           wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Auto connect enabled: try to "
+                       "reconnect (wps=%d)",
+                       wpa_s->key_mgmt == WPA_KEY_MGMT_WPS);
+               if (wpa_s->wpa_state >= WPA_ASSOCIATING)
+                       wpa_supplicant_req_scan(wpa_s, 0, 100000);
+       } else {
+               wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Auto connect disabled: do not "
+                       "try to re-connect");
+               wpa_s->reassociate = 0;
+               wpa_s->disconnected = 1;
+               wpa_supplicant_cancel_sched_scan(wpa_s);
+       }
        bssid = wpa_s->bssid;
        if (is_zero_ether_addr(bssid))
                bssid = wpa_s->pending_bssid;
-       wpa_blacklist_add(wpa_s, bssid);
+       wpas_connection_failed(wpa_s, bssid);
        wpa_sm_notify_disassoc(wpa_s->wpa);
        wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid=" MACSTR
                " reason=%d",
                MAC2STR(bssid), reason_code);
        if (wpa_supplicant_dynamic_keys(wpa_s)) {
-               wpa_printf(MSG_DEBUG, "Disconnect event - remove keys");
+               wpa_dbg(wpa_s, MSG_DEBUG, "Disconnect event - remove keys");
                wpa_s->keys_cleared = 0;
                wpa_clear_keys(wpa_s, wpa_s->bssid);
        }
        wpa_supplicant_mark_disassoc(wpa_s);
-       bgscan_deinit(wpa_s);
-       wpa_s->bgscan_ssid = NULL;
-#ifdef CONFIG_SME
-       if (authenticating &&
-           (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)) {
-               /*
-                * 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_printf(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);
-       }
-#endif /* CONFIG_SME */
+
+       if (authenticating && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME))
+               sme_disassoc_while_authenticating(wpa_s, prev_pending_bssid);
 }
 
 
 #ifdef CONFIG_DELAYED_MIC_ERROR_REPORT
-static void wpa_supplicant_delayed_mic_error_report(void *eloop_ctx,
-                                                   void *sock_ctx)
+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_printf(MSG_DEBUG, "WPA: Sending pending MIC error report");
+       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;
 }
@@ -1406,8 +1683,8 @@ wpa_supplicant_event_michael_mic_failure(struct wpa_supplicant *wpa_s,
                                sec = os_random() % 60;
                        else
                                sec = WPA_GET_BE32(rval) % 60;
-                       wpa_printf(MSG_DEBUG, "WPA: Delay MIC error report %d "
-                                  "seconds", sec);
+                       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(
@@ -1454,18 +1731,24 @@ wpa_supplicant_event_interface_status(struct wpa_supplicant *wpa_s,
                if (!wpa_s->interface_removed)
                        break;
                wpa_s->interface_removed = 0;
-               wpa_printf(MSG_DEBUG, "Configured interface was added.");
+               wpa_dbg(wpa_s, MSG_DEBUG, "Configured interface was added");
                if (wpa_supplicant_driver_init(wpa_s) < 0) {
-                       wpa_printf(MSG_INFO, "Failed to initialize the driver "
-                                  "after interface was added.");
+                       wpa_msg(wpa_s, MSG_INFO, "Failed to initialize the "
+                               "driver after interface was added");
                }
+               wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
                break;
        case EVENT_INTERFACE_REMOVED:
-               wpa_printf(MSG_DEBUG, "Configured interface was 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_IBSS_RSN
+               ibss_rsn_deinit(wpa_s->ibss_rsn);
+               wpa_s->ibss_rsn = NULL;
+#endif /* CONFIG_IBSS_RSN */
 #ifdef CONFIG_TERMINATE_ONLASTIF
                /* check if last interface */
                if (!any_interfaces(wpa_s->global->ifaces))
@@ -1488,6 +1771,25 @@ wpa_supplicant_event_stkstart(struct wpa_supplicant *wpa_s,
 #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_start(wpa_s->wpa, data->tdls.peer);
+               break;
+       case TDLS_REQUEST_TEARDOWN:
+               wpa_tdls_send_teardown(wpa_s->wpa, data->tdls.peer,
+                                      data->tdls.reason_code);
+               break;
+       }
+}
+#endif /* CONFIG_TDLS */
+
+
 #ifdef CONFIG_IEEE80211R
 static void
 wpa_supplicant_event_ft_response(struct wpa_supplicant *wpa_s,
@@ -1512,8 +1814,17 @@ wpa_supplicant_event_ft_response(struct wpa_supplicant *wpa_s,
 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);
 }
 #endif /* CONFIG_IBSS_RSN */
@@ -1536,19 +1847,19 @@ static void ft_rx_action(struct wpa_supplicant *wpa_s, const u8 *data,
        sta_addr = data + 1;
        target_ap_addr = data + 1 + ETH_ALEN;
        status = WPA_GET_LE16(data + 1 + 2 * ETH_ALEN);
-       wpa_printf(MSG_DEBUG, "FT: Received FT Action Response: STA " MACSTR
-                  " TargetAP " MACSTR " status %u",
-                  MAC2STR(sta_addr), MAC2STR(target_ap_addr), status);
+       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_printf(MSG_DEBUG, "FT: Foreign STA Address " MACSTR
-                          " in FT Action Response", MAC2STR(sta_addr));
+               wpa_dbg(wpa_s, MSG_DEBUG, "FT: Foreign STA Address " MACSTR
+                       " in FT Action Response", MAC2STR(sta_addr));
                return;
        }
 
        if (status) {
-               wpa_printf(MSG_DEBUG, "FT: FT Action Response indicates "
-                          "failure (status code %d)", status);
+               wpa_dbg(wpa_s, MSG_DEBUG, "FT: FT Action Response indicates "
+                       "failure (status code %d)", status);
                /* TODO: report error to FT code(?) */
                return;
        }
@@ -1573,12 +1884,94 @@ static void ft_rx_action(struct wpa_supplicant *wpa_s, const u8 *data,
 #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 wnm_action_rx(struct wpa_supplicant *wpa_s, struct rx_action *rx)
+{
+       u8 action, mode;
+       const u8 *pos, *end;
+
+       if (rx->data == NULL || rx->len == 0)
+               return;
+
+       pos = rx->data;
+       end = pos + rx->len;
+       action = *pos++;
+
+       wpa_printf(MSG_DEBUG, "WNM: RX action %u from " MACSTR,
+                  action, MAC2STR(rx->sa));
+       switch (action) {
+       case WNM_BSS_TRANS_MGMT_REQ:
+               if (pos + 5 > end)
+                       break;
+               wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management "
+                          "Request: dialog_token=%u request_mode=0x%x "
+                          "disassoc_timer=%u validity_interval=%u",
+                          pos[0], pos[1], WPA_GET_LE16(pos + 2), pos[4]);
+               mode = pos[1];
+               pos += 5;
+               if (mode & 0x08)
+                       pos += 12; /* BSS Termination Duration */
+               if (mode & 0x10) {
+                       char url[256];
+                       if (pos + 1 > end || pos + 1 + pos[0] > end) {
+                               wpa_printf(MSG_DEBUG, "WNM: Invalid BSS "
+                                          "Transition Management Request "
+                                          "(URL)");
+                               break;
+                       }
+                       os_memcpy(url, pos + 1, pos[0]);
+                       url[pos[0]] = '\0';
+                       wpa_msg(wpa_s, MSG_INFO, "WNM: ESS Disassociation "
+                               "Imminent - session_info_url=%s", url);
+               }
+               break;
+       }
+}
+
+
 void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                          union wpa_event_data *data)
 {
        struct wpa_supplicant *wpa_s = ctx;
        u16 reason_code = 0;
 
+       if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED &&
+           event != EVENT_INTERFACE_ENABLED &&
+           event != EVENT_INTERFACE_STATUS) {
+               wpa_dbg(wpa_s, MSG_DEBUG,
+                       "Ignore event %s (%d) while interface is disabled",
+                       event_to_string(event), event);
+               return;
+       }
+
+       wpa_dbg(wpa_s, MSG_DEBUG, "Event %s (%d) received",
+               event_to_string(event), event);
+
        switch (event) {
        case EVENT_AUTH:
                sme_event_auth(wpa_s, data);
@@ -1587,24 +1980,67 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                wpa_supplicant_event_assoc(wpa_s, data);
                break;
        case EVENT_DISASSOC:
-               wpa_printf(MSG_DEBUG, "Disassociation notification");
+               wpa_dbg(wpa_s, MSG_DEBUG, "Disassociation notification");
+               if (data) {
+                       wpa_dbg(wpa_s, MSG_DEBUG, " * reason %u",
+                               data->disassoc_info.reason_code);
+                       if (data->disassoc_info.addr)
+                               wpa_dbg(wpa_s, MSG_DEBUG, " * address " MACSTR,
+                                       MAC2STR(data->disassoc_info.addr));
+               }
 #ifdef CONFIG_AP
                if (wpa_s->ap_iface && data && data->disassoc_info.addr) {
                        hostapd_notif_disassoc(wpa_s->ap_iface->bss[0],
                                               data->disassoc_info.addr);
                        break;
                }
+               if (wpa_s->ap_iface) {
+                       wpa_dbg(wpa_s, MSG_DEBUG, "Ignore disassoc event in "
+                               "AP mode");
+                       break;
+               }
 #endif /* CONFIG_AP */
-               if (data)
-                       reason_code = data->deauth_info.reason_code;
+               if (data) {
+                       reason_code = data->disassoc_info.reason_code;
+                       wpa_hexdump(MSG_DEBUG, "Disassociation frame IE(s)",
+                                   data->disassoc_info.ie,
+                                   data->disassoc_info.ie_len);
+#ifdef CONFIG_P2P
+                       wpas_p2p_disassoc_notif(
+                               wpa_s, data->disassoc_info.addr, reason_code,
+                               data->disassoc_info.ie,
+                               data->disassoc_info.ie_len);
+#endif /* CONFIG_P2P */
+               }
                if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
                        sme_event_disassoc(wpa_s, data);
                /* fall through */
        case EVENT_DEAUTH:
                if (event == EVENT_DEAUTH) {
-                       wpa_printf(MSG_DEBUG, "Deauthentication notification");
-                       if (data)
+                       wpa_dbg(wpa_s, MSG_DEBUG,
+                               "Deauthentication notification");
+                       if (data) {
                                reason_code = data->deauth_info.reason_code;
+                               wpa_dbg(wpa_s, MSG_DEBUG, " * reason %u",
+                                       data->deauth_info.reason_code);
+                               if (data->deauth_info.addr) {
+                                       wpa_dbg(wpa_s, MSG_DEBUG, " * address "
+                                               MACSTR,
+                                               MAC2STR(data->deauth_info.
+                                                       addr));
+                               }
+                               wpa_hexdump(MSG_DEBUG,
+                                           "Deauthentication frame IE(s)",
+                                           data->deauth_info.ie,
+                                           data->deauth_info.ie_len);
+#ifdef CONFIG_P2P
+                               wpas_p2p_deauth_notif(
+                                       wpa_s, data->deauth_info.addr,
+                                       reason_code,
+                                       data->deauth_info.ie,
+                                       data->deauth_info.ie_len);
+#endif /* CONFIG_P2P */
+                       }
                }
 #ifdef CONFIG_AP
                if (wpa_s->ap_iface && data && data->deauth_info.addr) {
@@ -1612,6 +2048,11 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                                               data->deauth_info.addr);
                        break;
                }
+               if (wpa_s->ap_iface) {
+                       wpa_dbg(wpa_s, MSG_DEBUG, "Ignore deauth event in "
+                               "AP mode");
+                       break;
+               }
 #endif /* CONFIG_AP */
                wpa_supplicant_event_disassoc(wpa_s, reason_code);
                break;
@@ -1637,6 +2078,11 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                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_IEEE80211R
        case EVENT_FT_RESPONSE:
                wpa_supplicant_event_ft_response(wpa_s, data);
@@ -1648,18 +2094,69 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                break;
 #endif /* CONFIG_IBSS_RSN */
        case EVENT_ASSOC_REJECT:
-               sme_event_assoc_reject(wpa_s, data);
+               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);
                break;
        case EVENT_AUTH_TIMED_OUT:
-               sme_event_auth_timed_out(wpa_s, data);
+               if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
+                       sme_event_auth_timed_out(wpa_s, data);
                break;
        case EVENT_ASSOC_TIMED_OUT:
-               sme_event_assoc_timed_out(wpa_s, data);
+               if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
+                       sme_event_assoc_timed_out(wpa_s, data);
                break;
-#ifdef CONFIG_AP
        case EVENT_TX_STATUS:
-               if (wpa_s->ap_iface == NULL)
+               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,
@@ -1674,25 +2171,52 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                                     data->tx_status.ack);
                        break;
                }
+#endif /* CONFIG_AP */
+               break;
+#ifdef CONFIG_AP
+       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.frame,
-                                      data->rx_from_unknown.len);
+               ap_rx_from_unknown_sta(wpa_s, data->rx_from_unknown.addr,
+                                      data->rx_from_unknown.wds);
                break;
        case EVENT_RX_MGMT:
-               if (wpa_s->ap_iface == NULL)
+               if (wpa_s->ap_iface == NULL) {
+#ifdef CONFIG_P2P
+                       u16 fc, stype;
+                       const struct ieee80211_mgmt *mgmt;
+                       mgmt = (const struct ieee80211_mgmt *)
+                               data->rx_mgmt.frame;
+                       fc = le_to_host16(mgmt->frame_control);
+                       stype = WLAN_FC_GET_STYPE(fc);
+                       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);
+                               break;
+                       }
+#endif /* CONFIG_P2P */
+                       wpa_dbg(wpa_s, MSG_DEBUG, "AP: ignore received "
+                               "management frame in non-AP mode");
                        break;
+               }
                ap_mgmt_rx(wpa_s, &data->rx_mgmt);
                break;
 #endif /* CONFIG_AP */
        case EVENT_RX_ACTION:
-               wpa_printf(MSG_DEBUG, "Received Action frame: SA=" MACSTR
-                          " Category=%u DataLen=%d freq=%d MHz",
-                          MAC2STR(data->rx_action.sa),
-                          data->rx_action.category, (int) data->rx_action.len,
-                          data->rx_action.freq);
+               wpa_dbg(wpa_s, MSG_DEBUG, "Received Action frame: SA=" MACSTR
+                       " Category=%u DataLen=%d freq=%d MHz",
+                       MAC2STR(data->rx_action.sa),
+                       data->rx_action.category, (int) data->rx_action.len,
+                       data->rx_action.freq);
 #ifdef CONFIG_IEEE80211R
                if (data->rx_action.category == WLAN_ACTION_FT) {
                        ft_rx_action(wpa_s, data->rx_action.data,
@@ -1700,19 +2224,157 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                        break;
                }
 #endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+#ifdef CONFIG_SME
+               if (data->rx_action.category == WLAN_ACTION_SA_QUERY) {
+                       sme_sa_query_rx(wpa_s, data->rx_action.sa,
+                                       data->rx_action.data,
+                                       data->rx_action.len);
+                       break;
+               }
+#endif /* CONFIG_SME */
+#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_GAS
+               if (data->rx_action.category == WLAN_ACTION_PUBLIC &&
+                   gas_query_rx(wpa_s->gas, data->rx_action.da,
+                                data->rx_action.sa, data->rx_action.bssid,
+                                data->rx_action.data, data->rx_action.len,
+                                data->rx_action.freq) == 0)
+                       break;
+#endif /* CONFIG_GAS */
+               if (data->rx_action.category == WLAN_ACTION_WNM) {
+                       wnm_action_rx(wpa_s, &data->rx_action);
+                       break;
+               }
+#ifdef CONFIG_TDLS
+               if (data->rx_action.category == WLAN_ACTION_PUBLIC &&
+                   data->rx_action.len >= 4 &&
+                   data->rx_action.data[0] == WLAN_TDLS_DISCOVERY_RESPONSE) {
+                       wpa_dbg(wpa_s, MSG_DEBUG, "TDLS: Received Discovery "
+                               "Response from " MACSTR,
+                               MAC2STR(data->rx_action.sa));
+                       break;
+               }
+#endif /* CONFIG_TDLS */
+#ifdef CONFIG_P2P
+               wpas_p2p_rx_action(wpa_s, data->rx_action.da,
+                                  data->rx_action.sa,
+                                  data->rx_action.bssid,
+                                  data->rx_action.category,
+                                  data->rx_action.data,
+                                  data->rx_action.len, data->rx_action.freq);
+#endif /* CONFIG_P2P */
                break;
-#ifdef CONFIG_CLIENT_MLME
-       case EVENT_MLME_RX: {
-               struct ieee80211_rx_status rx_status;
-               os_memset(&rx_status, 0, sizeof(rx_status));
-               rx_status.freq = data->mlme_rx.freq;
-               rx_status.channel = data->mlme_rx.channel;
-               rx_status.ssi = data->mlme_rx.ssi;
-               ieee80211_sta_rx(wpa_s, data->mlme_rx.buf, data->mlme_rx.len,
-                                &rx_status);
+       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);
+                       break;
+               }
+#endif /* CONFIG_AP */
+#ifdef CONFIG_P2P
+               wpas_p2p_probe_req_rx(wpa_s, data->rx_probe_req.sa,
+                                     data->rx_probe_req.da,
+                                     data->rx_probe_req.bssid,
+                                     data->rx_probe_req.ie,
+                                     data->rx_probe_req.ie_len);
+#endif /* CONFIG_P2P */
                break;
-       }
-#endif /* CONFIG_CLIENT_MLME */
+       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 */
+#ifdef CONFIG_P2P
+               wpas_p2p_remain_on_channel_cb(
+                       wpa_s, data->remain_on_channel.freq,
+                       data->remain_on_channel.duration);
+#endif /* CONFIG_P2P */
+               break;
+       case EVENT_CANCEL_REMAIN_ON_CHANNEL:
+#ifdef CONFIG_OFFCHANNEL
+               offchannel_cancel_remain_on_channel_cb(
+                       wpa_s, data->remain_on_channel.freq);
+#endif /* CONFIG_OFFCHANNEL */
+#ifdef CONFIG_P2P
+               wpas_p2p_cancel_remain_on_channel_cb(
+                       wpa_s, data->remain_on_channel.freq);
+#endif /* CONFIG_P2P */
+               break;
+#ifdef CONFIG_P2P
+       case EVENT_P2P_DEV_FOUND: {
+               struct p2p_peer_info peer_info;
+
+               os_memset(&peer_info, 0, sizeof(peer_info));
+               if (data->p2p_dev_found.dev_addr)
+                       os_memcpy(peer_info.p2p_device_addr,
+                                 data->p2p_dev_found.dev_addr, ETH_ALEN);
+               if (data->p2p_dev_found.pri_dev_type)
+                       os_memcpy(peer_info.pri_dev_type,
+                                 data->p2p_dev_found.pri_dev_type,
+                                 sizeof(peer_info.pri_dev_type));
+               if (data->p2p_dev_found.dev_name)
+                       os_strlcpy(peer_info.device_name,
+                                  data->p2p_dev_found.dev_name,
+                                  sizeof(peer_info.device_name));
+               peer_info.config_methods = data->p2p_dev_found.config_methods;
+               peer_info.dev_capab = data->p2p_dev_found.dev_capab;
+               peer_info.group_capab = data->p2p_dev_found.group_capab;
+
+               /*
+                * FIX: new_device=1 is not necessarily correct. We should
+                * maintain a P2P peer database in wpa_supplicant and update
+                * this information based on whether the peer is truly new.
+                */
+               wpas_dev_found(wpa_s, data->p2p_dev_found.addr, &peer_info, 1);
+               break;
+               }
+       case EVENT_P2P_GO_NEG_REQ_RX:
+               wpas_go_neg_req_rx(wpa_s, data->p2p_go_neg_req_rx.src,
+                                  data->p2p_go_neg_req_rx.dev_passwd_id);
+               break;
+       case EVENT_P2P_GO_NEG_COMPLETED:
+               wpas_go_neg_completed(wpa_s, data->p2p_go_neg_completed.res);
+               break;
+       case EVENT_P2P_PROV_DISC_REQUEST:
+               wpas_prov_disc_req(wpa_s, data->p2p_prov_disc_req.peer,
+                                  data->p2p_prov_disc_req.config_methods,
+                                  data->p2p_prov_disc_req.dev_addr,
+                                  data->p2p_prov_disc_req.pri_dev_type,
+                                  data->p2p_prov_disc_req.dev_name,
+                                  data->p2p_prov_disc_req.supp_config_methods,
+                                  data->p2p_prov_disc_req.dev_capab,
+                                  data->p2p_prov_disc_req.group_capab,
+                                  NULL, 0);
+               break;
+       case EVENT_P2P_PROV_DISC_RESPONSE:
+               wpas_prov_disc_resp(wpa_s, data->p2p_prov_disc_resp.peer,
+                                   data->p2p_prov_disc_resp.config_methods);
+               break;
+       case EVENT_P2P_SD_REQUEST:
+               wpas_sd_request(wpa_s, data->p2p_sd_req.freq,
+                               data->p2p_sd_req.sa,
+                               data->p2p_sd_req.dialog_token,
+                               data->p2p_sd_req.update_indic,
+                               data->p2p_sd_req.tlvs,
+                               data->p2p_sd_req.tlvs_len);
+               break;
+       case EVENT_P2P_SD_RESPONSE:
+               wpas_sd_response(wpa_s, data->p2p_sd_resp.sa,
+                                data->p2p_sd_resp.update_indic,
+                                data->p2p_sd_resp.tlvs,
+                                data->p2p_sd_resp.tlvs_len);
+               break;
+#endif /* CONFIG_P2P */
        case EVENT_EAPOL_RX:
                wpa_supplicant_rx_eapol(wpa_s, data->eapol_rx.src,
                                        data->eapol_rx.data,
@@ -1720,10 +2382,116 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                break;
        case EVENT_SIGNAL_CHANGE:
                bgscan_notify_signal_change(
-                       wpa_s, data->signal_change.above_threshold);
+                       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);
+#ifdef CONFIG_AP
+                       if (!wpa_s->ap_iface) {
+                               wpa_supplicant_set_state(wpa_s,
+                                                        WPA_DISCONNECTED);
+                               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");
+               wpa_supplicant_mark_disassoc(wpa_s);
+               wpa_supplicant_set_state(wpa_s, WPA_INTERFACE_DISABLED);
+               break;
+       case EVENT_CHANNEL_LIST_CHANGED:
+               if (wpa_s->drv_priv == NULL)
+                       break; /* Ignore event during drv initialization */
+
+               free_hw_features(wpa_s);
+               wpa_s->hw.modes = wpa_drv_get_hw_feature_data(
+                       wpa_s, &wpa_s->hw.num_modes, &wpa_s->hw.flags);
+
+#ifdef CONFIG_P2P
+               wpas_p2p_update_channel_list(wpa_s);
+#endif /* CONFIG_P2P */
+               break;
+       case EVENT_INTERFACE_UNAVAILABLE:
+#ifdef CONFIG_P2P
+               wpas_p2p_interface_unavailable(wpa_s);
+#endif /* CONFIG_P2P */
+               break;
+       case EVENT_BEST_CHANNEL:
+               wpa_dbg(wpa_s, MSG_DEBUG, "Best channel event received "
+                       "(%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;
+#ifdef CONFIG_P2P
+               wpas_p2p_update_best_channels(wpa_s, data->best_chan.freq_24,
+                                             data->best_chan.freq_5,
+                                             data->best_chan.freq_overall);
+#endif /* CONFIG_P2P */
+               break;
+       case EVENT_UNPROT_DEAUTH:
+               wpa_supplicant_event_unprot_deauth(wpa_s,
+                                                  &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_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->sched_scanning = 0;
+               wpa_supplicant_notify_scanning(wpa_s, 0);
+
+               /*
+                * If we timed out, start a new sched scan to continue
+                * searching for more SSIDs.
+                */
+               if (wpa_s->sched_scan_timed_out)
+                       wpa_supplicant_req_sched_scan(wpa_s);
+               break;
+       case EVENT_WPS_BUTTON_PUSHED:
+#ifdef CONFIG_WPS
+               wpas_wps_start_pbc(wpa_s, NULL, 0);
+#endif /* CONFIG_WPS */
                break;
        default:
-               wpa_printf(MSG_INFO, "Unknown event %d", event);
+               wpa_msg(wpa_s, MSG_INFO, "Unknown event %d", event);
                break;
        }
 }
diff --git a/wpa_supplicant/examples/p2p-action-udhcp.sh b/wpa_supplicant/examples/p2p-action-udhcp.sh
new file mode 100644 (file)
index 0000000..d7d0e79
--- /dev/null
@@ -0,0 +1,69 @@
+#!/bin/sh
+
+IFNAME=$1
+CMD=$2
+
+kill_daemon() {
+    NAME=$1
+    PF=$2
+
+    if [ ! -r $PF ]; then
+       return
+    fi
+
+    PID=`cat $PF`
+    if [ $PID -gt 0 ]; then
+       if ps $PID | grep -q $NAME; then
+           kill $PID
+       fi
+    fi
+    rm $PF
+}
+
+if [ "$CMD" = "P2P-GROUP-STARTED" ]; then
+    GIFNAME=$3
+    if [ "$4" = "GO" ]; then
+       kill_daemon udhcpc /var/run/udhcpc-$GIFNAME.pid
+       ifconfig $GIFNAME 192.168.42.1 up
+       udhcpd /etc/udhcpd-p2p.conf
+    fi
+    if [ "$4" = "client" ]; then
+       kill_daemon udhcpc /var/run/udhcpc-$GIFNAME.pid
+       kill_daemon udhcpd /var/run/udhcpd-$GIFNAME.pid
+       udhcpc -i $GIFNAME -p /var/run/udhcpc-$GIFNAME.pid \
+               -s /etc/udhcpc.script
+    fi
+fi
+
+if [ "$CMD" = "P2P-GROUP-REMOVED" ]; then
+    GIFNAME=$3
+    if [ "$4" = "GO" ]; then
+       kill_daemon udhcpd /var/run/udhcpd-$GIFNAME.pid
+       ifconfig $GIFNAME 0.0.0.0
+    fi
+    if [ "$4" = "client" ]; then
+       kill_daemon udhcpc /var/run/udhcpc-$GIFNAME.pid
+       ifconfig $GIFNAME 0.0.0.0
+    fi
+fi
+
+if [ "$CMD" = "P2P-CROSS-CONNECT-ENABLE" ]; then
+    GIFNAME=$3
+    UPLINK=$4
+    # enable NAT/masquarade $GIFNAME -> $UPLINK
+    iptables -P FORWARD DROP
+    iptables -t nat -A POSTROUTING -o $UPLINK -j MASQUERADE
+    iptables -A FORWARD -i $UPLINK -o $GIFNAME -m state --state RELATED,ESTABLISHED -j ACCEPT
+    iptables -A FORWARD -i $GIFNAME -o $UPLINK -j ACCEPT
+    sysctl net.ipv4.ip_forward=1
+fi
+
+if [ "$CMD" = "P2P-CROSS-CONNECT-DISABLE" ]; then
+    GIFNAME=$3
+    UPLINK=$4
+    # disable NAT/masquarade $GIFNAME -> $UPLINK
+    sysctl net.ipv4.ip_forward=0
+    iptables -t nat -D POSTROUTING -o $UPLINK -j MASQUERADE
+    iptables -D FORWARD -i $UPLINK -o $GIFNAME -m state --state RELATED,ESTABLISHED -j ACCEPT
+    iptables -D FORWARD -i $GIFNAME -o $UPLINK -j ACCEPT
+fi
diff --git a/wpa_supplicant/examples/p2p-action.sh b/wpa_supplicant/examples/p2p-action.sh
new file mode 100644 (file)
index 0000000..8759f54
--- /dev/null
@@ -0,0 +1,83 @@
+#!/bin/sh
+
+IFNAME=$1
+CMD=$2
+
+kill_daemon() {
+    NAME=$1
+    PF=$2
+
+    if [ ! -r $PF ]; then
+       return
+    fi
+
+    PID=`cat $PF`
+    if [ $PID -gt 0 ]; then
+       if ps $PID | grep -q $NAME; then
+           kill $PID
+       fi
+    fi
+    rm $PF
+}
+
+if [ "$CMD" = "P2P-GROUP-STARTED" ]; then
+    GIFNAME=$3
+    if [ "$4" = "GO" ]; then
+       kill_daemon dhclient /var/run/dhclient-$GIFNAME.pid
+       rm /var/run/dhclient.leases-$GIFNAME
+       kill_daemon dnsmasq /var/run/dnsmasq.pid-$GIFNAME
+       ifconfig $GIFNAME 192.168.42.1 up
+       if ! dnsmasq -x /var/run/dnsmasq.pid-$GIFNAME \
+           -i $GIFNAME \
+           -F192.168.42.11,192.168.42.99; then
+           # another dnsmasq instance may be running and blocking us; try to
+           # start with -z to avoid that
+           dnsmasq -x /var/run/dnsmasq.pid-$GIFNAME \
+               -i $GIFNAME \
+               -F192.168.42.11,192.168.42.99 --listen-address 192.168.42.1 -z
+       fi
+    fi
+    if [ "$4" = "client" ]; then
+       kill_daemon dhclient /var/run/dhclient-$GIFNAME.pid
+       rm /var/run/dhclient.leases-$GIFNAME
+       kill_daemon dnsmasq /var/run/dnsmasq.pid-$GIFNAME
+       dhclient -pf /var/run/dhclient-$GIFNAME.pid \
+           -lf /var/run/dhclient.leases-$GIFNAME \
+           -nw \
+           $GIFNAME
+    fi
+fi
+
+if [ "$CMD" = "P2P-GROUP-REMOVED" ]; then
+    GIFNAME=$3
+    if [ "$4" = "GO" ]; then
+       kill_daemon dnsmasq /var/run/dnsmasq.pid-$GIFNAME
+       ifconfig $GIFNAME 0.0.0.0
+    fi
+    if [ "$4" = "client" ]; then
+       kill_daemon dhclient /var/run/dhclient-$GIFNAME.pid
+       rm /var/run/dhclient.leases-$GIFNAME
+       ifconfig $GIFNAME 0.0.0.0
+    fi
+fi
+
+if [ "$CMD" = "P2P-CROSS-CONNECT-ENABLE" ]; then
+    GIFNAME=$3
+    UPLINK=$4
+    # enable NAT/masquarade $GIFNAME -> $UPLINK
+    iptables -P FORWARD DROP
+    iptables -t nat -A POSTROUTING -o $UPLINK -j MASQUERADE
+    iptables -A FORWARD -i $UPLINK -o $GIFNAME -m state --state RELATED,ESTABLISHED -j ACCEPT
+    iptables -A FORWARD -i $GIFNAME -o $UPLINK -j ACCEPT
+    sysctl net.ipv4.ip_forward=1
+fi
+
+if [ "$CMD" = "P2P-CROSS-CONNECT-DISABLE" ]; then
+    GIFNAME=$3
+    UPLINK=$4
+    # disable NAT/masquarade $GIFNAME -> $UPLINK
+    sysctl net.ipv4.ip_forward=0
+    iptables -t nat -D POSTROUTING -o $UPLINK -j MASQUERADE
+    iptables -D FORWARD -i $UPLINK -o $GIFNAME -m state --state RELATED,ESTABLISHED -j ACCEPT
+    iptables -D FORWARD -i $GIFNAME -o $UPLINK -j ACCEPT
+fi
diff --git a/wpa_supplicant/examples/udhcpd-p2p.conf b/wpa_supplicant/examples/udhcpd-p2p.conf
new file mode 100644 (file)
index 0000000..df59094
--- /dev/null
@@ -0,0 +1,120 @@
+# Sample udhcpd configuration file (/etc/udhcpd.conf)
+
+# The start and end of the IP lease block
+
+start          192.168.42.20   #default: 192.168.0.20
+end            192.168.42.254  #default: 192.168.0.254
+
+
+# The interface that udhcpd will use
+
+interface      wlan2           #default: eth0
+
+
+# The maximim number of leases (includes addressesd reserved
+# by OFFER's, DECLINE's, and ARP conficts
+
+#max_leases    254             #default: 254
+
+
+# If remaining is true (default), udhcpd will store the time
+# remaining for each lease in the udhcpd leases file. This is
+# for embedded systems that cannot keep time between reboots.
+# If you set remaining to no, the absolute time that the lease
+# expires at will be stored in the dhcpd.leases file.
+
+#remaining     yes             #default: yes
+
+
+# The time period at which udhcpd will write out a dhcpd.leases
+# file. If this is 0, udhcpd will never automatically write a
+# lease file. (specified in seconds)
+
+#auto_time     7200            #default: 7200 (2 hours)
+
+
+# The amount of time that an IP will be reserved (leased) for if a
+# DHCP decline message is received (seconds).
+
+#decline_time  3600            #default: 3600 (1 hour)
+
+
+# The amount of time that an IP will be reserved (leased) for if an
+# ARP conflct occurs. (seconds
+
+#conflict_time 3600            #default: 3600 (1 hour)
+
+
+# How long an offered address is reserved (leased) in seconds
+
+#offer_time    60              #default: 60 (1 minute)
+
+# If a lease to be given is below this value, the full lease time is
+# instead used (seconds).
+
+#min_lease     60              #defult: 60
+
+
+# The location of the leases file
+
+#lease_file    /var/lib/misc/udhcpd.leases     #defualt: /var/lib/misc/udhcpd.leases
+
+# The location of the pid file
+pidfile        /var/run/udhcpd-wlan2.pid       #default: /var/run/udhcpd.pid
+
+# Every time udhcpd writes a leases file, the below script will be called.
+# Useful for writing the lease file to flash every few hours.
+
+#notify_file                           #default: (no script)
+
+#notify_file   dumpleases      # <--- useful for debugging
+
+# The following are bootp specific options, setable by udhcpd.
+
+#siaddr                192.168.0.22            #default: 0.0.0.0
+
+#sname         zorak                   #default: (none)
+
+#boot_file     /var/nfs_root           #default: (none)
+
+# The remainer of options are DHCP options and can be specifed with the
+# keyword 'opt' or 'option'. If an option can take multiple items, such
+# as the dns option, they can be listed on the same line, or multiple
+# lines. The only option with a default is 'lease'.
+
+#Examles
+opt    dns     192.168.2.1
+option subnet  255.255.255.0
+option domain  atherosowl.com
+option lease   864000          # 10 days of seconds
+
+
+# Currently supported options, for more info, see options.c
+#opt subnet
+#opt timezone
+#opt router
+#opt timesvr
+#opt namesvr
+#opt dns
+#opt logsvr
+#opt cookiesvr
+#opt lprsvr
+#opt bootsize
+#opt domain
+#opt swapsvr
+#opt rootpath
+#opt ipttl
+#opt mtu
+#opt broadcast
+#opt wins
+#opt lease
+#opt ntpsrv
+#opt tftp
+#opt bootfile
+
+
+# Static leases map
+#static_lease 00:60:08:11:CE:4E 192.168.0.54
+#static_lease 00:60:08:11:CE:3E 192.168.0.44
+
+
index b040e0a..d90ef18 100755 (executable)
@@ -59,12 +59,12 @@ def showBss(bss):
                          dbus_interface=dbus.PROPERTIES_IFACE)
        ssid = byte_array_to_string(val)
 
-       val = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'WPAIE',
+       val = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'WPA',
                          dbus_interface=dbus.PROPERTIES_IFACE)
        wpa = "no"
        if val != None:
                wpa = "yes"
-       val = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'RSNIE',
+       val = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'RSN',
                          dbus_interface=dbus.PROPERTIES_IFACE)
        wpa2 = "no"
        if val != None:
diff --git a/wpa_supplicant/gas_query.c b/wpa_supplicant/gas_query.c
new file mode 100644 (file)
index 0000000..3b736da
--- /dev/null
@@ -0,0 +1,481 @@
+/*
+ * Generic advertisement service (GAS) query
+ * Copyright (c) 2009, Atheros Communications
+ * Copyright (c) 2011, Qualcomm Atheros
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/gas.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "offchannel.h"
+#include "gas_query.h"
+
+
+#define GAS_QUERY_TIMEOUT 5
+
+
+struct gas_query_pending {
+       struct dl_list list;
+       u8 addr[ETH_ALEN];
+       u8 dialog_token;
+       u8 next_frag_id;
+       unsigned int wait_comeback:1;
+       unsigned int offchannel_tx_started:1;
+       int freq;
+       u16 status_code;
+       struct wpabuf *adv_proto;
+       struct wpabuf *resp;
+       void (*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);
+       void *ctx;
+};
+
+struct gas_query {
+       struct wpa_supplicant *wpa_s;
+       struct dl_list pending; /* struct gas_query_pending */
+};
+
+
+static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx);
+static void gas_query_timeout(void *eloop_data, void *user_ctx);
+
+
+struct gas_query * gas_query_init(struct wpa_supplicant *wpa_s)
+{
+       struct gas_query *gas;
+
+       gas = os_zalloc(sizeof(*gas));
+       if (gas == NULL)
+               return NULL;
+
+       gas->wpa_s = wpa_s;
+       dl_list_init(&gas->pending);
+
+       return gas;
+}
+
+
+static void gas_query_done(struct gas_query *gas,
+                          struct gas_query_pending *query,
+                          enum gas_query_result result)
+{
+       if (query->offchannel_tx_started)
+               offchannel_send_action_done(gas->wpa_s);
+       eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
+       eloop_cancel_timeout(gas_query_timeout, gas, query);
+       dl_list_del(&query->list);
+       query->cb(query->ctx, query->addr, query->dialog_token, result,
+                 query->adv_proto, query->resp, query->status_code);
+       wpabuf_free(query->adv_proto);
+       wpabuf_free(query->resp);
+       os_free(query);
+}
+
+
+void gas_query_deinit(struct gas_query *gas)
+{
+       struct gas_query_pending *query, *next;
+
+       if (gas == NULL)
+               return;
+
+       dl_list_for_each_safe(query, next, &gas->pending,
+                             struct gas_query_pending, list)
+               gas_query_done(gas, query, GAS_QUERY_DELETED_AT_DEINIT);
+
+       os_free(gas);
+}
+
+
+static struct gas_query_pending *
+gas_query_get_pending(struct gas_query *gas, const u8 *addr, u8 dialog_token)
+{
+       struct gas_query_pending *q;
+       dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
+               if (os_memcmp(q->addr, addr, ETH_ALEN) == 0 &&
+                   q->dialog_token == dialog_token)
+                       return q;
+       }
+       return NULL;
+}
+
+
+static int gas_query_append(struct gas_query_pending *query, const u8 *data,
+                           size_t len)
+{
+       if (wpabuf_resize(&query->resp, len) < 0) {
+               wpa_printf(MSG_DEBUG, "GAS: No memory to store the response");
+               return -1;
+       }
+       wpabuf_put_data(query->resp, data, len);
+       return 0;
+}
+
+
+static int gas_query_tx(struct gas_query *gas, struct gas_query_pending *query,
+                       struct wpabuf *req)
+{
+       int res;
+       wpa_printf(MSG_DEBUG, "GAS: Send action frame to " MACSTR " len=%u "
+                  "freq=%d", MAC2STR(query->addr),
+                  (unsigned int) wpabuf_len(req), query->freq);
+       res = offchannel_send_action(gas->wpa_s, query->freq, query->addr,
+                                    gas->wpa_s->own_addr, query->addr,
+                                    wpabuf_head(req), wpabuf_len(req), 1000,
+                                    NULL, 0);
+       if (res == 0)
+               query->offchannel_tx_started = 1;
+       return res;
+}
+
+
+static void gas_query_tx_comeback_req(struct gas_query *gas,
+                                     struct gas_query_pending *query)
+{
+       struct wpabuf *req;
+
+       req = gas_build_comeback_req(query->dialog_token);
+       if (req == NULL) {
+               gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
+               return;
+       }
+
+       if (gas_query_tx(gas, query, req) < 0) {
+               wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
+                          MACSTR, MAC2STR(query->addr));
+               gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
+       }
+
+       wpabuf_free(req);
+}
+
+
+static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx)
+{
+       struct gas_query *gas = eloop_data;
+       struct gas_query_pending *query = user_ctx;
+
+       wpa_printf(MSG_DEBUG, "GAS: Comeback timeout for request to " MACSTR,
+                  MAC2STR(query->addr));
+       gas_query_tx_comeback_req(gas, query);
+}
+
+
+static void gas_query_tx_comeback_req_delay(struct gas_query *gas,
+                                           struct gas_query_pending *query,
+                                           u16 comeback_delay)
+{
+       unsigned int secs, usecs;
+
+       secs = (comeback_delay * 1024) / 1000000;
+       usecs = comeback_delay * 1024 - secs * 1000000;
+       wpa_printf(MSG_DEBUG, "GAS: Send comeback request to " MACSTR
+                  " in %u secs %u usecs", MAC2STR(query->addr), secs, usecs);
+       eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
+       eloop_register_timeout(secs, usecs, gas_query_tx_comeback_timeout,
+                              gas, query);
+}
+
+
+static void gas_query_rx_initial(struct gas_query *gas,
+                                struct gas_query_pending *query,
+                                const u8 *adv_proto, const u8 *resp,
+                                size_t len, u16 comeback_delay)
+{
+       wpa_printf(MSG_DEBUG, "GAS: Received initial response from "
+                  MACSTR " (dialog_token=%u comeback_delay=%u)",
+                  MAC2STR(query->addr), query->dialog_token, comeback_delay);
+
+       query->adv_proto = wpabuf_alloc_copy(adv_proto, 2 + adv_proto[1]);
+       if (query->adv_proto == NULL) {
+               gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
+               return;
+       }
+
+       if (comeback_delay) {
+               query->wait_comeback = 1;
+               gas_query_tx_comeback_req_delay(gas, query, comeback_delay);
+               return;
+       }
+
+       /* Query was completed without comeback mechanism */
+       if (gas_query_append(query, resp, len) < 0) {
+               gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
+               return;
+       }
+
+       gas_query_done(gas, query, GAS_QUERY_SUCCESS);
+}
+
+
+static void gas_query_rx_comeback(struct gas_query *gas,
+                                 struct gas_query_pending *query,
+                                 const u8 *adv_proto, const u8 *resp,
+                                 size_t len, u8 frag_id, u8 more_frags,
+                                 u16 comeback_delay)
+{
+       wpa_printf(MSG_DEBUG, "GAS: Received comeback response from "
+                  MACSTR " (dialog_token=%u frag_id=%u more_frags=%u "
+                  "comeback_delay=%u)",
+                  MAC2STR(query->addr), query->dialog_token, frag_id,
+                  more_frags, comeback_delay);
+
+       if ((size_t) 2 + adv_proto[1] != wpabuf_len(query->adv_proto) ||
+           os_memcmp(adv_proto, wpabuf_head(query->adv_proto),
+                     wpabuf_len(query->adv_proto)) != 0) {
+               wpa_printf(MSG_DEBUG, "GAS: Advertisement Protocol changed "
+                          "between initial and comeback response from "
+                          MACSTR, MAC2STR(query->addr));
+               gas_query_done(gas, query, GAS_QUERY_PEER_ERROR);
+               return;
+       }
+
+       if (comeback_delay) {
+               if (frag_id) {
+                       wpa_printf(MSG_DEBUG, "GAS: Invalid comeback response "
+                                  "with non-zero frag_id and comeback_delay "
+                                  "from " MACSTR, MAC2STR(query->addr));
+                       gas_query_done(gas, query, GAS_QUERY_PEER_ERROR);
+                       return;
+               }
+               gas_query_tx_comeback_req_delay(gas, query, comeback_delay);
+               return;
+       }
+
+       if (frag_id != query->next_frag_id) {
+               wpa_printf(MSG_DEBUG, "GAS: Unexpected frag_id in response "
+                          "from " MACSTR, MAC2STR(query->addr));
+               gas_query_done(gas, query, GAS_QUERY_PEER_ERROR);
+               return;
+       }
+       query->next_frag_id++;
+
+       if (gas_query_append(query, resp, len) < 0) {
+               gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
+               return;
+       }
+
+       if (more_frags) {
+               gas_query_tx_comeback_req(gas, query);
+               return;
+       }
+
+       gas_query_done(gas, query, GAS_QUERY_SUCCESS);
+}
+
+
+int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa,
+                const u8 *bssid, const u8 *data, size_t len, int freq)
+{
+       struct gas_query_pending *query;
+       u8 action, dialog_token, frag_id = 0, more_frags = 0;
+       u16 comeback_delay, resp_len;
+       const u8 *pos, *adv_proto;
+
+       if (gas == NULL || len < 4)
+               return -1;
+
+       pos = data;
+       action = *pos++;
+       dialog_token = *pos++;
+
+       if (action != WLAN_PA_GAS_INITIAL_RESP &&
+           action != WLAN_PA_GAS_COMEBACK_RESP)
+               return -1; /* Not a GAS response */
+
+       query = gas_query_get_pending(gas, sa, dialog_token);
+       if (query == NULL) {
+               wpa_printf(MSG_DEBUG, "GAS: No pending query found for " MACSTR
+                          " dialog token %u", MAC2STR(sa), dialog_token);
+               return -1;
+       }
+
+       if (query->wait_comeback && action == WLAN_PA_GAS_INITIAL_RESP) {
+               wpa_printf(MSG_DEBUG, "GAS: Unexpected initial response from "
+                          MACSTR " dialog token %u when waiting for comeback "
+                          "response", MAC2STR(sa), dialog_token);
+               return 0;
+       }
+
+       if (!query->wait_comeback && action == WLAN_PA_GAS_COMEBACK_RESP) {
+               wpa_printf(MSG_DEBUG, "GAS: Unexpected comeback response from "
+                          MACSTR " dialog token %u when waiting for initial "
+                          "response", MAC2STR(sa), dialog_token);
+               return 0;
+       }
+
+       query->status_code = WPA_GET_LE16(pos);
+       pos += 2;
+
+       if (query->status_code != WLAN_STATUS_SUCCESS) {
+               wpa_printf(MSG_DEBUG, "GAS: Query to " MACSTR " dialog token "
+                          "%u failed - status code %u",
+                          MAC2STR(sa), dialog_token, query->status_code);
+               gas_query_done(gas, query, GAS_QUERY_FAILURE);
+               return 0;
+       }
+
+       if (action == WLAN_PA_GAS_COMEBACK_RESP) {
+               if (pos + 1 > data + len)
+                       return 0;
+               frag_id = *pos & 0x7f;
+               more_frags = (*pos & 0x80) >> 7;
+               pos++;
+       }
+
+       /* Comeback Delay */
+       if (pos + 2 > data + len)
+               return 0;
+       comeback_delay = WPA_GET_LE16(pos);
+       pos += 2;
+
+       /* Advertisement Protocol element */
+       if (pos + 2 > data + len || pos + 2 + pos[1] > data + len) {
+               wpa_printf(MSG_DEBUG, "GAS: No room for Advertisement "
+                          "Protocol element in the response from " MACSTR,
+                          MAC2STR(sa));
+               return 0;
+       }
+
+       if (*pos != WLAN_EID_ADV_PROTO) {
+               wpa_printf(MSG_DEBUG, "GAS: Unexpected Advertisement "
+                          "Protocol element ID %u in response from " MACSTR,
+                          *pos, MAC2STR(sa));
+               return 0;
+       }
+
+       adv_proto = pos;
+       pos += 2 + pos[1];
+
+       /* Query Response Length */
+       if (pos + 2 > data + len) {
+               wpa_printf(MSG_DEBUG, "GAS: No room for GAS Response Length");
+               return 0;
+       }
+       resp_len = WPA_GET_LE16(pos);
+       pos += 2;
+
+       if (pos + resp_len > data + len) {
+               wpa_printf(MSG_DEBUG, "GAS: Truncated Query Response in "
+                          "response from " MACSTR, MAC2STR(sa));
+               return 0;
+       }
+
+       if (pos + resp_len < data + len) {
+               wpa_printf(MSG_DEBUG, "GAS: Ignore %u octets of extra data "
+                          "after Query Response from " MACSTR,
+                          (unsigned int) (data + len - pos - resp_len),
+                          MAC2STR(sa));
+       }
+
+       if (action == WLAN_PA_GAS_COMEBACK_RESP)
+               gas_query_rx_comeback(gas, query, adv_proto, pos, resp_len,
+                                     frag_id, more_frags, comeback_delay);
+       else
+               gas_query_rx_initial(gas, query, adv_proto, pos, resp_len,
+                                    comeback_delay);
+
+       return 0;
+}
+
+
+static void gas_query_timeout(void *eloop_data, void *user_ctx)
+{
+       struct gas_query *gas = eloop_data;
+       struct gas_query_pending *query = user_ctx;
+
+       wpa_printf(MSG_DEBUG, "GAS: No response received for query to " MACSTR,
+                  MAC2STR(query->addr));
+       gas_query_done(gas, query, GAS_QUERY_TIMEOUT);
+}
+
+
+static int gas_query_dialog_token_available(struct gas_query *gas,
+                                           const u8 *dst, u8 dialog_token)
+{
+       struct gas_query_pending *q;
+       dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
+               if (os_memcmp(dst, q->addr, ETH_ALEN) == 0 &&
+                   dialog_token == q->dialog_token)
+                       return 0;
+       }
+
+       return 1;
+}
+
+
+int gas_query_req(struct gas_query *gas, const u8 *dst, int freq,
+                 struct wpabuf *req,
+                 void (*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),
+                 void *ctx)
+{
+       struct gas_query_pending *query;
+       int dialog_token;
+
+       if (wpabuf_len(req) < 3)
+               return -1;
+
+       for (dialog_token = 0; dialog_token < 256; dialog_token++) {
+               if (gas_query_dialog_token_available(gas, dst, dialog_token))
+                       break;
+       }
+       if (dialog_token == 256)
+               return -1; /* Too many pending queries */
+
+       query = os_zalloc(sizeof(*query));
+       if (query == NULL)
+               return -1;
+
+       os_memcpy(query->addr, dst, ETH_ALEN);
+       query->dialog_token = dialog_token;
+       query->freq = freq;
+       query->cb = cb;
+       query->ctx = ctx;
+       dl_list_add(&gas->pending, &query->list);
+
+       *(wpabuf_mhead_u8(req) + 2) = dialog_token;
+
+       wpa_printf(MSG_DEBUG, "GAS: Starting request for " MACSTR
+                  " dialog_token %u", MAC2STR(dst), dialog_token);
+       if (gas_query_tx(gas, query, req) < 0) {
+               wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
+                          MACSTR, MAC2STR(query->addr));
+               os_free(query);
+               return -1;
+       }
+
+       eloop_register_timeout(GAS_QUERY_TIMEOUT, 0, gas_query_timeout,
+                              gas, query);
+
+       return dialog_token;
+}
+
+
+void gas_query_cancel(struct gas_query *gas, const u8 *dst, u8 dialog_token)
+{
+       struct gas_query_pending *query;
+
+       query = gas_query_get_pending(gas, dst, dialog_token);
+       if (query)
+               gas_query_done(gas, query, GAS_QUERY_CANCELLED);
+
+}
diff --git a/wpa_supplicant/gas_query.h b/wpa_supplicant/gas_query.h
new file mode 100644 (file)
index 0000000..64c3825
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Generic advertisement service (GAS) query
+ * Copyright (c) 2009, Atheros Communications
+ * Copyright (c) 2011, Qualcomm Atheros
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef GAS_QUERY_H
+#define GAS_QUERY_H
+
+struct gas_query;
+
+#ifdef CONFIG_GAS
+
+struct gas_query * gas_query_init(struct wpa_supplicant *wpa_s);
+void gas_query_deinit(struct gas_query *gas);
+int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa,
+                const u8 *bssid, const u8 *data, size_t len, int freq);
+
+enum gas_query_result {
+       GAS_QUERY_SUCCESS,
+       GAS_QUERY_FAILURE,
+       GAS_QUERY_TIMEOUT,
+       GAS_QUERY_PEER_ERROR,
+       GAS_QUERY_INTERNAL_ERROR,
+       GAS_QUERY_CANCELLED,
+       GAS_QUERY_DELETED_AT_DEINIT
+};
+
+int gas_query_req(struct gas_query *gas, const u8 *dst, int freq,
+                 struct wpabuf *req,
+                 void (*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),
+                 void *ctx);
+void gas_query_cancel(struct gas_query *gas, const u8 *dst, u8 dialog_token);
+
+#else /* CONFIG_GAS */
+
+static inline struct gas_query * gas_query_init(struct wpa_supplicant *wpa_s)
+{
+       return (void *) 1;
+}
+
+static inline void gas_query_deinit(struct gas_query *gas)
+{
+}
+
+#endif /* CONFIG_GAS */
+
+
+#endif /* GAS_QUERY_H */
index 0e33253..fc1a586 100644 (file)
@@ -39,6 +39,13 @@ static void supp_set_state(void *ctx, enum wpa_states 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)
 {
@@ -125,6 +132,8 @@ static int supp_set_key(void *ctx, enum wpa_alg alg,
                }
        }
 
+       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);
 }
@@ -153,6 +162,12 @@ static void supp_cancel_auth_timeout(void *ctx)
 }
 
 
+static void supp_deauthenticate(void * ctx, int reason_code)
+{
+       wpa_printf(MSG_DEBUG, "SUPP: %s (TODO)", __func__);
+}
+
+
 int ibss_rsn_supp_init(struct ibss_rsn_peer *peer, const u8 *own_addr,
                       const u8 *psk)
 {
@@ -163,6 +178,7 @@ int ibss_rsn_supp_init(struct ibss_rsn_peer *peer, const u8 *own_addr,
        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;
@@ -170,6 +186,7 @@ int ibss_rsn_supp_init(struct ibss_rsn_peer *peer, const u8 *own_addr,
        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");
@@ -273,6 +290,24 @@ static int auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg,
 }
 
 
+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 int ibss_rsn_auth_init_group(struct ibss_rsn *ibss_rsn,
                                    const u8 *own_addr)
 {
@@ -288,6 +323,7 @@ static int ibss_rsn_auth_init_group(struct ibss_rsn *ibss_rsn,
        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;
@@ -295,6 +331,7 @@ static int ibss_rsn_auth_init_group(struct ibss_rsn *ibss_rsn,
        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;
 
        ibss_rsn->auth_group = wpa_init(own_addr, &conf, &cb);
        if (ibss_rsn->auth_group == NULL) {
@@ -302,6 +339,8 @@ static int ibss_rsn_auth_init_group(struct ibss_rsn *ibss_rsn,
                return -1;
        }
 
+       wpa_init_keys(ibss_rsn->auth_group);
+
        return 0;
 }
 
@@ -341,6 +380,18 @@ int ibss_rsn_start(struct ibss_rsn *ibss_rsn, const u8 *addr)
 {
        struct ibss_rsn_peer *peer;
 
+       if (ibss_rsn == NULL)
+               return -1;
+
+       for (peer = ibss_rsn->peers; peer; peer = peer->next) {
+               if (os_memcmp(addr, peer->addr, ETH_ALEN) == 0) {
+                       wpa_printf(MSG_DEBUG, "RSN: IBSS Authenticator and "
+                                  "Supplicant for peer " MACSTR " already "
+                                  "running", MAC2STR(addr));
+                       return 0;
+               }
+       }
+
        wpa_printf(MSG_DEBUG, "RSN: Starting IBSS Authenticator and "
                   "Supplicant for peer " MACSTR, MAC2STR(addr));
 
@@ -369,6 +420,46 @@ int ibss_rsn_start(struct ibss_rsn *ibss_rsn, const u8 *addr)
 }
 
 
+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;
@@ -483,6 +574,9 @@ int ibss_rsn_rx_eapol(struct ibss_rsn *ibss_rsn, const u8 *src_addr,
 {
        struct ibss_rsn_peer *peer;
 
+       if (ibss_rsn == NULL)
+               return -1;
+
        for (peer = ibss_rsn->peers; peer; peer = peer->next) {
                if (os_memcmp(src_addr, peer->addr, ETH_ALEN) == 0)
                        return ibss_rsn_process_rx_eapol(ibss_rsn, peer,
@@ -506,5 +600,7 @@ int ibss_rsn_rx_eapol(struct ibss_rsn *ibss_rsn, const u8 *src_addr,
 
 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);
 }
index 11e63ad..dbc889f 100644 (file)
@@ -42,6 +42,7 @@ struct ibss_rsn {
 struct ibss_rsn * ibss_rsn_init(struct wpa_supplicant *wpa_s);
 void ibss_rsn_deinit(struct ibss_rsn *ibss_rsn);
 int ibss_rsn_start(struct ibss_rsn *ibss_rsn, const u8 *addr);
+void ibss_rsn_stop(struct ibss_rsn *ibss_rsn, const u8 *peermac);
 int ibss_rsn_rx_eapol(struct ibss_rsn *ibss_rsn, const u8 *src_addr,
                      const u8 *buf, size_t len);
 void ibss_rsn_set_psk(struct ibss_rsn *ibss_rsn, const u8 *psk);
diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c
new file mode 100644 (file)
index 0000000..cd074a3
--- /dev/null
@@ -0,0 +1,1138 @@
+/*
+ * Interworking (IEEE 802.11u)
+ * Copyright (c) 2011, Qualcomm Atheros
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/gas.h"
+#include "common/wpa_ctrl.h"
+#include "drivers/driver.h"
+#include "eap_common/eap_defs.h"
+#include "eap_peer/eap_methods.h"
+#include "wpa_supplicant_i.h"
+#include "config.h"
+#include "bss.h"
+#include "scan.h"
+#include "notify.h"
+#include "gas_query.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 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;
+
+       anqp_resp_cb(wpa_s, dst, dialog_token, result, adv_proto, resp,
+                    status_code);
+       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[] = {
+               ANQP_CAPABILITY_LIST,
+               ANQP_VENUE_NAME,
+               ANQP_NETWORK_AUTH_TYPE,
+               ANQP_ROAMING_CONSORTIUM,
+               ANQP_IP_ADDR_TYPE_AVAILABILITY,
+               ANQP_NAI_REALM,
+               ANQP_3GPP_CELLULAR_NETWORK,
+               ANQP_DOMAIN_NAME
+       };
+       struct wpabuf *extra = NULL;
+
+       wpa_printf(MSG_DEBUG, "Interworking: ANQP Query Request to " MACSTR,
+                  MAC2STR(bss->bssid));
+
+       buf = anqp_build_req(info_ids, sizeof(info_ids) / sizeof(info_ids[0]),
+                            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_printf(MSG_DEBUG, "ANQP: Failed to send Query Request");
+               ret = -1;
+       } else
+               wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token "
+                          "%u", res);
+
+       wpabuf_free(buf);
+       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 = os_malloc(realm_len + 1);
+       if (r->realm == NULL)
+               return NULL;
+       os_memcpy(r->realm, pos, realm_len);
+       r->realm[realm_len] = '\0';
+       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_zalloc(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;
+
+       if (anqp == NULL || wpabuf_len(anqp) < 2)
+               return NULL;
+
+       pos = wpabuf_head_u8(anqp);
+       end = pos + wpabuf_len(anqp);
+       num = WPA_GET_LE16(pos);
+       wpa_printf(MSG_DEBUG, "NAI Realm Count: %u", num);
+       pos += 2;
+
+       if (num * 5 > end - pos) {
+               wpa_printf(MSG_DEBUG, "Invalid NAI Realm Count %u - not "
+                          "enough data (%u octets) for that many realms",
+                          num, (unsigned int) (end - pos));
+               return NULL;
+       }
+
+       realm = os_zalloc(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 nai_realm_eap *eap)
+{
+       if (eap_get_name(EAP_VENDOR_IETF, eap->method) == NULL)
+               return 0; /* method not supported */
+
+       if (eap->method != EAP_TYPE_TTLS && eap->method != EAP_TYPE_PEAP) {
+               /* Only tunneled methods with username/password supported */
+               return 0;
+       }
+
+       if (eap->method == EAP_TYPE_PEAP &&
+           eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL)
+               return 0;
+
+       if (eap->method == EAP_TYPE_TTLS) {
+               if (eap->inner_method == 0 && eap->inner_non_eap == 0)
+                       return 0;
+               if (eap->inner_method &&
+                   eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL)
+                       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)
+                       return 0;
+       }
+
+       if (eap->inner_method &&
+           eap->inner_method != EAP_TYPE_GTC &&
+           eap->inner_method != EAP_TYPE_MSCHAPV2)
+               return 0;
+
+       return 1;
+}
+
+
+struct nai_realm_eap * nai_realm_find_eap(struct wpa_supplicant *wpa_s,
+                                         struct nai_realm *realm)
+{
+       u8 e;
+
+       if (wpa_s->conf->home_username == NULL ||
+           wpa_s->conf->home_username[0] == '\0' ||
+           wpa_s->conf->home_password == NULL ||
+           wpa_s->conf->home_password[0] == '\0')
+               return NULL;
+
+       for (e = 0; e < realm->eap_count; e++) {
+               struct nai_realm_eap *eap = &realm->eap[e];
+               if (nai_realm_cred_username(eap))
+                       return eap;
+       }
+
+       return NULL;
+}
+
+
+#ifdef INTERWORKING_3GPP
+
+static int plmn_id_match(struct wpabuf *anqp, const char *imsi)
+{
+       const char *sep;
+       u8 plmn[3];
+       const u8 *pos, *end;
+       u8 udhl;
+
+       sep = os_strchr(imsi, '-');
+       if (sep == NULL || (sep - imsi != 5 && sep - imsi != 6))
+               return 0;
+
+       /* See Annex A of 3GPP TS 24.234 v8.1.0 for description */
+       plmn[0] = (imsi[0] - '0') | ((imsi[1] - '0') << 4);
+       plmn[1] = imsi[2] - '0';
+       if (sep - imsi == 6)
+               plmn[1] |= (imsi[5] - '0') << 4;
+       else
+               plmn[1] |= 0xf0;
+       plmn[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;
+
+       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;
+                       num = *pos++;
+                       for (i = 0; i < num; i++) {
+                               if (pos + 3 > end)
+                                       break;
+                               if (os_memcmp(pos, plmn, 3) == 0)
+                                       return 1; /* Found matching PLMN */
+                       }
+               }
+
+               pos = l_end;
+       }
+
+       return 0;
+}
+
+
+static int set_root_nai(struct wpa_ssid *ssid, const char *imsi, char prefix)
+{
+       const char *sep, *msin;
+       char nai[100], *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 == NULL)
+               return -1;
+       plmn_len = sep - imsi;
+       if (plmn_len != 5 && plmn_len != 6)
+               return -1;
+       msin = sep + 1;
+       msin_len = os_strlen(msin);
+
+       pos = nai;
+       end = pos + sizeof(nai);
+       *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];
+       }
+       pos += os_snprintf(pos, end - pos, ".mcc%c%c%c.3gppnetwork.org",
+                          imsi[0], imsi[1], imsi[2]);
+
+       return wpa_config_set_quoted(ssid, "identity", nai);
+}
+
+#endif /* INTERWORKING_3GPP */
+
+
+static int interworking_connect_3gpp(struct wpa_supplicant *wpa_s,
+                                    struct wpa_bss *bss)
+{
+#ifdef INTERWORKING_3GPP
+       struct wpa_ssid *ssid;
+       const u8 *ie;
+
+       ie = wpa_bss_get_ie(bss, WLAN_EID_SSID);
+       if (ie == NULL)
+               return -1;
+       wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR " (3GPP)",
+                  MAC2STR(bss->bssid));
+
+       ssid = wpa_config_add_network(wpa_s->conf);
+       if (ssid == NULL)
+               return -1;
+
+       wpas_notify_network_added(wpa_s, ssid);
+       wpa_config_set_network_defaults(ssid);
+       ssid->temporary = 1;
+       ssid->ssid = os_zalloc(ie[1] + 1);
+       if (ssid->ssid == NULL)
+               goto fail;
+       os_memcpy(ssid->ssid, ie + 2, ie[1]);
+       ssid->ssid_len = ie[1];
+
+       /* TODO: figure out whether to use EAP-SIM, EAP-AKA, or EAP-AKA' */
+       if (wpa_config_set(ssid, "eap", "SIM", 0) < 0) {
+               wpa_printf(MSG_DEBUG, "EAP-SIM not supported");
+               goto fail;
+       }
+       if (set_root_nai(ssid, wpa_s->conf->home_imsi, '1') < 0) {
+               wpa_printf(MSG_DEBUG, "Failed to set Root NAI");
+               goto fail;
+       }
+
+       if (wpa_s->conf->home_milenage && wpa_s->conf->home_milenage[0]) {
+               if (wpa_config_set_quoted(ssid, "password",
+                                         wpa_s->conf->home_milenage) < 0)
+                       goto fail;
+       } else {
+               /* TODO: PIN */
+               if (wpa_config_set_quoted(ssid, "pcsc", "") < 0)
+                       goto fail;
+       }
+
+       if (wpa_s->conf->home_password && wpa_s->conf->home_password[0] &&
+           wpa_config_set_quoted(ssid, "password", wpa_s->conf->home_password)
+           < 0)
+               goto fail;
+
+       wpa_supplicant_select_network(wpa_s, ssid);
+
+       return 0;
+
+fail:
+       wpas_notify_network_removed(wpa_s, ssid);
+       wpa_config_remove_network(wpa_s->conf, ssid->id);
+#endif /* INTERWORKING_3GPP */
+       return -1;
+}
+
+
+int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+{
+       struct wpa_ssid *ssid;
+       struct nai_realm *realm;
+       struct nai_realm_eap *eap = NULL;
+       u16 count, i;
+       char buf[100];
+       const u8 *ie;
+
+       if (bss == NULL)
+               return -1;
+       ie = wpa_bss_get_ie(bss, WLAN_EID_SSID);
+       if (ie == NULL || ie[1] == 0) {
+               wpa_printf(MSG_DEBUG, "Interworking: No SSID known for "
+                          MACSTR, MAC2STR(bss->bssid));
+               return -1;
+       }
+
+       realm = nai_realm_parse(bss->anqp_nai_realm, &count);
+       if (realm == NULL) {
+               wpa_printf(MSG_DEBUG, "Interworking: Could not parse NAI "
+                          "Realm list from " MACSTR, MAC2STR(bss->bssid));
+               count = 0;
+       }
+
+       for (i = 0; i < count; i++) {
+               if (!nai_realm_match(&realm[i], wpa_s->conf->home_realm))
+                       continue;
+               eap = nai_realm_find_eap(wpa_s, &realm[i]);
+               if (eap)
+                       break;
+       }
+
+       if (!eap) {
+               if (interworking_connect_3gpp(wpa_s, bss) == 0) {
+                       if (realm)
+                               nai_realm_free(realm, count);
+                       return 0;
+               }
+
+               wpa_printf(MSG_DEBUG, "Interworking: No matching credentials "
+                          "and EAP method found for " MACSTR,
+                          MAC2STR(bss->bssid));
+               nai_realm_free(realm, count);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR,
+                  MAC2STR(bss->bssid));
+
+       ssid = wpa_config_add_network(wpa_s->conf);
+       if (ssid == NULL) {
+               nai_realm_free(realm, count);
+               return -1;
+       }
+       wpas_notify_network_added(wpa_s, ssid);
+       wpa_config_set_network_defaults(ssid);
+       ssid->temporary = 1;
+       ssid->ssid = os_zalloc(ie[1] + 1);
+       if (ssid->ssid == NULL)
+               goto fail;
+       os_memcpy(ssid->ssid, ie + 2, ie[1]);
+       ssid->ssid_len = ie[1];
+
+       if (wpa_config_set(ssid, "eap", eap_get_name(EAP_VENDOR_IETF,
+                                                    eap->method), 0) < 0)
+               goto fail;
+
+       if (wpa_s->conf->home_username && wpa_s->conf->home_username[0] &&
+           wpa_config_set_quoted(ssid, "identity",
+                                 wpa_s->conf->home_username) < 0)
+               goto fail;
+
+       if (wpa_s->conf->home_password && wpa_s->conf->home_password[0] &&
+           wpa_config_set_quoted(ssid, "password", wpa_s->conf->home_password)
+           < 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;
+               }
+               break;
+       case EAP_TYPE_PEAP:
+               os_snprintf(buf, sizeof(buf), "\"auth=%s\"",
+                           eap_get_name(EAP_VENDOR_IETF, eap->inner_method));
+               if (wpa_config_set(ssid, "phase2", buf, 0) < 0)
+                       goto fail;
+               break;
+       }
+
+       if (wpa_s->conf->home_ca_cert && wpa_s->conf->home_ca_cert[0] &&
+           wpa_config_set_quoted(ssid, "ca_cert", wpa_s->conf->home_ca_cert) <
+           0)
+               goto fail;
+
+       nai_realm_free(realm, count);
+
+       wpa_supplicant_select_network(wpa_s, ssid);
+
+       return 0;
+
+fail:
+       wpas_notify_network_removed(wpa_s, ssid);
+       wpa_config_remove_network(wpa_s->conf, ssid->id);
+       nai_realm_free(realm, count);
+       return -1;
+}
+
+
+static int interworking_credentials_available_3gpp(
+       struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+{
+       int ret = 0;
+
+#ifdef INTERWORKING_3GPP
+       if (bss->anqp_3gpp == NULL)
+               return ret;
+
+       if (wpa_s->conf->home_imsi == NULL || !wpa_s->conf->home_imsi[0] ||
+           wpa_s->conf->home_milenage == NULL ||
+           !wpa_s->conf->home_milenage[0])
+               return ret;
+
+       wpa_printf(MSG_DEBUG, "Interworking: Parsing 3GPP info from " MACSTR,
+                  MAC2STR(bss->bssid));
+       ret = plmn_id_match(bss->anqp_3gpp, wpa_s->conf->home_imsi);
+       wpa_printf(MSG_DEBUG, "PLMN match %sfound", ret ? "" : "not ");
+#endif /* INTERWORKING_3GPP */
+       return ret;
+}
+
+
+static int interworking_credentials_available_realm(
+       struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+{
+       struct nai_realm *realm;
+       u16 count, i;
+       int found = 0;
+
+       if (bss->anqp_nai_realm == NULL)
+               return 0;
+
+       if (wpa_s->conf->home_realm == NULL)
+               return 0;
+
+       wpa_printf(MSG_DEBUG, "Interworking: Parsing NAI Realm list from "
+                  MACSTR, MAC2STR(bss->bssid));
+       realm = nai_realm_parse(bss->anqp_nai_realm, &count);
+       if (realm == NULL) {
+               wpa_printf(MSG_DEBUG, "Interworking: Could not parse NAI "
+                          "Realm list from " MACSTR, MAC2STR(bss->bssid));
+               return 0;
+       }
+
+       for (i = 0; i < count; i++) {
+               if (!nai_realm_match(&realm[i], wpa_s->conf->home_realm))
+                       continue;
+               if (nai_realm_find_eap(wpa_s, &realm[i])) {
+                       found++;
+                       break;
+               }
+       }
+
+       nai_realm_free(realm, count);
+
+       return found;
+}
+
+
+static int interworking_credentials_available(struct wpa_supplicant *wpa_s,
+                                             struct wpa_bss *bss)
+{
+       return interworking_credentials_available_realm(wpa_s, bss) ||
+               interworking_credentials_available_3gpp(wpa_s, bss);
+}
+
+
+static void interworking_select_network(struct wpa_supplicant *wpa_s)
+{
+       struct wpa_bss *bss, *selected = NULL;
+       unsigned int count = 0;
+
+       wpa_s->network_select = 0;
+
+       dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+               if (!interworking_credentials_available(wpa_s, bss))
+                       continue;
+               count++;
+               wpa_msg(wpa_s, MSG_INFO, INTERWORKING_AP MACSTR,
+                       MAC2STR(bss->bssid));
+               if (selected == NULL && wpa_s->auto_select)
+                       selected = bss;
+       }
+
+       if (count == 0) {
+               wpa_msg(wpa_s, MSG_INFO, INTERWORKING_NO_MATCH "No network "
+                       "with matching credentials found");
+       }
+
+       if (selected)
+               interworking_connect(wpa_s, selected);
+}
+
+
+static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s)
+{
+       struct wpa_bss *bss;
+       int found = 0;
+       const u8 *ie;
+
+       if (!wpa_s->fetch_anqp_in_progress)
+               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 (!(bss->flags & WPA_BSS_ANQP_FETCH_TRIED)) {
+                       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) {
+               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);
+       }
+}
+
+
+static 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;
+       interworking_next_anqp_fetch(wpa_s);
+}
+
+
+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;
+
+       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)
+{
+       struct wpabuf *buf;
+       int ret = 0;
+       int freq;
+       struct wpa_bss *bss;
+       int res;
+
+       freq = wpa_s->assoc_freq;
+       bss = wpa_bss_get_bssid(wpa_s, dst);
+       if (bss)
+               freq = bss->freq;
+       if (freq <= 0)
+               return -1;
+
+       wpa_printf(MSG_DEBUG, "ANQP: Query Request to " MACSTR " for %u id(s)",
+                  MAC2STR(dst), (unsigned int) num_ids);
+
+       buf = anqp_build_req(info_ids, num_ids, NULL);
+       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");
+               ret = -1;
+       } else
+               wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token "
+                          "%u", res);
+
+       wpabuf_free(buf);
+       return ret;
+}
+
+
+static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s,
+                                           const u8 *sa, u16 info_id,
+                                           const u8 *data, size_t slen)
+{
+       const u8 *pos = data;
+       struct wpa_bss *bss = wpa_bss_get_bssid(wpa_s, sa);
+
+       switch (info_id) {
+       case ANQP_CAPABILITY_LIST:
+               wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+                       " ANQP Capability list", MAC2STR(sa));
+               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 (bss) {
+                       wpabuf_free(bss->anqp_venue_name);
+                       bss->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 (bss) {
+                       wpabuf_free(bss->anqp_network_auth_type);
+                       bss->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 (bss) {
+                       wpabuf_free(bss->anqp_roaming_consortium);
+                       bss->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 (bss) {
+                       wpabuf_free(bss->anqp_ip_addr_type_availability);
+                       bss->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 (bss) {
+                       wpabuf_free(bss->anqp_nai_realm);
+                       bss->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 (bss) {
+                       wpabuf_free(bss->anqp_3gpp);
+                       bss->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 (bss) {
+                       wpabuf_free(bss->anqp_domain_name);
+                       bss->anqp_domain_name = wpabuf_alloc_copy(pos, slen);
+               }
+               break;
+       case ANQP_VENDOR_SPECIFIC:
+               if (slen < 3)
+                       return;
+
+               switch (WPA_GET_BE24(pos)) {
+               default:
+                       wpa_printf(MSG_DEBUG, "Interworking: Unsupported "
+                                  "vendor-specific ANQP OUI %06x",
+                                  WPA_GET_BE24(pos));
+                       return;
+               }
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "Interworking: Unsupported ANQP Info ID "
+                          "%u", info_id);
+               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;
+
+       if (result != GAS_QUERY_SUCCESS)
+               return;
+
+       pos = wpabuf_head(adv_proto);
+       if (wpabuf_len(adv_proto) < 4 || pos[0] != WLAN_EID_ADV_PROTO ||
+           pos[1] < 2 || pos[3] != ACCESS_NETWORK_QUERY_PROTOCOL) {
+               wpa_printf(MSG_DEBUG, "ANQP: Unexpected Advertisement "
+                          "Protocol in response");
+               return;
+       }
+
+       pos = wpabuf_head(resp);
+       end = pos + wpabuf_len(resp);
+
+       while (pos < end) {
+               if (pos + 4 > end) {
+                       wpa_printf(MSG_DEBUG, "ANQP: Invalid element");
+                       break;
+               }
+               info_id = WPA_GET_LE16(pos);
+               pos += 2;
+               slen = WPA_GET_LE16(pos);
+               pos += 2;
+               if (pos + slen > end) {
+                       wpa_printf(MSG_DEBUG, "ANQP: Invalid element length "
+                                  "for Info ID %u", info_id);
+                       break;
+               }
+               interworking_parse_rx_anqp_resp(wpa_s, dst, info_id, pos,
+                                               slen);
+               pos += slen;
+       }
+}
+
+
+static void interworking_scan_res_handler(struct wpa_supplicant *wpa_s,
+                                         struct wpa_scan_results *scan_res)
+{
+       wpa_printf(MSG_DEBUG, "Interworking: Scan results available - start "
+                  "ANQP fetch");
+       interworking_start_fetch_anqp(wpa_s);
+}
+
+
+int interworking_select(struct wpa_supplicant *wpa_s, int auto_select)
+{
+       interworking_stop_fetch_anqp(wpa_s);
+       wpa_s->network_select = 1;
+       wpa_s->auto_select = !!auto_select;
+       wpa_printf(MSG_DEBUG, "Interworking: Start scan for network "
+                  "selection");
+       wpa_s->scan_res_handler = interworking_scan_res_handler;
+       wpa_s->scan_req = 2;
+       wpa_supplicant_req_scan(wpa_s, 0, 0);
+
+       return 0;
+}
diff --git a/wpa_supplicant/interworking.h b/wpa_supplicant/interworking.h
new file mode 100644 (file)
index 0000000..247df30
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Interworking (IEEE 802.11u)
+ * Copyright (c) 2011, Qualcomm Atheros
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef INTERWORKING_H
+#define INTERWORKING_H
+
+enum gas_query_result;
+
+int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst,
+                 u16 info_ids[], size_t num_ids);
+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);
+int interworking_fetch_anqp(struct wpa_supplicant *wpa_s);
+void interworking_stop_fetch_anqp(struct wpa_supplicant *wpa_s);
+int interworking_select(struct wpa_supplicant *wpa_s, int auto_select);
+int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss);
+
+#endif /* INTERWORKING_H */
index c0aa59c..e196f3c 100644 (file)
@@ -33,7 +33,8 @@ static void usage(void)
               "[-g<global ctrl>] \\\n"
               "        -i<ifname> -c<config file> [-C<ctrl>] [-D<driver>] "
               "[-p<driver_param>] \\\n"
-              "        [-b<br_ifname>] [-f<debug file>] \\\n"
+              "        [-b<br_ifname>] [-f<debug file>] [-e<entropy file>] "
+              "\\\n"
               "        [-o<override driver>] [-O<override ctrl>] \\\n"
               "        [-N -i<ifname> -c<conf> [-C<ctrl>] "
               "[-D<driver>] \\\n"
@@ -56,7 +57,8 @@ static void usage(void)
               "  -C = ctrl_interface parameter (only used if -c is not)\n"
               "  -i = interface name\n"
               "  -d = increase debugging verbosity (-dd even more)\n"
-              "  -D = driver name (can be multiple drivers: nl80211,wext)\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 */
@@ -143,7 +145,7 @@ int main(int argc, char *argv[])
        wpa_supplicant_fd_workaround();
 
        for (;;) {
-               c = getopt(argc, argv, "b:Bc:C:D:df:g:hi:KLNo:O:p:P:qstuvW");
+               c = getopt(argc, argv, "b:Bc:C:D:de:f:g:hi:KLNo:O:p:P:qstuvW");
                if (c < 0)
                        break;
                switch (c) {
@@ -172,6 +174,9 @@ int main(int argc, char *argv[])
                        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;
diff --git a/wpa_supplicant/mlme.c b/wpa_supplicant/mlme.c
deleted file mode 100644 (file)
index eb60ac5..0000000
+++ /dev/null
@@ -1,3198 +0,0 @@
-/*
- * WPA Supplicant - Client mode MLME
- * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi>
- * Copyright (c) 2004, Instant802 Networks, Inc.
- * Copyright (c) 2005-2006, Devicescape Software, Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
- */
-
-#include "includes.h"
-
-#include "common.h"
-#include "eloop.h"
-#include "config_ssid.h"
-#include "wpa_supplicant_i.h"
-#include "notify.h"
-#include "driver_i.h"
-#include "rsn_supp/wpa.h"
-#include "common/ieee802_11_defs.h"
-#include "common/ieee802_11_common.h"
-#include "mlme.h"
-
-
-/* Timeouts and intervals in milliseconds */
-#define IEEE80211_AUTH_TIMEOUT (200)
-#define IEEE80211_AUTH_MAX_TRIES 3
-#define IEEE80211_ASSOC_TIMEOUT (200)
-#define IEEE80211_ASSOC_MAX_TRIES 3
-#define IEEE80211_MONITORING_INTERVAL (2000)
-#define IEEE80211_PROBE_INTERVAL (60000)
-#define IEEE80211_RETRY_AUTH_INTERVAL (1000)
-#define IEEE80211_SCAN_INTERVAL (2000)
-#define IEEE80211_SCAN_INTERVAL_SLOW (15000)
-#define IEEE80211_IBSS_JOIN_TIMEOUT (20000)
-
-#define IEEE80211_PROBE_DELAY (33)
-#define IEEE80211_CHANNEL_TIME (33)
-#define IEEE80211_PASSIVE_CHANNEL_TIME (200)
-#define IEEE80211_SCAN_RESULT_EXPIRE (10000)
-#define IEEE80211_IBSS_MERGE_INTERVAL (30000)
-#define IEEE80211_IBSS_INACTIVITY_LIMIT (60000)
-
-#define IEEE80211_IBSS_MAX_STA_ENTRIES 128
-
-
-#define IEEE80211_FC(type, stype) host_to_le16((type << 2) | (stype << 4))
-
-
-struct ieee80211_sta_bss {
-       struct ieee80211_sta_bss *next;
-       struct ieee80211_sta_bss *hnext;
-
-       u8 bssid[ETH_ALEN];
-       u8 ssid[MAX_SSID_LEN];
-       size_t ssid_len;
-       u16 capability; /* host byte order */
-       int hw_mode;
-       int channel;
-       int freq;
-       int rssi;
-       u8 *ie;
-       size_t ie_len;
-       u8 *wpa_ie;
-       size_t wpa_ie_len;
-       u8 *rsn_ie;
-       size_t rsn_ie_len;
-       u8 *wmm_ie;
-       size_t wmm_ie_len;
-       u8 *mdie;
-       size_t mdie_len;
-#define IEEE80211_MAX_SUPP_RATES 32
-       u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
-       size_t supp_rates_len;
-       int beacon_int;
-       u64 timestamp;
-
-       int probe_resp;
-       struct os_time last_update;
-};
-
-
-static void ieee80211_send_probe_req(struct wpa_supplicant *wpa_s,
-                                    const u8 *dst,
-                                    const u8 *ssid, size_t ssid_len);
-static struct ieee80211_sta_bss *
-ieee80211_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid);
-static int ieee80211_sta_find_ibss(struct wpa_supplicant *wpa_s);
-static int ieee80211_sta_wep_configured(struct wpa_supplicant *wpa_s);
-static void ieee80211_sta_timer(void *eloop_ctx, void *timeout_ctx);
-static void ieee80211_sta_scan_timer(void *eloop_ctx, void *timeout_ctx);
-static void ieee80211_build_tspec(struct wpabuf *buf);
-static int ieee80211_sta_set_probe_req_ie(struct wpa_supplicant *wpa_s,
-                                         const u8 *ies, size_t ies_len);
-
-
-static int ieee80211_sta_set_channel(struct wpa_supplicant *wpa_s,
-                                    enum hostapd_hw_mode phymode, int chan,
-                                    int freq)
-{
-       size_t i;
-       struct hostapd_hw_modes *mode;
-
-       for (i = 0; i < wpa_s->mlme.num_modes; i++) {
-               mode = &wpa_s->mlme.modes[i];
-               if (mode->mode == phymode) {
-                       wpa_s->mlme.curr_rates = mode->rates;
-                       wpa_s->mlme.num_curr_rates = mode->num_rates;
-                       break;
-               }
-       }
-
-       return wpa_drv_set_channel(wpa_s, phymode, chan, freq);
-}
-
-
-static int ecw2cw(int ecw)
-{
-       int cw = 1;
-       while (ecw > 0) {
-               cw <<= 1;
-               ecw--;
-       }
-       return cw - 1;
-}
-
-
-static void ieee80211_sta_wmm_params(struct wpa_supplicant *wpa_s,
-                                    const u8 *wmm_param, size_t wmm_param_len)
-{
-       size_t left;
-       int count;
-       const u8 *pos;
-       u8 wmm_acm;
-
-       if (wmm_param_len < 8 || wmm_param[5] /* version */ != 1)
-               return;
-       count = wmm_param[6] & 0x0f;
-       if (count == wpa_s->mlme.wmm_last_param_set)
-               return;
-       wpa_s->mlme.wmm_last_param_set = count;
-
-       pos = wmm_param + 8;
-       left = wmm_param_len - 8;
-
-       wmm_acm = 0;
-       for (; left >= 4; left -= 4, pos += 4) {
-               int aci = (pos[0] >> 5) & 0x03;
-               int acm = (pos[0] >> 4) & 0x01;
-               int aifs, cw_max, cw_min, burst_time;
-
-               switch (aci) {
-               case 1: /* AC_BK */
-                       if (acm)
-                               wmm_acm |= BIT(1) | BIT(2); /* BK/- */
-                       break;
-               case 2: /* AC_VI */
-                       if (acm)
-                               wmm_acm |= BIT(4) | BIT(5); /* CL/VI */
-                       break;
-               case 3: /* AC_VO */
-                       if (acm)
-                               wmm_acm |= BIT(6) | BIT(7); /* VO/NC */
-                       break;
-               case 0: /* AC_BE */
-               default:
-                       if (acm)
-                               wmm_acm |= BIT(0) | BIT(3); /* BE/EE */
-                       break;
-               }
-
-               aifs = pos[0] & 0x0f;
-               cw_max = ecw2cw((pos[1] & 0xf0) >> 4);
-               cw_min = ecw2cw(pos[1] & 0x0f);
-               /* TXOP is in units of 32 usec; burst_time in 0.1 ms */
-               burst_time = (pos[2] | (pos[3] << 8)) * 32 / 100;
-               wpa_printf(MSG_DEBUG, "MLME: WMM aci=%d acm=%d aifs=%d "
-                          "cWmin=%d cWmax=%d burst=%d",
-                          aci, acm, aifs, cw_min, cw_max, burst_time);
-               /* TODO: driver configuration */
-       }
-}
-
-
-static void ieee80211_set_associated(struct wpa_supplicant *wpa_s, int assoc)
-{
-       if (wpa_s->mlme.associated == assoc && !assoc)
-               return;
-
-       wpa_s->mlme.associated = assoc;
-
-       if (assoc) {
-               union wpa_event_data data;
-               os_memset(&data, 0, sizeof(data));
-               wpa_s->mlme.prev_bssid_set = 1;
-               os_memcpy(wpa_s->mlme.prev_bssid, wpa_s->bssid, ETH_ALEN);
-               data.assoc_info.req_ies = wpa_s->mlme.assocreq_ies;
-               data.assoc_info.req_ies_len = wpa_s->mlme.assocreq_ies_len;
-               data.assoc_info.resp_ies = wpa_s->mlme.assocresp_ies;
-               data.assoc_info.resp_ies_len = wpa_s->mlme.assocresp_ies_len;
-               data.assoc_info.freq = wpa_s->mlme.freq;
-               wpa_supplicant_event(wpa_s, EVENT_ASSOC, &data);
-       } else {
-               wpa_supplicant_event(wpa_s, EVENT_DISASSOC, NULL);
-       }
-       os_get_time(&wpa_s->mlme.last_probe);
-}
-
-
-static int ieee80211_sta_tx(struct wpa_supplicant *wpa_s, const u8 *buf,
-                           size_t len)
-{
-       return wpa_drv_send_mlme(wpa_s, buf, len);
-}
-
-
-static void ieee80211_send_auth(struct wpa_supplicant *wpa_s,
-                               int transaction, const u8 *extra,
-                               size_t extra_len, int encrypt)
-{
-       u8 *buf;
-       size_t len;
-       struct ieee80211_mgmt *mgmt;
-
-       buf = os_malloc(sizeof(*mgmt) + 6 + extra_len);
-       if (buf == NULL) {
-               wpa_printf(MSG_DEBUG, "MLME: failed to allocate buffer for "
-                          "auth frame");
-               return;
-       }
-
-       mgmt = (struct ieee80211_mgmt *) buf;
-       len = 24 + 6;
-       os_memset(mgmt, 0, 24 + 6);
-       mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
-                                          WLAN_FC_STYPE_AUTH);
-       if (encrypt)
-               mgmt->frame_control |= host_to_le16(WLAN_FC_ISWEP);
-       os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
-       os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
-       os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
-       mgmt->u.auth.auth_alg = host_to_le16(wpa_s->mlme.auth_alg);
-       mgmt->u.auth.auth_transaction = host_to_le16(transaction);
-       wpa_s->mlme.auth_transaction = transaction + 1;
-       mgmt->u.auth.status_code = host_to_le16(0);
-       if (extra) {
-               os_memcpy(buf + len, extra, extra_len);
-               len += extra_len;
-       }
-
-       ieee80211_sta_tx(wpa_s, buf, len);
-       os_free(buf);
-}
-
-
-static void ieee80211_reschedule_timer(struct wpa_supplicant *wpa_s, int ms)
-{
-       eloop_cancel_timeout(ieee80211_sta_timer, wpa_s, NULL);
-       eloop_register_timeout(ms / 1000, 1000 * (ms % 1000),
-                              ieee80211_sta_timer, wpa_s, NULL);
-}
-
-
-static void ieee80211_authenticate(struct wpa_supplicant *wpa_s)
-{
-       u8 *extra;
-       size_t extra_len;
-
-       wpa_s->mlme.auth_tries++;
-       if (wpa_s->mlme.auth_tries > IEEE80211_AUTH_MAX_TRIES) {
-               wpa_printf(MSG_DEBUG, "MLME: authentication with AP " MACSTR
-                          " timed out", MAC2STR(wpa_s->bssid));
-               return;
-       }
-
-       wpa_s->mlme.state = IEEE80211_AUTHENTICATE;
-       wpa_printf(MSG_DEBUG, "MLME: authenticate with AP " MACSTR,
-                  MAC2STR(wpa_s->bssid));
-
-       extra = NULL;
-       extra_len = 0;
-
-#ifdef CONFIG_IEEE80211R
-       if ((wpa_s->mlme.key_mgmt == KEY_MGMT_FT_802_1X ||
-            wpa_s->mlme.key_mgmt == KEY_MGMT_FT_PSK) &&
-           wpa_s->mlme.ft_ies) {
-               struct ieee80211_sta_bss *bss;
-               struct rsn_mdie *mdie = NULL;
-               bss = ieee80211_bss_get(wpa_s, wpa_s->bssid);
-               if (bss && bss->mdie_len >= 2 + sizeof(*mdie))
-                       mdie = (struct rsn_mdie *) (bss->mdie + 2);
-               if (mdie &&
-                   os_memcmp(mdie->mobility_domain, wpa_s->mlme.current_md,
-                             MOBILITY_DOMAIN_ID_LEN) == 0) {
-                       wpa_printf(MSG_DEBUG, "MLME: Trying to use FT "
-                                  "over-the-air");
-                       wpa_s->mlme.auth_alg = WLAN_AUTH_FT;
-                       extra = wpa_s->mlme.ft_ies;
-                       extra_len = wpa_s->mlme.ft_ies_len;
-               }
-       }
-#endif /* CONFIG_IEEE80211R */
-
-       ieee80211_send_auth(wpa_s, 1, extra, extra_len, 0);
-
-       ieee80211_reschedule_timer(wpa_s, IEEE80211_AUTH_TIMEOUT);
-}
-
-
-static void ieee80211_send_assoc(struct wpa_supplicant *wpa_s)
-{
-       struct ieee80211_mgmt *mgmt;
-       u8 *pos, *ies, *buf;
-       int i, len;
-       u16 capab;
-       struct ieee80211_sta_bss *bss;
-       int wmm = 0;
-       size_t blen, buflen;
-
-       if (wpa_s->mlme.curr_rates == NULL) {
-               wpa_printf(MSG_DEBUG, "MLME: curr_rates not set for assoc");
-               return;
-       }
-
-       buflen = sizeof(*mgmt) + 200 + wpa_s->mlme.extra_ie_len +
-               wpa_s->mlme.ssid_len;
-#ifdef CONFIG_IEEE80211R
-       if (wpa_s->mlme.ft_ies)
-               buflen += wpa_s->mlme.ft_ies_len;
-#endif /* CONFIG_IEEE80211R */
-       buf = os_malloc(buflen);
-       if (buf == NULL) {
-               wpa_printf(MSG_DEBUG, "MLME: failed to allocate buffer for "
-                          "assoc frame");
-               return;
-       }
-       blen = 0;
-
-       capab = wpa_s->mlme.capab;
-       if (wpa_s->mlme.phymode == HOSTAPD_MODE_IEEE80211G) {
-               capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME |
-                       WLAN_CAPABILITY_SHORT_PREAMBLE;
-       }
-       bss = ieee80211_bss_get(wpa_s, wpa_s->bssid);
-       if (bss) {
-               if (bss->capability & WLAN_CAPABILITY_PRIVACY)
-                       capab |= WLAN_CAPABILITY_PRIVACY;
-               if (bss->wmm_ie) {
-                       wmm = 1;
-               }
-       }
-
-       mgmt = (struct ieee80211_mgmt *) buf;
-       blen += 24;
-       os_memset(mgmt, 0, 24);
-       os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
-       os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
-       os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
-
-       if (wpa_s->mlme.prev_bssid_set) {
-               blen += 10;
-               mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
-                                                  WLAN_FC_STYPE_REASSOC_REQ);
-               mgmt->u.reassoc_req.capab_info = host_to_le16(capab);
-               mgmt->u.reassoc_req.listen_interval = host_to_le16(1);
-               os_memcpy(mgmt->u.reassoc_req.current_ap,
-                         wpa_s->mlme.prev_bssid,
-                         ETH_ALEN);
-       } else {
-               blen += 4;
-               mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
-                                                  WLAN_FC_STYPE_ASSOC_REQ);
-               mgmt->u.assoc_req.capab_info = host_to_le16(capab);
-               mgmt->u.assoc_req.listen_interval = host_to_le16(1);
-       }
-
-       /* SSID */
-       ies = pos = buf + blen;
-       blen += 2 + wpa_s->mlme.ssid_len;
-       *pos++ = WLAN_EID_SSID;
-       *pos++ = wpa_s->mlme.ssid_len;
-       os_memcpy(pos, wpa_s->mlme.ssid, wpa_s->mlme.ssid_len);
-
-       len = wpa_s->mlme.num_curr_rates;
-       if (len > 8)
-               len = 8;
-       pos = buf + blen;
-       blen += len + 2;
-       *pos++ = WLAN_EID_SUPP_RATES;
-       *pos++ = len;
-       for (i = 0; i < len; i++)
-               *pos++ = (u8) (wpa_s->mlme.curr_rates[i] / 5);
-
-       if (wpa_s->mlme.num_curr_rates > len) {
-               pos = buf + blen;
-               blen += wpa_s->mlme.num_curr_rates - len + 2;
-               *pos++ = WLAN_EID_EXT_SUPP_RATES;
-               *pos++ = wpa_s->mlme.num_curr_rates - len;
-               for (i = len; i < wpa_s->mlme.num_curr_rates; i++)
-                       *pos++ = (u8) (wpa_s->mlme.curr_rates[i] / 5);
-       }
-
-       if (wpa_s->mlme.extra_ie && wpa_s->mlme.auth_alg != WLAN_AUTH_FT) {
-               pos = buf + blen;
-               blen += wpa_s->mlme.extra_ie_len;
-               os_memcpy(pos, wpa_s->mlme.extra_ie, wpa_s->mlme.extra_ie_len);
-       }
-
-#ifdef CONFIG_IEEE80211R
-       if ((wpa_s->mlme.key_mgmt == KEY_MGMT_FT_802_1X ||
-            wpa_s->mlme.key_mgmt == KEY_MGMT_FT_PSK) &&
-           wpa_s->mlme.auth_alg != WLAN_AUTH_FT &&
-           bss && bss->mdie &&
-           bss->mdie_len >= 2 + sizeof(struct rsn_mdie) &&
-           bss->mdie[1] >= sizeof(struct rsn_mdie)) {
-               pos = buf + blen;
-               blen += 2 + sizeof(struct rsn_mdie);
-               *pos++ = WLAN_EID_MOBILITY_DOMAIN;
-               *pos++ = sizeof(struct rsn_mdie);
-               os_memcpy(pos, bss->mdie + 2, MOBILITY_DOMAIN_ID_LEN);
-               pos += MOBILITY_DOMAIN_ID_LEN;
-               *pos++ = 0; /* FIX: copy from the target AP's MDIE */
-       }
-
-       if ((wpa_s->mlme.key_mgmt == KEY_MGMT_FT_802_1X ||
-            wpa_s->mlme.key_mgmt == KEY_MGMT_FT_PSK) &&
-           wpa_s->mlme.auth_alg == WLAN_AUTH_FT && wpa_s->mlme.ft_ies) {
-               pos = buf + blen;
-               os_memcpy(pos, wpa_s->mlme.ft_ies, wpa_s->mlme.ft_ies_len);
-               pos += wpa_s->mlme.ft_ies_len;
-               blen += wpa_s->mlme.ft_ies_len;
-       }
-#endif /* CONFIG_IEEE80211R */
-
-       if (wmm && wpa_s->mlme.wmm_enabled) {
-               pos = buf + blen;
-               blen += 9;
-               *pos++ = WLAN_EID_VENDOR_SPECIFIC;
-               *pos++ = 7; /* len */
-               *pos++ = 0x00; /* Microsoft OUI 00:50:F2 */
-               *pos++ = 0x50;
-               *pos++ = 0xf2;
-               *pos++ = 2; /* WMM */
-               *pos++ = 0; /* WMM info */
-               *pos++ = 1; /* WMM ver */
-               *pos++ = 0;
-       }
-
-       os_free(wpa_s->mlme.assocreq_ies);
-       wpa_s->mlme.assocreq_ies_len = (buf + blen) - ies;
-       wpa_s->mlme.assocreq_ies = os_malloc(wpa_s->mlme.assocreq_ies_len);
-       if (wpa_s->mlme.assocreq_ies) {
-               os_memcpy(wpa_s->mlme.assocreq_ies, ies,
-                         wpa_s->mlme.assocreq_ies_len);
-       }
-
-       ieee80211_sta_tx(wpa_s, buf, blen);
-       os_free(buf);
-}
-
-
-static void ieee80211_send_deauth(struct wpa_supplicant *wpa_s, u16 reason)
-{
-       u8 *buf;
-       size_t len;
-       struct ieee80211_mgmt *mgmt;
-
-       buf = os_zalloc(sizeof(*mgmt));
-       if (buf == NULL) {
-               wpa_printf(MSG_DEBUG, "MLME: failed to allocate buffer for "
-                          "deauth frame");
-               return;
-       }
-
-       mgmt = (struct ieee80211_mgmt *) buf;
-       len = 24;
-       os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
-       os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
-       os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
-       mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
-                                          WLAN_FC_STYPE_DEAUTH);
-       len += 2;
-       mgmt->u.deauth.reason_code = host_to_le16(reason);
-
-       ieee80211_sta_tx(wpa_s, buf, len);
-       os_free(buf);
-}
-
-
-static void ieee80211_send_disassoc(struct wpa_supplicant *wpa_s, u16 reason)
-{
-       u8 *buf;
-       size_t len;
-       struct ieee80211_mgmt *mgmt;
-
-       buf = os_zalloc(sizeof(*mgmt));
-       if (buf == NULL) {
-               wpa_printf(MSG_DEBUG, "MLME: failed to allocate buffer for "
-                          "disassoc frame");
-               return;
-       }
-
-       mgmt = (struct ieee80211_mgmt *) buf;
-       len = 24;
-       os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
-       os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
-       os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
-       mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
-                                          WLAN_FC_STYPE_DISASSOC);
-       len += 2;
-       mgmt->u.disassoc.reason_code = host_to_le16(reason);
-
-       ieee80211_sta_tx(wpa_s, buf, len);
-       os_free(buf);
-}
-
-
-static int ieee80211_privacy_mismatch(struct wpa_supplicant *wpa_s)
-{
-       struct ieee80211_sta_bss *bss;
-       int res = 0;
-
-       if (wpa_s->mlme.mixed_cell ||
-           wpa_s->mlme.key_mgmt != KEY_MGMT_NONE)
-               return 0;
-
-       bss = ieee80211_bss_get(wpa_s, wpa_s->bssid);
-       if (bss == NULL)
-               return 0;
-
-       if (ieee80211_sta_wep_configured(wpa_s) !=
-           !!(bss->capability & WLAN_CAPABILITY_PRIVACY))
-               res = 1;
-
-       return res;
-}
-
-
-static void ieee80211_associate(struct wpa_supplicant *wpa_s)
-{
-       wpa_s->mlme.assoc_tries++;
-       if (wpa_s->mlme.assoc_tries > IEEE80211_ASSOC_MAX_TRIES) {
-               wpa_printf(MSG_DEBUG, "MLME: association with AP " MACSTR
-                          " timed out", MAC2STR(wpa_s->bssid));
-               return;
-       }
-
-       wpa_s->mlme.state = IEEE80211_ASSOCIATE;
-       wpa_printf(MSG_DEBUG, "MLME: associate with AP " MACSTR,
-                  MAC2STR(wpa_s->bssid));
-       if (ieee80211_privacy_mismatch(wpa_s)) {
-               wpa_printf(MSG_DEBUG, "MLME: mismatch in privacy "
-                          "configuration and mixed-cell disabled - abort "
-                          "association");
-               return;
-       }
-
-       ieee80211_send_assoc(wpa_s);
-
-       ieee80211_reschedule_timer(wpa_s, IEEE80211_ASSOC_TIMEOUT);
-}
-
-
-static void ieee80211_associated(struct wpa_supplicant *wpa_s)
-{
-       int disassoc;
-
-       /* TODO: start monitoring current AP signal quality and number of
-        * missed beacons. Scan other channels every now and then and search
-        * for better APs. */
-       /* TODO: remove expired BSSes */
-
-       wpa_s->mlme.state = IEEE80211_ASSOCIATED;
-
-#if 0 /* FIX */
-       sta = sta_info_get(local, wpa_s->bssid);
-       if (sta == NULL) {
-               wpa_printf(MSG_DEBUG "MLME: No STA entry for own AP " MACSTR,
-                          MAC2STR(wpa_s->bssid));
-               disassoc = 1;
-       } else {
-               disassoc = 0;
-               if (time_after(jiffies,
-                              sta->last_rx + IEEE80211_MONITORING_INTERVAL)) {
-                       if (wpa_s->mlme.probereq_poll) {
-                               wpa_printf(MSG_DEBUG "MLME: No ProbeResp from "
-                                          "current AP " MACSTR " - assume "
-                                          "out of range",
-                                          MAC2STR(wpa_s->bssid));
-                               disassoc = 1;
-                       } else {
-                               ieee80211_send_probe_req(
-                                       wpa_s->bssid,
-                                       wpa_s->mlme.scan_ssid,
-                                       wpa_s->mlme.scan_ssid_len);
-                               wpa_s->mlme.probereq_poll = 1;
-                       }
-               } else {
-                       wpa_s->mlme.probereq_poll = 0;
-                       if (time_after(jiffies, wpa_s->mlme.last_probe +
-                                      IEEE80211_PROBE_INTERVAL)) {
-                               wpa_s->mlme.last_probe = jiffies;
-                               ieee80211_send_probe_req(wpa_s->bssid,
-                                                        wpa_s->mlme.ssid,
-                                                        wpa_s->mlme.ssid_len);
-                       }
-               }
-               sta_info_release(local, sta);
-       }
-#else
-       disassoc = 0;
-#endif
-       if (disassoc) {
-               wpa_supplicant_event(wpa_s, EVENT_DISASSOC, NULL);
-               ieee80211_reschedule_timer(wpa_s,
-                                          IEEE80211_MONITORING_INTERVAL +
-                                          30000);
-       } else {
-               ieee80211_reschedule_timer(wpa_s,
-                                          IEEE80211_MONITORING_INTERVAL);
-       }
-}
-
-
-static void ieee80211_send_probe_req(struct wpa_supplicant *wpa_s,
-                                    const u8 *dst,
-                                    const u8 *ssid, size_t ssid_len)
-{
-       u8 *buf;
-       size_t len;
-       struct ieee80211_mgmt *mgmt;
-       u8 *pos, *supp_rates;
-       u8 *esupp_rates = NULL;
-       int i;
-
-       buf = os_malloc(sizeof(*mgmt) + 200 + wpa_s->mlme.extra_probe_ie_len);
-       if (buf == NULL) {
-               wpa_printf(MSG_DEBUG, "MLME: failed to allocate buffer for "
-                          "probe request");
-               return;
-       }
-
-       mgmt = (struct ieee80211_mgmt *) buf;
-       len = 24;
-       os_memset(mgmt, 0, 24);
-       mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
-                                          WLAN_FC_STYPE_PROBE_REQ);
-       os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
-       if (dst) {
-               os_memcpy(mgmt->da, dst, ETH_ALEN);
-               os_memcpy(mgmt->bssid, dst, ETH_ALEN);
-       } else {
-               os_memset(mgmt->da, 0xff, ETH_ALEN);
-               os_memset(mgmt->bssid, 0xff, ETH_ALEN);
-       }
-       pos = buf + len;
-       len += 2 + ssid_len;
-       *pos++ = WLAN_EID_SSID;
-       *pos++ = ssid_len;
-       os_memcpy(pos, ssid, ssid_len);
-
-       supp_rates = buf + len;
-       len += 2;
-       supp_rates[0] = WLAN_EID_SUPP_RATES;
-       supp_rates[1] = 0;
-       for (i = 0; i < wpa_s->mlme.num_curr_rates; i++) {
-               if (esupp_rates) {
-                       pos = buf + len;
-                       len++;
-                       esupp_rates[1]++;
-               } else if (supp_rates[1] == 8) {
-                       esupp_rates = pos;
-                       esupp_rates[0] = WLAN_EID_EXT_SUPP_RATES;
-                       esupp_rates[1] = 1;
-                       pos = &esupp_rates[2];
-                       len += 3;
-               } else {
-                       pos = buf + len;
-                       len++;
-                       supp_rates[1]++;
-               }
-               *pos++ = wpa_s->mlme.curr_rates[i] / 5;
-       }
-
-       if (wpa_s->mlme.extra_probe_ie) {
-               os_memcpy(pos, wpa_s->mlme.extra_probe_ie,
-                         wpa_s->mlme.extra_probe_ie_len);
-               len += wpa_s->mlme.extra_probe_ie_len;
-       }
-
-       ieee80211_sta_tx(wpa_s, buf, len);
-       os_free(buf);
-}
-
-
-static int ieee80211_sta_wep_configured(struct wpa_supplicant *wpa_s)
-{
-#if 0 /* FIX */
-       if (sdata == NULL || sdata->default_key == NULL ||
-           sdata->default_key->alg != ALG_WEP)
-               return 0;
-       return 1;
-#else
-       return 0;
-#endif
-}
-
-
-static void ieee80211_auth_completed(struct wpa_supplicant *wpa_s)
-{
-       wpa_printf(MSG_DEBUG, "MLME: authenticated");
-       wpa_s->mlme.authenticated = 1;
-       ieee80211_associate(wpa_s);
-}
-
-
-static void ieee80211_auth_challenge(struct wpa_supplicant *wpa_s,
-                                    struct ieee80211_mgmt *mgmt,
-                                    size_t len,
-                                    struct ieee80211_rx_status *rx_status)
-{
-       u8 *pos;
-       struct ieee802_11_elems elems;
-
-       wpa_printf(MSG_DEBUG, "MLME: replying to auth challenge");
-       pos = mgmt->u.auth.variable;
-       if (ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems, 0)
-           == ParseFailed) {
-               wpa_printf(MSG_DEBUG, "MLME: failed to parse Auth(challenge)");
-               return;
-       }
-       if (elems.challenge == NULL) {
-               wpa_printf(MSG_DEBUG, "MLME: no challenge IE in shared key "
-                          "auth frame");
-               return;
-       }
-       ieee80211_send_auth(wpa_s, 3, elems.challenge - 2,
-                           elems.challenge_len + 2, 1);
-}
-
-
-static void ieee80211_rx_mgmt_auth(struct wpa_supplicant *wpa_s,
-                                  struct ieee80211_mgmt *mgmt,
-                                  size_t len,
-                                  struct ieee80211_rx_status *rx_status)
-{
-       struct wpa_ssid *ssid = wpa_s->current_ssid;
-       u16 auth_alg, auth_transaction, status_code;
-       int adhoc;
-
-       adhoc = ssid && ssid->mode == WPAS_MODE_IBSS;
-
-       if (wpa_s->mlme.state != IEEE80211_AUTHENTICATE && !adhoc) {
-               wpa_printf(MSG_DEBUG, "MLME: authentication frame received "
-                          "from " MACSTR ", but not in authenticate state - "
-                          "ignored", MAC2STR(mgmt->sa));
-               return;
-       }
-
-       if (len < 24 + 6) {
-               wpa_printf(MSG_DEBUG, "MLME: too short (%lu) authentication "
-                          "frame received from " MACSTR " - ignored",
-                          (unsigned long) len, MAC2STR(mgmt->sa));
-               return;
-       }
-
-       if (!adhoc && os_memcmp(wpa_s->bssid, mgmt->sa, ETH_ALEN) != 0) {
-               wpa_printf(MSG_DEBUG, "MLME: authentication frame received "
-                          "from unknown AP (SA=" MACSTR " BSSID=" MACSTR
-                          ") - ignored",
-                          MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid));
-               return;
-       }
-
-       if (adhoc && os_memcmp(wpa_s->bssid, mgmt->bssid, ETH_ALEN) != 0) {
-               wpa_printf(MSG_DEBUG, "MLME: authentication frame received "
-                          "from unknown BSSID (SA=" MACSTR " BSSID=" MACSTR
-                          ") - ignored",
-                          MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid));
-               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);
-
-       wpa_printf(MSG_DEBUG, "MLME: RX authentication from " MACSTR
-                  " (alg=%d transaction=%d status=%d)",
-                  MAC2STR(mgmt->sa), auth_alg, auth_transaction, status_code);
-
-       if (adhoc) {
-               /* IEEE 802.11 standard does not require authentication in IBSS
-                * networks and most implementations do not seem to use it.
-                * However, try to reply to authentication attempts if someone
-                * has actually implemented this.
-                * TODO: Could implement shared key authentication. */
-               if (auth_alg != WLAN_AUTH_OPEN || auth_transaction != 1) {
-                       wpa_printf(MSG_DEBUG, "MLME: unexpected IBSS "
-                                  "authentication frame (alg=%d "
-                                  "transaction=%d)",
-                                  auth_alg, auth_transaction);
-                       return;
-               }
-               ieee80211_send_auth(wpa_s, 2, NULL, 0, 0);
-       }
-
-       if (auth_alg != wpa_s->mlme.auth_alg ||
-           auth_transaction != wpa_s->mlme.auth_transaction) {
-               wpa_printf(MSG_DEBUG, "MLME: unexpected authentication frame "
-                          "(alg=%d transaction=%d)",
-                          auth_alg, auth_transaction);
-               return;
-       }
-
-       if (status_code != WLAN_STATUS_SUCCESS) {
-               wpa_printf(MSG_DEBUG, "MLME: AP denied authentication "
-                          "(auth_alg=%d code=%d)", wpa_s->mlme.auth_alg,
-                          status_code);
-               if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG) {
-                       const int num_algs = 3;
-                       u8 algs[num_algs];
-                       int i, pos;
-                       algs[0] = algs[1] = algs[2] = 0xff;
-                       if (wpa_s->mlme.auth_algs & WPA_AUTH_ALG_OPEN)
-                               algs[0] = WLAN_AUTH_OPEN;
-                       if (wpa_s->mlme.auth_algs & WPA_AUTH_ALG_SHARED)
-                               algs[1] = WLAN_AUTH_SHARED_KEY;
-                       if (wpa_s->mlme.auth_algs & WPA_AUTH_ALG_LEAP)
-                               algs[2] = WLAN_AUTH_LEAP;
-                       if (wpa_s->mlme.auth_alg == WLAN_AUTH_OPEN)
-                               pos = 0;
-                       else if (wpa_s->mlme.auth_alg == WLAN_AUTH_SHARED_KEY)
-                               pos = 1;
-                       else
-                               pos = 2;
-                       for (i = 0; i < num_algs; i++) {
-                               pos++;
-                               if (pos >= num_algs)
-                                       pos = 0;
-                               if (algs[pos] == wpa_s->mlme.auth_alg ||
-                                   algs[pos] == 0xff)
-                                       continue;
-                               if (algs[pos] == WLAN_AUTH_SHARED_KEY &&
-                                   !ieee80211_sta_wep_configured(wpa_s))
-                                       continue;
-                               wpa_s->mlme.auth_alg = algs[pos];
-                               wpa_printf(MSG_DEBUG, "MLME: set auth_alg=%d "
-                                          "for next try",
-                                          wpa_s->mlme.auth_alg);
-                               break;
-                       }
-               }
-               return;
-       }
-
-       switch (wpa_s->mlme.auth_alg) {
-       case WLAN_AUTH_OPEN:
-       case WLAN_AUTH_LEAP:
-               ieee80211_auth_completed(wpa_s);
-               break;
-       case WLAN_AUTH_SHARED_KEY:
-               if (wpa_s->mlme.auth_transaction == 4)
-                       ieee80211_auth_completed(wpa_s);
-               else
-                       ieee80211_auth_challenge(wpa_s, mgmt, len,
-                                                rx_status);
-               break;
-#ifdef CONFIG_IEEE80211R
-       case WLAN_AUTH_FT:
-       {
-               union wpa_event_data data;
-               struct wpabuf *ric = NULL;
-               os_memset(&data, 0, sizeof(data));
-               data.ft_ies.ies = mgmt->u.auth.variable;
-               data.ft_ies.ies_len = len -
-                       (mgmt->u.auth.variable - (u8 *) mgmt);
-               os_memcpy(data.ft_ies.target_ap, wpa_s->bssid, ETH_ALEN);
-               if (os_strcmp(wpa_s->driver->name, "test") == 0 &&
-                   wpa_s->mlme.wmm_enabled) {
-                       ric = wpabuf_alloc(200);
-                       if (ric) {
-                               /* Build simple RIC-Request: RDIE | TSPEC */
-
-                               /* RIC Data (RDIE) */
-                               wpabuf_put_u8(ric, WLAN_EID_RIC_DATA);
-                               wpabuf_put_u8(ric, 4);
-                               wpabuf_put_u8(ric, 0); /* RDIE Identifier */
-                               wpabuf_put_u8(ric, 1); /* Resource Descriptor
-                                                       * Count */
-                               wpabuf_put_le16(ric, 0); /* Status Code */
-
-                               /* WMM TSPEC */
-                               ieee80211_build_tspec(ric);
-
-                               data.ft_ies.ric_ies = wpabuf_head(ric);
-                               data.ft_ies.ric_ies_len = wpabuf_len(ric);
-                       }
-               }
-
-               wpa_supplicant_event(wpa_s, EVENT_FT_RESPONSE, &data);
-               wpabuf_free(ric);
-               ieee80211_auth_completed(wpa_s);
-               break;
-       }
-#endif /* CONFIG_IEEE80211R */
-       }
-}
-
-
-static void ieee80211_rx_mgmt_deauth(struct wpa_supplicant *wpa_s,
-                                    struct ieee80211_mgmt *mgmt,
-                                    size_t len,
-                                    struct ieee80211_rx_status *rx_status)
-{
-       u16 reason_code;
-
-       if (len < 24 + 2) {
-               wpa_printf(MSG_DEBUG, "MLME: too short (%lu) deauthentication "
-                          "frame received from " MACSTR " - ignored",
-                          (unsigned long) len, MAC2STR(mgmt->sa));
-               return;
-       }
-
-       if (os_memcmp(wpa_s->bssid, mgmt->sa, ETH_ALEN) != 0) {
-               wpa_printf(MSG_DEBUG, "MLME: deauthentication frame received "
-                          "from unknown AP (SA=" MACSTR " BSSID=" MACSTR
-                          ") - ignored",
-                          MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid));
-               return;
-       }
-
-       reason_code = le_to_host16(mgmt->u.deauth.reason_code);
-
-       wpa_printf(MSG_DEBUG, "MLME: RX deauthentication from " MACSTR
-                  " (reason=%d)", MAC2STR(mgmt->sa), reason_code);
-
-       if (wpa_s->mlme.authenticated)
-               wpa_printf(MSG_DEBUG, "MLME: deauthenticated");
-
-       if (wpa_s->mlme.state == IEEE80211_AUTHENTICATE ||
-           wpa_s->mlme.state == IEEE80211_ASSOCIATE ||
-           wpa_s->mlme.state == IEEE80211_ASSOCIATED) {
-               wpa_s->mlme.state = IEEE80211_AUTHENTICATE;
-               ieee80211_reschedule_timer(wpa_s,
-                                          IEEE80211_RETRY_AUTH_INTERVAL);
-       }
-
-       ieee80211_set_associated(wpa_s, 0);
-       wpa_s->mlme.authenticated = 0;
-}
-
-
-static void ieee80211_rx_mgmt_disassoc(struct wpa_supplicant *wpa_s,
-                                      struct ieee80211_mgmt *mgmt,
-                                      size_t len,
-                                      struct ieee80211_rx_status *rx_status)
-{
-       u16 reason_code;
-
-       if (len < 24 + 2) {
-               wpa_printf(MSG_DEBUG, "MLME: too short (%lu) disassociation "
-                          "frame received from " MACSTR " - ignored",
-                          (unsigned long) len, MAC2STR(mgmt->sa));
-               return;
-       }
-
-       if (os_memcmp(wpa_s->bssid, mgmt->sa, ETH_ALEN) != 0) {
-               wpa_printf(MSG_DEBUG, "MLME: disassociation frame received "
-                          "from unknown AP (SA=" MACSTR " BSSID=" MACSTR
-                          ") - ignored",
-                          MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid));
-               return;
-       }
-
-       reason_code = le_to_host16(mgmt->u.disassoc.reason_code);
-
-       wpa_printf(MSG_DEBUG, "MLME: RX disassociation from " MACSTR
-                  " (reason=%d)", MAC2STR(mgmt->sa), reason_code);
-
-       if (wpa_s->mlme.associated)
-               wpa_printf(MSG_DEBUG, "MLME: disassociated");
-
-       if (wpa_s->mlme.state == IEEE80211_ASSOCIATED) {
-               wpa_s->mlme.state = IEEE80211_ASSOCIATE;
-               ieee80211_reschedule_timer(wpa_s,
-                                          IEEE80211_RETRY_AUTH_INTERVAL);
-       }
-
-       ieee80211_set_associated(wpa_s, 0);
-}
-
-
-static void ieee80211_build_tspec(struct wpabuf *buf)
-{
-       struct wmm_tspec_element *tspec;
-       int tid, up;
-
-       tspec = wpabuf_put(buf, sizeof(*tspec));
-       tspec->eid = WLAN_EID_VENDOR_SPECIFIC;
-       tspec->length = sizeof(*tspec) - 2;
-       tspec->oui[0] = 0x00;
-       tspec->oui[1] = 0x50;
-       tspec->oui[2] = 0xf2;
-       tspec->oui_type = 2;
-       tspec->oui_subtype = 2;
-       tspec->version = 1;
-
-       tid = 1;
-       up = 6; /* Voice */
-       tspec->ts_info[0] = (tid << 1) |
-               (WMM_TSPEC_DIRECTION_BI_DIRECTIONAL << 5) |
-               BIT(7);
-       tspec->ts_info[1] = up << 3;
-       tspec->nominal_msdu_size = host_to_le16(1530);
-       tspec->mean_data_rate = host_to_le32(128000); /* bits per second */
-       tspec->minimum_phy_rate = host_to_le32(6000000);
-       tspec->surplus_bandwidth_allowance = host_to_le16(0x3000); /* 150% */
-}
-
-
-static void ieee80211_tx_addts(struct wpa_supplicant *wpa_s)
-{
-       struct wpabuf *buf;
-       struct ieee80211_mgmt *mgmt;
-       size_t alen;
-
-       wpa_printf(MSG_DEBUG, "MLME: Send ADDTS Request for Voice TSPEC");
-       mgmt = NULL;
-       alen = mgmt->u.action.u.wmm_action.variable - (u8 *) mgmt;
-
-       buf = wpabuf_alloc(alen + sizeof(struct wmm_tspec_element));
-       if (buf == NULL)
-               return;
-
-       mgmt = wpabuf_put(buf, alen);
-       os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
-       os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
-       os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
-       mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
-                                          WLAN_FC_STYPE_ACTION);
-       mgmt->u.action.category = WLAN_ACTION_WMM;
-       mgmt->u.action.u.wmm_action.action_code = WMM_ACTION_CODE_ADDTS_REQ;
-       mgmt->u.action.u.wmm_action.dialog_token = 1;
-       mgmt->u.action.u.wmm_action.status_code = 0;
-
-       ieee80211_build_tspec(buf);
-
-       ieee80211_sta_tx(wpa_s, wpabuf_head(buf), wpabuf_len(buf));
-       wpabuf_free(buf);
-}
-
-
-static void ieee80211_rx_mgmt_assoc_resp(struct wpa_supplicant *wpa_s,
-                                        struct ieee80211_mgmt *mgmt,
-                                        size_t len,
-                                        struct ieee80211_rx_status *rx_status,
-                                        int reassoc)
-{
-       u8 rates[32];
-       size_t rates_len;
-       u16 capab_info, status_code, aid;
-       struct ieee802_11_elems elems;
-       u8 *pos;
-
-       /* AssocResp and ReassocResp have identical structure, so process both
-        * of them in this function. */
-
-       if (wpa_s->mlme.state != IEEE80211_ASSOCIATE) {
-               wpa_printf(MSG_DEBUG, "MLME: association frame received from "
-                          MACSTR ", but not in associate state - ignored",
-                          MAC2STR(mgmt->sa));
-               return;
-       }
-
-       if (len < 24 + 6) {
-               wpa_printf(MSG_DEBUG, "MLME: too short (%lu) association "
-                          "frame received from " MACSTR " - ignored",
-                          (unsigned long) len, MAC2STR(mgmt->sa));
-               return;
-       }
-
-       if (os_memcmp(wpa_s->bssid, mgmt->sa, ETH_ALEN) != 0) {
-               wpa_printf(MSG_DEBUG, "MLME: association frame received from "
-                          "unknown AP (SA=" MACSTR " BSSID=" MACSTR ") - "
-                          "ignored", MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid));
-               return;
-       }
-
-       capab_info = le_to_host16(mgmt->u.assoc_resp.capab_info);
-       status_code = le_to_host16(mgmt->u.assoc_resp.status_code);
-       aid = le_to_host16(mgmt->u.assoc_resp.aid);
-       if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14)))
-               wpa_printf(MSG_DEBUG, "MLME: invalid aid value %d; bits 15:14 "
-                          "not set", aid);
-       aid &= ~(BIT(15) | BIT(14));
-
-       wpa_printf(MSG_DEBUG, "MLME: RX %sssocResp from " MACSTR
-                  " (capab=0x%x status=%d aid=%d)",
-                  reassoc ? "Rea" : "A", MAC2STR(mgmt->sa),
-                  capab_info, status_code, aid);
-
-       pos = mgmt->u.assoc_resp.variable;
-       if (ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems, 0)
-           == ParseFailed) {
-               wpa_printf(MSG_DEBUG, "MLME: failed to parse AssocResp");
-               return;
-       }
-
-       if (status_code != WLAN_STATUS_SUCCESS) {
-               wpa_printf(MSG_DEBUG, "MLME: AP denied association (code=%d)",
-                          status_code);
-#ifdef CONFIG_IEEE80211W
-               if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY &&
-                   elems.timeout_int && elems.timeout_int_len == 5 &&
-                   elems.timeout_int[0] == WLAN_TIMEOUT_ASSOC_COMEBACK) {
-                       u32 tu, ms;
-                       tu = WPA_GET_LE32(elems.timeout_int + 1);
-                       ms = tu * 1024 / 1000;
-                       wpa_printf(MSG_DEBUG, "MLME: AP rejected association "
-                                  "temporarily; comeback duration %u TU "
-                                  "(%u ms)", tu, ms);
-                       if (ms > IEEE80211_ASSOC_TIMEOUT) {
-                               wpa_printf(MSG_DEBUG, "MLME: Update timer "
-                                          "based on comeback duration");
-                               ieee80211_reschedule_timer(wpa_s, ms);
-                       }
-               }
-#endif /* CONFIG_IEEE80211W */
-               return;
-       }
-
-       if (elems.supp_rates == NULL) {
-               wpa_printf(MSG_DEBUG, "MLME: no SuppRates element in "
-                          "AssocResp");
-               return;
-       }
-
-       if (wpa_s->mlme.auth_alg == WLAN_AUTH_FT) {
-               if (!reassoc) {
-                       wpa_printf(MSG_DEBUG, "MLME: AP tried to use "
-                                  "association, not reassociation, response "
-                                  "with FT");
-                       return;
-               }
-               if (wpa_ft_validate_reassoc_resp(
-                           wpa_s->wpa, pos, len - (pos - (u8 *) mgmt),
-                           mgmt->sa) < 0) {
-                       wpa_printf(MSG_DEBUG, "MLME: FT validation of Reassoc"
-                                  "Resp failed");
-                       return;
-               }
-       } else if (wpa_sm_set_ft_params(wpa_s->wpa, pos,
-                                       len - (pos - (u8 *) mgmt)) < 0)
-               return;
-
-       wpa_printf(MSG_DEBUG, "MLME: associated");
-       wpa_s->mlme.aid = aid;
-       wpa_s->mlme.ap_capab = capab_info;
-
-       os_free(wpa_s->mlme.assocresp_ies);
-       wpa_s->mlme.assocresp_ies_len = len - (pos - (u8 *) mgmt);
-       wpa_s->mlme.assocresp_ies = os_malloc(wpa_s->mlme.assocresp_ies_len);
-       if (wpa_s->mlme.assocresp_ies) {
-               os_memcpy(wpa_s->mlme.assocresp_ies, pos,
-                         wpa_s->mlme.assocresp_ies_len);
-       }
-
-       ieee80211_set_associated(wpa_s, 1);
-
-       rates_len = elems.supp_rates_len;
-       if (rates_len > sizeof(rates))
-               rates_len = sizeof(rates);
-       os_memcpy(rates, elems.supp_rates, rates_len);
-       if (elems.ext_supp_rates) {
-               size_t _len = elems.ext_supp_rates_len;
-               if (_len > sizeof(rates) - rates_len)
-                       _len = sizeof(rates) - rates_len;
-               os_memcpy(rates + rates_len, elems.ext_supp_rates, _len);
-               rates_len += _len;
-       }
-
-       if (wpa_drv_set_bssid(wpa_s, wpa_s->bssid) < 0) {
-               wpa_printf(MSG_DEBUG, "MLME: failed to set BSSID for the "
-                          "netstack");
-       }
-       if (wpa_drv_set_ssid(wpa_s, wpa_s->mlme.ssid, wpa_s->mlme.ssid_len) <
-           0) {
-               wpa_printf(MSG_DEBUG, "MLME: failed to set SSID for the "
-                          "netstack");
-       }
-
-       /* Remove STA entry before adding a new one just in case to avoid
-        * problems with existing configuration (e.g., keys). */
-       wpa_drv_mlme_remove_sta(wpa_s, wpa_s->bssid);
-       if (wpa_drv_mlme_add_sta(wpa_s, wpa_s->bssid, rates, rates_len) < 0) {
-               wpa_printf(MSG_DEBUG, "MLME: failed to add STA entry to the "
-                          "netstack");
-       }
-
-       if (elems.wmm && wpa_s->mlme.wmm_enabled)
-               ieee80211_sta_wmm_params(wpa_s, elems.wmm, elems.wmm_len);
-
-       ieee80211_associated(wpa_s);
-
-       if (wpa_s->mlme.auth_alg != WLAN_AUTH_FT &&
-           os_strcmp(wpa_s->driver->name, "test") == 0 &&
-           elems.wmm && wpa_s->mlme.wmm_enabled) {
-               /* Test WMM-AC - send ADDTS for WMM TSPEC */
-               ieee80211_tx_addts(wpa_s);
-       }
-}
-
-
-/* Caller must hold local->sta_bss_lock */
-static void __ieee80211_bss_hash_add(struct wpa_supplicant *wpa_s,
-                                    struct ieee80211_sta_bss *bss)
-{
-       bss->hnext = wpa_s->mlme.sta_bss_hash[STA_HASH(bss->bssid)];
-       wpa_s->mlme.sta_bss_hash[STA_HASH(bss->bssid)] = bss;
-}
-
-
-/* Caller must hold local->sta_bss_lock */
-static void __ieee80211_bss_hash_del(struct wpa_supplicant *wpa_s,
-                                    struct ieee80211_sta_bss *bss)
-{
-       struct ieee80211_sta_bss *b, *prev = NULL;
-       b = wpa_s->mlme.sta_bss_hash[STA_HASH(bss->bssid)];
-       while (b) {
-               if (b == bss) {
-                       if (prev == NULL) {
-                               wpa_s->mlme.sta_bss_hash[STA_HASH(bss->bssid)]
-                                       = bss->hnext;
-                       } else {
-                               prev->hnext = bss->hnext;
-                       }
-                       break;
-               }
-               prev = b;
-               b = b->hnext;
-       }
-}
-
-
-static struct ieee80211_sta_bss *
-ieee80211_bss_add(struct wpa_supplicant *wpa_s, const u8 *bssid)
-{
-       struct ieee80211_sta_bss *bss;
-
-       bss = os_zalloc(sizeof(*bss));
-       if (bss == NULL)
-               return NULL;
-       os_memcpy(bss->bssid, bssid, ETH_ALEN);
-
-       /* TODO: order by RSSI? */
-       bss->next = wpa_s->mlme.sta_bss_list;
-       wpa_s->mlme.sta_bss_list = bss;
-       __ieee80211_bss_hash_add(wpa_s, bss);
-       return bss;
-}
-
-
-static struct ieee80211_sta_bss *
-ieee80211_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid)
-{
-       struct ieee80211_sta_bss *bss;
-
-       bss = wpa_s->mlme.sta_bss_hash[STA_HASH(bssid)];
-       while (bss) {
-               if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0)
-                       break;
-               bss = bss->hnext;
-       }
-       return bss;
-}
-
-
-static void ieee80211_bss_free(struct wpa_supplicant *wpa_s,
-                              struct ieee80211_sta_bss *bss)
-{
-       __ieee80211_bss_hash_del(wpa_s, bss);
-       os_free(bss->ie);
-       os_free(bss->wpa_ie);
-       os_free(bss->rsn_ie);
-       os_free(bss->wmm_ie);
-       os_free(bss->mdie);
-       os_free(bss);
-}
-
-
-static void ieee80211_bss_list_deinit(struct wpa_supplicant *wpa_s)
-{
-       struct ieee80211_sta_bss *bss, *prev;
-
-       bss = wpa_s->mlme.sta_bss_list;
-       wpa_s->mlme.sta_bss_list = NULL;
-       while (bss) {
-               prev = bss;
-               bss = bss->next;
-               ieee80211_bss_free(wpa_s, prev);
-       }
-}
-
-
-static void ieee80211_bss_info(struct wpa_supplicant *wpa_s,
-                              struct ieee80211_mgmt *mgmt,
-                              size_t len,
-                              struct ieee80211_rx_status *rx_status,
-                              int beacon)
-{
-       struct ieee802_11_elems elems;
-       size_t baselen;
-       int channel, invalid = 0, clen;
-       struct ieee80211_sta_bss *bss;
-       u64 timestamp;
-       u8 *pos, *ie_pos;
-       size_t ie_len;
-
-       if (!beacon && os_memcmp(mgmt->da, wpa_s->own_addr, ETH_ALEN))
-               return; /* ignore ProbeResp to foreign address */
-
-#if 0
-       wpa_printf(MSG_MSGDUMP, "MLME: RX %s from " MACSTR " to " MACSTR,
-                  beacon ? "Beacon" : "Probe Response",
-                  MAC2STR(mgmt->sa), MAC2STR(mgmt->da));
-#endif
-
-       baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt;
-       if (baselen > len)
-               return;
-
-       pos = mgmt->u.beacon.timestamp;
-       timestamp = WPA_GET_LE64(pos);
-
-#if 0 /* FIX */
-       if (local->conf.mode == IW_MODE_ADHOC && beacon &&
-           os_memcmp(mgmt->bssid, local->bssid, ETH_ALEN) == 0) {
-#ifdef IEEE80211_IBSS_DEBUG
-               static unsigned long last_tsf_debug = 0;
-               u64 tsf;
-               if (local->hw->get_tsf)
-                       tsf = local->hw->get_tsf(local->mdev);
-               else
-                       tsf = -1LLU;
-               if (time_after(jiffies, last_tsf_debug + 5 * HZ)) {
-                       wpa_printf(MSG_DEBUG, "RX beacon SA=" MACSTR " BSSID="
-                                  MACSTR " TSF=0x%llx BCN=0x%llx diff=%lld "
-                                  "@%ld",
-                                  MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid),
-                                  tsf, timestamp, tsf - timestamp, jiffies);
-                       last_tsf_debug = jiffies;
-               }
-#endif /* IEEE80211_IBSS_DEBUG */
-       }
-#endif
-
-       ie_pos = mgmt->u.beacon.variable;
-       ie_len = len - baselen;
-       if (ieee802_11_parse_elems(ie_pos, ie_len, &elems, 0) == ParseFailed)
-               invalid = 1;
-
-#if 0 /* FIX */
-       if (local->conf.mode == IW_MODE_ADHOC && elems.supp_rates &&
-           os_memcmp(mgmt->bssid, local->bssid, ETH_ALEN) == 0 &&
-           (sta = sta_info_get(local, mgmt->sa))) {
-               struct ieee80211_rate *rates;
-               size_t num_rates;
-               u32 supp_rates, prev_rates;
-               int i, j, oper_mode;
-
-               rates = local->curr_rates;
-               num_rates = local->num_curr_rates;
-               oper_mode = wpa_s->mlme.sta_scanning ?
-                       local->scan_oper_phymode : local->conf.phymode;
-               for (i = 0; i < local->hw->num_modes; i++) {
-                       struct ieee80211_hw_modes *mode = &local->hw->modes[i];
-                       if (oper_mode == mode->mode) {
-                               rates = mode->rates;
-                               num_rates = mode->num_rates;
-                               break;
-                       }
-               }
-
-               supp_rates = 0;
-               for (i = 0; i < elems.supp_rates_len +
-                            elems.ext_supp_rates_len; i++) {
-                       u8 rate = 0;
-                       int own_rate;
-                       if (i < elems.supp_rates_len)
-                               rate = elems.supp_rates[i];
-                       else if (elems.ext_supp_rates)
-                               rate = elems.ext_supp_rates
-                                       [i - elems.supp_rates_len];
-                       own_rate = 5 * (rate & 0x7f);
-                       if (oper_mode == MODE_ATHEROS_TURBO)
-                               own_rate *= 2;
-                       for (j = 0; j < num_rates; j++)
-                               if (rates[j].rate == own_rate)
-                                       supp_rates |= BIT(j);
-               }
-
-               prev_rates = sta->supp_rates;
-               sta->supp_rates &= supp_rates;
-               if (sta->supp_rates == 0) {
-                       /* No matching rates - this should not really happen.
-                        * Make sure that at least one rate is marked
-                        * supported to avoid issues with TX rate ctrl. */
-                       sta->supp_rates = wpa_s->mlme.supp_rates_bits;
-               }
-               if (sta->supp_rates != prev_rates) {
-                       wpa_printf(MSG_DEBUG, "MLME: updated supp_rates set "
-                                  "for " MACSTR " based on beacon info "
-                                  "(0x%x & 0x%x -> 0x%x)",
-                                  MAC2STR(sta->addr), prev_rates,
-                                  supp_rates, sta->supp_rates);
-               }
-               sta_info_release(local, sta);
-       }
-#endif
-
-       if (elems.ssid == NULL)
-               return;
-
-       if (elems.ds_params && elems.ds_params_len == 1)
-               channel = elems.ds_params[0];
-       else
-               channel = rx_status->channel;
-
-       bss = ieee80211_bss_get(wpa_s, mgmt->bssid);
-       if (bss == NULL) {
-               bss = ieee80211_bss_add(wpa_s, mgmt->bssid);
-               if (bss == NULL)
-                       return;
-       } else {
-#if 0
-               /* TODO: order by RSSI? */
-               spin_lock_bh(&local->sta_bss_lock);
-               list_move_tail(&bss->list, &local->sta_bss_list);
-               spin_unlock_bh(&local->sta_bss_lock);
-#endif
-       }
-
-       if (bss->probe_resp && beacon) {
-               /* Do not allow beacon to override data from Probe Response. */
-               return;
-       }
-
-       bss->beacon_int = le_to_host16(mgmt->u.beacon.beacon_int);
-       bss->capability = le_to_host16(mgmt->u.beacon.capab_info);
-
-       if (bss->ie == NULL || bss->ie_len < ie_len) {
-               os_free(bss->ie);
-               bss->ie = os_malloc(ie_len);
-       }
-       if (bss->ie) {
-               os_memcpy(bss->ie, ie_pos, ie_len);
-               bss->ie_len = ie_len;
-       }
-
-       if (elems.ssid && elems.ssid_len <= MAX_SSID_LEN) {
-               os_memcpy(bss->ssid, elems.ssid, elems.ssid_len);
-               bss->ssid_len = elems.ssid_len;
-       }
-
-       bss->supp_rates_len = 0;
-       if (elems.supp_rates) {
-               clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len;
-               if (clen > elems.supp_rates_len)
-                       clen = elems.supp_rates_len;
-               os_memcpy(&bss->supp_rates[bss->supp_rates_len],
-                         elems.supp_rates, clen);
-               bss->supp_rates_len += clen;
-       }
-       if (elems.ext_supp_rates) {
-               clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len;
-               if (clen > elems.ext_supp_rates_len)
-                       clen = elems.ext_supp_rates_len;
-               os_memcpy(&bss->supp_rates[bss->supp_rates_len],
-                         elems.ext_supp_rates, clen);
-               bss->supp_rates_len += clen;
-       }
-
-       if (elems.wpa_ie &&
-           (bss->wpa_ie == NULL || bss->wpa_ie_len != elems.wpa_ie_len ||
-            os_memcmp(bss->wpa_ie, elems.wpa_ie, elems.wpa_ie_len))) {
-               os_free(bss->wpa_ie);
-               bss->wpa_ie = os_malloc(elems.wpa_ie_len + 2);
-               if (bss->wpa_ie) {
-                       os_memcpy(bss->wpa_ie, elems.wpa_ie - 2,
-                                 elems.wpa_ie_len + 2);
-                       bss->wpa_ie_len = elems.wpa_ie_len + 2;
-               } else
-                       bss->wpa_ie_len = 0;
-       } else if (!elems.wpa_ie && bss->wpa_ie) {
-               os_free(bss->wpa_ie);
-               bss->wpa_ie = NULL;
-               bss->wpa_ie_len = 0;
-       }
-
-       if (elems.rsn_ie &&
-           (bss->rsn_ie == NULL || bss->rsn_ie_len != elems.rsn_ie_len ||
-            os_memcmp(bss->rsn_ie, elems.rsn_ie, elems.rsn_ie_len))) {
-               os_free(bss->rsn_ie);
-               bss->rsn_ie = os_malloc(elems.rsn_ie_len + 2);
-               if (bss->rsn_ie) {
-                       os_memcpy(bss->rsn_ie, elems.rsn_ie - 2,
-                                 elems.rsn_ie_len + 2);
-                       bss->rsn_ie_len = elems.rsn_ie_len + 2;
-               } else
-                       bss->rsn_ie_len = 0;
-       } else if (!elems.rsn_ie && bss->rsn_ie) {
-               os_free(bss->rsn_ie);
-               bss->rsn_ie = NULL;
-               bss->rsn_ie_len = 0;
-       }
-
-       if (elems.wmm &&
-           (bss->wmm_ie == NULL || bss->wmm_ie_len != elems.wmm_len ||
-            os_memcmp(bss->wmm_ie, elems.wmm, elems.wmm_len))) {
-               os_free(bss->wmm_ie);
-               bss->wmm_ie = os_malloc(elems.wmm_len + 2);
-               if (bss->wmm_ie) {
-                       os_memcpy(bss->wmm_ie, elems.wmm - 2,
-                                 elems.wmm_len + 2);
-                       bss->wmm_ie_len = elems.wmm_len + 2;
-               } else
-                       bss->wmm_ie_len = 0;
-       } else if (!elems.wmm && bss->wmm_ie) {
-               os_free(bss->wmm_ie);
-               bss->wmm_ie = NULL;
-               bss->wmm_ie_len = 0;
-       }
-
-#ifdef CONFIG_IEEE80211R
-       if (elems.mdie &&
-           (bss->mdie == NULL || bss->mdie_len != elems.mdie_len ||
-            os_memcmp(bss->mdie, elems.mdie, elems.mdie_len))) {
-               os_free(bss->mdie);
-               bss->mdie = os_malloc(elems.mdie_len + 2);
-               if (bss->mdie) {
-                       os_memcpy(bss->mdie, elems.mdie - 2,
-                                 elems.mdie_len + 2);
-                       bss->mdie_len = elems.mdie_len + 2;
-               } else
-                       bss->mdie_len = 0;
-       } else if (!elems.mdie && bss->mdie) {
-               os_free(bss->mdie);
-               bss->mdie = NULL;
-               bss->mdie_len = 0;
-       }
-#endif /* CONFIG_IEEE80211R */
-
-       bss->hw_mode = wpa_s->mlme.phymode;
-       bss->channel = channel;
-       bss->freq = wpa_s->mlme.freq;
-       if (channel != wpa_s->mlme.channel &&
-           (wpa_s->mlme.phymode == HOSTAPD_MODE_IEEE80211G ||
-            wpa_s->mlme.phymode == HOSTAPD_MODE_IEEE80211B) &&
-           channel >= 1 && channel <= 14) {
-               static const int freq_list[] = {
-                       2412, 2417, 2422, 2427, 2432, 2437, 2442,
-                       2447, 2452, 2457, 2462, 2467, 2472, 2484
-               };
-               /* IEEE 802.11g/b mode can receive packets from neighboring
-                * channels, so map the channel into frequency. */
-               bss->freq = freq_list[channel - 1];
-       }
-       bss->timestamp = timestamp;
-       os_get_time(&bss->last_update);
-       bss->rssi = rx_status->ssi;
-       if (!beacon)
-               bss->probe_resp++;
-}
-
-
-static void ieee80211_rx_mgmt_probe_resp(struct wpa_supplicant *wpa_s,
-                                        struct ieee80211_mgmt *mgmt,
-                                        size_t len,
-                                        struct ieee80211_rx_status *rx_status)
-{
-       ieee80211_bss_info(wpa_s, mgmt, len, rx_status, 0);
-}
-
-
-static void ieee80211_rx_mgmt_beacon(struct wpa_supplicant *wpa_s,
-                                    struct ieee80211_mgmt *mgmt,
-                                    size_t len,
-                                    struct ieee80211_rx_status *rx_status)
-{
-       int use_protection;
-       size_t baselen;
-       struct ieee802_11_elems elems;
-
-       ieee80211_bss_info(wpa_s, mgmt, len, rx_status, 1);
-
-       if (!wpa_s->mlme.associated ||
-           os_memcmp(wpa_s->bssid, mgmt->bssid, ETH_ALEN) != 0)
-               return;
-
-       /* Process beacon from the current BSS */
-       baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt;
-       if (baselen > len)
-               return;
-
-       if (ieee802_11_parse_elems(mgmt->u.beacon.variable, len - baselen,
-                                  &elems, 0) == ParseFailed)
-               return;
-
-       use_protection = 0;
-       if (elems.erp_info && elems.erp_info_len >= 1) {
-               use_protection =
-                       (elems.erp_info[0] & ERP_INFO_USE_PROTECTION) != 0;
-       }
-
-       if (use_protection != !!wpa_s->mlme.use_protection) {
-               wpa_printf(MSG_DEBUG, "MLME: CTS protection %s (BSSID=" MACSTR
-                          ")",
-                          use_protection ? "enabled" : "disabled",
-                          MAC2STR(wpa_s->bssid));
-               wpa_s->mlme.use_protection = use_protection ? 1 : 0;
-               wpa_s->mlme.cts_protect_erp_frames = use_protection;
-       }
-
-       if (elems.wmm && wpa_s->mlme.wmm_enabled) {
-               ieee80211_sta_wmm_params(wpa_s, elems.wmm,
-                                        elems.wmm_len);
-       }
-}
-
-
-static void ieee80211_rx_mgmt_probe_req(struct wpa_supplicant *wpa_s,
-                                       struct ieee80211_mgmt *mgmt,
-                                       size_t len,
-                                       struct ieee80211_rx_status *rx_status)
-{
-       int tx_last_beacon, adhoc;
-#if 0 /* FIX */
-       struct ieee80211_mgmt *resp;
-#endif
-       u8 *pos, *end;
-       struct wpa_ssid *ssid = wpa_s->current_ssid;
-
-       adhoc = ssid && ssid->mode == WPAS_MODE_IBSS;
-
-       if (!adhoc || wpa_s->mlme.state != IEEE80211_IBSS_JOINED ||
-           len < 24 + 2 || wpa_s->mlme.probe_resp == NULL)
-               return;
-
-#if 0 /* FIX */
-       if (local->hw->tx_last_beacon)
-               tx_last_beacon = local->hw->tx_last_beacon(local->mdev);
-       else
-#endif
-               tx_last_beacon = 1;
-
-#ifdef IEEE80211_IBSS_DEBUG
-       wpa_printf(MSG_DEBUG, "MLME: RX ProbeReq SA=" MACSTR " DA=" MACSTR
-                  " BSSID=" MACSTR " (tx_last_beacon=%d)",
-                  MAC2STR(mgmt->sa), MAC2STR(mgmt->da),
-                  MAC2STR(mgmt->bssid), tx_last_beacon);
-#endif /* IEEE80211_IBSS_DEBUG */
-
-       if (!tx_last_beacon)
-               return;
-
-       if (os_memcmp(mgmt->bssid, wpa_s->bssid, ETH_ALEN) != 0 &&
-           os_memcmp(mgmt->bssid, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) != 0)
-               return;
-
-       end = ((u8 *) mgmt) + len;
-       pos = mgmt->u.probe_req.variable;
-       if (pos[0] != WLAN_EID_SSID ||
-           pos + 2 + pos[1] > end) {
-               wpa_printf(MSG_DEBUG, "MLME: Invalid SSID IE in ProbeReq from "
-                          MACSTR, MAC2STR(mgmt->sa));
-               return;
-       }
-       if (pos[1] != 0 &&
-           (pos[1] != wpa_s->mlme.ssid_len ||
-            os_memcmp(pos + 2, wpa_s->mlme.ssid, wpa_s->mlme.ssid_len) != 0))
-       {
-               /* Ignore ProbeReq for foreign SSID */
-               return;
-       }
-
-#if 0 /* FIX */
-       /* Reply with ProbeResp */
-       skb = skb_copy(wpa_s->mlme.probe_resp, GFP_ATOMIC);
-       if (skb == NULL)
-               return;
-
-       resp = (struct ieee80211_mgmt *) skb->data;
-       os_memcpy(resp->da, mgmt->sa, ETH_ALEN);
-#ifdef IEEE80211_IBSS_DEBUG
-       wpa_printf(MSG_DEBUG, "MLME: Sending ProbeResp to " MACSTR,
-                  MAC2STR(resp->da));
-#endif /* IEEE80211_IBSS_DEBUG */
-       ieee80211_sta_tx(wpa_s, skb, 0, 1);
-#endif
-}
-
-
-#ifdef CONFIG_IEEE80211R
-static void ieee80211_rx_mgmt_ft_action(struct wpa_supplicant *wpa_s,
-                                       struct ieee80211_mgmt *mgmt,
-                                       size_t len,
-                                       struct ieee80211_rx_status *rx_status)
-{
-       union wpa_event_data data;
-       u16 status;
-       u8 *sta_addr, *target_ap_addr;
-
-       if (len < 24 + 1 + sizeof(mgmt->u.action.u.ft_action_resp)) {
-               wpa_printf(MSG_DEBUG, "MLME: Too short FT Action frame");
-               return;
-       }
-
-       /*
-        * Only FT Action Response is needed for now since reservation
-        * protocol is not supported.
-        */
-       if (mgmt->u.action.u.ft_action_resp.action != 2) {
-               wpa_printf(MSG_DEBUG, "MLME: Unexpected FT Action %d",
-                          mgmt->u.action.u.ft_action_resp.action);
-               return;
-       }
-
-       status = le_to_host16(mgmt->u.action.u.ft_action_resp.status_code);
-       sta_addr = mgmt->u.action.u.ft_action_resp.sta_addr;
-       target_ap_addr = mgmt->u.action.u.ft_action_resp.target_ap_addr;
-       wpa_printf(MSG_DEBUG, "MLME: Received FT Action Response: STA " MACSTR
-                  " TargetAP " MACSTR " Status Code %d",
-                  MAC2STR(sta_addr), MAC2STR(target_ap_addr), status);
-       if (os_memcmp(sta_addr, wpa_s->own_addr, ETH_ALEN) != 0) {
-               wpa_printf(MSG_DEBUG, "MLME: Foreign STA Address " MACSTR
-                          " in FT Action Response", MAC2STR(sta_addr));
-               return;
-       }
-
-       if (status) {
-               wpa_printf(MSG_DEBUG, "MLME: FT Action Response indicates "
-                          "failure (status code %d)", status);
-               /* TODO: report error to FT code(?) */
-               return;
-       }
-
-       os_memset(&data, 0, sizeof(data));
-       data.ft_ies.ies = mgmt->u.action.u.ft_action_resp.variable;
-       data.ft_ies.ies_len = len - (mgmt->u.action.u.ft_action_resp.variable -
-                                    (u8 *) mgmt);
-       data.ft_ies.ft_action = 1;
-       os_memcpy(data.ft_ies.target_ap, target_ap_addr, ETH_ALEN);
-       wpa_supplicant_event(wpa_s, EVENT_FT_RESPONSE, &data);
-       /* TODO: should only re-associate, if EVENT_FT_RESPONSE was processed
-        * successfully */
-       wpa_s->mlme.prev_bssid_set = 1;
-       wpa_s->mlme.auth_alg = WLAN_AUTH_FT;
-       os_memcpy(wpa_s->mlme.prev_bssid, wpa_s->bssid, ETH_ALEN);
-       os_memcpy(wpa_s->bssid, target_ap_addr, ETH_ALEN);
-       ieee80211_associate(wpa_s);
-}
-#endif /* CONFIG_IEEE80211R */
-
-
-#ifdef CONFIG_IEEE80211W
-
-/* MLME-SAQuery.response */
-static int ieee80211_sta_send_sa_query_resp(struct wpa_supplicant *wpa_s,
-                                           const u8 *addr, const u8 *trans_id)
-{
-       struct ieee80211_mgmt *mgmt;
-       int res;
-       size_t len;
-
-       mgmt = os_zalloc(sizeof(*mgmt));
-       if (mgmt == NULL) {
-               wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
-                          "SA Query action frame");
-               return -1;
-       }
-
-       len = 24;
-       os_memcpy(mgmt->da, addr, ETH_ALEN);
-       os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
-       os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
-       mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
-                                          WLAN_FC_STYPE_ACTION);
-       mgmt->u.action.category = WLAN_ACTION_SA_QUERY;
-       mgmt->u.action.u.sa_query_resp.action = WLAN_SA_QUERY_RESPONSE;
-       os_memcpy(mgmt->u.action.u.sa_query_resp.trans_id, trans_id,
-                 WLAN_SA_QUERY_TR_ID_LEN);
-       len += 1 + sizeof(mgmt->u.action.u.sa_query_resp);
-
-       res = ieee80211_sta_tx(wpa_s, (u8 *) mgmt, len);
-       os_free(mgmt);
-
-       return res;
-}
-
-
-static void ieee80211_rx_mgmt_sa_query_action(
-       struct wpa_supplicant *wpa_s, struct ieee80211_mgmt *mgmt, size_t len,
-       struct ieee80211_rx_status *rx_status)
-{
-       if (len < 24 + 1 + sizeof(mgmt->u.action.u.sa_query_req)) {
-               wpa_printf(MSG_DEBUG, "MLME: Too short SA Query Action frame");
-               return;
-       }
-
-       if (mgmt->u.action.u.sa_query_req.action != WLAN_SA_QUERY_REQUEST) {
-               wpa_printf(MSG_DEBUG, "MLME: Unexpected SA Query Action %d",
-                          mgmt->u.action.u.sa_query_req.action);
-               return;
-       }
-
-       if (os_memcmp(mgmt->sa, wpa_s->bssid, ETH_ALEN) != 0) {
-               wpa_printf(MSG_DEBUG, "MLME: Ignore SA Query from unknown "
-                          "source " MACSTR, MAC2STR(mgmt->sa));
-               return;
-       }
-
-       if (wpa_s->mlme.state == IEEE80211_ASSOCIATE) {
-               wpa_printf(MSG_DEBUG, "MLME: Ignore SA query request during "
-                          "association process");
-               return;
-       }
-
-       wpa_printf(MSG_DEBUG, "MLME: Replying to SA Query request");
-       ieee80211_sta_send_sa_query_resp(wpa_s, mgmt->sa, mgmt->u.action.u.
-                                        sa_query_req.trans_id);
-}
-
-#endif /* CONFIG_IEEE80211W */
-
-
-static void dump_tspec(struct wmm_tspec_element *tspec)
-{
-       int up, psb, dir, tid;
-       u16 val;
-
-       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->medium_time);
-       wpa_printf(MSG_DEBUG, "WMM: Medium Time: %u (= %u usec/sec)",
-                  val, 32 * val);
-}
-
-
-static int is_wmm_tspec(const u8 *ie, size_t len)
-{
-       const struct wmm_tspec_element *tspec;
-
-       if (len < sizeof(*tspec))
-               return 0;
-
-       tspec = (const struct wmm_tspec_element *) ie;
-       if (tspec->eid != WLAN_EID_VENDOR_SPECIFIC ||
-           tspec->length < sizeof(*tspec) - 2 ||
-           tspec->oui[0] != 0x00 || tspec->oui[1] != 0x50 ||
-           tspec->oui[2] != 0xf2 || tspec->oui_type != 2 ||
-           tspec->oui_subtype != 2 || tspec->version != 1)
-               return 0;
-
-       return 1;
-}
-
-
-static void ieee80211_rx_addts_resp(
-       struct wpa_supplicant *wpa_s, struct ieee80211_mgmt *mgmt, size_t len,
-       size_t var_len)
-{
-       struct wmm_tspec_element *tspec;
-
-       wpa_printf(MSG_DEBUG, "WMM: Received ADDTS Response");
-       wpa_hexdump(MSG_MSGDUMP, "WMM: ADDTS Response IE(s)",
-                   mgmt->u.action.u.wmm_action.variable, var_len);
-       if (!is_wmm_tspec(mgmt->u.action.u.wmm_action.variable, var_len))
-               return;
-       tspec = (struct wmm_tspec_element *)
-               mgmt->u.action.u.wmm_action.variable;
-       dump_tspec(tspec);
-}
-
-
-static void ieee80211_rx_delts(
-       struct wpa_supplicant *wpa_s, struct ieee80211_mgmt *mgmt, size_t len,
-       size_t var_len)
-{
-       struct wmm_tspec_element *tspec;
-
-       wpa_printf(MSG_DEBUG, "WMM: Received DELTS");
-       wpa_hexdump(MSG_MSGDUMP, "WMM: DELTS IE(s)",
-                   mgmt->u.action.u.wmm_action.variable, var_len);
-       if (!is_wmm_tspec(mgmt->u.action.u.wmm_action.variable, var_len))
-               return;
-       tspec = (struct wmm_tspec_element *)
-               mgmt->u.action.u.wmm_action.variable;
-       dump_tspec(tspec);
-}
-
-
-static void ieee80211_rx_mgmt_wmm_action(
-       struct wpa_supplicant *wpa_s, struct ieee80211_mgmt *mgmt, size_t len,
-       struct ieee80211_rx_status *rx_status)
-{
-       size_t alen;
-
-       alen = mgmt->u.action.u.wmm_action.variable - (u8 *) mgmt;
-       if (len < alen) {
-               wpa_printf(MSG_DEBUG, "WMM: Received Action frame too short");
-               return;
-       }
-
-       wpa_printf(MSG_DEBUG, "WMM: Received Action frame: Action Code %d, "
-                  "Dialog Token %d, Status Code %d",
-                  mgmt->u.action.u.wmm_action.action_code,
-                  mgmt->u.action.u.wmm_action.dialog_token,
-                  mgmt->u.action.u.wmm_action.status_code);
-
-       switch (mgmt->u.action.u.wmm_action.action_code) {
-       case WMM_ACTION_CODE_ADDTS_RESP:
-               ieee80211_rx_addts_resp(wpa_s, mgmt, len, len - alen);
-               break;
-       case WMM_ACTION_CODE_DELTS:
-               ieee80211_rx_delts(wpa_s, mgmt, len, len - alen);
-               break;
-       default:
-               wpa_printf(MSG_DEBUG, "WMM: Unsupported Action Code %d",
-                          mgmt->u.action.u.wmm_action.action_code);
-               break;
-       }
-}
-
-
-static void ieee80211_rx_mgmt_action(struct wpa_supplicant *wpa_s,
-                                    struct ieee80211_mgmt *mgmt,
-                                    size_t len,
-                                    struct ieee80211_rx_status *rx_status)
-{
-       wpa_printf(MSG_DEBUG, "MLME: received Action frame");
-
-       if (len < 25)
-               return;
-
-       switch (mgmt->u.action.category) {
-#ifdef CONFIG_IEEE80211R
-       case WLAN_ACTION_FT:
-               ieee80211_rx_mgmt_ft_action(wpa_s, mgmt, len, rx_status);
-               break;
-#endif /* CONFIG_IEEE80211R */
-#ifdef CONFIG_IEEE80211W
-       case WLAN_ACTION_SA_QUERY:
-               ieee80211_rx_mgmt_sa_query_action(wpa_s, mgmt, len, rx_status);
-               break;
-#endif /* CONFIG_IEEE80211W */
-       case WLAN_ACTION_WMM:
-               ieee80211_rx_mgmt_wmm_action(wpa_s, mgmt, len, rx_status);
-               break;
-       case WLAN_ACTION_PUBLIC:
-               if (wpa_s->mlme.public_action_cb) {
-                       wpa_s->mlme.public_action_cb(
-                               wpa_s->mlme.public_action_cb_ctx,
-                               (u8 *) mgmt, len, rx_status->freq);
-                       return;
-               }
-               break;
-       default:
-               wpa_printf(MSG_DEBUG, "MLME: unknown Action Category %d",
-                          mgmt->u.action.category);
-               break;
-       }
-}
-
-
-static void ieee80211_sta_rx_mgmt(struct wpa_supplicant *wpa_s,
-                                 const u8 *buf, size_t len,
-                                 struct ieee80211_rx_status *rx_status)
-{
-       struct ieee80211_mgmt *mgmt;
-       u16 fc;
-
-       if (len < 24)
-               return;
-
-       mgmt = (struct ieee80211_mgmt *) buf;
-       fc = le_to_host16(mgmt->frame_control);
-
-       switch (WLAN_FC_GET_STYPE(fc)) {
-       case WLAN_FC_STYPE_PROBE_REQ:
-               ieee80211_rx_mgmt_probe_req(wpa_s, mgmt, len, rx_status);
-               break;
-       case WLAN_FC_STYPE_PROBE_RESP:
-               ieee80211_rx_mgmt_probe_resp(wpa_s, mgmt, len, rx_status);
-               break;
-       case WLAN_FC_STYPE_BEACON:
-               ieee80211_rx_mgmt_beacon(wpa_s, mgmt, len, rx_status);
-               break;
-       case WLAN_FC_STYPE_AUTH:
-               ieee80211_rx_mgmt_auth(wpa_s, mgmt, len, rx_status);
-               break;
-       case WLAN_FC_STYPE_ASSOC_RESP:
-               ieee80211_rx_mgmt_assoc_resp(wpa_s, mgmt, len, rx_status, 0);
-               break;
-       case WLAN_FC_STYPE_REASSOC_RESP:
-               ieee80211_rx_mgmt_assoc_resp(wpa_s, mgmt, len, rx_status, 1);
-               break;
-       case WLAN_FC_STYPE_DEAUTH:
-               ieee80211_rx_mgmt_deauth(wpa_s, mgmt, len, rx_status);
-               break;
-       case WLAN_FC_STYPE_DISASSOC:
-               ieee80211_rx_mgmt_disassoc(wpa_s, mgmt, len, rx_status);
-               break;
-       case WLAN_FC_STYPE_ACTION:
-               ieee80211_rx_mgmt_action(wpa_s, mgmt, len, rx_status);
-               break;
-       default:
-               wpa_printf(MSG_DEBUG, "MLME: received unknown management "
-                          "frame - stype=%d", WLAN_FC_GET_STYPE(fc));
-               break;
-       }
-}
-
-
-static void ieee80211_sta_rx_scan(struct wpa_supplicant *wpa_s,
-                                 const u8 *buf, size_t len,
-                                 struct ieee80211_rx_status *rx_status)
-{
-       struct ieee80211_mgmt *mgmt;
-       u16 fc;
-
-       if (len < 24)
-               return;
-
-       mgmt = (struct ieee80211_mgmt *) buf;
-       fc = le_to_host16(mgmt->frame_control);
-
-       if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT) {
-               if (WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_RESP) {
-                       ieee80211_rx_mgmt_probe_resp(wpa_s, mgmt,
-                                                    len, rx_status);
-               } else if (WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON) {
-                       ieee80211_rx_mgmt_beacon(wpa_s, mgmt, len, rx_status);
-               }
-       }
-}
-
-
-static int ieee80211_sta_active_ibss(struct wpa_supplicant *wpa_s)
-{
-       int active = 0;
-
-#if 0 /* FIX */
-       list_for_each(ptr, &local->sta_list) {
-               sta = list_entry(ptr, struct sta_info, list);
-               if (sta->dev == dev &&
-                   time_after(sta->last_rx + IEEE80211_IBSS_MERGE_INTERVAL,
-                              jiffies)) {
-                       active++;
-                       break;
-               }
-       }
-#endif
-
-       return active;
-}
-
-
-static void ieee80211_sta_expire(struct wpa_supplicant *wpa_s)
-{
-#if 0 /* FIX */
-       list_for_each_safe(ptr, n, &local->sta_list) {
-               sta = list_entry(ptr, struct sta_info, list);
-               if (time_after(jiffies, sta->last_rx +
-                              IEEE80211_IBSS_INACTIVITY_LIMIT)) {
-                       wpa_printf(MSG_DEBUG, "MLME: expiring inactive STA "
-                                  MACSTR, MAC2STR(sta->addr));
-                       sta_info_free(local, sta, 1);
-               }
-       }
-#endif
-}
-
-
-static void ieee80211_sta_merge_ibss(struct wpa_supplicant *wpa_s)
-{
-       struct wpa_driver_scan_params params;
-
-       ieee80211_reschedule_timer(wpa_s, IEEE80211_IBSS_MERGE_INTERVAL);
-
-       ieee80211_sta_expire(wpa_s);
-       if (ieee80211_sta_active_ibss(wpa_s))
-               return;
-
-       wpa_printf(MSG_DEBUG, "MLME: No active IBSS STAs - trying to scan for "
-                  "other IBSS networks with same SSID (merge)");
-       os_memset(&params, 0, sizeof(params));
-       params.ssids[0].ssid = wpa_s->mlme.ssid;
-       params.ssids[0].ssid_len = wpa_s->mlme.ssid_len;
-       params.num_ssids = wpa_s->mlme.ssid_len ? 1 : 0;
-       ieee80211_sta_req_scan(wpa_s, &params);
-}
-
-
-static void ieee80211_sta_timer(void *eloop_ctx, void *timeout_ctx)
-{
-       struct wpa_supplicant *wpa_s = eloop_ctx;
-
-       switch (wpa_s->mlme.state) {
-       case IEEE80211_DISABLED:
-               break;
-       case IEEE80211_AUTHENTICATE:
-               ieee80211_authenticate(wpa_s);
-               break;
-       case IEEE80211_ASSOCIATE:
-               ieee80211_associate(wpa_s);
-               break;
-       case IEEE80211_ASSOCIATED:
-               ieee80211_associated(wpa_s);
-               break;
-       case IEEE80211_IBSS_SEARCH:
-               ieee80211_sta_find_ibss(wpa_s);
-               break;
-       case IEEE80211_IBSS_JOINED:
-               ieee80211_sta_merge_ibss(wpa_s);
-               break;
-       default:
-               wpa_printf(MSG_DEBUG, "ieee80211_sta_timer: Unknown state %d",
-                          wpa_s->mlme.state);
-               break;
-       }
-
-       if (ieee80211_privacy_mismatch(wpa_s)) {
-               wpa_printf(MSG_DEBUG, "MLME: privacy configuration mismatch "
-                          "and mixed-cell disabled - disassociate");
-
-               ieee80211_send_disassoc(wpa_s, WLAN_REASON_UNSPECIFIED);
-               ieee80211_set_associated(wpa_s, 0);
-       }
-}
-
-
-static void ieee80211_sta_new_auth(struct wpa_supplicant *wpa_s)
-{
-       struct wpa_ssid *ssid = wpa_s->current_ssid;
-       if (ssid && ssid->mode != WPAS_MODE_INFRA)
-               return;
-
-#if 0 /* FIX */
-       if (local->hw->reset_tsf) {
-               /* Reset own TSF to allow time synchronization work. */
-               local->hw->reset_tsf(local->mdev);
-       }
-#endif
-
-       wpa_s->mlme.wmm_last_param_set = -1; /* allow any WMM update */
-
-
-       if (wpa_s->mlme.auth_algs & WPA_AUTH_ALG_OPEN)
-               wpa_s->mlme.auth_alg = WLAN_AUTH_OPEN;
-       else if (wpa_s->mlme.auth_algs & WPA_AUTH_ALG_SHARED)
-               wpa_s->mlme.auth_alg = WLAN_AUTH_SHARED_KEY;
-       else if (wpa_s->mlme.auth_algs & WPA_AUTH_ALG_LEAP)
-               wpa_s->mlme.auth_alg = WLAN_AUTH_LEAP;
-       else
-               wpa_s->mlme.auth_alg = WLAN_AUTH_OPEN;
-       wpa_printf(MSG_DEBUG, "MLME: Initial auth_alg=%d",
-                  wpa_s->mlme.auth_alg);
-       wpa_s->mlme.auth_transaction = -1;
-       wpa_s->mlme.auth_tries = wpa_s->mlme.assoc_tries = 0;
-       ieee80211_authenticate(wpa_s);
-}
-
-
-static int ieee80211_ibss_allowed(struct wpa_supplicant *wpa_s)
-{
-#if 0 /* FIX */
-       int m, c;
-
-       for (m = 0; m < local->hw->num_modes; m++) {
-               struct ieee80211_hw_modes *mode = &local->hw->modes[m];
-               if (mode->mode != local->conf.phymode)
-                       continue;
-               for (c = 0; c < mode->num_channels; c++) {
-                       struct ieee80211_channel *chan = &mode->channels[c];
-                       if (chan->flag & IEEE80211_CHAN_W_SCAN &&
-                           chan->chan == local->conf.channel) {
-                               if (chan->flag & IEEE80211_CHAN_W_IBSS)
-                                       return 1;
-                               break;
-                       }
-               }
-       }
-#endif
-
-       return 0;
-}
-
-
-static int ieee80211_sta_join_ibss(struct wpa_supplicant *wpa_s,
-                                  struct ieee80211_sta_bss *bss)
-{
-       int res = 0, rates, done = 0, bssid_changed;
-       struct ieee80211_mgmt *mgmt;
-#if 0 /* FIX */
-       struct ieee80211_tx_control control;
-       struct ieee80211_rate *rate;
-       struct rate_control_extra extra;
-#endif
-       u8 *pos, *buf;
-       size_t len;
-
-       /* Remove possible STA entries from other IBSS networks. */
-#if 0 /* FIX */
-       sta_info_flush(local, NULL);
-
-       if (local->hw->reset_tsf) {
-               /* Reset own TSF to allow time synchronization work. */
-               local->hw->reset_tsf(local->mdev);
-       }
-#endif
-       bssid_changed = os_memcmp(wpa_s->bssid, bss->bssid, ETH_ALEN);
-       os_memcpy(wpa_s->bssid, bss->bssid, ETH_ALEN);
-       if (bssid_changed)
-               wpas_notify_bssid_changed(wpa_s);
-
-#if 0 /* FIX */
-       local->conf.beacon_int = bss->beacon_int >= 10 ? bss->beacon_int : 10;
-
-       sdata->drop_unencrypted = bss->capability &
-               host_to_le16(WLAN_CAPABILITY_PRIVACY) ? 1 : 0;
-#endif
-
-#if 0 /* FIX */
-       os_memset(&rq, 0, sizeof(rq));
-       rq.m = bss->freq * 100000;
-       rq.e = 1;
-       res = ieee80211_ioctl_siwfreq(wpa_s, NULL, &rq, NULL);
-#endif
-
-       if (!ieee80211_ibss_allowed(wpa_s)) {
-#if 0 /* FIX */
-               wpa_printf(MSG_DEBUG, "MLME: IBSS not allowed on channel %d "
-                          "(%d MHz)", local->conf.channel,
-                          local->conf.freq);
-#endif
-               return -1;
-       }
-
-       /* Set beacon template based on scan results */
-       buf = os_malloc(400);
-       len = 0;
-       do {
-               if (buf == NULL)
-                       break;
-
-               mgmt = (struct ieee80211_mgmt *) buf;
-               len += 24 + sizeof(mgmt->u.beacon);
-               os_memset(mgmt, 0, 24 + sizeof(mgmt->u.beacon));
-               mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
-                                                  WLAN_FC_STYPE_BEACON);
-               os_memset(mgmt->da, 0xff, ETH_ALEN);
-               os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
-               os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
-#if 0 /* FIX */
-               mgmt->u.beacon.beacon_int =
-                       host_to_le16(local->conf.beacon_int);
-#endif
-               mgmt->u.beacon.capab_info = host_to_le16(bss->capability);
-
-               pos = buf + len;
-               len += 2 + wpa_s->mlme.ssid_len;
-               *pos++ = WLAN_EID_SSID;
-               *pos++ = wpa_s->mlme.ssid_len;
-               os_memcpy(pos, wpa_s->mlme.ssid, wpa_s->mlme.ssid_len);
-
-               rates = bss->supp_rates_len;
-               if (rates > 8)
-                       rates = 8;
-               pos = buf + len;
-               len += 2 + rates;
-               *pos++ = WLAN_EID_SUPP_RATES;
-               *pos++ = rates;
-               os_memcpy(pos, bss->supp_rates, rates);
-
-               pos = buf + len;
-               len += 2 + 1;
-               *pos++ = WLAN_EID_DS_PARAMS;
-               *pos++ = 1;
-               *pos++ = bss->channel;
-
-               pos = buf + len;
-               len += 2 + 2;
-               *pos++ = WLAN_EID_IBSS_PARAMS;
-               *pos++ = 2;
-               /* FIX: set ATIM window based on scan results */
-               *pos++ = 0;
-               *pos++ = 0;
-
-               if (bss->supp_rates_len > 8) {
-                       rates = bss->supp_rates_len - 8;
-                       pos = buf + len;
-                       len += 2 + rates;
-                       *pos++ = WLAN_EID_EXT_SUPP_RATES;
-                       *pos++ = rates;
-                       os_memcpy(pos, &bss->supp_rates[8], rates);
-               }
-
-#if 0 /* FIX */
-               os_memset(&control, 0, sizeof(control));
-               control.pkt_type = PKT_PROBE_RESP;
-               os_memset(&extra, 0, sizeof(extra));
-               extra.endidx = local->num_curr_rates;
-               rate = rate_control_get_rate(wpa_s, skb, &extra);
-               if (rate == NULL) {
-                       wpa_printf(MSG_DEBUG, "MLME: Failed to determine TX "
-                                  "rate for IBSS beacon");
-                       break;
-               }
-               control.tx_rate = (wpa_s->mlme.short_preamble &&
-                                  (rate->flags & IEEE80211_RATE_PREAMBLE2)) ?
-                       rate->val2 : rate->val;
-               control.antenna_sel = local->conf.antenna_sel;
-               control.power_level = local->conf.power_level;
-               control.no_ack = 1;
-               control.retry_limit = 1;
-               control.rts_cts_duration = 0;
-#endif
-
-#if 0 /* FIX */
-               wpa_s->mlme.probe_resp = skb_copy(skb, GFP_ATOMIC);
-               if (wpa_s->mlme.probe_resp) {
-                       mgmt = (struct ieee80211_mgmt *)
-                               wpa_s->mlme.probe_resp->data;
-                       mgmt->frame_control =
-                               IEEE80211_FC(WLAN_FC_TYPE_MGMT,
-                                            WLAN_FC_STYPE_PROBE_RESP);
-               } else {
-                       wpa_printf(MSG_DEBUG, "MLME: Could not allocate "
-                                  "ProbeResp template for IBSS");
-               }
-
-               if (local->hw->beacon_update &&
-                   local->hw->beacon_update(wpa_s, skb, &control) == 0) {
-                       wpa_printf(MSG_DEBUG, "MLME: Configured IBSS beacon "
-                                  "template based on scan results");
-                       skb = NULL;
-               }
-
-               rates = 0;
-               for (i = 0; i < bss->supp_rates_len; i++) {
-                       int rate = (bss->supp_rates[i] & 0x7f) * 5;
-                       if (local->conf.phymode == MODE_ATHEROS_TURBO)
-                               rate *= 2;
-                       for (j = 0; j < local->num_curr_rates; j++)
-                               if (local->curr_rates[j] == rate)
-                                       rates |= BIT(j);
-               }
-               wpa_s->mlme.supp_rates_bits = rates;
-#endif
-               done = 1;
-       } while (0);
-
-       os_free(buf);
-       if (!done) {
-               wpa_printf(MSG_DEBUG, "MLME: Failed to configure IBSS beacon "
-                          "template");
-       }
-
-       wpa_s->mlme.state = IEEE80211_IBSS_JOINED;
-       ieee80211_reschedule_timer(wpa_s, IEEE80211_IBSS_MERGE_INTERVAL);
-
-       return res;
-}
-
-
-#if 0 /* FIX */
-static int ieee80211_sta_create_ibss(struct wpa_supplicant *wpa_s)
-{
-       struct ieee80211_sta_bss *bss;
-       u8 bssid[ETH_ALEN], *pos;
-       int i;
-
-#if 0
-       /* Easier testing, use fixed BSSID. */
-       os_memset(bssid, 0xfe, ETH_ALEN);
-#else
-       /* Generate random, not broadcast, locally administered BSSID. Mix in
-        * own MAC address to make sure that devices that do not have proper
-        * random number generator get different BSSID. */
-       os_get_random(bssid, ETH_ALEN);
-       for (i = 0; i < ETH_ALEN; i++)
-               bssid[i] ^= wpa_s->own_addr[i];
-       bssid[0] &= ~0x01;
-       bssid[0] |= 0x02;
-#endif
-
-       wpa_printf(MSG_DEBUG, "MLME: Creating new IBSS network, BSSID "
-                  MACSTR "", MAC2STR(bssid));
-
-       bss = ieee80211_bss_add(wpa_s, bssid);
-       if (bss == NULL)
-               return -ENOMEM;
-
-#if 0 /* FIX */
-       if (local->conf.beacon_int == 0)
-               local->conf.beacon_int = 100;
-       bss->beacon_int = local->conf.beacon_int;
-       bss->hw_mode = local->conf.phymode;
-       bss->channel = local->conf.channel;
-       bss->freq = local->conf.freq;
-#endif
-       os_get_time(&bss->last_update);
-       bss->capability = host_to_le16(WLAN_CAPABILITY_IBSS);
-#if 0 /* FIX */
-       if (sdata->default_key) {
-               bss->capability |= host_to_le16(WLAN_CAPABILITY_PRIVACY);
-       } else
-               sdata->drop_unencrypted = 0;
-       bss->supp_rates_len = local->num_curr_rates;
-#endif
-       pos = bss->supp_rates;
-#if 0 /* FIX */
-       for (i = 0; i < local->num_curr_rates; i++) {
-               int rate = local->curr_rates[i];
-               if (local->conf.phymode == MODE_ATHEROS_TURBO)
-                       rate /= 2;
-               *pos++ = (u8) (rate / 5);
-       }
-#endif
-
-       return ieee80211_sta_join_ibss(wpa_s, bss);
-}
-#endif
-
-
-static int ieee80211_sta_find_ibss(struct wpa_supplicant *wpa_s)
-{
-       struct ieee80211_sta_bss *bss;
-       int found = 0;
-       u8 bssid[ETH_ALEN];
-       int active_ibss;
-       struct os_time now;
-
-       if (wpa_s->mlme.ssid_len == 0)
-               return -EINVAL;
-
-       active_ibss = ieee80211_sta_active_ibss(wpa_s);
-#ifdef IEEE80211_IBSS_DEBUG
-       wpa_printf(MSG_DEBUG, "MLME: sta_find_ibss (active_ibss=%d)",
-                  active_ibss);
-#endif /* IEEE80211_IBSS_DEBUG */
-       for (bss = wpa_s->mlme.sta_bss_list; bss; bss = bss->next) {
-               if (wpa_s->mlme.ssid_len != bss->ssid_len ||
-                   os_memcmp(wpa_s->mlme.ssid, bss->ssid, bss->ssid_len) != 0
-                   || !(bss->capability & WLAN_CAPABILITY_IBSS))
-                       continue;
-#ifdef IEEE80211_IBSS_DEBUG
-               wpa_printf(MSG_DEBUG, "   bssid=" MACSTR " found",
-                          MAC2STR(bss->bssid));
-#endif /* IEEE80211_IBSS_DEBUG */
-               os_memcpy(bssid, bss->bssid, ETH_ALEN);
-               found = 1;
-               if (active_ibss ||
-                   os_memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0)
-                       break;
-       }
-
-#ifdef IEEE80211_IBSS_DEBUG
-       wpa_printf(MSG_DEBUG, "   sta_find_ibss: selected " MACSTR " current "
-                  MACSTR, MAC2STR(bssid), MAC2STR(wpa_s->bssid));
-#endif /* IEEE80211_IBSS_DEBUG */
-       if (found && os_memcmp(wpa_s->bssid, bssid, ETH_ALEN) != 0 &&
-           (bss = ieee80211_bss_get(wpa_s, bssid))) {
-               wpa_printf(MSG_DEBUG, "MLME: Selected IBSS BSSID " MACSTR
-                          " based on configured SSID",
-                          MAC2STR(bssid));
-               return ieee80211_sta_join_ibss(wpa_s, bss);
-       }
-#ifdef IEEE80211_IBSS_DEBUG
-       wpa_printf(MSG_DEBUG, "   did not try to join ibss");
-#endif /* IEEE80211_IBSS_DEBUG */
-
-       /* Selected IBSS not found in current scan results - try to scan */
-       os_get_time(&now);
-#if 0 /* FIX */
-       if (wpa_s->mlme.state == IEEE80211_IBSS_JOINED &&
-           !ieee80211_sta_active_ibss(wpa_s)) {
-               ieee80211_reschedule_timer(wpa_s,
-                                          IEEE80211_IBSS_MERGE_INTERVAL);
-       } else if (time_after(jiffies, wpa_s->mlme.last_scan_completed +
-                             IEEE80211_SCAN_INTERVAL)) {
-               wpa_printf(MSG_DEBUG, "MLME: Trigger new scan to find an IBSS "
-                          "to join");
-               return ieee80211_sta_req_scan(wpa_s->mlme.ssid,
-                                             wpa_s->mlme.ssid_len);
-       } else if (wpa_s->mlme.state != IEEE80211_IBSS_JOINED) {
-               int interval = IEEE80211_SCAN_INTERVAL;
-
-               if (time_after(jiffies, wpa_s->mlme.ibss_join_req +
-                              IEEE80211_IBSS_JOIN_TIMEOUT)) {
-                       if (wpa_s->mlme.create_ibss &&
-                           ieee80211_ibss_allowed(wpa_s))
-                               return ieee80211_sta_create_ibss(wpa_s);
-                       if (wpa_s->mlme.create_ibss) {
-                               wpa_printf(MSG_DEBUG, "MLME: IBSS not allowed "
-                                          "on the configured channel %d "
-                                          "(%d MHz)",
-                                          local->conf.channel,
-                                          local->conf.freq);
-                       }
-
-                       /* No IBSS found - decrease scan interval and continue
-                        * scanning. */
-                       interval = IEEE80211_SCAN_INTERVAL_SLOW;
-               }
-
-               wpa_s->mlme.state = IEEE80211_IBSS_SEARCH;
-               ieee80211_reschedule_timer(wpa_s, interval);
-               return 0;
-       }
-#endif
-
-       return 0;
-}
-
-
-int ieee80211_sta_get_ssid(struct wpa_supplicant *wpa_s, u8 *ssid,
-                          size_t *len)
-{
-       os_memcpy(ssid, wpa_s->mlme.ssid, wpa_s->mlme.ssid_len);
-       *len = wpa_s->mlme.ssid_len;
-       return 0;
-}
-
-
-int ieee80211_sta_associate(struct wpa_supplicant *wpa_s,
-                           struct wpa_driver_associate_params *params)
-{
-       struct ieee80211_sta_bss *bss;
-       int bssid_changed;
-
-       wpa_s->mlme.bssid_set = 0;
-       wpa_s->mlme.freq = params->freq;
-       if (params->bssid) {
-               bssid_changed = os_memcmp(wpa_s->bssid, params->bssid,
-                                         ETH_ALEN);
-               os_memcpy(wpa_s->bssid, params->bssid, ETH_ALEN);
-               if (bssid_changed)
-                       wpas_notify_bssid_changed(wpa_s);
-
-               if (!is_zero_ether_addr(params->bssid))
-                       wpa_s->mlme.bssid_set = 1;
-               bss = ieee80211_bss_get(wpa_s, wpa_s->bssid);
-               if (bss) {
-                       wpa_s->mlme.phymode = bss->hw_mode;
-                       wpa_s->mlme.channel = bss->channel;
-                       wpa_s->mlme.freq = bss->freq;
-               }
-       }
-
-#if 0 /* FIX */
-       /* TODO: This should always be done for IBSS, even if IEEE80211_QOS is
-        * not defined. */
-       if (local->hw->conf_tx) {
-               struct ieee80211_tx_queue_params qparam;
-               int i;
-
-               os_memset(&qparam, 0, sizeof(qparam));
-               /* TODO: are these ok defaults for all hw_modes? */
-               qparam.aifs = 2;
-               qparam.cw_min =
-                       local->conf.phymode == MODE_IEEE80211B ? 31 : 15;
-               qparam.cw_max = 1023;
-               qparam.burst_time = 0;
-               for (i = IEEE80211_TX_QUEUE_DATA0; i < NUM_TX_DATA_QUEUES; i++)
-               {
-                       local->hw->conf_tx(wpa_s, i + IEEE80211_TX_QUEUE_DATA0,
-                                          &qparam);
-               }
-               /* IBSS uses different parameters for Beacon sending */
-               qparam.cw_min++;
-               qparam.cw_min *= 2;
-               qparam.cw_min--;
-               local->hw->conf_tx(wpa_s, IEEE80211_TX_QUEUE_BEACON, &qparam);
-       }
-#endif
-
-       if (wpa_s->mlme.ssid_len != params->ssid_len ||
-           os_memcmp(wpa_s->mlme.ssid, params->ssid, params->ssid_len) != 0)
-               wpa_s->mlme.prev_bssid_set = 0;
-       os_memcpy(wpa_s->mlme.ssid, params->ssid, params->ssid_len);
-       os_memset(wpa_s->mlme.ssid + params->ssid_len, 0,
-                 MAX_SSID_LEN - params->ssid_len);
-       wpa_s->mlme.ssid_len = params->ssid_len;
-       wpa_s->mlme.ssid_set = 1;
-
-       os_free(wpa_s->mlme.extra_ie);
-       if (params->wpa_ie == NULL || params->wpa_ie_len == 0) {
-               wpa_s->mlme.extra_ie = NULL;
-               wpa_s->mlme.extra_ie_len = 0;
-       } else {
-               wpa_s->mlme.extra_ie = os_malloc(params->wpa_ie_len);
-               if (wpa_s->mlme.extra_ie == NULL) {
-                       wpa_s->mlme.extra_ie_len = 0;
-                       return -1;
-               }
-               os_memcpy(wpa_s->mlme.extra_ie, params->wpa_ie,
-                         params->wpa_ie_len);
-               wpa_s->mlme.extra_ie_len = params->wpa_ie_len;
-       }
-
-       wpa_s->mlme.key_mgmt = params->key_mgmt_suite;
-
-       ieee80211_sta_set_channel(wpa_s, wpa_s->mlme.phymode,
-                                 wpa_s->mlme.channel, wpa_s->mlme.freq);
-
-       if (params->mode == WPAS_MODE_IBSS && !wpa_s->mlme.bssid_set) {
-               os_get_time(&wpa_s->mlme.ibss_join_req);
-               wpa_s->mlme.state = IEEE80211_IBSS_SEARCH;
-               return ieee80211_sta_find_ibss(wpa_s);
-       }
-
-       if (wpa_s->mlme.bssid_set)
-               ieee80211_sta_new_auth(wpa_s);
-
-       return 0;
-}
-
-
-static void ieee80211_sta_save_oper_chan(struct wpa_supplicant *wpa_s)
-{
-       wpa_s->mlme.scan_oper_channel = wpa_s->mlme.channel;
-       wpa_s->mlme.scan_oper_freq = wpa_s->mlme.freq;
-       wpa_s->mlme.scan_oper_phymode = wpa_s->mlme.phymode;
-}
-
-
-static int ieee80211_sta_restore_oper_chan(struct wpa_supplicant *wpa_s)
-{
-       wpa_s->mlme.channel = wpa_s->mlme.scan_oper_channel;
-       wpa_s->mlme.freq = wpa_s->mlme.scan_oper_freq;
-       wpa_s->mlme.phymode = wpa_s->mlme.scan_oper_phymode;
-       if (wpa_s->mlme.freq == 0)
-               return 0;
-       return ieee80211_sta_set_channel(wpa_s, wpa_s->mlme.phymode,
-                                        wpa_s->mlme.channel,
-                                        wpa_s->mlme.freq);
-}
-
-
-static int ieee80211_active_scan(struct wpa_supplicant *wpa_s)
-{
-       size_t m;
-       int c;
-
-       for (m = 0; m < wpa_s->mlme.num_modes; m++) {
-               struct hostapd_hw_modes *mode = &wpa_s->mlme.modes[m];
-               if ((int) mode->mode != (int) wpa_s->mlme.phymode)
-                       continue;
-               for (c = 0; c < mode->num_channels; c++) {
-                       struct hostapd_channel_data *chan = &mode->channels[c];
-                       if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
-                           chan->chan == wpa_s->mlme.channel) {
-                               if (!(chan->flag & HOSTAPD_CHAN_PASSIVE_SCAN))
-                                       return 1;
-                               break;
-                       }
-               }
-       }
-
-       return 0;
-}
-
-
-static void ieee80211_sta_scan_timer(void *eloop_ctx, void *timeout_ctx)
-{
-       struct wpa_supplicant *wpa_s = eloop_ctx;
-       struct hostapd_hw_modes *mode;
-       struct hostapd_channel_data *chan;
-       int skip = 0;
-       int timeout = 0;
-       struct wpa_ssid *ssid = wpa_s->current_ssid;
-       int adhoc;
-
-       if (!wpa_s->mlme.sta_scanning || wpa_s->mlme.modes == NULL)
-               return;
-
-       adhoc = ssid && ssid->mode == 1;
-
-       switch (wpa_s->mlme.scan_state) {
-       case SCAN_SET_CHANNEL:
-               mode = &wpa_s->mlme.modes[wpa_s->mlme.scan_hw_mode_idx];
-               if (wpa_s->mlme.scan_hw_mode_idx >=
-                   (int) wpa_s->mlme.num_modes ||
-                   (wpa_s->mlme.scan_hw_mode_idx + 1 ==
-                    (int) wpa_s->mlme.num_modes
-                    && wpa_s->mlme.scan_channel_idx >= mode->num_channels)) {
-                       if (ieee80211_sta_restore_oper_chan(wpa_s)) {
-                               wpa_printf(MSG_DEBUG, "MLME: failed to "
-                                          "restore operational channel after "
-                                          "scan");
-                       }
-                       wpa_printf(MSG_DEBUG, "MLME: scan completed");
-                       wpa_s->mlme.sta_scanning = 0;
-                       os_get_time(&wpa_s->mlme.last_scan_completed);
-                       wpa_supplicant_event(wpa_s, EVENT_SCAN_RESULTS, NULL);
-                       if (adhoc) {
-                               if (!wpa_s->mlme.bssid_set ||
-                                   (wpa_s->mlme.state ==
-                                    IEEE80211_IBSS_JOINED &&
-                                    !ieee80211_sta_active_ibss(wpa_s)))
-                                       ieee80211_sta_find_ibss(wpa_s);
-                       }
-                       return;
-               }
-               skip = !(wpa_s->mlme.hw_modes & (1 << mode->mode));
-               chan = &mode->channels[wpa_s->mlme.scan_channel_idx];
-               if ((chan->flag & HOSTAPD_CHAN_DISABLED) ||
-                   (adhoc && (chan->flag & HOSTAPD_CHAN_NO_IBSS)) ||
-                   (wpa_s->mlme.hw_modes & (1 << HOSTAPD_MODE_IEEE80211G) &&
-                    mode->mode == HOSTAPD_MODE_IEEE80211B &&
-                    wpa_s->mlme.scan_skip_11b))
-                       skip = 1;
-               if (!skip && wpa_s->mlme.scan_freqs) {
-                       int i, found = 0;
-                       for (i = 0; wpa_s->mlme.scan_freqs[i]; i++) {
-                               if (wpa_s->mlme.scan_freqs[i] == chan->freq) {
-                                       found = 1;
-                                       break;
-                               }
-                       }
-                       if (!found)
-                               skip = 1;
-               }
-
-               if (!skip) {
-                       wpa_printf(MSG_MSGDUMP,
-                                  "MLME: scan channel %d (%d MHz)",
-                                  chan->chan, chan->freq);
-
-                       wpa_s->mlme.channel = chan->chan;
-                       wpa_s->mlme.freq = chan->freq;
-                       wpa_s->mlme.phymode = mode->mode;
-                       if (ieee80211_sta_set_channel(wpa_s, mode->mode,
-                                                     chan->chan, chan->freq))
-                       {
-                               wpa_printf(MSG_DEBUG, "MLME: failed to set "
-                                          "channel %d (%d MHz) for scan",
-                                          chan->chan, chan->freq);
-                               skip = 1;
-                       }
-               }
-
-               wpa_s->mlme.scan_channel_idx++;
-               if (wpa_s->mlme.scan_channel_idx >=
-                   wpa_s->mlme.modes[wpa_s->mlme.scan_hw_mode_idx].
-                   num_channels) {
-                       wpa_s->mlme.scan_hw_mode_idx++;
-                       wpa_s->mlme.scan_channel_idx = 0;
-               }
-
-               if (skip) {
-                       timeout = 0;
-                       break;
-               }
-
-               timeout = IEEE80211_PROBE_DELAY;
-               wpa_s->mlme.scan_state = SCAN_SEND_PROBE;
-               break;
-       case SCAN_SEND_PROBE:
-               if (ieee80211_active_scan(wpa_s)) {
-                       ieee80211_send_probe_req(wpa_s, NULL,
-                                                wpa_s->mlme.scan_ssid,
-                                                wpa_s->mlme.scan_ssid_len);
-                       timeout = IEEE80211_CHANNEL_TIME;
-               } else {
-                       timeout = IEEE80211_PASSIVE_CHANNEL_TIME;
-               }
-               wpa_s->mlme.scan_state = SCAN_SET_CHANNEL;
-               break;
-       }
-
-       eloop_register_timeout(timeout / 1000, 1000 * (timeout % 1000),
-                              ieee80211_sta_scan_timer, wpa_s, NULL);
-}
-
-
-int ieee80211_sta_req_scan(struct wpa_supplicant *wpa_s,
-                          struct wpa_driver_scan_params *params)
-{
-       const u8 *ssid = params->ssids[0].ssid;
-       size_t ssid_len = params->ssids[0].ssid_len;
-
-       if (ssid_len > MAX_SSID_LEN)
-               return -1;
-
-       /* MLME-SCAN.request (page 118)  page 144 (11.1.3.1)
-        * BSSType: INFRASTRUCTURE, INDEPENDENT, ANY_BSS
-        * BSSID: MACAddress
-        * SSID
-        * ScanType: ACTIVE, PASSIVE
-        * ProbeDelay: delay (in microseconds) to be used prior to transmitting
-        *    a Probe frame during active scanning
-        * ChannelList
-        * MinChannelTime (>= ProbeDelay), in TU
-        * MaxChannelTime: (>= MinChannelTime), in TU
-        */
-
-        /* MLME-SCAN.confirm
-         * BSSDescriptionSet
-         * ResultCode: SUCCESS, INVALID_PARAMETERS
-        */
-
-       /* TODO: if assoc, move to power save mode for the duration of the
-        * scan */
-
-       if (wpa_s->mlme.sta_scanning)
-               return -1;
-
-       wpa_printf(MSG_DEBUG, "MLME: starting scan");
-
-       ieee80211_sta_set_probe_req_ie(wpa_s, params->extra_ies,
-                                      params->extra_ies_len);
-
-       os_free(wpa_s->mlme.scan_freqs);
-       if (params->freqs) {
-               int i;
-               for (i = 0; params->freqs[i]; i++)
-                       ;
-               wpa_s->mlme.scan_freqs = os_malloc((i + 1) * sizeof(int));
-               if (wpa_s->mlme.scan_freqs)
-                       os_memcpy(wpa_s->mlme.scan_freqs, params->freqs,
-                                 (i + 1) * sizeof(int));
-       } else
-               wpa_s->mlme.scan_freqs = NULL;
-
-       ieee80211_sta_save_oper_chan(wpa_s);
-
-       wpa_s->mlme.sta_scanning = 1;
-       /* TODO: stop TX queue? */
-
-       if (ssid) {
-               wpa_s->mlme.scan_ssid_len = ssid_len;
-               os_memcpy(wpa_s->mlme.scan_ssid, ssid, ssid_len);
-       } else
-               wpa_s->mlme.scan_ssid_len = 0;
-       wpa_s->mlme.scan_skip_11b = 1; /* FIX: clear this is 11g is not
-                                       * supported */
-       wpa_s->mlme.scan_state = SCAN_SET_CHANNEL;
-       wpa_s->mlme.scan_hw_mode_idx = 0;
-       wpa_s->mlme.scan_channel_idx = 0;
-       eloop_register_timeout(0, 1, ieee80211_sta_scan_timer, wpa_s, NULL);
-
-       return 0;
-}
-
-
-struct wpa_scan_results *
-ieee80211_sta_get_scan_results(struct wpa_supplicant *wpa_s)
-{
-       size_t ap_num = 0;
-       struct wpa_scan_results *res;
-       struct wpa_scan_res *r;
-       struct ieee80211_sta_bss *bss;
-
-       res = os_zalloc(sizeof(*res));
-       for (bss = wpa_s->mlme.sta_bss_list; bss; bss = bss->next)
-               ap_num++;
-       res->res = os_zalloc(ap_num * sizeof(struct wpa_scan_res *));
-       if (res->res == NULL) {
-               os_free(res);
-               return NULL;
-       }
-
-       for (bss = wpa_s->mlme.sta_bss_list; bss; bss = bss->next) {
-               r = os_zalloc(sizeof(*r) + bss->ie_len);
-               if (r == NULL)
-                       break;
-               os_memcpy(r->bssid, bss->bssid, ETH_ALEN);
-               r->freq = bss->freq;
-               r->beacon_int = bss->beacon_int;
-               r->caps = bss->capability;
-               r->level = bss->rssi;
-               r->tsf = bss->timestamp;
-               if (bss->ie) {
-                       r->ie_len = bss->ie_len;
-                       os_memcpy(r + 1, bss->ie, bss->ie_len);
-               }
-
-               res->res[res->num++] = r;
-       }
-
-       return res;
-}
-
-
-#if 0 /* FIX */
-struct sta_info * ieee80211_ibss_add_sta(struct wpa_supplicant *wpa_s,
-                                        struct sk_buff *skb, u8 *bssid,
-                                        u8 *addr)
-{
-       struct ieee80211_local *local = dev->priv;
-       struct list_head *ptr;
-       struct sta_info *sta;
-       struct wpa_supplicant *sta_dev = NULL;
-
-       /* TODO: Could consider removing the least recently used entry and
-        * allow new one to be added. */
-       if (local->num_sta >= IEEE80211_IBSS_MAX_STA_ENTRIES) {
-               if (net_ratelimit()) {
-                       wpa_printf(MSG_DEBUG, "MLME: No room for a new IBSS "
-                                  "STA entry " MACSTR, MAC2STR(addr));
-               }
-               return NULL;
-       }
-
-       spin_lock_bh(&local->sub_if_lock);
-       list_for_each(ptr, &local->sub_if_list) {
-               sdata = list_entry(ptr, struct ieee80211_sub_if_data, list);
-               if (sdata->type == IEEE80211_SUB_IF_TYPE_STA &&
-                   os_memcmp(bssid, sdata->u.sta.bssid, ETH_ALEN) == 0) {
-                       sta_dev = sdata->dev;
-                       break;
-               }
-       }
-       spin_unlock_bh(&local->sub_if_lock);
-
-       if (sta_dev == NULL)
-               return NULL;
-
-       wpa_printf(MSG_DEBUG, "MLME: Adding new IBSS station " MACSTR
-                  " (dev=%s)", MAC2STR(addr), sta_dev->name);
-
-       sta = sta_info_add(wpa_s, addr);
-       if (sta == NULL) {
-               return NULL;
-       }
-
-       sta->dev = sta_dev;
-       sta->supp_rates = wpa_s->mlme.supp_rates_bits;
-
-       rate_control_rate_init(local, sta);
-
-       return sta; /* caller will call sta_info_release() */
-}
-#endif
-
-
-int ieee80211_sta_deauthenticate(struct wpa_supplicant *wpa_s, u16 reason)
-{
-       wpa_printf(MSG_DEBUG, "MLME: deauthenticate(reason=%d)", reason);
-
-       ieee80211_send_deauth(wpa_s, reason);
-       ieee80211_set_associated(wpa_s, 0);
-       return 0;
-}
-
-
-int ieee80211_sta_disassociate(struct wpa_supplicant *wpa_s, u16 reason)
-{
-       wpa_printf(MSG_DEBUG, "MLME: disassociate(reason=%d)", reason);
-
-       if (!wpa_s->mlme.associated)
-               return -1;
-
-       ieee80211_send_disassoc(wpa_s, reason);
-       ieee80211_set_associated(wpa_s, 0);
-       return 0;
-}
-
-
-void ieee80211_sta_rx(struct wpa_supplicant *wpa_s, const u8 *buf, size_t len,
-                     struct ieee80211_rx_status *rx_status)
-{
-       struct ieee80211_mgmt *mgmt;
-       u16 fc;
-       const u8 *pos;
-
-       /* wpa_hexdump(MSG_MSGDUMP, "MLME: Received frame", buf, len); */
-
-       if (wpa_s->mlme.sta_scanning) {
-               ieee80211_sta_rx_scan(wpa_s, buf, len, rx_status);
-               return;
-       }
-
-       if (len < 24)
-               return;
-
-       mgmt = (struct ieee80211_mgmt *) buf;
-       fc = le_to_host16(mgmt->frame_control);
-
-       if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT)
-               ieee80211_sta_rx_mgmt(wpa_s, buf, len, rx_status);
-       else if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA) {
-               if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) !=
-                   WLAN_FC_FROMDS)
-                       return;
-               /* mgmt->sa is actually BSSID for FromDS data frames */
-               if (os_memcmp(mgmt->sa, wpa_s->bssid, ETH_ALEN) != 0)
-                       return;
-               /* Skip IEEE 802.11 and LLC headers */
-               pos = buf + 24 + 6;
-               if (WPA_GET_BE16(pos) != ETH_P_EAPOL)
-                       return;
-               pos += 2;
-               /* mgmt->bssid is actually BSSID for SA data frames */
-               wpa_supplicant_rx_eapol(wpa_s, mgmt->bssid,
-                                       pos, buf + len - pos);
-       }
-}
-
-
-void ieee80211_sta_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);
-}
-
-
-int ieee80211_sta_init(struct wpa_supplicant *wpa_s)
-{
-       u16 num_modes, flags;
-
-       wpa_s->mlme.modes = wpa_drv_get_hw_feature_data(wpa_s, &num_modes,
-                                                       &flags);
-       if (wpa_s->mlme.modes == NULL) {
-               wpa_printf(MSG_ERROR, "MLME: Failed to read supported "
-                          "channels and rates from the driver");
-               return -1;
-       }
-
-       wpa_s->mlme.num_modes = num_modes;
-
-       wpa_s->mlme.hw_modes = 1 << HOSTAPD_MODE_IEEE80211A;
-       wpa_s->mlme.hw_modes |= 1 << HOSTAPD_MODE_IEEE80211B;
-       wpa_s->mlme.hw_modes |= 1 << HOSTAPD_MODE_IEEE80211G;
-
-       wpa_s->mlme.wmm_enabled = 1;
-
-       return 0;
-}
-
-
-void ieee80211_sta_deinit(struct wpa_supplicant *wpa_s)
-{
-       eloop_cancel_timeout(ieee80211_sta_timer, wpa_s, NULL);
-       eloop_cancel_timeout(ieee80211_sta_scan_timer, wpa_s, NULL);
-       os_free(wpa_s->mlme.extra_ie);
-       wpa_s->mlme.extra_ie = NULL;
-       os_free(wpa_s->mlme.extra_probe_ie);
-       wpa_s->mlme.extra_probe_ie = NULL;
-       os_free(wpa_s->mlme.assocreq_ies);
-       wpa_s->mlme.assocreq_ies = NULL;
-       os_free(wpa_s->mlme.assocresp_ies);
-       wpa_s->mlme.assocresp_ies = NULL;
-       ieee80211_bss_list_deinit(wpa_s);
-       ieee80211_sta_free_hw_features(wpa_s->mlme.modes,
-                                      wpa_s->mlme.num_modes);
-#ifdef CONFIG_IEEE80211R
-       os_free(wpa_s->mlme.ft_ies);
-       wpa_s->mlme.ft_ies = NULL;
-       wpa_s->mlme.ft_ies_len = 0;
-#endif /* CONFIG_IEEE80211R */
-
-       os_free(wpa_s->mlme.scan_freqs);
-       wpa_s->mlme.scan_freqs = NULL;
-}
-
-
-#ifdef CONFIG_IEEE80211R
-
-int ieee80211_sta_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md,
-                               const u8 *ies, size_t ies_len)
-{
-       if (md == NULL) {
-               wpa_printf(MSG_DEBUG, "MLME: Clear FT mobility domain");
-               os_memset(wpa_s->mlme.current_md, 0, MOBILITY_DOMAIN_ID_LEN);
-       } else {
-               wpa_printf(MSG_DEBUG, "MLME: Update FT IEs for MD " MACSTR,
-                          MAC2STR(md));
-               os_memcpy(wpa_s->mlme.current_md, md, MOBILITY_DOMAIN_ID_LEN);
-       }
-
-       wpa_hexdump(MSG_DEBUG, "MLME: FT IEs", ies, ies_len);
-       os_free(wpa_s->mlme.ft_ies);
-       wpa_s->mlme.ft_ies = os_malloc(ies_len);
-       if (wpa_s->mlme.ft_ies == NULL)
-               return -1;
-       os_memcpy(wpa_s->mlme.ft_ies, ies, ies_len);
-       wpa_s->mlme.ft_ies_len = ies_len;
-
-       return 0;
-}
-
-
-int ieee80211_sta_send_ft_action(struct wpa_supplicant *wpa_s, u8 action,
-                                const u8 *target_ap,
-                                const u8 *ies, size_t ies_len)
-{
-       u8 *buf;
-       size_t len;
-       struct ieee80211_mgmt *mgmt;
-       int res;
-
-       /*
-        * Action frame payload:
-        * Category[1] = 6 (Fast BSS Transition)
-        * Action[1] = 1 (Fast BSS Transition Request)
-        * STA Address
-        * Target AP Address
-        * FT IEs
-        */
-
-       buf = os_zalloc(sizeof(*mgmt) + ies_len);
-       if (buf == NULL) {
-               wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
-                          "FT action frame");
-               return -1;
-       }
-
-       mgmt = (struct ieee80211_mgmt *) buf;
-       len = 24;
-       os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
-       os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
-       os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
-       mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
-                                          WLAN_FC_STYPE_ACTION);
-       mgmt->u.action.category = WLAN_ACTION_FT;
-       mgmt->u.action.u.ft_action_req.action = action;
-       os_memcpy(mgmt->u.action.u.ft_action_req.sta_addr, wpa_s->own_addr,
-                 ETH_ALEN);
-       os_memcpy(mgmt->u.action.u.ft_action_req.target_ap_addr, target_ap,
-                 ETH_ALEN);
-       os_memcpy(mgmt->u.action.u.ft_action_req.variable, ies, ies_len);
-       len += 1 + sizeof(mgmt->u.action.u.ft_action_req) + ies_len;
-
-       wpa_printf(MSG_DEBUG, "MLME: Send FT Action Frame: Action=%d "
-                  "Target AP=" MACSTR " body_len=%lu",
-                  action, MAC2STR(target_ap), (unsigned long) ies_len);
-
-       res = ieee80211_sta_tx(wpa_s, buf, len);
-       os_free(buf);
-
-       return res;
-}
-
-#endif /* CONFIG_IEEE80211R */
-
-
-static int ieee80211_sta_set_probe_req_ie(struct wpa_supplicant *wpa_s,
-                                         const u8 *ies, size_t ies_len)
-{
-       os_free(wpa_s->mlme.extra_probe_ie);
-       wpa_s->mlme.extra_probe_ie = NULL;
-       wpa_s->mlme.extra_probe_ie_len = 0;
-
-       if (ies == NULL)
-               return 0;
-
-       wpa_s->mlme.extra_probe_ie = os_malloc(ies_len);
-       if (wpa_s->mlme.extra_probe_ie == NULL)
-               return -1;
-
-       os_memcpy(wpa_s->mlme.extra_probe_ie, ies, ies_len);
-       wpa_s->mlme.extra_probe_ie_len = ies_len;
-
-       return 0;
-}
diff --git a/wpa_supplicant/mlme.h b/wpa_supplicant/mlme.h
deleted file mode 100644 (file)
index 5db3665..0000000
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * WPA Supplicant - Client mode MLME
- * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
- * Copyright (c) 2004, Instant802 Networks, Inc.
- * Copyright (c) 2005-2006, Devicescape Software, Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
- */
-
-#ifndef MLME_H
-#define MLME_H
-
-struct wpa_supplicant;
-
-struct ieee80211_rx_status {
-       int freq;
-        int channel;
-        int ssi;
-};
-
-#ifdef CONFIG_CLIENT_MLME
-
-int ieee80211_sta_init(struct wpa_supplicant *wpa_s);
-void ieee80211_sta_deinit(struct wpa_supplicant *wpa_s);
-int ieee80211_sta_req_scan(struct wpa_supplicant *wpa_s,
-                          struct wpa_driver_scan_params *params);
-int ieee80211_sta_deauthenticate(struct wpa_supplicant *wpa_s, u16 reason);
-int ieee80211_sta_disassociate(struct wpa_supplicant *wpa_s, u16 reason);
-int ieee80211_sta_associate(struct wpa_supplicant *wpa_s,
-                           struct wpa_driver_associate_params *params);
-int ieee80211_sta_get_ssid(struct wpa_supplicant *wpa_s, u8 *ssid,
-                          size_t *len);
-void ieee80211_sta_free_hw_features(struct hostapd_hw_modes *hw_features,
-                                   size_t num_hw_features);
-void ieee80211_sta_rx(struct wpa_supplicant *wpa_s, const u8 *buf, size_t len,
-                     struct ieee80211_rx_status *rx_status);
-struct wpa_scan_results *
-ieee80211_sta_get_scan_results(struct wpa_supplicant *wpa_s);
-int ieee80211_sta_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md,
-                               const u8 *ies, size_t ies_len);
-int ieee80211_sta_send_ft_action(struct wpa_supplicant *wpa_s, u8 action,
-                                const u8 *target_ap,
-                                const u8 *ies, size_t ies_len);
-
-#else /* CONFIG_CLIENT_MLME */
-
-static inline int ieee80211_sta_init(struct wpa_supplicant *wpa_s)
-{
-       return 0;
-}
-
-static inline void ieee80211_sta_deinit(struct wpa_supplicant *wpa_s)
-{
-}
-
-static inline int ieee80211_sta_req_scan(struct wpa_supplicant *wpa_s,
-                                        struct wpa_driver_scan_params *params)
-{
-       return -1;
-}
-
-static inline int ieee80211_sta_deauthenticate(struct wpa_supplicant *wpa_s,
-                                              u16 reason)
-{
-       return -1;
-}
-
-static inline int ieee80211_sta_disassociate(struct wpa_supplicant *wpa_s,
-                                            u16 reason)
-{
-       return -1;
-}
-
-static inline int
-ieee80211_sta_associate(struct wpa_supplicant *wpa_s,
-                       struct wpa_driver_associate_params *params)
-{
-       return -1;
-}
-
-static inline int ieee80211_sta_get_ssid(struct wpa_supplicant *wpa_s,
-                                        u8 *ssid, size_t *len)
-{
-       return -1;
-}
-
-static inline void
-ieee80211_sta_free_hw_features(struct hostapd_hw_modes *hw_features,
-                              size_t num_hw_features)
-{
-}
-
-static inline void
-ieee80211_sta_rx(struct wpa_supplicant *wpa_s, const u8 *buf, size_t len,
-                struct ieee80211_rx_status *rx_status)
-{
-}
-
-static inline struct wpa_scan_results *
-ieee80211_sta_get_scan_results(struct wpa_supplicant *wpa_s)
-{
-       return NULL;
-}
-
-static inline int
-ieee80211_sta_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md,
-                           const u8 *ies, size_t ies_len)
-{
-       return -1;
-}
-
-static inline int
-ieee80211_sta_send_ft_action(struct wpa_supplicant *wpa_s, u8 action,
-                            const u8 *target_ap,
-                            const u8 *ies, size_t ies_len)
-{
-       return -1;
-}
-
-#endif /* CONFIG_CLIENT_MLME */
-
-#endif /* MLME_H */
index ac65b4f..136d25f 100644 (file)
 #include "dbus/dbus_common.h"
 #include "dbus/dbus_old.h"
 #include "dbus/dbus_new.h"
+#include "rsn_supp/wpa.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)
@@ -81,6 +84,22 @@ void wpas_notify_state_changed(struct wpa_supplicant *wpa_s,
 
        /* notify the new DBus API */
        wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_STATE);
+
+#ifdef CONFIG_P2P
+       if (new_state == WPA_COMPLETED)
+               wpas_p2p_notif_connected(wpa_s);
+       else if (old_state >= WPA_ASSOCIATED && new_state < WPA_ASSOCIATED)
+               wpas_p2p_notif_disconnected(wpa_s);
+#endif /* CONFIG_P2P */
+
+       sme_state_changed(wpa_s);
+
+#ifdef ANDROID
+       wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_STATE_CHANGE
+                    "id=%d state=%d BSSID=" MACSTR,
+                    wpa_s->current_ssid ? wpa_s->current_ssid->id : -1,
+                    new_state, MAC2STR(wpa_s->pending_bssid));
+#endif /* ANDROID */
 }
 
 
@@ -102,6 +121,12 @@ void wpas_notify_bssid_changed(struct wpa_supplicant *wpa_s)
 }
 
 
+void wpas_notify_auth_changed(struct wpa_supplicant *wpa_s)
+{
+       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)
 {
@@ -116,6 +141,15 @@ void wpas_notify_network_selected(struct wpa_supplicant *wpa_s,
 }
 
 
+void wpas_notify_network_request(struct wpa_supplicant *wpa_s,
+                                struct wpa_ssid *ssid,
+                                enum wpa_ctrl_req_type rtype,
+                                const char *default_txt)
+{
+       wpas_dbus_signal_network_request(wpa_s, ssid, rtype, default_txt);
+}
+
+
 void wpas_notify_scanning(struct wpa_supplicant *wpa_s)
 {
        /* notify the old DBus API */
@@ -182,14 +216,45 @@ void wpas_notify_wps_event_success(struct wpa_supplicant *wpa_s)
 void wpas_notify_network_added(struct wpa_supplicant *wpa_s,
                               struct wpa_ssid *ssid)
 {
-       wpas_dbus_register_network(wpa_s, ssid);
+       /*
+        * Networks objects created during any P2P activities should not be
+        * exposed out. They might/will confuse certain non-P2P aware
+        * applications since these network objects won't behave like
+        * regular ones.
+        */
+       if (wpa_s->global->p2p_group_formation != wpa_s)
+               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)
 {
-       wpas_dbus_unregister_network(wpa_s, ssid->id);
+       if (wpa_s->wpa)
+               wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
+       if (wpa_s->global->p2p_group_formation != wpa_s)
+               wpas_dbus_unregister_network(wpa_s, ssid->id);
+#ifdef CONFIG_P2P
+       wpas_p2p_network_removed(wpa_s, ssid);
+#endif /* CONFIG_P2P */
 }
 
 
@@ -337,3 +402,209 @@ void wpas_notify_resume(struct wpa_global *global)
                        wpa_supplicant_req_scan(wpa_s, 0, 100000);
        }
 }
+
+
+#ifdef CONFIG_P2P
+
+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_unregister_p2p_group(wpa_s, ssid);
+
+       wpas_dbus_signal_p2p_group_removed(wpa_s, role);
+}
+
+
+void wpas_notify_p2p_go_neg_req(struct wpa_supplicant *wpa_s,
+                               const u8 *src, u16 dev_passwd_id)
+{
+       wpas_dbus_signal_p2p_go_neg_req(wpa_s, src, dev_passwd_id);
+}
+
+
+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_wps_failed(struct wpa_supplicant *wpa_s,
+                               struct wps_event_fail *fail)
+{
+       wpas_dbus_signal_p2p_wps_failed(wpa_s, fail);
+}
+
+#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);
+
+       /*
+        * Register a group member object corresponding to this peer and
+        * emit a PeerJoined signal. This will check if it really is a
+        * P2P group.
+        */
+       wpas_dbus_register_p2p_groupmember(wpa_s, sta);
+
+       /*
+        * Create 'peer-joined' signal on group object -- will also
+        * check P2P itself.
+        */
+       wpas_dbus_signal_p2p_peer_joined(wpa_s, sta);
+#endif /* CONFIG_P2P */
+}
+
+
+static void wpas_notify_ap_sta_deauthorized(struct wpa_supplicant *wpa_s,
+                                           const u8 *sta)
+{
+#ifdef CONFIG_P2P
+       /*
+        * Unregister a group member object corresponding to this peer
+        * if this is a P2P group.
+        */
+       wpas_dbus_unregister_p2p_groupmember(wpa_s, sta);
+
+       /*
+        * Create 'peer-disconnected' signal on group object if this
+        * is a P2P group.
+        */
+       wpas_dbus_signal_p2p_peer_disconnected(wpa_s, sta);
+#endif /* CONFIG_P2P */
+}
+
+
+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);
+}
+
+
+void wpas_notify_certification(struct wpa_supplicant *wpa_s, int depth,
+                              const char *subject, 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);
+               }
+       }
+
+       /* notify the old DBus API */
+       wpa_supplicant_dbus_notify_certification(wpa_s, depth, subject,
+                                                cert_hash, cert);
+       /* notify the new DBus API */
+       wpas_dbus_signal_certification(wpa_s, depth, subject, cert_hash, cert);
+}
index 2e70bdb..236a31e 100644 (file)
@@ -15,6 +15,8 @@
 #ifndef NOTIFY_H
 #define NOTIFY_H
 
+#include "p2p/p2p.h"
+
 struct wps_credential;
 struct wps_event_m2d;
 struct wps_event_fail;
@@ -29,10 +31,15 @@ void wpas_notify_state_changed(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);
@@ -78,4 +85,47 @@ 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_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);
+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_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 *cert_hash,
+                              const struct wpabuf *cert);
+
 #endif /* NOTIFY_H */
diff --git a/wpa_supplicant/offchannel.c b/wpa_supplicant/offchannel.c
new file mode 100644 (file)
index 0000000..790f14a
--- /dev/null
@@ -0,0 +1,314 @@
+/*
+ * wpa_supplicant - Off-channel Action frame TX/RX
+ * Copyright (c) 2009-2010, Atheros Communications
+ * Copyright (c) 2011, Qualcomm Atheros
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "utils/eloop.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "offchannel.h"
+
+
+
+static struct wpa_supplicant *
+wpas_get_tx_interface(struct wpa_supplicant *wpa_s, const u8 *src)
+{
+       struct wpa_supplicant *iface;
+
+       if (os_memcmp(src, wpa_s->own_addr, ETH_ALEN) == 0)
+               return wpa_s;
+
+       /*
+        * Try to find a group interface that matches with the source address.
+        */
+       iface = wpa_s->global->ifaces;
+       while (iface) {
+               if (os_memcmp(wpa_s->pending_action_src,
+                             iface->own_addr, ETH_ALEN) == 0)
+                       break;
+               iface = iface->next;
+       }
+       if (iface) {
+               wpa_printf(MSG_DEBUG, "P2P: Use group interface %s "
+                          "instead of interface %s for Action TX",
+                          iface->ifname, wpa_s->ifname);
+               return iface;
+       }
+
+       return wpa_s;
+}
+
+
+static void wpas_send_action_cb(void *eloop_ctx, void *timeout_ctx)
+{
+       struct wpa_supplicant *wpa_s = eloop_ctx;
+       struct wpa_supplicant *iface;
+       int res;
+       int without_roc;
+
+       without_roc = wpa_s->pending_action_without_roc;
+       wpa_s->pending_action_without_roc = 0;
+       wpa_printf(MSG_DEBUG, "Off-channel: Send Action callback "
+                  "(without_roc=%d pending_action_tx=%p)",
+                  without_roc, wpa_s->pending_action_tx);
+
+       if (wpa_s->pending_action_tx == NULL)
+               return;
+
+       /*
+        * This call is likely going to be on the P2P device instance if the
+        * driver uses a separate interface for that purpose. However, some
+        * Action frames are actually sent within a P2P Group and when that is
+        * the case, we need to follow power saving (e.g., GO buffering the
+        * frame for a client in PS mode or a client following the advertised
+        * NoA from its GO). To make that easier for the driver, select the
+        * correct group interface here.
+        */
+       iface = wpas_get_tx_interface(wpa_s, wpa_s->pending_action_src);
+
+       if (wpa_s->off_channel_freq != wpa_s->pending_action_freq &&
+           wpa_s->pending_action_freq != 0 &&
+           wpa_s->pending_action_freq != iface->assoc_freq) {
+               wpa_printf(MSG_DEBUG, "Off-channel: Pending Action frame TX "
+                          "waiting for another freq=%u (off_channel_freq=%u "
+                          "assoc_freq=%u)",
+                          wpa_s->pending_action_freq,
+                          wpa_s->off_channel_freq,
+                          iface->assoc_freq);
+               if (without_roc && wpa_s->off_channel_freq == 0) {
+                       /*
+                        * We may get here if wpas_send_action() found us to be
+                        * on the correct channel, but remain-on-channel cancel
+                        * event was received before getting here.
+                        */
+                       wpa_printf(MSG_DEBUG, "Off-channel: Schedule "
+                                  "remain-on-channel to send Action frame");
+                       if (wpa_drv_remain_on_channel(
+                                   wpa_s, wpa_s->pending_action_freq, 200) <
+                           0) {
+                               wpa_printf(MSG_DEBUG, "Off-channel: Failed to "
+                                          "request driver to remain on "
+                                          "channel (%u MHz) for Action Frame "
+                                          "TX", wpa_s->pending_action_freq);
+                       } else {
+                               wpa_s->off_channel_freq = 0;
+                               wpa_s->roc_waiting_drv_freq =
+                                       wpa_s->pending_action_freq;
+                       }
+               }
+               return;
+       }
+
+       wpa_printf(MSG_DEBUG, "Off-channel: Sending pending Action frame to "
+                  MACSTR " using interface %s",
+                  MAC2STR(wpa_s->pending_action_dst), iface->ifname);
+       res = wpa_drv_send_action(iface, wpa_s->pending_action_freq, 0,
+                                 wpa_s->pending_action_dst,
+                                 wpa_s->pending_action_src,
+                                 wpa_s->pending_action_bssid,
+                                 wpabuf_head(wpa_s->pending_action_tx),
+                                 wpabuf_len(wpa_s->pending_action_tx),
+                                 wpa_s->pending_action_no_cck);
+       if (res) {
+               wpa_printf(MSG_DEBUG, "Off-channel: Failed to send the "
+                          "pending Action frame");
+               /*
+                * Use fake TX status event to allow state machines to
+                * continue.
+                */
+               offchannel_send_action_tx_status(
+                       wpa_s, wpa_s->pending_action_dst,
+                       wpabuf_head(wpa_s->pending_action_tx),
+                       wpabuf_len(wpa_s->pending_action_tx),
+                       OFFCHANNEL_SEND_ACTION_FAILED);
+       }
+}
+
+
+void offchannel_send_action_tx_status(
+       struct wpa_supplicant *wpa_s, const u8 *dst, const u8 *data,
+       size_t data_len, enum offchannel_send_action_result result)
+{
+       if (wpa_s->pending_action_tx == NULL) {
+               wpa_printf(MSG_DEBUG, "Off-channel: Ignore Action TX status - "
+                          "no pending operation");
+               return;
+       }
+
+       if (os_memcmp(dst, wpa_s->pending_action_dst, ETH_ALEN) != 0) {
+               wpa_printf(MSG_DEBUG, "Off-channel: Ignore Action TX status - "
+                          "unknown destination address");
+               return;
+       }
+
+       wpabuf_free(wpa_s->pending_action_tx);
+       wpa_s->pending_action_tx = NULL;
+
+       wpa_printf(MSG_DEBUG, "Off-channel: TX status result=%d cb=%p",
+                  result, wpa_s->pending_action_tx_status_cb);
+
+       if (wpa_s->pending_action_tx_status_cb) {
+               wpa_s->pending_action_tx_status_cb(
+                       wpa_s, wpa_s->pending_action_freq,
+                       wpa_s->pending_action_dst, wpa_s->pending_action_src,
+                       wpa_s->pending_action_bssid,
+                       data, data_len, result);
+       }
+}
+
+
+int offchannel_send_action(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,
+                          void (*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),
+                          int no_cck)
+{
+       wpa_printf(MSG_DEBUG, "Off-channel: Send action frame: freq=%d dst="
+                  MACSTR " src=" MACSTR " bssid=" MACSTR " len=%d",
+                  freq, MAC2STR(dst), MAC2STR(src), MAC2STR(bssid),
+                  (int) len);
+
+       wpa_s->pending_action_tx_status_cb = tx_cb;
+
+       if (wpa_s->pending_action_tx) {
+               wpa_printf(MSG_DEBUG, "Off-channel: Dropped pending Action "
+                          "frame TX to " MACSTR,
+                          MAC2STR(wpa_s->pending_action_dst));
+               wpabuf_free(wpa_s->pending_action_tx);
+       }
+       wpa_s->pending_action_tx = wpabuf_alloc(len);
+       if (wpa_s->pending_action_tx == NULL) {
+               wpa_printf(MSG_DEBUG, "Off-channel: Failed to allocate Action "
+                          "frame TX buffer (len=%llu)",
+                          (unsigned long long) len);
+               return -1;
+       }
+       wpabuf_put_data(wpa_s->pending_action_tx, buf, len);
+       os_memcpy(wpa_s->pending_action_src, src, ETH_ALEN);
+       os_memcpy(wpa_s->pending_action_dst, dst, ETH_ALEN);
+       os_memcpy(wpa_s->pending_action_bssid, bssid, ETH_ALEN);
+       wpa_s->pending_action_freq = freq;
+       wpa_s->pending_action_no_cck = no_cck;
+
+       if (freq != 0 && wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) {
+               struct wpa_supplicant *iface;
+
+               iface = wpas_get_tx_interface(wpa_s,
+                                             wpa_s->pending_action_src);
+               wpa_s->action_tx_wait_time = wait_time;
+
+               return wpa_drv_send_action(
+                       iface, wpa_s->pending_action_freq,
+                       wait_time, wpa_s->pending_action_dst,
+                       wpa_s->pending_action_src, wpa_s->pending_action_bssid,
+                       wpabuf_head(wpa_s->pending_action_tx),
+                       wpabuf_len(wpa_s->pending_action_tx),
+                       wpa_s->pending_action_no_cck);
+       }
+
+       if (freq) {
+               struct wpa_supplicant *tx_iface;
+               tx_iface = wpas_get_tx_interface(wpa_s, src);
+               if (tx_iface->assoc_freq == freq) {
+                       wpa_printf(MSG_DEBUG, "Off-channel: Already on "
+                                  "requested channel (TX interface operating "
+                                  "channel)");
+                       freq = 0;
+               }
+       }
+
+       if (wpa_s->off_channel_freq == freq || freq == 0) {
+               wpa_printf(MSG_DEBUG, "Off-channel: Already on requested "
+                          "channel; send Action frame immediately");
+               /* TODO: Would there ever be need to extend the current
+                * duration on the channel? */
+               wpa_s->pending_action_without_roc = 1;
+               eloop_cancel_timeout(wpas_send_action_cb, wpa_s, NULL);
+               eloop_register_timeout(0, 0, wpas_send_action_cb, wpa_s, NULL);
+               return 0;
+       }
+       wpa_s->pending_action_without_roc = 0;
+
+       if (wpa_s->roc_waiting_drv_freq == freq) {
+               wpa_printf(MSG_DEBUG, "Off-channel: Already waiting for "
+                          "driver to get to frequency %u MHz; continue "
+                          "waiting to send the Action frame", freq);
+               return 0;
+       }
+
+       wpa_printf(MSG_DEBUG, "Off-channel: Schedule Action frame to be "
+                  "transmitted once the driver gets to the requested "
+                  "channel");
+       if (wait_time > wpa_s->max_remain_on_chan)
+               wait_time = wpa_s->max_remain_on_chan;
+       if (wpa_drv_remain_on_channel(wpa_s, freq, wait_time) < 0) {
+               wpa_printf(MSG_DEBUG, "Off-channel: Failed to request driver "
+                          "to remain on channel (%u MHz) for Action "
+                          "Frame TX", freq);
+               return -1;
+       }
+       wpa_s->off_channel_freq = 0;
+       wpa_s->roc_waiting_drv_freq = freq;
+
+       return 0;
+}
+
+
+void offchannel_send_action_done(struct wpa_supplicant *wpa_s)
+{
+       wpa_printf(MSG_DEBUG, "Off-channel: Action frame sequence done "
+                  "notification");
+       wpabuf_free(wpa_s->pending_action_tx);
+       wpa_s->pending_action_tx = NULL;
+       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX &&
+           wpa_s->action_tx_wait_time)
+               wpa_drv_send_action_cancel_wait(wpa_s);
+
+       if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) {
+               wpa_drv_cancel_remain_on_channel(wpa_s);
+               wpa_s->off_channel_freq = 0;
+               wpa_s->roc_waiting_drv_freq = 0;
+       }
+}
+
+
+void offchannel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+                                    unsigned int freq, unsigned int duration)
+{
+       wpa_s->roc_waiting_drv_freq = 0;
+       wpa_s->off_channel_freq = freq;
+       wpas_send_action_cb(wpa_s, NULL);
+}
+
+
+void offchannel_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+                                           unsigned int freq)
+{
+       wpa_s->off_channel_freq = 0;
+}
+
+
+void offchannel_deinit(struct wpa_supplicant *wpa_s)
+{
+       wpabuf_free(wpa_s->pending_action_tx);
+       wpa_s->pending_action_tx = NULL;
+       eloop_cancel_timeout(wpas_send_action_cb, wpa_s, NULL);
+}
diff --git a/wpa_supplicant/offchannel.h b/wpa_supplicant/offchannel.h
new file mode 100644 (file)
index 0000000..60e0d03
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * wpa_supplicant - Off-channel Action frame TX/RX
+ * Copyright (c) 2009-2010, Atheros Communications
+ * Copyright (c) 2011, Qualcomm Atheros
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef OFFCHANNEL_H
+#define OFFCHANNEL_H
+
+int offchannel_send_action(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,
+                          void (*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),
+                          int no_cck);
+void offchannel_send_action_done(struct wpa_supplicant *wpa_s);
+void offchannel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+                                    unsigned int freq, unsigned int duration);
+void offchannel_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+                                           unsigned int freq);
+void offchannel_deinit(struct wpa_supplicant *wpa_s);
+void offchannel_send_action_tx_status(
+       struct wpa_supplicant *wpa_s, const u8 *dst, const u8 *data,
+       size_t data_len, enum offchannel_send_action_result result);
+
+#endif /* OFFCHANNEL_H */
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
new file mode 100644 (file)
index 0000000..ed0ad59
--- /dev/null
@@ -0,0 +1,4474 @@
+/*
+ * wpa_supplicant - P2P
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#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/p2p_hostapd.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"
+
+
+/*
+ * How many times to try to scan to find the GO before giving up on join
+ * request.
+ */
+#define P2P_MAX_JOIN_SCAN_ATTEMPTS 10
+
+#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 */
+
+
+static void wpas_p2p_long_listen_timeout(void *eloop_ctx, void *timeout_ctx);
+static struct wpa_supplicant *
+wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated,
+                        int go);
+static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s);
+static void wpas_p2p_join_scan(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);
+static void wpas_p2p_pd_before_join_timeout(void *eloop_ctx,
+                                           void *timeout_ctx);
+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_scan_res_handler(struct wpa_supplicant *wpa_s,
+                                     struct wpa_scan_results *scan_res)
+{
+       size_t i;
+
+       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];
+               if (p2p_scan_res_handler(wpa_s->global->p2p, bss->bssid,
+                                        bss->freq, bss->level,
+                                        (const u8 *) (bss + 1),
+                                        bss->ie_len) > 0)
+                       break;
+       }
+
+       p2p_scan_res_handled(wpa_s->global->p2p);
+}
+
+
+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)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       struct wpa_driver_scan_params params;
+       int ret;
+       struct wpabuf *wps_ie, *ies;
+       int social_channels[] = { 2412, 2437, 2462, 0, 0 };
+       size_t ielen;
+       int was_in_p2p_scan;
+
+       if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+               return -1;
+
+       os_memset(&params, 0, sizeof(params));
+
+       /* P2P Wildcard SSID */
+       params.num_ssids = 1;
+       params.ssids[0].ssid = (u8 *) P2P_WILDCARD_SSID;
+       params.ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN;
+
+       wpa_s->wps->dev.p2p = 1;
+       wps_ie = wps_build_probe_req_ie(0, &wpa_s->wps->dev, wpa_s->wps->uuid,
+                                       WPS_REQ_ENROLLEE,
+                                       num_req_dev_types, req_dev_types);
+       if (wps_ie == NULL)
+               return -1;
+
+       ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p);
+       ies = wpabuf_alloc(wpabuf_len(wps_ie) + ielen);
+       if (ies == NULL) {
+               wpabuf_free(wps_ie);
+               return -1;
+       }
+       wpabuf_put_buf(ies, wps_ie);
+       wpabuf_free(wps_ie);
+
+       p2p_scan_ie(wpa_s->global->p2p, ies, dev_id);
+
+       params.p2p_probe = 1;
+       params.extra_ies = wpabuf_head(ies);
+       params.extra_ies_len = wpabuf_len(ies);
+
+       switch (type) {
+       case P2P_SCAN_SOCIAL:
+               params.freqs = social_channels;
+               break;
+       case P2P_SCAN_FULL:
+               break;
+       case P2P_SCAN_SPECIFIC:
+               social_channels[0] = freq;
+               social_channels[1] = 0;
+               params.freqs = social_channels;
+               break;
+       case P2P_SCAN_SOCIAL_PLUS_ONE:
+               social_channels[3] = freq;
+               params.freqs = social_channels;
+               break;
+       }
+
+       was_in_p2p_scan = wpa_s->scan_res_handler == wpas_p2p_scan_res_handler;
+       wpa_s->scan_res_handler = wpas_p2p_scan_res_handler;
+       ret = wpa_drv_scan(wpa_s, &params);
+
+       wpabuf_free(ies);
+
+       if (ret) {
+               wpa_s->scan_res_handler = NULL;
+               if (wpa_s->scanning || was_in_p2p_scan) {
+                       wpa_s->p2p_cb_on_scan_complete = 1;
+                       ret = 1;
+               }
+       }
+
+       return ret;
+}
+
+
+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 wpas_p2p_group_delete(struct wpa_supplicant *wpa_s)
+{
+       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.
+                */
+               ssid = wpa_s->conf->ssid;
+               while (ssid) {
+                       if (ssid->p2p_group &&
+                           (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION ||
+                            (ssid->key_mgmt & WPA_KEY_MGMT_WPS)))
+                               break;
+                       ssid = ssid->next;
+               }
+       }
+       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;
+               wpa_supplicant_deauthenticate(wpa_s,
+                                             WLAN_REASON_DEAUTH_LEAVING);
+               gtype = "client";
+       } else
+               gtype = "GO";
+       if (wpa_s->cross_connect_in_use) {
+               wpa_s->cross_connect_in_use = 0;
+               wpa_msg(wpa_s->parent, MSG_INFO,
+                       P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s",
+                       wpa_s->ifname, wpa_s->cross_connect_uplink);
+       }
+       switch (wpa_s->removal_reason) {
+       case P2P_GROUP_REMOVAL_REQUESTED:
+               reason = " reason=REQUESTED";
+               break;
+       case P2P_GROUP_REMOVAL_IDLE_TIMEOUT:
+               reason = " reason=IDLE";
+               break;
+       case P2P_GROUP_REMOVAL_UNAVAILABLE:
+               reason = " reason=UNAVAILABLE";
+               break;
+       default:
+               reason = "";
+               break;
+       }
+       wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_REMOVED "%s %s%s",
+               wpa_s->ifname, gtype, reason);
+
+       eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL);
+
+       if (ssid)
+               wpas_notify_p2p_group_removed(wpa_s, ssid, gtype);
+
+       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);
+               wpa_supplicant_remove_iface(wpa_s->global, wpa_s);
+               wpa_s = global->ifaces;
+               if (wpa_s && ifname)
+                       wpa_drv_if_remove(wpa_s, type, ifname);
+               os_free(ifname);
+               return;
+       }
+
+       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);
+}
+
+
+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) {
+               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) {
+               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);
+       }
+
+#ifndef CONFIG_NO_CONFIG_WRITE
+       if (changed && wpa_s->conf->update_config &&
+           wpa_config_write(wpa_s->confname, wpa_s->conf)) {
+               wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
+       }
+#endif /* CONFIG_NO_CONFIG_WRITE */
+
+       return s->id;
+}
+
+
+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;
+
+       ssid = wpa_s->current_ssid;
+       if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GO ||
+           !ssid->p2p_persistent_group)
+               return;
+
+       for (s = wpa_s->parent->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 * ETH_ALEN, addr,
+                             ETH_ALEN) == 0)
+                       return; /* already in list */
+       }
+
+       n = os_realloc(s->p2p_client_list,
+                      (s->num_p2p_clients + 1) * ETH_ALEN);
+       if (n == NULL)
+               return;
+       os_memcpy(n + s->num_p2p_clients * ETH_ALEN, addr, ETH_ALEN);
+       s->p2p_client_list = n;
+       s->num_p2p_clients++;
+
+#ifndef CONFIG_NO_CONFIG_WRITE
+       if (wpa_s->parent->conf->update_config &&
+           wpa_config_write(wpa_s->parent->confname, wpa_s->parent->conf))
+               wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
+#endif /* CONFIG_NO_CONFIG_WRITE */
+}
+
+
+static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s,
+                                          int success)
+{
+       struct wpa_ssid *ssid;
+       const char *ssid_txt;
+       int client;
+       int persistent;
+       u8 go_dev_addr[ETH_ALEN];
+       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;
+       wpa_s->global->p2p_group_formation = NULL;
+       wpa_s->p2p_in_provisioning = 0;
+
+       if (!success) {
+               wpa_msg(wpa_s->parent, MSG_INFO,
+                       P2P_EVENT_GROUP_FORMATION_FAILURE);
+               wpas_p2p_group_delete(wpa_s);
+               return;
+       }
+
+       wpa_msg(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) {
+               ssid_txt = wpa_ssid_txt(ssid->ssid, ssid->ssid_len);
+               client = ssid->mode == WPAS_MODE_INFRA;
+               if (ssid->mode == WPAS_MODE_P2P_GO) {
+                       persistent = ssid->p2p_persistent_group;
+                       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 {
+               ssid_txt = "";
+               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 if (ssid && ssid->passphrase == NULL && ssid->psk_set) {
+               char psk[65];
+               wpa_snprintf_hex(psk, sizeof(psk), ssid->psk, 32);
+               wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
+                       "%s GO ssid=\"%s\" freq=%d psk=%s go_dev_addr=" MACSTR
+                       "%s",
+                       wpa_s->ifname, ssid_txt, ssid->frequency, psk,
+                       MAC2STR(go_dev_addr),
+                       persistent ? " [PERSISTENT]" : "");
+               wpas_p2p_cross_connect_setup(wpa_s);
+               wpas_p2p_set_group_idle_timeout(wpa_s);
+       } else {
+               wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
+                       "%s GO ssid=\"%s\" freq=%d passphrase=\"%s\" "
+                       "go_dev_addr=" MACSTR "%s",
+                       wpa_s->ifname, ssid_txt, ssid ? ssid->frequency : 0,
+                       ssid && ssid->passphrase ? ssid->passphrase : "",
+                       MAC2STR(go_dev_addr),
+                       persistent ? " [PERSISTENT]" : "");
+               wpas_p2p_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);
+       if (network_id < 0 && ssid)
+               network_id = ssid->id;
+       if (!client)
+               wpas_notify_p2p_group_started(wpa_s, ssid, network_id, 0);
+}
+
+
+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;
+
+       if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled)
+               return;
+       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+               return;
+
+       switch (result) {
+       case OFFCHANNEL_SEND_ACTION_SUCCESS:
+               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->pending_pd_before_join = 0;
+               wpa_printf(MSG_DEBUG, "P2P: Starting pending "
+                          "join-existing-group operation (no ACK for PD "
+                          "Req)");
+               wpas_p2p_join_start(wpa_s);
+       }
+}
+
+
+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;
+       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;
+       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_printf(MSG_DEBUG, "P2P: Start WPS Enrollee for peer " MACSTR,
+                  MAC2STR(res->peer_interface_addr));
+       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);
+       else {
+               u16 dev_pw_id = DEV_PW_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 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;
+
+       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;
+               wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
+                       "%s GO ssid=\"%s\" freq=%d passphrase=\"%s\" "
+                       "go_dev_addr=" MACSTR "%s",
+                       wpa_s->ifname,
+                       wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
+                       ssid->frequency,
+                       params->passphrase ? params->passphrase : "",
+                       MAC2STR(wpa_s->global->p2p_dev_addr),
+                       params->persistent_group ? " [PERSISTENT]" : "");
+
+               if (params->persistent_group)
+                       network_id = wpas_p2p_store_persistent_group(
+                               wpa_s->parent, ssid,
+                               wpa_s->global->p2p_dev_addr);
+               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);
+               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);
+       else if (wpa_s->p2p_pin[0])
+               wpa_supplicant_ap_wps_pin(wpa_s, params->peer_interface_addr,
+                                         wpa_s->p2p_pin, NULL, 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;
+
+       if (wpas_copy_go_neg_results(wpa_s, params) < 0)
+               return;
+
+       ssid = wpa_config_add_network(wpa_s->conf);
+       if (ssid == NULL)
+               return;
+
+       wpa_s->show_group_started = 0;
+
+       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->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->passphrase = os_strdup(params->passphrase);
+
+       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->connect_without_scan = ssid;
+       wpa_s->reassociate = 1;
+       wpa_s->disconnected = 0;
+       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_intra_bss = s->p2p_intra_bss;
+       d->persistent_reconnect = s->persistent_reconnect;
+}
+
+
+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;
+       }
+
+       os_snprintf(ifname, sizeof(ifname), "p2p-%s-%d", wpa_s->ifname,
+                   wpa_s->p2p_group_idx);
+       if (os_strlen(ifname) >= IFNAMSIZ &&
+           os_strlen(wpa_s->ifname) < IFNAMSIZ) {
+               /* Try to avoid going over the IFNAMSIZ length limit */
+               os_snprintf(ifname, sizeof(ifname), "p2p-%d",
+                           wpa_s->p2p_group_idx);
+       }
+       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';
+}
+
+
+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;
+       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);
+       if (group_wpa_s == NULL) {
+               wpa_printf(MSG_ERROR, "P2P: Failed to create new "
+                          "wpa_supplicant interface");
+               return NULL;
+       }
+       wpa_s->pending_interface_name[0] = '\0';
+       group_wpa_s->parent = wpa_s;
+       group_wpa_s->p2p_group_interface = go ? P2P_GROUP_INTERFACE_GO :
+               P2P_GROUP_INTERFACE_CLIENT;
+       wpa_s->global->p2p_group_formation = group_wpa_s;
+
+       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");
+       if (wpa_s->global->p2p)
+               p2p_group_formation_failed(wpa_s->global->p2p);
+       else if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+               wpa_drv_p2p_group_formation_failed(wpa_s);
+       wpas_group_formation_completed(wpa_s, 0);
+}
+
+
+void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res)
+{
+       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(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;
+       }
+
+       wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_SUCCESS);
+       wpas_notify_p2p_go_neg_completed(wpa_s, res);
+
+       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);
+                       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;
+
+               if (res->role_go)
+                       wpas_start_wps_go(group_wpa_s, res, 1);
+               else
+                       wpas_start_wps_enrollee(group_wpa_s, res);
+       } else {
+               wpa_s->p2p_in_provisioning = 1;
+               wpa_s->global->p2p_group_formation = wpa_s;
+
+               if (res->role_go)
+                       wpas_start_wps_go(wpa_s, res, 1);
+               else
+                       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);
+}
+
+
+void wpas_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_REQUEST MACSTR
+               " dev_passwd_id=%u", MAC2STR(src), dev_passwd_id);
+
+       wpas_notify_p2p_go_neg_req(wpa_s, src, dev_passwd_id);
+}
+
+
+void wpas_dev_found(void *ctx, const u8 *addr,
+                   const struct p2p_peer_info *info,
+                   int new_device)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       char devtype[WPS_DEV_TYPE_BUFSIZE];
+
+       wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_DEVICE_FOUND MACSTR
+               " p2p_dev_addr=" MACSTR
+               " pri_dev_type=%s name='%s' config_methods=0x%x "
+               "dev_capab=0x%x group_capab=0x%x",
+               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);
+
+       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(wpa_s, MSG_INFO, P2P_EVENT_DEVICE_LOST
+               "p2p_dev_addr=" MACSTR, MAC2STR(dev_addr));
+
+       wpas_notify_p2p_device_lost(wpa_s, dev_addr);
+}
+
+
+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;
+
+       wpa_drv_set_ap_wps_ie(wpa_s, NULL, probe_resp_ie, NULL);
+
+       if (wpa_drv_probe_req_report(wpa_s, 1) < 0) {
+               wpa_printf(MSG_DEBUG, "P2P: Failed to request the driver to "
+                          "report received Probe Request frames");
+               return -1;
+       }
+
+       wpa_s->pending_listen_freq = freq;
+       wpa_s->pending_listen_duration = duration;
+
+       if (wpa_drv_remain_on_channel(wpa_s, freq, duration) < 0) {
+               wpa_printf(MSG_DEBUG, "P2P: Failed to request the driver "
+                          "to remain on channel (%u MHz) for Listen "
+                          "state", freq);
+               wpa_s->pending_listen_freq = 0;
+               return -1;
+       }
+       wpa_s->off_channel_freq = 0;
+       wpa_s->roc_waiting_drv_freq = freq;
+
+       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);
+}
+
+
+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));
+}
+
+
+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_proto_not_avail(struct wpabuf *resp, u8 srv_proto,
+                                       u8 srv_trans_id)
+{
+       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, P2P_SD_PROTO_NOT_AVAILABLE);
+       /* Response Data: empty */
+       WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
+}
+
+
+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 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;
+       struct wpabuf buf;
+       u8 *len_pos;
+
+       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;
+       }
+
+       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);
+
+       wpabuf_set(&buf, query, query_len);
+       bsrv = wpas_p2p_service_get_bonjour(wpa_s, &buf);
+       if (bsrv == NULL) {
+               wpa_printf(MSG_DEBUG, "P2P: Requested Bonjour 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);
+               return;
+       }
+
+       /* 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));
+
+       if (wpabuf_tailroom(resp) >=
+           wpabuf_len(bsrv->query) + 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 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);
+}
+
+
+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;
+               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);
+}
+
+
+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);
+
+               pos = tlv_end;
+       }
+
+       wpas_notify_p2p_sd_response(wpa_s, sa, update_indic, tlvs, tlvs_len);
+}
+
+
+void * wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst,
+                          const struct wpabuf *tlvs)
+{
+       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+               return (void *) wpa_drv_p2p_sd_request(wpa_s, dst, tlvs);
+       if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+               return NULL;
+       return p2p_sd_request(wpa_s->global->p2p, dst, tlvs);
+}
+
+
+void * wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst,
+                               u8 version, const char *query)
+{
+       struct wpabuf *tlvs;
+       void *ret;
+
+       tlvs = wpabuf_alloc(2 + 1 + 1 + 1 + os_strlen(query));
+       if (tlvs == NULL)
+               return NULL;
+       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;
+}
+
+
+int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, void *req)
+{
+       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+               return wpa_drv_p2p_sd_cancel_request(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, 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->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) {
+               wpa_drv_p2p_sd_response(wpa_s, freq, dst, dialog_token,
+                                       resp_tlvs);
+               return;
+       }
+       if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+               return;
+       p2p_sd_response(wpa_s->global->p2p, freq, dst, dialog_token,
+                       resp_tlvs);
+}
+
+
+void wpas_p2p_sd_service_update(struct wpa_supplicant *wpa_s)
+{
+       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) {
+               wpa_drv_p2p_service_update(wpa_s);
+               return;
+       }
+       if (wpa_s->global->p2p)
+               p2p_sd_service_update(wpa_s->global->p2p);
+}
+
+
+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_sd_service_update(wpa_s);
+}
+
+
+int wpas_p2p_service_add_bonjour(struct wpa_supplicant *wpa_s,
+                                struct wpabuf *query, struct wpabuf *resp)
+{
+       struct p2p_srv_bonjour *bsrv;
+
+       bsrv = wpas_p2p_service_get_bonjour(wpa_s, query);
+       if (bsrv) {
+               wpabuf_free(query);
+               wpabuf_free(bsrv->resp);
+               bsrv->resp = resp;
+               return 0;
+       }
+
+       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;
+}
+
+
+static void wpas_prov_disc_local_display(struct wpa_supplicant *wpa_s,
+                                        const u8 *peer, const char *params,
+                                        unsigned int generated_pin)
+{
+       wpa_msg(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(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_ENTER_PIN MACSTR "%s",
+               MAC2STR(peer), params);
+}
+
+
+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;
+
+       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;
+       }
+       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 : "");
+       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(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);
+}
+
+
+void wpas_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       unsigned int generated_pin = 0;
+
+       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);
+               return;
+       }
+
+       if (config_methods & WPS_CONFIG_DISPLAY)
+               wpas_prov_disc_local_keypad(wpa_s, peer, "");
+       else if (config_methods & WPS_CONFIG_KEYPAD) {
+               generated_pin = wps_generate_pin();
+               wpas_prov_disc_local_display(wpa_s, peer, "", generated_pin);
+       } else if (config_methods & WPS_CONFIG_PUSHBUTTON)
+               wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_PBC_RESP MACSTR,
+                       MAC2STR(peer));
+
+       wpas_notify_p2p_provision_discovery(wpa_s, peer, 0 /* response */,
+                                           P2P_PROV_DISC_SUCCESS,
+                                           config_methods, generated_pin);
+}
+
+
+void wpas_prov_disc_fail(void *ctx, const u8 *peer,
+                        enum p2p_prov_disc_status status)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+
+       wpas_notify_p2p_provision_discovery(wpa_s, peer, 0 /* response */,
+                                           status, 0, 0);
+}
+
+
+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)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       struct wpa_ssid *s;
+       u8 cur_bssid[ETH_ALEN];
+       int res;
+       struct wpa_supplicant *grp;
+
+       if (!persistent_group) {
+               wpa_printf(MSG_DEBUG, "P2P: Invitation from " MACSTR
+                          " to join an active group", MAC2STR(sa));
+               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;
+               }
+               /*
+                * 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 (!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:
+       if (wpa_s->current_ssid && wpa_drv_get_bssid(wpa_s, cur_bssid) == 0 &&
+           wpa_s->assoc_freq) {
+               wpa_printf(MSG_DEBUG, "P2P: Trying to force channel to match "
+                          "the channel we are already using");
+               *force_freq = wpa_s->assoc_freq;
+       }
+
+       res = wpa_drv_shared_freq(wpa_s);
+       if (res > 0) {
+               wpa_printf(MSG_DEBUG, "P2P: Trying to force channel to match "
+                          "with the channel we are already using on a "
+                          "shared interface");
+               *force_freq = res;
+       }
+
+       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",
+                          MAC2STR(sa), op_freq);
+               if (s) {
+                       wpas_p2p_group_add_persistent(
+                               wpa_s, s, s->mode == WPAS_MODE_P2P_GO, 0);
+               } else if (bssid) {
+                       wpas_p2p_join(wpa_s, bssid, go_dev_addr,
+                                     wpa_s->p2p_wps_method);
+               }
+               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(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(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RECEIVED
+                               "sa=" MACSTR " go_dev_addr=" MACSTR
+                               " unknown-network",
+                               MAC2STR(sa), MAC2STR(go_dev_addr));
+               }
+               return;
+       }
+
+       wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RECEIVED "sa=" MACSTR
+               " persistent=%d", MAC2STR(sa), s->id);
+}
+
+
+static void wpas_invitation_result(void *ctx, int status, const u8 *bssid)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       struct wpa_ssid *ssid;
+
+       if (bssid) {
+               wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RESULT
+                       "status=%d " MACSTR,
+                       status, MAC2STR(bssid));
+       } else {
+               wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RESULT
+                       "status=%d ", status);
+       }
+       wpas_notify_p2p_invitation_result(wpa_s, status, bssid);
+
+       if (wpa_s->pending_invite_ssid_id == -1)
+               return; /* Invitation to active group */
+
+       if (status != P2P_SC_SUCCESS) {
+               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;
+       }
+
+       wpas_p2p_group_add_persistent(wpa_s, ssid,
+                                     ssid->mode == WPAS_MODE_P2P_GO, 0);
+}
+
+
+static int wpas_p2p_default_channels(struct wpa_supplicant *wpa_s,
+                                    struct p2p_channels *chan)
+{
+       int i, cla = 0;
+
+       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 = 11;
+       for (i = 0; i < 11; i++)
+               chan->reg_class[cla].channel[i] = i + 1;
+       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 = 4;
+       chan->reg_class[cla].channel[0] = 36;
+       chan->reg_class[cla].channel[1] = 40;
+       chan->reg_class[cla].channel[2] = 44;
+       chan->reg_class[cla].channel[3] = 48;
+       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 = 4;
+       chan->reg_class[cla].channel[0] = 149;
+       chan->reg_class[cla].channel[1] = 153;
+       chan->reg_class[cla].channel[2] = 157;
+       chan->reg_class[cla].channel[3] = 161;
+       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;
+}
+
+
+static int has_channel(struct hostapd_hw_modes *mode, u8 chan, int *flags)
+{
+       int i;
+
+       for (i = 0; i < mode->num_channels; i++) {
+               if (mode->channels[i].chan == chan) {
+                       if (flags)
+                               *flags = mode->channels[i].flag;
+                       return !(mode->channels[i].flag &
+                                (HOSTAPD_CHAN_DISABLED |
+                                 HOSTAPD_CHAN_PASSIVE_SCAN |
+                                 HOSTAPD_CHAN_NO_IBSS |
+                                 HOSTAPD_CHAN_RADAR));
+               }
+       }
+
+       return 0;
+}
+
+
+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 } bw;
+};
+
+static int wpas_p2p_setup_channels(struct wpa_supplicant *wpa_s,
+                                  struct p2p_channels *chan)
+{
+       struct hostapd_hw_modes *mode;
+       int cla, op;
+       struct p2p_oper_class_map op_class[] = {
+               { HOSTAPD_MODE_IEEE80211G, 81, 1, 13, 1, BW20 },
+               { HOSTAPD_MODE_IEEE80211G, 82, 14, 14, 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, 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 },
+               { -1, 0, 0, 0, 0, BW20 }
+       };
+
+       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);
+       }
+
+       cla = 0;
+
+       for (op = 0; op_class[op].op_class; op++) {
+               struct p2p_oper_class_map *o = &op_class[op];
+               u8 ch;
+               struct p2p_reg_class *reg = NULL;
+
+               mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, o->mode);
+               if (mode == NULL)
+                       continue;
+               for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) {
+                       int flag;
+                       if (!has_channel(mode, ch, &flag))
+                               continue;
+                       if (o->bw == BW40MINUS &&
+                           (!(flag & HOSTAPD_CHAN_HT40MINUS) ||
+                            !has_channel(mode, ch - 4, NULL)))
+                               continue;
+                       if (o->bw == BW40PLUS &&
+                           (!(flag & HOSTAPD_CHAN_HT40PLUS) ||
+                            !has_channel(mode, ch + 4, NULL)))
+                               continue;
+                       if (reg == NULL) {
+                               wpa_printf(MSG_DEBUG, "P2P: Add operating "
+                                          "class %u", o->op_class);
+                               reg = &chan->reg_class[cla];
+                               cla++;
+                               reg->reg_class = o->op_class;
+                       }
+                       reg->channel[reg->channels] = ch;
+                       reg->channels++;
+               }
+               if (reg) {
+                       wpa_hexdump(MSG_DEBUG, "P2P: Channels",
+                                   reg->channel, reg->channels);
+               }
+       }
+
+       chan->reg_classes = cla;
+
+       return 0;
+}
+
+
+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);
+}
+
+
+static int wpas_go_connected(void *ctx, const u8 *dev_addr)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+
+       for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+               struct wpa_ssid *ssid = wpa_s->current_ssid;
+               if (ssid == NULL)
+                       continue;
+               if (ssid->mode != WPAS_MODE_INFRA)
+                       continue;
+               if (wpa_s->wpa_state != WPA_COMPLETED &&
+                   wpa_s->wpa_state != WPA_GROUP_HANDSHAKE)
+                       continue;
+               if (os_memcmp(wpa_s->go_dev_addr, dev_addr, ETH_ALEN) == 0)
+                       return 1;
+       }
+
+       return 0;
+}
+
+
+/**
+ * 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;
+       unsigned int r;
+       int i;
+
+       if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE))
+               return 0;
+
+       if (global->p2p)
+               return 0;
+
+       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) {
+               struct p2p_params params;
+
+               wpa_printf(MSG_DEBUG, "P2P: Use driver-based P2P management");
+               os_memset(&params, 0, sizeof(params));
+               params.dev_name = wpa_s->conf->device_name;
+               os_memcpy(params.pri_dev_type, wpa_s->conf->device_type,
+                         WPS_DEV_TYPE_LEN);
+               params.num_sec_dev_types = wpa_s->conf->num_sec_device_types;
+               os_memcpy(params.sec_dev_type,
+                         wpa_s->conf->sec_device_type,
+                         params.num_sec_dev_types * WPS_DEV_TYPE_LEN);
+
+               if (wpa_drv_p2p_set_params(wpa_s, &params) < 0)
+                       return -1;
+
+               return 0;
+       }
+
+       os_memset(&p2p, 0, sizeof(p2p));
+       p2p.msg_ctx = wpa_s;
+       p2p.cb_ctx = wpa_s;
+       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.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;
+
+       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 (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;
+       } else {
+               p2p.reg_class = 81;
+               /*
+                * Pick one of the social channels randomly as the listen
+                * channel.
+                */
+               os_get_random((u8 *) &r, sizeof(r));
+               p2p.channel = 1 + (r % 3) * 5;
+       }
+       wpa_printf(MSG_DEBUG, "P2P: Own listen channel: %d", 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 {
+               p2p.op_reg_class = 81;
+               /*
+                * Use random operation channel from (1, 6, 11) if no other
+                * preference is indicated.
+                */
+               os_get_random((u8 *) &r, sizeof(r));
+               p2p.op_channel = 1 + (r % 3) * 5;
+               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->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);
+
+       if (wpas_p2p_setup_channels(wpa_s, &p2p.channels)) {
+               wpa_printf(MSG_ERROR, "P2P: Failed to configure supported "
+                          "channel list");
+               return -1;
+       }
+
+       os_memcpy(p2p.pri_dev_type, wpa_s->conf->device_type,
+                 WPS_DEV_TYPE_LEN);
+
+       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;
+
+       global->p2p = p2p_init(&p2p);
+       if (global->p2p == NULL)
+               return -1;
+
+       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]);
+       }
+
+       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_group_formation_timeout, wpa_s, NULL);
+       eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
+       eloop_cancel_timeout(wpas_p2p_pd_before_join_timeout, 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);
+
+       /* 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.
+ */
+void wpas_p2p_deinit_global(struct wpa_global *global)
+{
+       struct wpa_supplicant *wpa_s, *tmp;
+
+       if (global->p2p == NULL)
+               return;
+
+       /* Remove remaining P2P group interfaces */
+       wpa_s = global->ifaces;
+       if (wpa_s)
+               wpas_p2p_service_flush(wpa_s);
+       while (wpa_s && wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE)
+               wpa_s = wpa_s->next;
+       while (wpa_s) {
+               enum wpa_driver_if_type type;
+               tmp = global->ifaces;
+               while (tmp &&
+                      (tmp == wpa_s ||
+                       tmp->p2p_group_interface == NOT_P2P_GROUP_INTERFACE)) {
+                       tmp = tmp->next;
+               }
+               if (tmp == NULL)
+                       break;
+               type = wpas_p2p_if_type(tmp->p2p_group_interface);
+               /* 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;
+}
+
+
+static int wpas_p2p_create_iface(struct wpa_supplicant *wpa_s)
+{
+       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)
+{
+       if (persistent_group && wpa_s->conf->persistent_reconnect)
+               persistent_group = 2;
+
+       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) {
+               return wpa_drv_p2p_connect(wpa_s, peer_addr, wps_method,
+                                          go_intent, own_interface_addr,
+                                          force_freq, persistent_group);
+       }
+
+       return p2p_connect(wpa_s->global->p2p, peer_addr, wps_method,
+                          go_intent, own_interface_addr, force_freq,
+                          persistent_group);
+}
+
+
+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)
+{
+       if (persistent_group && wpa_s->conf->persistent_reconnect)
+               persistent_group = 2;
+
+       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+               return -1;
+
+       return p2p_authorize(wpa_s->global->p2p, peer_addr, wps_method,
+                            go_intent, own_interface_addr, force_freq,
+                            persistent_group);
+}
+
+
+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);
+               wpa_msg(wpa_s->parent, MSG_INFO,
+                       P2P_EVENT_GROUP_FORMATION_FAILURE);
+       }
+}
+
+
+static void wpas_p2p_pd_before_join_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+       struct wpa_supplicant *wpa_s = eloop_ctx;
+       if (!wpa_s->pending_pd_before_join)
+               return;
+       /*
+        * Provision Discovery Response may have been lost - try to connect
+        * anyway since we do not need any information from this PD.
+        */
+       wpa_printf(MSG_DEBUG, "P2P: PD timeout for join-existing-group - "
+                  "try to connect anyway");
+       wpas_p2p_join_start(wpa_s);
+}
+
+
+static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s,
+                                  struct wpa_scan_results *scan_res)
+{
+       struct wpa_bss *bss;
+       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 join",
+                  scan_res ? (int) scan_res->num : -1);
+
+       if (scan_res)
+               wpas_p2p_scan_res_handler(wpa_s, scan_res);
+
+       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_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);
+       }
+       bss = wpa_bss_get_bssid(wpa_s, wpa_s->pending_join_iface_addr);
+       if (bss) {
+               freq = bss->freq;
+               wpa_printf(MSG_DEBUG, "P2P: Target GO operating frequency "
+                          "from BSS table: %d MHz", freq);
+       }
+       if (freq > 0) {
+               u16 method;
+
+               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;
+               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: Provisioning 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, method, 1,
+                                     freq) < 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;
+               }
+
+               /*
+                * Actual join operation will be started from the Action frame
+                * TX status callback (if no ACK is received) or when the
+                * Provision Discovery Response is received. Use a short
+                * timeout as a backup mechanism should the Provision Discovery
+                * Response be lost for any reason.
+                */
+               eloop_cancel_timeout(wpas_p2p_pd_before_join_timeout, wpa_s,
+                                    NULL);
+               eloop_register_timeout(2, 0, wpas_p2p_pd_before_join_timeout,
+                                      wpa_s, NULL);
+               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);
+}
+
+
+static void wpas_p2p_join_scan(void *eloop_ctx, void *timeout_ctx)
+{
+       struct wpa_supplicant *wpa_s = eloop_ctx;
+       int ret;
+       struct wpa_driver_scan_params params;
+       struct wpabuf *wps_ie, *ies;
+       size_t ielen;
+
+       os_memset(&params, 0, sizeof(params));
+
+       /* P2P Wildcard SSID */
+       params.num_ssids = 1;
+       params.ssids[0].ssid = (u8 *) P2P_WILDCARD_SSID;
+       params.ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN;
+
+       wpa_s->wps->dev.p2p = 1;
+       wps_ie = wps_build_probe_req_ie(0, &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);
+
+       /*
+        * Run a scan to update BSS table and start Provision Discovery once
+        * the new scan results become available.
+        */
+       wpa_s->scan_res_handler = wpas_p2p_scan_res_join;
+       ret = wpa_drv_scan(wpa_s, &params);
+
+       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 int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr,
+                        const u8 *dev_addr, enum p2p_wps_method wps_method)
+{
+       wpa_printf(MSG_DEBUG, "P2P: Request to join existing group (iface "
+                  MACSTR " dev " MACSTR ")",
+                  MAC2STR(iface_addr), MAC2STR(dev_addr));
+
+       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(wpa_s, NULL);
+       return 0;
+}
+
+
+static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s)
+{
+       struct wpa_supplicant *group;
+       struct p2p_go_neg_results res;
+
+       eloop_cancel_timeout(wpas_p2p_pd_before_join_timeout, wpa_s, NULL);
+       group = wpas_p2p_get_group_iface(wpa_s, 0, 0);
+       if (group == NULL)
+               return -1;
+       if (group != wpa_s) {
+               os_memcpy(group->p2p_pin, wpa_s->p2p_pin,
+                         sizeof(group->p2p_pin));
+               group->p2p_wps_method = wpa_s->p2p_wps_method;
+       }
+
+       group->p2p_in_provisioning = 1;
+
+       os_memset(&res, 0, sizeof(res));
+       os_memcpy(res.peer_interface_addr, wpa_s->pending_join_iface_addr,
+                 ETH_ALEN);
+       res.wps_method = wpa_s->pending_join_wps_method;
+       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;
+}
+
+
+/**
+ * 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
+ * @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
+ * 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 join, int auth, int go_intent,
+                    int freq)
+{
+       int force_freq = 0, oper_freq = 0;
+       u8 bssid[ETH_ALEN];
+       int ret = 0;
+       enum wpa_driver_if_type iftype;
+
+       if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+               return -1;
+
+       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;
+
+       if (pin)
+               os_strlcpy(wpa_s->p2p_pin, pin, sizeof(wpa_s->p2p_pin));
+       else if (wps_method == WPS_PIN_DISPLAY) {
+               ret = wps_generate_pin();
+               os_snprintf(wpa_s->p2p_pin, sizeof(wpa_s->p2p_pin), "%08d",
+                           ret);
+               wpa_printf(MSG_DEBUG, "P2P: Randomly generated PIN: %s",
+                          wpa_s->p2p_pin);
+       } else
+               wpa_s->p2p_pin[0] = '\0';
+
+       if (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 (wpas_p2p_join(wpa_s, iface_addr, dev_addr, wps_method) <
+                   0)
+                       return -1;
+               return ret;
+       }
+
+       if (wpa_s->current_ssid && wpa_drv_get_bssid(wpa_s, bssid) == 0 &&
+           wpa_s->assoc_freq)
+               oper_freq = wpa_s->assoc_freq;
+       else {
+               oper_freq = wpa_drv_shared_freq(wpa_s);
+               if (oper_freq < 0)
+                       oper_freq = 0;
+       }
+
+       if (freq > 0) {
+               if (!p2p_supported_freq(wpa_s->global->p2p, freq)) {
+                       wpa_printf(MSG_DEBUG, "P2P: The forced channel "
+                                  "(%u MHz) is not supported for P2P uses",
+                                  freq);
+                       return -3;
+               }
+
+               if (oper_freq > 0 && freq != oper_freq &&
+                   !(wpa_s->drv_flags &
+                     WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT)) {
+                       wpa_printf(MSG_DEBUG, "P2P: Cannot start P2P group "
+                                  "on %u MHz while connected on another "
+                                  "channel (%u MHz)", freq, oper_freq);
+                       return -2;
+               }
+               wpa_printf(MSG_DEBUG, "P2P: Trying to force us to use the "
+                          "requested channel (%u MHz)", freq);
+               force_freq = freq;
+       } else if (oper_freq > 0 &&
+                  !p2p_supported_freq(wpa_s->global->p2p, oper_freq)) {
+               if (!(wpa_s->drv_flags &
+                     WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT)) {
+                       wpa_printf(MSG_DEBUG, "P2P: Cannot start P2P group "
+                                  "while connected on non-P2P supported "
+                                  "channel (%u MHz)", oper_freq);
+                       return -2;
+               }
+               wpa_printf(MSG_DEBUG, "P2P: Current operating channel "
+                          "(%u MHz) not available for P2P - try to use "
+                          "another channel", oper_freq);
+               force_freq = 0;
+       } else if (oper_freq > 0) {
+               wpa_printf(MSG_DEBUG, "P2P: Trying to force us to use the "
+                          "channel we are already using (%u MHz) on another "
+                          "interface", oper_freq);
+               force_freq = oper_freq;
+       }
+
+       wpa_s->create_p2p_iface = wpas_p2p_create_iface(wpa_s);
+
+       if (!wpa_s->create_p2p_iface) {
+               if (auth) {
+                       if (wpas_p2p_auth_go_neg(wpa_s, peer_addr, wps_method,
+                                                go_intent, wpa_s->own_addr,
+                                                force_freq, persistent_group)
+                           < 0)
+                               return -1;
+                       return ret;
+               }
+               if (wpas_p2p_start_go_neg(wpa_s, peer_addr, wps_method,
+                                         go_intent, wpa_s->own_addr,
+                                         force_freq, persistent_group) < 0)
+                       return -1;
+               return ret;
+       }
+
+       /* 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 (auth) {
+               if (wpas_p2p_auth_go_neg(wpa_s, peer_addr, wps_method,
+                                        go_intent,
+                                        wpa_s->pending_interface_addr,
+                                        force_freq, persistent_group) < 0)
+                       return -1;
+               return ret;
+       }
+       if (wpas_p2p_start_go_neg(wpa_s, peer_addr, wps_method, go_intent,
+                                 wpa_s->pending_interface_addr,
+                                 force_freq, persistent_group) < 0) {
+               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;
+       if (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;
+       }
+}
+
+
+static int wpas_p2p_listen_start(struct wpa_supplicant *wpa_s,
+                                unsigned int timeout)
+{
+       /* Limit maximum Listen state time based on driver limitation. */
+       if (timeout > wpa_s->max_remain_on_chan)
+               timeout = wpa_s->max_remain_on_chan;
+
+       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+               return wpa_drv_p2p_listen(wpa_s, timeout);
+
+       return p2p_listen(wpa_s->global->p2p, timeout);
+}
+
+
+/**
+ * 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, wpa_s->pending_action_tx);
+       if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+               return;
+       if (p2p_listen_end(wpa_s->global->p2p, freq) > 0)
+               return; /* P2P module started a new operation */
+       if (wpa_s->pending_action_tx)
+               return;
+       if (wpa_s->p2p_long_listen > 0)
+               wpa_s->p2p_long_listen -= wpa_s->max_remain_on_chan;
+       if (wpa_s->p2p_long_listen > 0) {
+               wpa_printf(MSG_DEBUG, "P2P: Continuing long Listen state");
+               wpas_p2p_listen_start(wpa_s, wpa_s->p2p_long_listen);
+       }
+}
+
+
+/**
+ * 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;
+
+       if (os_strcmp(ifname, "*") == 0) {
+               struct wpa_supplicant *prev;
+               wpa_s = global->ifaces;
+               while (wpa_s) {
+                       prev = wpa_s;
+                       wpa_s = wpa_s->next;
+                       wpas_p2p_disconnect(prev);
+               }
+               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(wpa_s);
+}
+
+
+static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s,
+                                  struct p2p_go_neg_results *params,
+                                  int freq)
+{
+       u8 bssid[ETH_ALEN];
+       int res;
+
+       os_memset(params, 0, sizeof(*params));
+       params->role_go = 1;
+       if (freq) {
+               wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on forced "
+                          "frequency %d MHz", freq);
+               params->freq = freq;
+       } else if (wpa_s->conf->p2p_oper_reg_class == 81 &&
+                  wpa_s->conf->p2p_oper_channel >= 1 &&
+                  wpa_s->conf->p2p_oper_channel <= 11) {
+               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_reg_class == 115 ||
+                  wpa_s->conf->p2p_oper_reg_class == 124) {
+               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_overall_freq > 0 &&
+                  p2p_supported_freq(wpa_s->global->p2p,
+                                     wpa_s->best_overall_freq)) {
+               params->freq = wpa_s->best_overall_freq;
+               wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best overall "
+                          "channel %d MHz", params->freq);
+       } else if (wpa_s->conf->p2p_oper_channel == 0 &&
+                  wpa_s->best_24_freq > 0 &&
+                  p2p_supported_freq(wpa_s->global->p2p,
+                                     wpa_s->best_24_freq)) {
+               params->freq = wpa_s->best_24_freq;
+               wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 2.4 GHz "
+                          "channel %d MHz", params->freq);
+       } else if (wpa_s->conf->p2p_oper_channel == 0 &&
+                  wpa_s->best_5_freq > 0 &&
+                  p2p_supported_freq(wpa_s->global->p2p,
+                                     wpa_s->best_5_freq)) {
+               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 {
+               params->freq = 2412;
+               wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz (no preference "
+                          "known)", params->freq);
+       }
+
+       if (wpa_s->current_ssid && wpa_drv_get_bssid(wpa_s, bssid) == 0 &&
+           wpa_s->assoc_freq && !freq) {
+               wpa_printf(MSG_DEBUG, "P2P: Force GO on the channel we are "
+                          "already using");
+               params->freq = wpa_s->assoc_freq;
+       }
+
+       res = wpa_drv_shared_freq(wpa_s);
+       if (res > 0 && !freq) {
+               wpa_printf(MSG_DEBUG, "P2P: Force GO on the channel we are "
+                          "already using on a shared interface");
+               params->freq = res;
+       } else if (res > 0 && freq != res &&
+                  !(wpa_s->drv_flags &
+                    WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT)) {
+               wpa_printf(MSG_DEBUG, "P2P: Cannot start P2P group on %u MHz "
+                          "while connected on another channel (%u MHz)",
+                          freq, res);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+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))
+               return wpa_s;
+
+       if (wpas_p2p_add_group_interface(wpa_s, go ? WPA_IF_P2P_GO :
+                                        WPA_IF_P2P_CLIENT) < 0)
+               return NULL;
+       group_wpa_s = wpas_p2p_init_group_interface(wpa_s, go);
+       if (group_wpa_s == NULL) {
+               wpas_p2p_remove_pending_group_interface(wpa_s);
+               return NULL;
+       }
+
+       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
+ * 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)
+{
+       struct p2p_go_neg_results params;
+       unsigned int r;
+
+       if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+               return -1;
+
+       /* 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(wpa_s);
+
+       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(wpa_s->global->p2p,
+                                      wpa_s->best_24_freq)) {
+                       freq = wpa_s->best_24_freq;
+                       wpa_printf(MSG_DEBUG, "P2P: Use best 2.4 GHz band "
+                                  "channel: %d MHz", freq);
+               } else {
+                       os_get_random((u8 *) &r, sizeof(r));
+                       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(wpa_s->global->p2p,
+                                      wpa_s->best_5_freq)) {
+                       freq = wpa_s->best_5_freq;
+                       wpa_printf(MSG_DEBUG, "P2P: Use best 5 GHz band "
+                                  "channel: %d MHz", freq);
+               } else {
+                       os_get_random((u8 *) &r, sizeof(r));
+                       freq = 5180 + (r % 4) * 20;
+                       if (!p2p_supported_freq(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(wpa_s->global->p2p, freq)) {
+               wpa_printf(MSG_DEBUG, "P2P: The forced channel for GO "
+                          "(%u MHz) is not supported for P2P uses",
+                          freq);
+               return -1;
+       }
+
+       if (wpas_p2p_init_go_params(wpa_s, &params, freq))
+               return -1;
+       if (params.freq &&
+           !p2p_supported_freq(wpa_s->global->p2p, params.freq)) {
+               wpa_printf(MSG_DEBUG, "P2P: The selected channel for GO "
+                          "(%u MHz) is not supported for P2P uses",
+                          params.freq);
+               return -1;
+       }
+       p2p_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)
+{
+       struct wpa_ssid *ssid;
+
+       wpa_s = wpas_p2p_get_group_iface(wpa_s, addr_allocated, 0);
+       if (wpa_s == NULL)
+               return -1;
+
+       wpa_supplicant_ap_deinit(wpa_s);
+
+       ssid = wpa_config_add_network(wpa_s->conf);
+       if (ssid == NULL)
+               return -1;
+       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_supplicant_select_network(wpa_s, ssid);
+
+       wpa_s->show_group_started = 1;
+
+       return 0;
+}
+
+
+int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s,
+                                 struct wpa_ssid *ssid, int addr_allocated,
+                                 int freq)
+{
+       struct p2p_go_neg_results params;
+       int go = 0;
+
+       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");
+               return 0;
+       }
+
+       /* Make sure we are not running find during connection establishment */
+       wpas_p2p_stop_find(wpa_s);
+
+       if (ssid->mode == WPAS_MODE_INFRA)
+               return wpas_start_p2p_client(wpa_s, ssid, addr_allocated);
+
+       if (ssid->mode != WPAS_MODE_P2P_GO)
+               return -1;
+
+       if (wpas_p2p_init_go_params(wpa_s, &params, freq))
+               return -1;
+
+       params.role_go = 1;
+       if (ssid->passphrase == NULL ||
+           os_strlen(ssid->passphrase) >= sizeof(params.passphrase)) {
+               wpa_printf(MSG_DEBUG, "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;
+
+       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)
+               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,
+                                      int persistent_group,
+                                      int group_formation)
+{
+       struct p2p_group *group;
+       struct p2p_group_config *cfg;
+
+       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+               return NULL;
+       if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+               return NULL;
+
+       cfg = os_zalloc(sizeof(*cfg));
+       if (cfg == NULL)
+               return NULL;
+
+       if (persistent_group && wpa_s->conf->persistent_reconnect)
+               cfg->persistent_group = 2;
+       else if (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;
+       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 (!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);
+       if (wpa_s->global->p2p)
+               p2p_wps_success_cb(wpa_s->global->p2p, peer_addr);
+       else if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+               wpa_drv_wps_success_cb(wpa_s, peer_addr);
+       wpas_group_formation_completed(wpa_s, 1);
+}
+
+
+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);
+}
+
+
+int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
+                      const char *config_method, int join)
+{
+       u16 config_methods;
+
+       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");
+               return -1;
+       }
+
+       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) {
+               return wpa_drv_p2p_prov_disc_req(wpa_s, peer_addr,
+                                                config_methods, join);
+       }
+
+       if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled)
+               return -1;
+
+       return p2p_prov_disc_req(wpa_s->global->p2p, peer_addr,
+                                config_methods, join, 0);
+}
+
+
+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 (!wpa_s->pending_action_tx)
+               return;
+
+       wpa_printf(MSG_DEBUG, "P2P: Drop pending Action TX due to new "
+                  "operation request");
+       wpabuf_free(wpa_s->pending_action_tx);
+       wpa_s->pending_action_tx = NULL;
+}
+
+
+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)
+{
+       wpas_p2p_clear_pending_action_tx(wpa_s);
+       wpa_s->p2p_long_listen = 0;
+
+       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+               return wpa_drv_p2p_find(wpa_s, timeout, type);
+
+       if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+               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);
+}
+
+
+void wpas_p2p_stop_find(struct wpa_supplicant *wpa_s)
+{
+       wpas_p2p_clear_pending_action_tx(wpa_s);
+       wpa_s->p2p_long_listen = 0;
+       eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL);
+       eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
+       wpa_s->p2p_cb_on_scan_complete = 0;
+
+       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) {
+               wpa_drv_p2p_stop_find(wpa_s);
+               return;
+       }
+
+       if (wpa_s->global->p2p)
+               p2p_stop_find(wpa_s->global->p2p);
+
+       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;
+       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,
+                         const u8 *ie, size_t ie_len)
+{
+       if (wpa_s->global->p2p_disabled)
+               return 0;
+       if (wpa_s->global->p2p == NULL)
+               return 0;
+
+       return p2p_probe_req_rx(wpa_s->global->p2p, addr, dst, bssid,
+                               ie, ie_len);
+}
+
+
+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);
+}
+
+
+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->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+               return wpa_drv_p2p_reject(wpa_s, addr);
+
+       if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+               return -1;
+
+       return p2p_reject(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)
+{
+       enum p2p_invite_role role;
+       u8 *bssid = NULL;
+
+       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;
+
+       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+               return wpa_drv_p2p_invite(wpa_s, peer_addr, role, bssid,
+                                         ssid->ssid, ssid->ssid_len,
+                                         go_dev_addr, 1);
+
+       if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+               return -1;
+
+       return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid,
+                         ssid->ssid, ssid->ssid_len, 0, go_dev_addr, 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;
+
+       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;
+       }
+
+       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;
+       } 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;
+       }
+       wpa_s->parent->pending_invite_ssid_id = -1;
+
+       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+               return wpa_drv_p2p_invite(wpa_s, peer_addr, role, bssid,
+                                         ssid->ssid, ssid->ssid_len,
+                                         go_dev_addr, persistent);
+
+       if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+               return -1;
+
+       return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid,
+                         ssid->ssid, ssid->ssid_len, wpa_s->assoc_freq,
+                         go_dev_addr, persistent);
+}
+
+
+void wpas_p2p_completed(struct wpa_supplicant *wpa_s)
+{
+       struct wpa_ssid *ssid = wpa_s->current_ssid;
+       const char *ssid_txt;
+       u8 go_dev_addr[ETH_ALEN];
+       int network_id = -1;
+       int persistent;
+       int freq;
+
+       if (!wpa_s->show_group_started || !ssid)
+               return;
+
+       wpa_s->show_group_started = 0;
+
+       ssid_txt = wpa_ssid_txt(ssid->ssid, ssid->ssid_len);
+       os_memset(go_dev_addr, 0, ETH_ALEN);
+       if (ssid->bssid_set)
+               os_memcpy(go_dev_addr, ssid->bssid, ETH_ALEN);
+       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;
+       if (ssid->passphrase == NULL && ssid->psk_set) {
+               char psk[65];
+               wpa_snprintf_hex(psk, sizeof(psk), ssid->psk, 32);
+               wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
+                       "%s client ssid=\"%s\" freq=%d psk=%s go_dev_addr="
+                       MACSTR "%s",
+                       wpa_s->ifname, ssid_txt, freq, psk,
+                       MAC2STR(go_dev_addr),
+                       persistent ? " [PERSISTENT]" : "");
+       } else {
+               wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
+                       "%s client ssid=\"%s\" freq=%d passphrase=\"%s\" "
+                       "go_dev_addr=" MACSTR "%s",
+                       wpa_s->ifname, ssid_txt, freq,
+                       ssid->passphrase ? ssid->passphrase : "",
+                       MAC2STR(go_dev_addr),
+                       persistent ? " [PERSISTENT]" : "");
+       }
+
+       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)
+{
+       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+               return -1;
+       if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+               return -1;
+
+       if (wpa_s->wpa_state < WPA_ASSOCIATED ||
+           wpa_s->current_ssid == NULL ||
+           wpa_s->current_ssid->mode != WPAS_MODE_INFRA)
+               return -1;
+
+       return p2p_presence_req(wpa_s->global->p2p, wpa_s->bssid,
+                               wpa_s->own_addr, wpa_s->assoc_freq,
+                               duration1, interval1, duration2, interval2);
+}
+
+
+int wpas_p2p_ext_listen(struct wpa_supplicant *wpa_s, unsigned int period,
+                       unsigned int interval)
+{
+       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+               return -1;
+
+       if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+               return -1;
+
+       return p2p_ext_listen(wpa_s->global->p2p, period, interval);
+}
+
+
+static int wpas_p2p_is_client(struct wpa_supplicant *wpa_s)
+{
+       return wpa_s->current_ssid != NULL &&
+               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");
+       wpa_s->removal_reason = P2P_GROUP_REMOVAL_IDLE_TIMEOUT;
+       wpas_p2p_group_delete(wpa_s);
+}
+
+
+static void wpas_p2p_set_group_idle_timeout(struct wpa_supplicant *wpa_s)
+{
+       unsigned int timeout;
+
+       eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL);
+       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 (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;
+       }
+
+       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);
+}
+
+
+void wpas_p2p_deauth_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
+                          u16 reason_code, const u8 *ie, size_t ie_len)
+{
+       if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+               return;
+       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+               return;
+
+       p2p_deauth_notif(wpa_s->global->p2p, bssid, reason_code, ie, ie_len);
+}
+
+
+void wpas_p2p_disassoc_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
+                            u16 reason_code, const u8 *ie, size_t ie_len)
+{
+       if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+               return;
+       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+               return;
+
+       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;
+               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;
+               } else {
+                       reg_class = 81;
+                       /*
+                        * Pick one of the social channels randomly as the
+                        * listen channel.
+                        */
+                       os_get_random((u8 *) &r, sizeof(r));
+                       channel = 1 + (r % 3) * 5;
+               }
+               ret = p2p_set_listen_channel(p2p, reg_class, channel);
+               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.
+                        */
+                       os_get_random((u8 *) &r, sizeof(r));
+                       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");
+               }
+       }
+}
+
+
+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;
+       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+               return -1;
+
+       wpa_s->global->cross_connection = enabled;
+       p2p_set_cross_connect(wpa_s->global->p2p, enabled);
+
+       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(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(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(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);
+}
+
+
+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)
+                       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(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);
+
+       if (wpa_s->global->p2p)
+               p2p_group_formation_failed(wpa_s->global->p2p);
+
+       eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
+                            wpa_s->parent, NULL);
+
+       wpas_group_formation_completed(wpa_s, 0);
+       return 1;
+}
+
+
+void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s)
+{
+       struct p2p_channels chan;
+
+       if (wpa_s->global == NULL || wpa_s->global->p2p == NULL)
+               return;
+
+       os_memset(&chan, 0, sizeof(chan));
+       if (wpas_p2p_setup_channels(wpa_s, &chan)) {
+               wpa_printf(MSG_ERROR, "P2P: Failed to update supported "
+                          "channel list");
+               return;
+       }
+
+       p2p_update_channel_list(wpa_s->global->p2p, &chan);
+}
+
+
+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;
+       }
+
+       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);
+                       wpas_p2p_group_delete(wpa_s);
+                       break;
+               }
+       }
+
+       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");
+       wpa_s->removal_reason = P2P_GROUP_REMOVAL_UNAVAILABLE;
+       wpas_p2p_group_delete(wpa_s);
+}
+
+
+void wpas_p2p_update_best_channels(struct wpa_supplicant *wpa_s,
+                                  int freq_24, int freq_5, int freq_overall)
+{
+       struct p2p_data *p2p = wpa_s->global->p2p;
+       if (p2p == NULL || (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT))
+               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 || (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT))
+               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;
+
+       wpa_s->removal_reason = P2P_GROUP_REMOVAL_REQUESTED;
+       wpas_p2p_group_delete(wpa_s);
+
+       return 0;
+}
+
+
+int wpas_p2p_in_progress(struct wpa_supplicant *wpa_s)
+{
+       if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+               return 0;
+
+       return p2p_in_progress(wpa_s->global->p2p);
+}
+
+
+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 (os_memcmp(s->bssid, addr, ETH_ALEN) == 0)
+                       return s; /* peer is GO in the persistent group */
+               if (s->mode != WPAS_MODE_P2P_GO || s->p2p_client_list == NULL)
+                       continue;
+               for (i = 0; i < s->num_p2p_clients; i++) {
+                       if (os_memcmp(s->p2p_client_list + i * ETH_ALEN,
+                                     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 (addr == NULL)
+               return;
+       wpas_p2p_add_persistent_group_client(wpa_s, addr);
+}
diff --git a/wpa_supplicant/p2p_supplicant.h b/wpa_supplicant/p2p_supplicant.h
new file mode 100644 (file)
index 0000000..4887823
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * wpa_supplicant - P2P
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef 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;
+
+int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s);
+void wpas_p2p_deinit(struct wpa_supplicant *wpa_s);
+void wpas_p2p_deinit_global(struct wpa_global *global);
+int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
+                    const char *pin, enum p2p_wps_method wps_method,
+                    int persistent_group, int join, int auth, int go_intent,
+                    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);
+int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname);
+int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group,
+                      int freq);
+int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s,
+                                 struct wpa_ssid *ssid, int addr_allocated,
+                                 int freq);
+struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s,
+                                      int persistent_group,
+                                      int group_formation);
+void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
+                         int registrar);
+int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
+                      const char *config_method, int join);
+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);
+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_assoc_req_ie(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+                         u8 *buf, size_t len, int p2p_group);
+int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s, const u8 *addr,
+                         const u8 *dst, const u8 *bssid,
+                         const u8 *ie, size_t ie_len);
+void wpas_p2p_rx_action(struct wpa_supplicant *wpa_s, const u8 *da,
+                       const u8 *sa, const u8 *bssid,
+                       u8 category, const u8 *data, size_t len, int freq);
+void wpas_p2p_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ies);
+void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s);
+void wpas_dev_found(void *ctx, const u8 *addr,
+                   const struct p2p_peer_info *info,
+                   int new_device);
+void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res);
+void wpas_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id);
+void wpas_prov_disc_req(void *ctx, const u8 *peer, u16 config_methods,
+                       const u8 *dev_addr, const u8 *pri_dev_type,
+                       const char *dev_name, u16 supp_config_methods,
+                       u8 dev_capab, u8 group_capab, const u8 *group_id,
+                       size_t group_id_len);
+void wpas_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods);
+void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token,
+                    u16 update_indic, const u8 *tlvs, size_t tlvs_len);
+void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic,
+                     const u8 *tlvs, size_t tlvs_len);
+void * wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst,
+                          const struct wpabuf *tlvs);
+void * wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst,
+                               u8 version, const char *query);
+int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, void *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_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 wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname,
+                         const u8 *peer_addr, const u8 *go_dev_addr);
+void wpas_p2p_completed(struct wpa_supplicant *wpa_s);
+int wpas_p2p_presence_req(struct wpa_supplicant *wpa_s, u32 duration1,
+                         u32 interval1, u32 duration2, u32 interval2);
+int wpas_p2p_ext_listen(struct wpa_supplicant *wpa_s, unsigned int period,
+                       unsigned int interval);
+void wpas_p2p_deauth_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
+                          u16 reason_code, const u8 *ie, size_t ie_len);
+void wpas_p2p_disassoc_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
+                            u16 reason_code, const u8 *ie, size_t ie_len);
+void wpas_p2p_update_config(struct wpa_supplicant *wpa_s);
+int wpas_p2p_set_noa(struct wpa_supplicant *wpa_s, u8 count, int start,
+                    int duration);
+int wpas_p2p_set_cross_connect(struct wpa_supplicant *wpa_s, int enabled);
+void wpas_p2p_notif_connected(struct wpa_supplicant *wpa_s);
+void wpas_p2p_notif_disconnected(struct wpa_supplicant *wpa_s);
+int wpas_p2p_notif_pbc_overlap(struct wpa_supplicant *wpa_s);
+void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s);
+int wpas_p2p_cancel(struct wpa_supplicant *wpa_s);
+void wpas_p2p_interface_unavailable(struct wpa_supplicant *wpa_s);
+void wpas_p2p_update_best_channels(struct wpa_supplicant *wpa_s,
+                                  int freq_24, int freq_5, int freq_overall);
+int wpas_p2p_unauthorize(struct wpa_supplicant *wpa_s, const char *addr);
+int wpas_p2p_disconnect(struct wpa_supplicant *wpa_s);
+void wpas_p2p_wps_failed(struct wpa_supplicant *wpa_s,
+                        struct wps_event_fail *fail);
+int wpas_p2p_in_progress(struct wpa_supplicant *wpa_s);
+void wpas_p2p_network_removed(struct wpa_supplicant *wpa_s,
+                             struct wpa_ssid *ssid);
+struct wpa_ssid * wpas_p2p_get_persistent(struct wpa_supplicant *wpa_s,
+                                         const u8 *addr, const u8 *ssid,
+                                         size_t ssid_len);
+void wpas_p2p_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s,
+                                      const u8 *addr);
+
+#endif /* P2P_SUPPLICANT_H */
index edc8c83..afa3be4 100644 (file)
@@ -20,8 +20,9 @@
 #include "config.h"
 #include "wpa_supplicant_i.h"
 #include "driver_i.h"
-#include "mlme.h"
 #include "wps_supplicant.h"
+#include "p2p_supplicant.h"
+#include "p2p/p2p.h"
 #include "notify.h"
 #include "bss.h"
 #include "scan.h"
@@ -42,21 +43,21 @@ static void wpa_supplicant_gen_assoc_event(struct wpa_supplicant *wpa_s)
                        wpas_notify_network_changed(wpa_s);
        }
        wpa_supplicant_initiate_eapol(wpa_s);
-       wpa_printf(MSG_DEBUG, "Already associated with a configured network - "
-                  "generating associated event");
+       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_config *conf,
+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 = conf->ssid; ssid; ssid = ssid->next) {
+       for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
                if (!(ssid->key_mgmt & WPA_KEY_MGMT_WPS))
                        continue;
 
@@ -69,6 +70,16 @@ static int wpas_wps_in_use(struct wpa_config *conf,
                        return 2;
        }
 
+#ifdef CONFIG_P2P
+       if (!wpa_s->global->p2p_disabled && wpa_s->global->p2p) {
+               wpa_s->wps->dev.p2p = 1;
+               if (!wps) {
+                       wps = 1;
+                       *req_type = WPS_REQ_ENROLLEE_INFO;
+               }
+       }
+#endif /* CONFIG_P2P */
+
        return wps;
 }
 #endif /* CONFIG_WPS */
@@ -77,12 +88,13 @@ static int wpas_wps_in_use(struct wpa_config *conf,
 int wpa_supplicant_enabled_networks(struct wpa_config *conf)
 {
        struct wpa_ssid *ssid = conf->ssid;
+       int count = 0;
        while (ssid) {
                if (!ssid->disabled)
-                       return 1;
+                       count++;
                ssid = ssid->next;
        }
-       return 0;
+       return count;
 }
 
 
@@ -97,8 +109,8 @@ static void wpa_supplicant_assoc_try(struct wpa_supplicant *wpa_s,
 
        /* ap_scan=2 mode - try to associate with each SSID. */
        if (ssid == NULL) {
-               wpa_printf(MSG_DEBUG, "wpa_supplicant_scan: Reached "
-                          "end of scan list - go back to beginning");
+               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;
@@ -189,11 +201,7 @@ int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s,
 
        wpa_supplicant_notify_scanning(wpa_s, 1);
 
-       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
-               ret = ieee80211_sta_req_scan(wpa_s, params);
-       else
-               ret = wpa_drv_scan(wpa_s, params);
-
+       ret = wpa_drv_scan(wpa_s, params);
        if (ret) {
                wpa_supplicant_notify_scanning(wpa_s, 0);
                wpas_notify_scan_done(wpa_s, 0);
@@ -204,6 +212,63 @@ int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s,
 }
 
 
+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);
+}
+
+
+static 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;
+}
+
+
+static 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)
 {
@@ -237,20 +302,137 @@ wpa_supplicant_build_filter_ssids(struct wpa_config *conf, size_t *num_ssids)
 }
 
 
-static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
+static void wpa_supplicant_optimize_freqs(
+       struct wpa_supplicant *wpa_s, struct wpa_driver_scan_params *params)
 {
-       struct wpa_supplicant *wpa_s = eloop_ctx;
-       struct wpa_ssid *ssid;
-       int scan_req = 0, ret;
-       struct wpabuf *wps_ie = NULL;
+#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_zalloc(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++;
+       }
+#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_zalloc(2 * sizeof(int));
+               if (params->freqs)
+                       params->freqs[0] = wpa_s->wps_freq;
+               wpa_s->after_wps--;
+       }
+
+#endif /* CONFIG_WPS */
+}
+
+
+#ifdef CONFIG_INTERWORKING
+static void wpas_add_interworking_elements(struct wpa_supplicant *wpa_s,
+                                          struct wpabuf *buf)
+{
+       if (wpa_s->conf->interworking == 0)
+               return;
+
+       wpabuf_put_u8(buf, WLAN_EID_EXT_CAPAB);
+       wpabuf_put_u8(buf, 4);
+       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, 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 wpa_driver_scan_params *params)
+{
+       struct wpabuf *extra_ie = NULL;
 #ifdef CONFIG_WPS
        int wps = 0;
        enum wps_request_type req_type = WPS_REQ_ENROLLEE_INFO;
 #endif /* CONFIG_WPS */
+
+#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, &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 */
+
+#endif /* CONFIG_WPS */
+
+       return extra_ie;
+}
+
+
+static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
+{
+       struct wpa_supplicant *wpa_s = eloop_ctx;
+       struct wpa_ssid *ssid;
+       int scan_req = 0, ret;
+       struct wpabuf *extra_ie;
        struct wpa_driver_scan_params params;
        size_t max_ssids;
        enum wpa_states prev_state;
 
+       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) {
                wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
                return;
@@ -258,15 +440,15 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
 
        if (!wpa_supplicant_enabled_networks(wpa_s->conf) &&
            !wpa_s->scan_req) {
-               wpa_printf(MSG_DEBUG, "No enabled networks - do not scan");
+               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_printf(MSG_DEBUG, "Using wired authentication - "
-                          "overriding ap_scan configuration");
+               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);
        }
@@ -276,8 +458,21 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
                return;
        }
 
-       if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) ||
-           wpa_s->conf->ap_scan == 2)
+#ifdef CONFIG_P2P
+       if (wpas_p2p_in_progress(wpa_s)) {
+               if (wpa_s->wpa_state == WPA_SCANNING) {
+                       wpa_dbg(wpa_s, MSG_DEBUG, "Delay station mode scan "
+                               "while P2P operation is in progress");
+                       wpa_supplicant_req_scan(wpa_s, 5, 0);
+               } else {
+                       wpa_dbg(wpa_s, MSG_DEBUG, "Do not request scan while "
+                               "P2P operation is in progress");
+               }
+               return;
+       }
+#endif /* CONFIG_P2P */
+
+       if (wpa_s->conf->ap_scan == 2)
                max_ssids = 1;
        else {
                max_ssids = wpa_s->max_scan_ssids;
@@ -285,10 +480,6 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
                        max_ssids = WPAS_MAX_SCAN_SSIDS;
        }
 
-#ifdef CONFIG_WPS
-       wps = wpas_wps_in_use(wpa_s->conf, &req_type);
-#endif /* CONFIG_WPS */
-
        scan_req = wpa_s->scan_req;
        wpa_s->scan_req = 0;
 
@@ -299,6 +490,20 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
            wpa_s->wpa_state == WPA_INACTIVE)
                wpa_supplicant_set_state(wpa_s, WPA_SCANNING);
 
+       if (scan_req != 2 && wpa_s->connect_without_scan) {
+               for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+                       if (ssid == wpa_s->connect_without_scan)
+                               break;
+               }
+               wpa_s->connect_without_scan = NULL;
+               if (ssid) {
+                       wpa_printf(MSG_DEBUG, "Start a pre-selected network "
+                                  "without scan step");
+                       wpa_supplicant_associate(wpa_s, NULL, ssid);
+                       return;
+               }
+       }
+
        /* Find the starting point from which to continue scanning */
        ssid = wpa_s->conf->ssid;
        if (wpa_s->prev_scan_ssid != WILDCARD_SSID_SCAN) {
@@ -311,9 +516,8 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
                }
        }
 
-       if (scan_req != 2 && (wpa_s->conf->ap_scan == 2 ||
-                             wpa_s->connect_without_scan)) {
-               wpa_s->connect_without_scan = 0;
+       if (scan_req != 2 && wpa_s->conf->ap_scan == 2) {
+               wpa_s->connect_without_scan = NULL;
                wpa_supplicant_assoc_try(wpa_s, ssid);
                return;
        } else if (wpa_s->conf->ap_scan == 2) {
@@ -365,52 +569,55 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
        if (ssid) {
                wpa_s->prev_scan_ssid = ssid;
                if (max_ssids > 1) {
-                       wpa_printf(MSG_DEBUG, "Include wildcard SSID in the "
-                                  "scan request");
+                       wpa_dbg(wpa_s, MSG_DEBUG, "Include wildcard SSID in "
+                               "the scan request");
                        params.num_ssids++;
                }
-               wpa_printf(MSG_DEBUG, "Starting AP scan for specific SSID(s)");
+               wpa_dbg(wpa_s, MSG_DEBUG, "Starting AP scan for specific "
+                       "SSID(s)");
        } else {
                wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
                params.num_ssids++;
-               wpa_printf(MSG_DEBUG, "Starting AP scan for wildcard SSID");
+               wpa_dbg(wpa_s, MSG_DEBUG, "Starting AP scan for wildcard "
+                       "SSID");
        }
 
-#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_printf(MSG_DEBUG, "WPS: Scan only frequency %u MHz that "
-                          "was used during provisioning", wpa_s->wps_freq);
-               params.freqs = os_zalloc(2 * sizeof(int));
-               if (params.freqs)
-                       params.freqs[0] = wpa_s->wps_freq;
-               wpa_s->after_wps--;
-       }
+       wpa_supplicant_optimize_freqs(wpa_s, &params);
+       extra_ie = wpa_supplicant_extra_ies(wpa_s, &params);
 
-       if (wps) {
-               wps_ie = wps_build_probe_req_ie(wps == 2, &wpa_s->wps->dev,
-                                               wpa_s->wps->uuid, req_type);
-               if (wps_ie) {
-                       params.extra_ies = wpabuf_head(wps_ie);
-                       params.extra_ies_len = wpabuf_len(wps_ie);
-               }
-       }
-#endif /* CONFIG_WPS */
+       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;
 
        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) {
+               /*
+                * 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 */
 
        ret = wpa_supplicant_trigger_scan(wpa_s, &params);
 
-       wpabuf_free(wps_ie);
+       wpabuf_free(extra_ie);
        os_free(params.freqs);
        os_free(params.filter_ssids);
 
        if (ret) {
-               wpa_printf(MSG_WARNING, "Failed to initiate AP scan.");
+               wpa_msg(wpa_s, MSG_WARNING, "Failed to initiate AP scan");
                if (prev_state != wpa_s->wpa_state)
                        wpa_supplicant_set_state(wpa_s, prev_state);
                wpa_supplicant_req_scan(wpa_s, 1, 0);
@@ -445,13 +652,13 @@ void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec)
                        ssid = ssid->next;
                }
                if (ssid) {
-                       wpa_msg(wpa_s, MSG_DEBUG, "Not rescheduling scan to "
+                       wpa_dbg(wpa_s, MSG_DEBUG, "Not rescheduling scan to "
                                "ensure that specific SSID scans occur");
                        return;
                }
        }
 
-       wpa_msg(wpa_s, MSG_DEBUG, "Setting scan request: %d sec %d usec",
+       wpa_dbg(wpa_s, MSG_DEBUG, "Setting scan request: %d sec %d usec",
                sec, usec);
        eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL);
        eloop_register_timeout(sec, usec, wpa_supplicant_scan, wpa_s, NULL);
@@ -459,6 +666,160 @@ void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec)
 
 
 /**
+ * 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
+ *
+ * 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
+ *
+ * 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;
+       enum wpa_states prev_state;
+       struct wpa_ssid *ssid;
+       struct wpabuf *wps_ie = NULL;
+       int ret;
+       unsigned int max_sched_scan_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 (wpa_s->sched_scanning)
+               return 0;
+
+       os_memset(&params, 0, sizeof(params));
+
+       /* If we can't allocate space for the filters, we just don't filter */
+       params.filter_ssids = os_zalloc(wpa_s->max_match_sets *
+                                       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);
+
+       /* 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");
+
+               wpa_s->sched_scan_interval = 2;
+               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;
+       }
+
+       while (ssid) {
+               if (ssid->disabled) {
+                       wpa_s->prev_sched_ssid = ssid;
+                       ssid = ssid->next;
+                       continue;
+               }
+
+               if (params.filter_ssids && 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++;
+               }
+
+               if (ssid->scan_ssid) {
+                       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;
+                               break;
+                       }
+               }
+
+               if (params.num_filter_ssids >= wpa_s->max_match_sets)
+                       break;
+               wpa_s->prev_sched_ssid = ssid;
+               ssid = ssid->next;
+       }
+
+       if (!params.num_ssids) {
+               os_free(params.filter_ssids);
+               return 0;
+       }
+
+       if (wpa_s->wps)
+               wps_ie = wpa_supplicant_extra_ies(wpa_s, &params);
+
+       wpa_dbg(wpa_s, MSG_DEBUG,
+               "Starting sched scan: interval %d timeout %d",
+               wpa_s->sched_scan_interval, wpa_s->sched_scan_timeout);
+
+       ret = wpa_supplicant_start_sched_scan(wpa_s, &params,
+                                             wpa_s->sched_scan_interval);
+       wpabuf_free(wps_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;
+       }
+
+       return 0;
+}
+
+
+/**
  * wpa_supplicant_cancel_scan - Cancel a scheduled scan request
  * @wpa_s: Pointer to wpa_supplicant data
  *
@@ -467,11 +828,28 @@ void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec)
  */
 void wpa_supplicant_cancel_scan(struct wpa_supplicant *wpa_s)
 {
-       wpa_msg(wpa_s, MSG_DEBUG, "Cancelling scan request");
+       wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling scan request");
        eloop_cancel_timeout(wpa_supplicant_scan, 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);
+}
+
+
 void wpa_supplicant_notify_scanning(struct wpa_supplicant *wpa_s,
                                    int scanning)
 {
@@ -575,15 +953,62 @@ struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res,
 }
 
 
+struct wpabuf * wpa_scan_get_vendor_ie_multi_beacon(
+       const struct wpa_scan_res *res, u32 vendor_type)
+{
+       struct wpabuf *buf;
+       const u8 *end, *pos;
+
+       if (res->beacon_ie_len == 0)
+               return NULL;
+       buf = wpabuf_alloc(res->beacon_ie_len);
+       if (buf == NULL)
+               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]))
+                       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
+
 /* Compare function for sorting scan results. Return >0 if @b is considered
  * better. */
 static int wpa_scan_result_compar(const void *a, const void *b)
 {
+#define IS_5GHZ(n) (n > 4000)
+#define MIN(a,b) a < b ? a : b
        struct wpa_scan_res **_wa = (void *) a;
        struct wpa_scan_res **_wb = (void *) b;
        struct wpa_scan_res *wa = *_wa;
        struct wpa_scan_res *wb = *_wb;
        int wpa_a, wpa_b, maxrate_a, maxrate_b;
+       int snr_a, snr_b;
 
        /* WPA/WPA2 support preferred */
        wpa_a = wpa_scan_get_vendor_ie(wa, WPA_IE_VENDOR_TYPE) != NULL ||
@@ -604,17 +1029,78 @@ static int wpa_scan_result_compar(const void *a, const void *b)
            (wb->caps & IEEE80211_CAP_PRIVACY) == 0)
                return -1;
 
-       /* best/max rate preferred if signal level close enough XXX */
-       if ((wa->level && wb->level && abs(wb->level - wa->level) < 5) ||
+       if ((wa->flags & wb->flags & WPA_SCAN_LEVEL_DBM) &&
+           !((wa->flags | wb->flags) & WPA_SCAN_NOISE_INVALID)) {
+               snr_a = MIN(wa->level - wa->noise, GREAT_SNR);
+               snr_b = MIN(wb->level - wb->noise, GREAT_SNR);
+       } else {
+               /* Not suitable information to calculate SNR, so use level */
+               snr_a = wa->level;
+               snr_b = wb->level;
+       }
+
+       /* best/max rate preferred if SNR close enough */
+        if ((snr_a && snr_b && abs(snr_b - snr_a) < 5) ||
            (wa->qual && wb->qual && abs(wb->qual - wa->qual) < 10)) {
                maxrate_a = wpa_scan_get_max_rate(wa);
                maxrate_b = wpa_scan_get_max_rate(wb);
                if (maxrate_a != maxrate_b)
                        return maxrate_b - maxrate_a;
+               if (IS_5GHZ(wa->freq) ^ IS_5GHZ(wb->freq))
+                       return IS_5GHZ(wa->freq) ? -1 : 1;
        }
 
        /* use freq for channel preference */
 
+       /* all things being equal, use SNR; if SNRs are
+        * identical, use quality values since some drivers may only report
+        * that value and leave the signal level zero */
+       if (snr_b == snr_a)
+               return wb->qual - wa->qual;
+       return snr_b - snr_a;
+#undef MIN
+#undef IS_5GHZ
+}
+
+
+#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 */
@@ -622,6 +1108,36 @@ static int wpa_scan_result_compar(const void *a, const void *b)
                return wb->qual - wa->qual;
        return wb->level - wa->level;
 }
+#endif /* CONFIG_WPS */
+
+
+static void dump_scan_res(struct wpa_scan_results *scan_res)
+{
+       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];
+               if ((r->flags & (WPA_SCAN_LEVEL_DBM | WPA_SCAN_NOISE_INVALID))
+                   == WPA_SCAN_LEVEL_DBM) {
+                       int snr = r->level - r->noise;
+                       wpa_printf(MSG_EXCESSIVE, MACSTR " freq=%d qual=%d "
+                                  "noise=%d level=%d snr=%d%s flags=0x%x",
+                                  MAC2STR(r->bssid), r->freq, r->qual,
+                                  r->noise, r->level, snr,
+                                  snr >= GREAT_SNR ? "*" : "", r->flags);
+               } else {
+                       wpa_printf(MSG_EXCESSIVE, MACSTR " freq=%d qual=%d "
+                                  "noise=%d level=%d flags=0x%x",
+                                  MAC2STR(r->bssid), r->freq, r->qual,
+                                  r->noise, r->level, r->flags);
+               }
+       }
+}
 
 
 /**
@@ -641,18 +1157,25 @@ wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s,
 {
        struct wpa_scan_results *scan_res;
        size_t i;
+       int (*compar)(const void *, const void *) = wpa_scan_result_compar;
 
-       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
-               scan_res = ieee80211_sta_get_scan_results(wpa_s);
-       else
-               scan_res = wpa_drv_get_scan_results2(wpa_s);
+       scan_res = wpa_drv_get_scan_results2(wpa_s);
        if (scan_res == NULL) {
-               wpa_printf(MSG_DEBUG, "Failed to get scan results");
+               wpa_dbg(wpa_s, MSG_DEBUG, "Failed to get scan results");
                return NULL;
        }
 
+#ifdef CONFIG_WPS
+       if (wpas_wps_in_progress(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 *),
-             wpa_scan_result_compar);
+             compar);
+       dump_scan_res(scan_res);
 
        wpa_bss_update_start(wpa_s);
        for (i = 0; i < scan_res->num; i++)
@@ -673,17 +1196,3 @@ int wpa_supplicant_update_scan_results(struct wpa_supplicant *wpa_s)
 
        return 0;
 }
-
-
-void wpa_scan_results_free(struct wpa_scan_results *res)
-{
-       size_t i;
-
-       if (res == NULL)
-               return;
-
-       for (i = 0; i < res->num; i++)
-               os_free(res->res[i]);
-       os_free(res->res);
-       os_free(res);
-}
index 441fdbb..7fb84e6 100644 (file)
 
 int wpa_supplicant_enabled_networks(struct wpa_config *conf);
 void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec);
+int wpa_supplicant_delayed_sched_scan(struct wpa_supplicant *wpa_s,
+                                     int sec, int usec);
+int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s);
 void wpa_supplicant_cancel_scan(struct wpa_supplicant *wpa_s);
+void wpa_supplicant_cancel_sched_scan(struct wpa_supplicant *wpa_s);
 void wpa_supplicant_notify_scanning(struct wpa_supplicant *wpa_s,
                                    int scanning);
 struct wpa_driver_scan_params;
@@ -32,6 +36,7 @@ const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res,
                                  u32 vendor_type);
 struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res,
                                             u32 vendor_type);
-void wpa_scan_results_free(struct wpa_scan_results *res);
+struct wpabuf * wpa_scan_get_vendor_ie_multi_beacon(
+       const struct wpa_scan_res *res, u32 vendor_type);
 
 #endif /* SCAN_H */
index 5604e97..4c17ef3 100644 (file)
@@ -15,6 +15,7 @@
 #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 "driver_i.h"
 #include "wpas_glue.h"
 #include "wps_supplicant.h"
+#include "p2p_supplicant.h"
 #include "notify.h"
 #include "blacklist.h"
 #include "bss.h"
 #include "scan.h"
 #include "sme.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);
+#ifdef CONFIG_IEEE80211W
+static void sme_stop_sa_query(struct wpa_supplicant *wpa_s);
+#endif /* CONFIG_IEEE80211W */
+
+
 void sme_authenticate(struct wpa_supplicant *wpa_s,
                      struct wpa_bss *bss, struct wpa_ssid *ssid)
 {
@@ -46,8 +58,8 @@ void sme_authenticate(struct wpa_supplicant *wpa_s,
        int i, bssid_changed;
 
        if (bss == NULL) {
-               wpa_printf(MSG_ERROR, "SME: No scan result available for the "
-                          "network");
+               wpa_msg(wpa_s, MSG_ERROR, "SME: No scan result available for "
+                       "the network");
                return;
        }
 
@@ -60,6 +72,7 @@ void sme_authenticate(struct wpa_supplicant *wpa_s,
        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)
@@ -80,12 +93,12 @@ void sme_authenticate(struct wpa_supplicant *wpa_s,
                }
        }
 #endif /* IEEE8021X_EAPOL */
-       wpa_printf(MSG_DEBUG, "Automatic auth_alg selection: 0x%x",
-                  params.auth_alg);
+       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_printf(MSG_DEBUG, "Overriding auth_alg selection: 0x%x",
-                          params.auth_alg);
+               wpa_dbg(wpa_s, MSG_DEBUG, "Overriding auth_alg selection: "
+                       "0x%x", params.auth_alg);
        }
 
        for (i = 0; i < NUM_WEP_KEYS; i++) {
@@ -119,8 +132,8 @@ void sme_authenticate(struct wpa_supplicant *wpa_s,
                if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
                                              wpa_s->sme.assoc_req_ie,
                                              &wpa_s->sme.assoc_req_ie_len)) {
-                       wpa_printf(MSG_WARNING, "SME: Failed to set WPA key "
-                                  "management and encryption suites");
+                       wpa_msg(wpa_s, MSG_WARNING, "SME: Failed to set WPA "
+                               "key management and encryption suites");
                        return;
                }
        } else if (ssid->key_mgmt &
@@ -132,9 +145,9 @@ void sme_authenticate(struct wpa_supplicant *wpa_s,
                if (wpa_supplicant_set_suites(wpa_s, NULL, ssid,
                                              wpa_s->sme.assoc_req_ie,
                                              &wpa_s->sme.assoc_req_ie_len)) {
-                       wpa_printf(MSG_WARNING, "SME: Failed to set WPA key "
-                                  "management and encryption suites (no scan "
-                                  "results)");
+                       wpa_msg(wpa_s, MSG_WARNING, "SME: Failed to set WPA "
+                               "key management and encryption suites (no "
+                               "scan results)");
                        return;
                }
 #ifdef CONFIG_WPS
@@ -185,8 +198,8 @@ void sme_authenticate(struct wpa_supplicant *wpa_s,
                if (wpa_s->sme.ft_used &&
                    os_memcmp(md, wpa_s->sme.mobility_domain, 2) == 0 &&
                    wpa_sm_has_ptk(wpa_s->wpa)) {
-                       wpa_printf(MSG_DEBUG, "SME: Trying to use FT "
-                                  "over-the-air");
+                       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;
@@ -202,16 +215,50 @@ void sme_authenticate(struct wpa_supplicant *wpa_s,
                if (rsn && wpa_parse_wpa_ie(rsn, 2 + rsn[1], &_ie) == 0 &&
                    _ie.capabilities &
                    (WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR)) {
-                       wpa_printf(MSG_DEBUG, "WPA: Selected AP supports MFP: "
-                                  "require MFP");
+                       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_INTERWORKING
+       if (wpa_s->conf->interworking) {
+               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 + 6, pos,
+                          wpa_s->sme.assoc_req_ie_len -
+                          (pos - wpa_s->sme.assoc_req_ie));
+               wpa_s->sme.assoc_req_ie_len += 6;
+               *pos++ = WLAN_EID_EXT_CAPAB;
+               *pos++ = 4;
+               *pos++ = 0x00;
+               *pos++ = 0x00;
+               *pos++ = 0x00;
+               *pos++ = 0x80; /* Bit 31 - Interworking */
+       }
+#endif /* CONFIG_INTERWORKING */
+
+       wpa_supplicant_cancel_sched_scan(wpa_s);
        wpa_supplicant_cancel_scan(wpa_s);
 
-       wpa_msg(wpa_s, MSG_INFO, "Trying to authenticate with " MACSTR
+       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);
 
@@ -226,13 +273,15 @@ void sme_authenticate(struct wpa_supplicant *wpa_s,
 
        wpa_s->sme.auth_alg = params.auth_alg;
        if (wpa_drv_authenticate(wpa_s, &params) < 0) {
-               wpa_msg(wpa_s, MSG_INFO, "Authentication request to the "
+               wpa_msg(wpa_s, MSG_INFO, "SME: Authentication request to the "
                        "driver failed");
-               wpa_supplicant_req_scan(wpa_s, 1, 0);
+               wpas_connection_failed(wpa_s, bss->bssid);
+               wpa_supplicant_mark_disassoc(wpa_s);
                return;
        }
 
-       /* TODO: add timeout on authentication */
+       eloop_register_timeout(SME_AUTH_TIMEOUT, 0, sme_auth_timer, wpa_s,
+                              NULL);
 
        /*
         * Association will be started based on the authentication event from
@@ -246,46 +295,50 @@ 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_printf(MSG_DEBUG, "SME: Ignore authentication event when "
-                          "network is not selected");
+               wpa_dbg(wpa_s, MSG_DEBUG, "SME: Ignore authentication event "
+                       "when network is not selected");
                return;
        }
 
        if (wpa_s->wpa_state != WPA_AUTHENTICATING) {
-               wpa_printf(MSG_DEBUG, "SME: Ignore authentication event when "
-                          "not in authenticating state");
+               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_printf(MSG_DEBUG, "SME: Ignore authentication with "
-                          "unexpected peer " MACSTR,
-                          MAC2STR(data->auth.peer));
+               wpa_dbg(wpa_s, MSG_DEBUG, "SME: Ignore authentication with "
+                       "unexpected peer " MACSTR,
+                       MAC2STR(data->auth.peer));
                return;
        }
 
-       wpa_printf(MSG_DEBUG, "SME: Authentication response: peer=" MACSTR
-                  " auth_type=%d status_code=%d",
-                  MAC2STR(data->auth.peer), data->auth.auth_type,
-                  data->auth.status_code);
+       wpa_dbg(wpa_s, MSG_DEBUG, "SME: Authentication response: peer=" MACSTR
+               " auth_type=%d status_code=%d",
+               MAC2STR(data->auth.peer), data->auth.auth_type,
+               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);
+
        if (data->auth.status_code != WLAN_STATUS_SUCCESS) {
-               wpa_printf(MSG_DEBUG, "SME: Authentication failed (status "
-                          "code %d)", data->auth.status_code);
+               wpa_dbg(wpa_s, MSG_DEBUG, "SME: Authentication failed (status "
+                       "code %d)", data->auth.status_code);
 
                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)
+                   wpa_s->current_ssid->auth_alg == WPA_AUTH_ALG_LEAP) {
+                       wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
                        return;
+               }
 
                switch (data->auth.auth_type) {
                case WLAN_AUTH_OPEN:
                        wpa_s->current_ssid->auth_alg = WPA_AUTH_ALG_SHARED;
 
-                       wpa_printf(MSG_DEBUG, "SME: Trying SHARED auth");
+                       wpa_dbg(wpa_s, MSG_DEBUG, "SME: Trying SHARED auth");
                        wpa_supplicant_associate(wpa_s, wpa_s->current_bss,
                                                 wpa_s->current_ssid);
                        return;
@@ -293,7 +346,7 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data)
                case WLAN_AUTH_SHARED_KEY:
                        wpa_s->current_ssid->auth_alg = WPA_AUTH_ALG_LEAP;
 
-                       wpa_printf(MSG_DEBUG, "SME: Trying LEAP auth");
+                       wpa_dbg(wpa_s, MSG_DEBUG, "SME: Trying LEAP auth");
                        wpa_supplicant_associate(wpa_s, wpa_s->current_bss,
                                                 wpa_s->current_ssid);
                        return;
@@ -333,6 +386,8 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
        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 = cipher_suite2driver(wpa_s->pairwise_cipher);
+       params.group_suite = cipher_suite2driver(wpa_s->group_cipher);
 #ifdef CONFIG_IEEE80211R
        if (auth_type == WLAN_AUTH_FT && wpa_s->sme.ft_ies) {
                params.wpa_ie = wpa_s->sme.ft_ies;
@@ -354,26 +409,37 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
        if (params.wpa_ie == NULL ||
            ieee802_11_parse_elems(params.wpa_ie, params.wpa_ie_len, &elems, 0)
            < 0) {
-               wpa_printf(MSG_DEBUG, "SME: Could not parse own IEs?!");
+               wpa_dbg(wpa_s, MSG_DEBUG, "SME: Could not parse own IEs?!");
                os_memset(&elems, 0, sizeof(elems));
        }
-       if (elems.rsn_ie)
+       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)
+       } 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
+       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, "Association request to the driver "
-                       "failed");
-               wpa_supplicant_req_scan(wpa_s, 5, 0);
+               wpa_msg(wpa_s, MSG_INFO, "SME: Association request to the "
+                       "driver failed");
+               wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
+               os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
                return;
        }
 
-       /* TODO: add timeout on association */
+       eloop_register_timeout(SME_ASSOC_TIMEOUT, 0, sme_assoc_timer, wpa_s,
+                              NULL);
 }
 
 
@@ -381,7 +447,7 @@ 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_printf(MSG_DEBUG, "SME: Remove mobility domain");
+               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;
@@ -401,90 +467,303 @@ int sme_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md,
 }
 
 
-void sme_event_assoc_reject(struct wpa_supplicant *wpa_s,
-                           union wpa_event_data *data)
+static void sme_deauth(struct wpa_supplicant *wpa_s)
 {
        int bssid_changed;
-       int timeout = 5000;
-
-       wpa_printf(MSG_DEBUG, "SME: Association with " MACSTR " failed: "
-                  "status code %d", MAC2STR(wpa_s->pending_bssid),
-                  data->assoc_reject.status_code);
 
        bssid_changed = !is_zero_ether_addr(wpa_s->bssid);
 
-       /*
-        * 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.
-        */
        if (wpa_drv_deauthenticate(wpa_s, wpa_s->pending_bssid,
                                   WLAN_REASON_DEAUTH_LEAVING) < 0) {
-               wpa_msg(wpa_s, MSG_INFO,
-                       "Deauth request to the driver failed");
+               wpa_msg(wpa_s, MSG_INFO, "SME: Deauth request to the driver "
+                       "failed");
        }
        wpa_s->sme.prev_bssid_set = 0;
 
-       if (wpa_blacklist_add(wpa_s, wpa_s->pending_bssid) == 0) {
-               struct wpa_blacklist *b;
-               b = wpa_blacklist_get(wpa_s, wpa_s->pending_bssid);
-               if (b && b->count < 3) {
-                       /*
-                        * Speed up next attempt if there could be other APs
-                        * that could accept association.
-                        */
-                       timeout = 100;
-               }
-       }
+       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);
 
        /*
-        * TODO: if more than one possible AP is available in scan results,
-        * could try the other ones before requesting a new scan.
+        * 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.
         */
-       wpa_supplicant_req_scan(wpa_s, timeout / 1000,
-                               1000 * (timeout % 1000));
+       sme_deauth(wpa_s);
 }
 
 
 void sme_event_auth_timed_out(struct wpa_supplicant *wpa_s,
                              union wpa_event_data *data)
 {
-       wpa_printf(MSG_DEBUG, "SME: Authentication timed out");
-       wpa_supplicant_req_scan(wpa_s, 5, 0);
+       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_printf(MSG_DEBUG, "SME: Association timed out");
+       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);
-       wpa_supplicant_req_scan(wpa_s, 5, 0);
 }
 
 
 void sme_event_disassoc(struct wpa_supplicant *wpa_s,
                        union wpa_event_data *data)
 {
-       wpa_printf(MSG_DEBUG, "SME: Disassociation event received");
-       if (wpa_s->sme.prev_bssid_set &&
-           !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)) {
+       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_printf(MSG_DEBUG, "SME: Deauthenticate to clear driver "
-                          "state");
+               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_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 */
+
+       eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL);
+       eloop_cancel_timeout(sme_auth_timer, 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_time now, passed;
+       os_get_time(&now);
+       os_time_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(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_time(&wpa_s->sme.sa_query_start);
+       }
+       trans_id = nbuf + wpa_s->sme.sa_query_count * WLAN_SA_QUERY_TR_ID_LEN;
+       wpa_s->sme.sa_query_trans_id = nbuf;
+       wpa_s->sme.sa_query_count++;
+
+       os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN);
+
+       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);
+}
+
+
+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;
+
+       if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME))
+               return;
+       if (wpa_s->wpa_state != WPA_COMPLETED)
+               return;
+       ssid = wpa_s->current_ssid;
+       if (ssid == NULL || ssid->ieee80211w == 0)
+               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;
+
+       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 3ec8cc9..a59b38d 100644 (file)
@@ -32,6 +32,14 @@ void sme_event_assoc_timed_out(struct wpa_supplicant *wpa_s,
                               union wpa_event_data *data);
 void sme_event_disassoc(struct wpa_supplicant *wpa_s,
                        union wpa_event_data *data);
+void sme_event_unprot_disconnect(struct wpa_supplicant *wpa_s, const u8 *sa,
+                                const u8 *da, u16 reason_code);
+void sme_sa_query_rx(struct wpa_supplicant *wpa_s, const u8 *sa,
+                    const u8 *data, size_t len);
+void sme_state_changed(struct wpa_supplicant *wpa_s);
+void sme_disassoc_while_authenticating(struct wpa_supplicant *wpa_s,
+                                      const u8 *prev_pending_bssid);
+void sme_deinit(struct wpa_supplicant *wpa_s);
 
 #else /* CONFIG_SME */
 
@@ -73,6 +81,26 @@ static inline void sme_event_disassoc(struct wpa_supplicant *wpa_s,
 {
 }
 
+static inline void sme_event_unprot_disconnect(struct wpa_supplicant *wpa_s,
+                                              const u8 *sa, const u8 *da,
+                                              u16 reason_code)
+{
+}
+
+static inline void sme_state_changed(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void
+sme_disassoc_while_authenticating(struct wpa_supplicant *wpa_s,
+                                 const u8 *prev_pending_bssid)
+{
+}
+
+static inline void sme_deinit(struct wpa_supplicant *wpa_s)
+{
+}
+
 #endif /* CONFIG_SME */
 
 #endif /* SME_H */
index 217908e..e018e05 100644 (file)
@@ -15,7 +15,7 @@ SOURCE                wpa_supplicant.c events.c
 SOURCEPATH     ..\..\src\rsn_supp
 SOURCE         wpa.c preauth.c pmksa_cache.c peerkey.c wpa_ie.c
 SOURCEPATH     ..\..\src\drivers
-SOURCE         drivers.c
+SOURCE         drivers.c driver_common.c
 SOURCEPATH     ..\..\src\common
 SOURCE         wpa_common.c
 SOURCEPATH     ..\..\src\utils
diff --git a/wpa_supplicant/systemd/wpa_supplicant-nl80211@.service.in b/wpa_supplicant/systemd/wpa_supplicant-nl80211@.service.in
new file mode 100644 (file)
index 0000000..76aba12
--- /dev/null
@@ -0,0 +1,13 @@
+[Unit]
+Description=WPA supplicant daemon (interface- and nl80211 driver-specific version)
+Requires=sys-subsystem-net-devices-%i.device
+After=sys-subsystem-net-devices-%i.device
+
+# NetworkManager users will probably want the dbus version instead.
+
+[Service]
+Type=simple
+ExecStart=@BINDIR@/wpa_supplicant -c/etc/wpa_supplicant/wpa_supplicant-nl80211-%I.conf -Dnl80211 -i%I
+
+[Install]
+Alias=multi-user.target.wants/wpa_supplicant-nl80211@wlan0.service
diff --git a/wpa_supplicant/systemd/wpa_supplicant-wired@.service.in b/wpa_supplicant/systemd/wpa_supplicant-wired@.service.in
new file mode 100644 (file)
index 0000000..ff384ae
--- /dev/null
@@ -0,0 +1,13 @@
+[Unit]
+Description=WPA supplicant daemon (interface- and wired driver-specific version)
+Requires=sys-subsystem-net-devices-%i.device
+After=sys-subsystem-net-devices-%i.device
+
+# NetworkManager users will probably want the dbus version instead.
+
+[Service]
+Type=simple
+ExecStart=@BINDIR@/wpa_supplicant -c/etc/wpa_supplicant/wpa_supplicant-wired-%I.conf -Dwired -i%I
+
+[Install]
+Alias=multi-user.target.wants/wpa_supplicant-wired@wlan0.service
diff --git a/wpa_supplicant/systemd/wpa_supplicant.service.in b/wpa_supplicant/systemd/wpa_supplicant.service.in
new file mode 100644 (file)
index 0000000..4351ad8
--- /dev/null
@@ -0,0 +1,11 @@
+[Unit]
+Description=WPA supplicant
+
+[Service]
+Type=dbus
+BusName=fi.epitest.hostap.WPASupplicant
+ExecStart=@BINDIR@/wpa_supplicant -u
+
+[Install]
+WantedBy=multi-user.target
+Alias=dbus-fi.epitest.hostap.WPASupplicant.service
diff --git a/wpa_supplicant/systemd/wpa_supplicant@.service.in b/wpa_supplicant/systemd/wpa_supplicant@.service.in
new file mode 100644 (file)
index 0000000..c215567
--- /dev/null
@@ -0,0 +1,13 @@
+[Unit]
+Description=WPA supplicant daemon (interface-specific version)
+Requires=sys-subsystem-net-devices-%i.device
+After=sys-subsystem-net-devices-%i.device
+
+# NetworkManager users will probably want the dbus version instead.
+
+[Service]
+Type=simple
+ExecStart=@BINDIR@/wpa_supplicant -c/etc/wpa_supplicant/wpa_supplicant-%I.conf -i%I
+
+[Install]
+Alias=multi-user.target.wants/wpa_supplicant@wlan0.service
diff --git a/wpa_supplicant/vs2005/eapol_test/eapol_test.vcproj b/wpa_supplicant/vs2005/eapol_test/eapol_test.vcproj
new file mode 100644 (file)
index 0000000..38b29c4
--- /dev/null
@@ -0,0 +1,469 @@
+<?xml version="1.0" encoding="Windows-1252"?>\r
+<VisualStudioProject\r
+       ProjectType="Visual C++"\r
+       Version="8.00"\r
+       Name="eapol_test"\r
+       ProjectGUID="{0E3F2C6D-1372-48D6-BCAB-E584917C4DE3}"\r
+       RootNamespace="eapol_test"\r
+       Keyword="Win32Proj"\r
+       >\r
+       <Platforms>\r
+               <Platform\r
+                       Name="Win32"\r
+               />\r
+       </Platforms>\r
+       <ToolFiles>\r
+       </ToolFiles>\r
+       <Configurations>\r
+               <Configuration\r
+                       Name="Debug|Win32"\r
+                       OutputDirectory="$(SolutionDir)$(ConfigurationName)"\r
+                       IntermediateDirectory="$(ConfigurationName)"\r
+                       ConfigurationType="1"\r
+                       CharacterSet="1"\r
+                       >\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCMIDLTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               Optimization="0"\r
+                               AdditionalIncludeDirectories="..\..;..\..\..\src;..\..\..\src\utils;C:\dev\WpdPack\include;C:\dev\openssl\include"\r
+                               PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"\r
+                               MinimalRebuild="true"\r
+                               BasicRuntimeChecks="3"\r
+                               RuntimeLibrary="3"\r
+                               UsePrecompiledHeader="0"\r
+                               WarningLevel="3"\r
+                               Detect64BitPortabilityProblems="true"\r
+                               DebugInformationFormat="4"\r
+                               DisableSpecificWarnings="4244;4267;4311"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManagedResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCLinkerTool"\r
+                               AdditionalDependencies="ws2_32.lib Crypt32.lib Winscard.lib Packet.lib wpcap.lib libeay32MT.lib ssleay32Mt.lib"\r
+                               LinkIncremental="2"\r
+                               AdditionalLibraryDirectories="C:\dev\WpdPack\lib;C:\dev\openssl\lib"\r
+                               GenerateDebugInformation="true"\r
+                               SubSystem="1"\r
+                               TargetMachine="1"\r
+                       />\r
+                       <Tool\r
+                               Name="VCALinkTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManifestTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXDCMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCBscMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCFxCopTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCAppVerifierTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebDeploymentTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"\r
+                       />\r
+               </Configuration>\r
+               <Configuration\r
+                       Name="Release|Win32"\r
+                       OutputDirectory="$(SolutionDir)$(ConfigurationName)"\r
+                       IntermediateDirectory="$(ConfigurationName)"\r
+                       ConfigurationType="1"\r
+                       CharacterSet="1"\r
+                       WholeProgramOptimization="1"\r
+                       >\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCMIDLTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               AdditionalIncludeDirectories="..\..;..\..\..\src;..\..\..\src\utils;C:\dev\WpdPack\include;C:\dev\openssl\include"\r
+                               PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"\r
+                               RuntimeLibrary="2"\r
+                               UsePrecompiledHeader="0"\r
+                               WarningLevel="3"\r
+                               Detect64BitPortabilityProblems="true"\r
+                               DebugInformationFormat="3"\r
+                               DisableSpecificWarnings="4244;4267;4311"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManagedResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCLinkerTool"\r
+                               AdditionalDependencies="ws2_32.lib Crypt32.lib Winscard.lib Packet.lib wpcap.lib libeay32MT.lib ssleay32Mt.lib"\r
+                               LinkIncremental="1"\r
+                               AdditionalLibraryDirectories="C:\dev\WpdPack\lib;C:\dev\openssl\lib"\r
+                               GenerateDebugInformation="true"\r
+                               SubSystem="1"\r
+                               OptimizeReferences="2"\r
+                               EnableCOMDATFolding="2"\r
+                               TargetMachine="1"\r
+                       />\r
+                       <Tool\r
+                               Name="VCALinkTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManifestTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXDCMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCBscMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCFxCopTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCAppVerifierTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebDeploymentTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"\r
+                       />\r
+               </Configuration>\r
+       </Configurations>\r
+       <References>\r
+       </References>\r
+       <Files>\r
+               <Filter\r
+                       Name="Source Files"\r
+                       Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"\r
+                       UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"\r
+                       >\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\aes-cbc.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\aes-ctr.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\aes-eax.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\aes-encblock.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\aes-omac1.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\aes-unwrap.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\aes-wrap.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\utils\base64.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\blacklist.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\bss.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_common\chap.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\utils\common.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\config.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\config_file.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\crypto_openssl.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\ctrl_iface.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\ctrl_iface_named_pipe.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\drivers\driver_common.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_peer\eap.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_peer\eap_aka.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_common\eap_common.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_peer\eap_gtc.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_peer\eap_leap.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_peer\eap_md5.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_peer\eap_methods.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_peer\eap_mschapv2.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_peer\eap_otp.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_peer\eap_peap.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_common\eap_peap_common.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\eap_register.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_peer\eap_sim.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_common\eap_sim_common.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_peer\eap_tls.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_peer\eap_tls_common.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_peer\eap_tnc.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_peer\eap_ttls.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eapol_supp\eapol_supp_sm.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\eapol_test.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\utils\eloop_win.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\events.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\fips_prf_openssl.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\utils\ip_addr.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\l2_packet\l2_packet_winpcap.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\md5.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\ms_funcs.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_peer\mschapv2.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\notify.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\utils\os_win32.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\utils\pcsc_funcs.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\rsn_supp\peerkey.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\rsn_supp\pmksa_cache.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\rsn_supp\preauth.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\radius\radius.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\radius\radius_client.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\scan.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\sha1.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\sha1-pbkdf2.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\sha1-tlsprf.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\tls_openssl.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_peer\tncc.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\rsn_supp\wpa.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\common\wpa_common.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\utils\wpa_debug.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\rsn_supp\wpa_ie.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\wpa_supplicant.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\utils\wpabuf.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\wpas_glue.c"\r
+                               >\r
+                       </File>\r
+               </Filter>\r
+               <Filter\r
+                       Name="Header Files"\r
+                       Filter="h;hpp;hxx;hm;inl;inc;xsd"\r
+                       UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"\r
+                       >\r
+               </Filter>\r
+               <Filter\r
+                       Name="Resource Files"\r
+                       Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"\r
+                       UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"\r
+                       >\r
+               </Filter>\r
+       </Files>\r
+       <Globals>\r
+       </Globals>\r
+</VisualStudioProject>\r
diff --git a/wpa_supplicant/vs2005/wpa_cli/wpa_cli.vcproj b/wpa_supplicant/vs2005/wpa_cli/wpa_cli.vcproj
new file mode 100644 (file)
index 0000000..d2de768
--- /dev/null
@@ -0,0 +1,215 @@
+<?xml version="1.0" encoding="Windows-1252"?>\r
+<VisualStudioProject\r
+       ProjectType="Visual C++"\r
+       Version="8.00"\r
+       Name="wpa_cli"\r
+       ProjectGUID="{E3A7B181-22CC-4DA3-8410-6AD69879A9EC}"\r
+       RootNamespace="wpa_cli"\r
+       Keyword="Win32Proj"\r
+       >\r
+       <Platforms>\r
+               <Platform\r
+                       Name="Win32"\r
+               />\r
+       </Platforms>\r
+       <ToolFiles>\r
+       </ToolFiles>\r
+       <Configurations>\r
+               <Configuration\r
+                       Name="Debug|Win32"\r
+                       OutputDirectory="$(SolutionDir)$(ConfigurationName)"\r
+                       IntermediateDirectory="$(ConfigurationName)"\r
+                       ConfigurationType="1"\r
+                       CharacterSet="0"\r
+                       >\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCMIDLTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               Optimization="0"\r
+                               AdditionalIncludeDirectories="..\..\..\src;..\..\..\src\utils"\r
+                               PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"\r
+                               MinimalRebuild="true"\r
+                               BasicRuntimeChecks="3"\r
+                               RuntimeLibrary="3"\r
+                               UsePrecompiledHeader="0"\r
+                               WarningLevel="3"\r
+                               Detect64BitPortabilityProblems="true"\r
+                               DebugInformationFormat="4"\r
+                               DisableSpecificWarnings="4244;4267"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManagedResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCLinkerTool"\r
+                               AdditionalDependencies="ws2_32.lib"\r
+                               LinkIncremental="2"\r
+                               GenerateDebugInformation="true"\r
+                               SubSystem="1"\r
+                               TargetMachine="1"\r
+                       />\r
+                       <Tool\r
+                               Name="VCALinkTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManifestTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXDCMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCBscMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCFxCopTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCAppVerifierTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebDeploymentTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"\r
+                       />\r
+               </Configuration>\r
+               <Configuration\r
+                       Name="Release|Win32"\r
+                       OutputDirectory="$(SolutionDir)$(ConfigurationName)"\r
+                       IntermediateDirectory="$(ConfigurationName)"\r
+                       ConfigurationType="1"\r
+                       CharacterSet="0"\r
+                       WholeProgramOptimization="1"\r
+                       >\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCMIDLTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               AdditionalIncludeDirectories="..\..\..\src;..\..\..\src\utils"\r
+                               PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"\r
+                               RuntimeLibrary="2"\r
+                               UsePrecompiledHeader="0"\r
+                               WarningLevel="3"\r
+                               Detect64BitPortabilityProblems="true"\r
+                               DebugInformationFormat="3"\r
+                               DisableSpecificWarnings="4244;4267"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManagedResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCLinkerTool"\r
+                               AdditionalDependencies="ws2_32.lib"\r
+                               LinkIncremental="1"\r
+                               GenerateDebugInformation="true"\r
+                               SubSystem="1"\r
+                               OptimizeReferences="2"\r
+                               EnableCOMDATFolding="2"\r
+                               TargetMachine="1"\r
+                       />\r
+                       <Tool\r
+                               Name="VCALinkTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManifestTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXDCMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCBscMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCFxCopTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCAppVerifierTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebDeploymentTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"\r
+                       />\r
+               </Configuration>\r
+       </Configurations>\r
+       <References>\r
+       </References>\r
+       <Files>\r
+               <Filter\r
+                       Name="Source Files"\r
+                       Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"\r
+                       UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"\r
+                       >\r
+                       <File\r
+                               RelativePath="..\..\..\src\utils\common.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\utils\os_win32.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\wpa_cli.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\common\wpa_ctrl.c"\r
+                               >\r
+                       </File>\r
+               </Filter>\r
+               <Filter\r
+                       Name="Header Files"\r
+                       Filter="h;hpp;hxx;hm;inl;inc;xsd"\r
+                       UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"\r
+                       >\r
+               </Filter>\r
+               <Filter\r
+                       Name="Resource Files"\r
+                       Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"\r
+                       UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"\r
+                       >\r
+               </Filter>\r
+       </Files>\r
+       <Globals>\r
+       </Globals>\r
+</VisualStudioProject>\r
diff --git a/wpa_supplicant/vs2005/wpa_passphrase/wpa_passphrase.vcproj b/wpa_supplicant/vs2005/wpa_passphrase/wpa_passphrase.vcproj
new file mode 100644 (file)
index 0000000..b107842
--- /dev/null
@@ -0,0 +1,232 @@
+<?xml version="1.0" encoding="Windows-1252"?>\r
+<VisualStudioProject\r
+       ProjectType="Visual C++"\r
+       Version="8.00"\r
+       Name="wpa_passphrase"\r
+       ProjectGUID="{ADBE4EA8-F0C5-40C2-AE89-C56D0F2EC1DF}"\r
+       RootNamespace="wpa_passphrase"\r
+       Keyword="Win32Proj"\r
+       >\r
+       <Platforms>\r
+               <Platform\r
+                       Name="Win32"\r
+               />\r
+       </Platforms>\r
+       <ToolFiles>\r
+       </ToolFiles>\r
+       <Configurations>\r
+               <Configuration\r
+                       Name="Debug|Win32"\r
+                       OutputDirectory="$(SolutionDir)$(ConfigurationName)"\r
+                       IntermediateDirectory="$(ConfigurationName)"\r
+                       ConfigurationType="1"\r
+                       CharacterSet="0"\r
+                       >\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCMIDLTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               Optimization="0"\r
+                               AdditionalIncludeDirectories="..\..\..\src;..\..\..\src\utils;C:\dev\openssl\include"\r
+                               PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"\r
+                               MinimalRebuild="true"\r
+                               BasicRuntimeChecks="3"\r
+                               RuntimeLibrary="3"\r
+                               UsePrecompiledHeader="0"\r
+                               WarningLevel="3"\r
+                               Detect64BitPortabilityProblems="true"\r
+                               DebugInformationFormat="4"\r
+                               DisableSpecificWarnings="4244;4267"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManagedResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCLinkerTool"\r
+                               AdditionalDependencies="ws2_32.lib"\r
+                               LinkIncremental="2"\r
+                               AdditionalLibraryDirectories=""\r
+                               GenerateDebugInformation="true"\r
+                               SubSystem="1"\r
+                               TargetMachine="1"\r
+                       />\r
+                       <Tool\r
+                               Name="VCALinkTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManifestTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXDCMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCBscMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCFxCopTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCAppVerifierTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebDeploymentTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"\r
+                       />\r
+               </Configuration>\r
+               <Configuration\r
+                       Name="Release|Win32"\r
+                       OutputDirectory="$(SolutionDir)$(ConfigurationName)"\r
+                       IntermediateDirectory="$(ConfigurationName)"\r
+                       ConfigurationType="1"\r
+                       CharacterSet="0"\r
+                       WholeProgramOptimization="1"\r
+                       >\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCMIDLTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               AdditionalIncludeDirectories="..\..\..\src;..\..\..\src\utils;C:\dev\openssl\include"\r
+                               PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"\r
+                               RuntimeLibrary="2"\r
+                               UsePrecompiledHeader="0"\r
+                               WarningLevel="3"\r
+                               Detect64BitPortabilityProblems="true"\r
+                               DebugInformationFormat="3"\r
+                               DisableSpecificWarnings="4244;4267"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManagedResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCLinkerTool"\r
+                               AdditionalDependencies="ws2_32.lib"\r
+                               LinkIncremental="1"\r
+                               GenerateDebugInformation="true"\r
+                               SubSystem="1"\r
+                               OptimizeReferences="2"\r
+                               EnableCOMDATFolding="2"\r
+                               TargetMachine="1"\r
+                       />\r
+                       <Tool\r
+                               Name="VCALinkTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManifestTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXDCMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCBscMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCFxCopTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCAppVerifierTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebDeploymentTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"\r
+                       />\r
+               </Configuration>\r
+       </Configurations>\r
+       <References>\r
+       </References>\r
+       <Files>\r
+               <Filter\r
+                       Name="Source Files"\r
+                       Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"\r
+                       UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"\r
+                       >\r
+                       <File\r
+                               RelativePath="..\..\..\src\utils\common.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\md5.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\md5-internal.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\utils\os_win32.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\sha1.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\sha1-internal.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\sha1-pbkdf2.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\wpa_passphrase.c"\r
+                               >\r
+                       </File>\r
+               </Filter>\r
+               <Filter\r
+                       Name="Header Files"\r
+                       Filter="h;hpp;hxx;hm;inl;inc;xsd"\r
+                       UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"\r
+                       >\r
+               </Filter>\r
+               <Filter\r
+                       Name="Resource Files"\r
+                       Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"\r
+                       UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"\r
+                       >\r
+               </Filter>\r
+       </Files>\r
+       <Globals>\r
+       </Globals>\r
+</VisualStudioProject>\r
diff --git a/wpa_supplicant/vs2005/wpa_supplicant/wpa_supplicant.vcproj b/wpa_supplicant/vs2005/wpa_supplicant/wpa_supplicant.vcproj
new file mode 100644 (file)
index 0000000..e3886b7
--- /dev/null
@@ -0,0 +1,457 @@
+<?xml version="1.0" encoding="Windows-1252"?>\r
+<VisualStudioProject\r
+       ProjectType="Visual C++"\r
+       Version="8.00"\r
+       Name="wpa_supplicant"\r
+       ProjectGUID="{8BCFDA77-AEDC-4168-8897-5B73105BBB87}"\r
+       RootNamespace="wpa_supplicant"\r
+       Keyword="Win32Proj"\r
+       >\r
+       <Platforms>\r
+               <Platform\r
+                       Name="Win32"\r
+               />\r
+       </Platforms>\r
+       <ToolFiles>\r
+       </ToolFiles>\r
+       <Configurations>\r
+               <Configuration\r
+                       Name="Debug|Win32"\r
+                       OutputDirectory="$(SolutionDir)$(ConfigurationName)"\r
+                       IntermediateDirectory="$(ConfigurationName)"\r
+                       ConfigurationType="1"\r
+                       CharacterSet="0"\r
+                       >\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCMIDLTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               Optimization="0"\r
+                               AdditionalIncludeDirectories="..\..;..\..\..\src;..\..\..\src\utils;C:\dev\WpdPack\include;C:\dev\openssl\include"\r
+                               PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"\r
+                               MinimalRebuild="true"\r
+                               BasicRuntimeChecks="3"\r
+                               RuntimeLibrary="3"\r
+                               UsePrecompiledHeader="0"\r
+                               WarningLevel="3"\r
+                               Detect64BitPortabilityProblems="true"\r
+                               DebugInformationFormat="4"\r
+                               DisableSpecificWarnings="4244;4267;4311"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManagedResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCLinkerTool"\r
+                               AdditionalDependencies="wbemuuid.lib ws2_32.lib Crypt32.lib Winscard.lib Packet.lib wpcap.lib libeay32MT.lib ssleay32Mt.lib"\r
+                               LinkIncremental="2"\r
+                               AdditionalLibraryDirectories="C:\dev\WpdPack\lib;C:\dev\openssl\lib"\r
+                               GenerateDebugInformation="true"\r
+                               SubSystem="1"\r
+                               TargetMachine="1"\r
+                       />\r
+                       <Tool\r
+                               Name="VCALinkTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManifestTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXDCMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCBscMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCFxCopTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCAppVerifierTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebDeploymentTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"\r
+                       />\r
+               </Configuration>\r
+               <Configuration\r
+                       Name="Release|Win32"\r
+                       OutputDirectory="$(SolutionDir)$(ConfigurationName)"\r
+                       IntermediateDirectory="$(ConfigurationName)"\r
+                       ConfigurationType="1"\r
+                       CharacterSet="0"\r
+                       WholeProgramOptimization="1"\r
+                       >\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCMIDLTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               AdditionalIncludeDirectories="..\..;..\..\..\src;..\..\..\src\utils;C:\dev\WpdPack\include;C:\dev\openssl\include"\r
+                               PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"\r
+                               RuntimeLibrary="2"\r
+                               UsePrecompiledHeader="0"\r
+                               WarningLevel="3"\r
+                               Detect64BitPortabilityProblems="true"\r
+                               DebugInformationFormat="3"\r
+                               DisableSpecificWarnings="4244;4267;4311"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManagedResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCLinkerTool"\r
+                               AdditionalDependencies="wbemuuid.lib ws2_32.lib Crypt32.lib Winscard.lib Packet.lib wpcap.lib libeay32MT.lib ssleay32Mt.lib"\r
+                               LinkIncremental="1"\r
+                               AdditionalLibraryDirectories="C:\dev\WpdPack\lib;C:\dev\openssl\lib"\r
+                               GenerateDebugInformation="true"\r
+                               SubSystem="1"\r
+                               OptimizeReferences="2"\r
+                               EnableCOMDATFolding="2"\r
+                               TargetMachine="1"\r
+                       />\r
+                       <Tool\r
+                               Name="VCALinkTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManifestTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXDCMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCBscMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCFxCopTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCAppVerifierTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebDeploymentTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"\r
+                       />\r
+               </Configuration>\r
+       </Configurations>\r
+       <References>\r
+       </References>\r
+       <Files>\r
+               <Filter\r
+                       Name="Source Files"\r
+                       Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"\r
+                       UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"\r
+                       >\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\aes-cbc.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\aes-ctr.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\aes-eax.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\aes-encblock.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\aes-omac1.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\aes-unwrap.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\aes-wrap.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\utils\base64.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\blacklist.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\bss.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_common\chap.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\utils\common.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\config.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\config_file.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\crypto_openssl.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\ctrl_iface.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\ctrl_iface_named_pipe.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\drivers\driver_common.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\drivers\driver_ndis.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\drivers\driver_ndis_.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\drivers\drivers.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_peer\eap.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_common\eap_common.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_peer\eap_gtc.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_peer\eap_leap.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_peer\eap_md5.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_peer\eap_methods.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_peer\eap_mschapv2.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_peer\eap_otp.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_peer\eap_peap.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_common\eap_peap_common.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\eap_register.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_peer\eap_tls.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_peer\eap_tls_common.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_peer\eap_tnc.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_peer\eap_ttls.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eapol_supp\eapol_supp_sm.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\utils\eloop_win.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\events.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\l2_packet\l2_packet_winpcap.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\main.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\md5.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\ms_funcs.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_peer\mschapv2.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\drivers\ndis_events.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\notify.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\utils\os_win32.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\utils\pcsc_funcs.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\rsn_supp\peerkey.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\rsn_supp\pmksa_cache.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\rsn_supp\preauth.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\scan.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\sha1.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\sha1-pbkdf2.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\sha1-tlsprf.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\tls_openssl.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_peer\tncc.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\rsn_supp\wpa.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\common\wpa_common.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\utils\wpa_debug.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\rsn_supp\wpa_ie.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\wpa_supplicant.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\utils\wpabuf.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\wpas_glue.c"\r
+                               >\r
+                       </File>\r
+               </Filter>\r
+               <Filter\r
+                       Name="Header Files"\r
+                       Filter="h;hpp;hxx;hm;inl;inc;xsd"\r
+                       UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"\r
+                       >\r
+               </Filter>\r
+               <Filter\r
+                       Name="Resource Files"\r
+                       Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"\r
+                       UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"\r
+                       >\r
+               </Filter>\r
+       </Files>\r
+       <Globals>\r
+       </Globals>\r
+</VisualStudioProject>\r
index 4d402e5..1034891 100755 (executable)
                                >\r
                        </File>\r
                        <File\r
+                               RelativePath="..\..\..\src\drivers\driver_common.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
                                RelativePath="..\..\..\src\drivers\driver_ndis.c"\r
                                >\r
                        </File>\r
index 162a0b8..7da13c3 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant - command line interface for wpa_supplicant daemon
- * Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
 #ifdef CONFIG_CTRL_IFACE_UNIX
 #include <dirent.h>
 #endif /* CONFIG_CTRL_IFACE_UNIX */
-#ifdef CONFIG_READLINE
-#include <readline/readline.h>
-#include <readline/history.h>
-#endif /* CONFIG_READLINE */
-#ifdef CONFIG_WPA_CLI_FORK
-#include <sys/wait.h>
-#endif /* CONFIG_WPA_CLI_FORK */
 
 #include "common/wpa_ctrl.h"
-#include "common.h"
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/edit.h"
+#include "utils/list.h"
 #include "common/version.h"
+#ifdef ANDROID
+#include <cutils/properties.h>
+#endif /* ANDROID */
 
 
 static const char *wpa_cli_version =
 "wpa_cli v" VERSION_STR "\n"
-"Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi> and contributors";
+"Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi> and contributors";
 
 
 static const char *wpa_cli_license =
@@ -91,22 +90,32 @@ static const char *wpa_cli_full_license =
 
 static struct wpa_ctrl *ctrl_conn;
 static struct wpa_ctrl *mon_conn;
-#ifdef CONFIG_WPA_CLI_FORK
-static pid_t mon_pid = 0;
-#endif /* CONFIG_WPA_CLI_FORK */
 static int wpa_cli_quit = 0;
 static int wpa_cli_attached = 0;
 static int wpa_cli_connected = 0;
 static int wpa_cli_last_id = 0;
-static const char *ctrl_iface_dir = "/var/run/wpa_supplicant";
+#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 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;
 
+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 void print_help();
+
+static void print_help(void);
+static void wpa_cli_mon_receive(int sock, void *eloop_ctx, void *sock_ctx);
 
 
 static void usage(void)
@@ -121,63 +130,195 @@ static void usage(void)
               "events from\n"
               "       wpa_supplicant\n"
               "  -B = run a daemon in the background\n"
-              "  default path: /var/run/wpa_supplicant\n"
+              "  default path: " CONFIG_CTRL_IFACE_DIR "\n"
               "  default interface: first interface found in socket path\n");
        print_help();
 }
 
 
-#ifdef CONFIG_WPA_CLI_FORK
-static int in_query = 0;
+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 wpa_cli_monitor_sig(int sig)
+static void cli_txt_list_flush(struct dl_list *list)
 {
-       if (sig == SIGUSR1)
-               in_query = 1;
-       else if (sig == SIGUSR2)
-               in_query = 0;
+       struct cli_txt_entry *e;
+       while ((e = dl_list_first(list, struct cli_txt_entry, list)))
+               cli_txt_list_free(e);
 }
 
-static void wpa_cli_monitor(void)
+
+static struct cli_txt_entry * cli_txt_list_get(struct dl_list *txt_list,
+                                              const char *txt)
 {
-       char buf[256];
-       size_t len = sizeof(buf) - 1;
-       struct timeval tv;
-       fd_set rfds;
+       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;
+}
 
-       signal(SIGUSR1, wpa_cli_monitor_sig);
-       signal(SIGUSR2, wpa_cli_monitor_sig);
 
-       while (mon_conn) {
-               int s = wpa_ctrl_get_fd(mon_conn);
-               tv.tv_sec = 5;
-               tv.tv_usec = 0;
-               FD_ZERO(&rfds);
-               FD_SET(s, &rfds);
-               if (select(s + 1, &rfds, NULL, NULL, &tv) < 0) {
-                       if (errno == EINTR)
-                               continue;
-                       perror("select");
-                       break;
-               }
-               if (mon_conn == 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
+static void cli_txt_list_del_word(struct dl_list *txt_list, const char *txt)
+{
+       const char *end;
+       char *buf;
+       end = os_strchr(txt, ' ');
+       if (end == NULL)
+               end = txt + os_strlen(txt);
+       buf = os_malloc(end - txt + 1);
+       if (buf == NULL)
+               return;
+       os_memcpy(buf, txt, end - txt);
+       buf[end - txt] = '\0';
+       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);
+}
+
+
+static int cli_txt_list_add_word(struct dl_list *txt_list, const char *txt)
+{
+       const char *end;
+       char *buf;
+       int ret;
+       end = os_strchr(txt, ' ');
+       if (end == NULL)
+               end = txt + os_strlen(txt);
+       buf = os_malloc(end - txt + 1);
+       if (buf == NULL)
+               return -1;
+       os_memcpy(buf, txt, end - txt);
+       buf[end - txt] = '\0';
+       ret = cli_txt_list_add(txt_list, buf);
+       os_free(buf);
+       return ret;
+}
+#endif /* CONFIG_P2P */
+
+
+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_zalloc((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;
-               if (FD_ISSET(s, &rfds)) {
-                       len = sizeof(buf) - 1;
-                       int res = wpa_ctrl_recv(mon_conn, buf, &len);
-                       if (res < 0) {
-                               perror("wpa_ctrl_recv");
-                               break;
-                       }
-                       buf[len] = '\0';
-                       if (in_query)
-                               printf("\r");
-                       printf("%s\n", buf);
-                       kill(getppid(), SIGUSR1);
+               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;
 }
-#endif /* CONFIG_WPA_CLI_FORK */
 
 
 static int wpa_cli_open_connection(const char *ifname, int attach)
@@ -192,20 +333,31 @@ static int wpa_cli_open_connection(const char *ifname, int attach)
        else
                mon_conn = NULL;
 #else /* CONFIG_CTRL_IFACE_UDP || CONFIG_CTRL_IFACE_NAMED_PIPE */
-       char *cfile;
+       char *cfile = NULL;
        int flen, res;
 
        if (ifname == NULL)
                return -1;
 
-       flen = os_strlen(ctrl_iface_dir) + os_strlen(ifname) + 2;
-       cfile = os_malloc(flen);
-       if (cfile == NULL)
-               return -1L;
-       res = os_snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ifname);
-       if (res < 0 || res >= flen) {
-               os_free(cfile);
-               return -1;
+#ifdef ANDROID
+       if (access(ctrl_iface_dir, F_OK) < 0) {
+               cfile = os_strdup(ifname);
+               if (cfile == NULL)
+                       return -1;
+       }
+#endif /* ANDROID */
+
+       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 (res < 0 || res >= flen) {
+                       os_free(cfile);
+                       return -1;
+               }
        }
 
        ctrl_conn = wpa_ctrl_open(cfile);
@@ -224,26 +376,15 @@ static int wpa_cli_open_connection(const char *ifname, int attach)
        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");
                        return -1;
                }
-
-#ifdef CONFIG_WPA_CLI_FORK
-               {
-                       pid_t p = fork();
-                       if (p < 0) {
-                               perror("fork");
-                               return -1;
-                       }
-                       if (p == 0) {
-                               wpa_cli_monitor();
-                               exit(0);
-                       } else
-                               mon_pid = p;
-               }
-#endif /* CONFIG_WPA_CLI_FORK */
        }
 
        return 0;
@@ -255,15 +396,6 @@ static void wpa_cli_close_connection(void)
        if (ctrl_conn == NULL)
                return;
 
-#ifdef CONFIG_WPA_CLI_FORK
-       if (mon_pid) {
-               int status;
-               kill(mon_pid, SIGPIPE);
-               wait(&status);
-               mon_pid = 0;
-       }
-#endif /* CONFIG_WPA_CLI_FORK */
-
        if (wpa_cli_attached) {
                wpa_ctrl_detach(interactive ? mon_conn : ctrl_conn);
                wpa_cli_attached = 0;
@@ -271,6 +403,7 @@ static void wpa_cli_close_connection(void)
        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;
        }
@@ -306,6 +439,8 @@ static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print)
        if (print) {
                buf[len] = '\0';
                printf("%s", buf);
+               if (interactive && len > 0 && buf[len - 1] != '\n')
+                       printf("\n");
        }
        return 0;
 }
@@ -330,6 +465,25 @@ static int wpa_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[])
 }
 
 
+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[])
+{
+       char cmd[256];
+       int ret;
+       if (argc == 0)
+               return -1;
+       ret = os_snprintf(cmd, sizeof(cmd), "NOTE %s", argv[0]);
+       if (ret < 0 || (size_t) ret >= sizeof(cmd))
+               return -1;
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
 static int wpa_cli_cmd_mib(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
        return wpa_ctrl_command(ctrl, "MIB");
@@ -359,6 +513,8 @@ static int wpa_cli_cmd_license(struct wpa_ctrl *ctrl, int argc, char *argv[])
 static int wpa_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
        wpa_cli_quit = 1;
+       if (interactive)
+               eloop_terminate();
        return 0;
 }
 
@@ -393,13 +549,17 @@ static int wpa_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
                return 0;
        }
 
-       if (argc != 2) {
+       if (argc != 1 && 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 (argc == 1)
+               res = os_snprintf(cmd, sizeof(cmd), "SET %s ", argv[0]);
+       else
+               res = os_snprintf(cmd, sizeof(cmd), "SET %s %s",
+                                 argv[0], argv[1]);
        if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
                printf("Too long SET command.\n");
                return -1;
@@ -408,6 +568,26 @@ static int wpa_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
 }
 
 
+static int wpa_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       char cmd[256];
+       int res;
+
+       if (argc != 1) {
+               printf("Invalid GET command: need one argument (variable "
+                      "name)\n");
+               return -1;
+       }
+
+       res = os_snprintf(cmd, sizeof(cmd), "GET %s", argv[0]);
+       if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+               printf("Too long GET command.\n");
+               return -1;
+       }
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
 static int wpa_cli_cmd_logoff(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
        return wpa_ctrl_command(ctrl, "LOGOFF");
@@ -467,6 +647,66 @@ static int wpa_cli_cmd_ap_scan(struct wpa_ctrl *ctrl, int argc, char *argv[])
 }
 
 
+static int wpa_cli_cmd_scan_interval(struct wpa_ctrl *ctrl, int argc,
+                                    char *argv[])
+{
+       char cmd[256];
+       int res;
+
+       if (argc != 1) {
+               printf("Invalid SCAN_INTERVAL command: needs one argument "
+                      "scan_interval value)\n");
+               return -1;
+       }
+       res = os_snprintf(cmd, sizeof(cmd), "SCAN_INTERVAL %s", argv[0]);
+       if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+               printf("Too long SCAN_INTERVAL command.\n");
+               return -1;
+       }
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_bss_expire_age(struct wpa_ctrl *ctrl, int argc,
+                                     char *argv[])
+{
+       char cmd[256];
+       int res;
+
+       if (argc != 1) {
+               printf("Invalid BSS_EXPIRE_AGE command: needs one argument "
+                      "(bss_expire_age value)\n");
+               return -1;
+       }
+       res = os_snprintf(cmd, sizeof(cmd), "BSS_EXPIRE_AGE %s", argv[0]);
+       if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+               printf("Too long BSS_EXPIRE_AGE command.\n");
+               return -1;
+       }
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_bss_expire_count(struct wpa_ctrl *ctrl, int argc,
+                                       char *argv[])
+{
+       char cmd[256];
+       int res;
+
+       if (argc != 1) {
+               printf("Invalid BSS_EXPIRE_COUNT command: needs one argument "
+                      "(bss_expire_count value)\n");
+               return -1;
+       }
+       res = os_snprintf(cmd, sizeof(cmd), "BSS_EXPIRE_COUNT %s", argv[0]);
+       if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+               printf("Too long BSS_EXPIRE_COUNT command.\n");
+               return -1;
+       }
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
 static int wpa_cli_cmd_stkstart(struct wpa_ctrl *ctrl, int argc,
                                char *argv[])
 {
@@ -561,6 +801,39 @@ static int wpa_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc, char *argv[])
 }
 
 
+static int wpa_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 (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+               printf("Too long WPS_CHECK_PIN command.\n");
+               return -1;
+       }
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+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_OOB
 static int wpa_cli_cmd_wps_oob(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
@@ -602,7 +875,7 @@ static int wpa_cli_cmd_wps_reg(struct wpa_ctrl *ctrl, int argc, char *argv[])
        if (argc == 2)
                res = os_snprintf(cmd, sizeof(cmd), "WPS_REG %s %s",
                                  argv[0], argv[1]);
-       else if (argc == 6) {
+       else if (argc == 5 || argc == 6) {
                char ssid_hex[2 * 32 + 1];
                char key_hex[2 * 64 + 1];
                int i;
@@ -615,10 +888,13 @@ static int wpa_cli_cmd_wps_reg(struct wpa_ctrl *ctrl, int argc, char *argv[])
                }
 
                key_hex[0] = '\0';
-               for (i = 0; i < 64; i++) {
-                       if (argv[5][i] == '\0')
-                               break;
-                       os_snprintf(&key_hex[i * 2], 3, "%02x", argv[5][i]);
+               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),
@@ -627,11 +903,11 @@ static int wpa_cli_cmd_wps_reg(struct wpa_ctrl *ctrl, int argc, char *argv[])
                                  key_hex);
        } else {
                printf("Invalid WPS_REG command: need two arguments:\n"
-                      "- BSSID: use 'any' to select any\n"
+                      "- BSSID of the target AP\n"
                       "- AP PIN\n");
                printf("Alternatively, six arguments can be used to "
                       "reconfigure the AP:\n"
-                      "- BSSID: use 'any' to select any\n"
+                      "- BSSID of the target AP\n"
                       "- AP PIN\n"
                       "- new SSID\n"
                       "- new auth (OPEN, WPAPSK, WPA2PSK)\n"
@@ -648,11 +924,44 @@ static int wpa_cli_cmd_wps_reg(struct wpa_ctrl *ctrl, int argc, char *argv[])
 }
 
 
+static int wpa_cli_cmd_wps_ap_pin(struct wpa_ctrl *ctrl, int argc,
+                                 char *argv[])
+{
+       char cmd[256];
+       int res;
+
+       if (argc < 1) {
+               printf("Invalid WPS_AP_PIN command: needs at least one "
+                      "argument\n");
+               return -1;
+       }
+
+       if (argc > 2)
+               res = os_snprintf(cmd, sizeof(cmd), "WPS_AP_PIN %s %s %s",
+                                 argv[0], argv[1], argv[2]);
+       else if (argc > 1)
+               res = os_snprintf(cmd, sizeof(cmd), "WPS_AP_PIN %s %s",
+                                 argv[0], argv[1]);
+       else
+               res = os_snprintf(cmd, sizeof(cmd), "WPS_AP_PIN %s",
+                                 argv[0]);
+       if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+               printf("Too long WPS_AP_PIN command.\n");
+               return -1;
+       }
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
 static int wpa_cli_cmd_wps_er_start(struct wpa_ctrl *ctrl, int argc,
                                    char *argv[])
 {
+       char cmd[100];
+       if (argc > 0) {
+               os_snprintf(cmd, sizeof(cmd), "WPS_ER_START %s", argv[0]);
+               return wpa_ctrl_command(ctrl, cmd);
+       }
        return wpa_ctrl_command(ctrl, "WPS_ER_START");
-
 }
 
 
@@ -670,15 +979,21 @@ static int wpa_cli_cmd_wps_er_pin(struct wpa_ctrl *ctrl, int argc,
        char cmd[256];
        int res;
 
-       if (argc != 2) {
-               printf("Invalid WPS_ER_PIN command: need two arguments:\n"
+       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");
+                      "- PIN: Enrollee PIN\n"
+                      "optional: - Enrollee MAC address\n");
                return -1;
        }
 
-       res = os_snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s",
-                         argv[0], argv[1]);
+       if (argc > 2)
+               res = os_snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s %s",
+                                 argv[0], argv[1], argv[2]);
+       else
+               res = os_snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s",
+                                 argv[0], argv[1]);
        if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
                printf("Too long WPS_ER_PIN command.\n");
                return -1;
@@ -732,43 +1047,118 @@ static int wpa_cli_cmd_wps_er_learn(struct wpa_ctrl *ctrl, int argc,
 }
 
 
-static int wpa_cli_cmd_ibss_rsn(struct wpa_ctrl *ctrl, int argc, char *argv[])
+static int wpa_cli_cmd_wps_er_set_config(struct wpa_ctrl *ctrl, int argc,
+                                        char *argv[])
 {
        char cmd[256];
        int res;
 
-       if (argc != 1) {
-               printf("Invalid IBSS_RSN command: needs one argument "
-                      "(Peer STA MAC address)\n");
+       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;
        }
 
-       res = os_snprintf(cmd, sizeof(cmd), "IBSS_RSN %s", argv[0]);
+       res = os_snprintf(cmd, sizeof(cmd), "WPS_ER_SET_CONFIG %s %s",
+                         argv[0], argv[1]);
        if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
-               printf("Too long IBSS_RSN command.\n");
+               printf("Too long WPS_ER_SET_CONFIG command.\n");
                return -1;
        }
        return wpa_ctrl_command(ctrl, cmd);
 }
 
 
-static int wpa_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[])
+static int wpa_cli_cmd_wps_er_config(struct wpa_ctrl *ctrl, int argc,
+                                    char *argv[])
 {
        char cmd[256];
        int res;
 
-       if (argc != 1) {
-               printf("Invalid LEVEL command: needs one argument (debug "
-                      "level)\n");
-               return -1;
-       }
-       res = os_snprintf(cmd, sizeof(cmd), "LEVEL %s", argv[0]);
-       if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
-               printf("Too long LEVEL command.\n");
-               return -1;
-       }
-       return wpa_ctrl_command(ctrl, cmd);
-}
+       if (argc == 5 || argc == 6) {
+               char ssid_hex[2 * 32 + 1];
+               char key_hex[2 * 64 + 1];
+               int i;
+
+               ssid_hex[0] = '\0';
+               for (i = 0; i < 32; 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 (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+               printf("Too long WPS_ER_CONFIG command.\n");
+               return -1;
+       }
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_ibss_rsn(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       char cmd[256];
+       int res;
+
+       if (argc != 1) {
+               printf("Invalid IBSS_RSN command: needs one argument "
+                      "(Peer STA MAC address)\n");
+               return -1;
+       }
+
+       res = os_snprintf(cmd, sizeof(cmd), "IBSS_RSN %s", argv[0]);
+       if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+               printf("Too long IBSS_RSN command.\n");
+               return -1;
+       }
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       char cmd[256];
+       int res;
+
+       if (argc != 1) {
+               printf("Invalid LEVEL command: needs one argument (debug "
+                      "level)\n");
+               return -1;
+       }
+       res = os_snprintf(cmd, sizeof(cmd), "LEVEL %s", argv[0]);
+       if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+               printf("Too long LEVEL command.\n");
+               return -1;
+       }
+       return wpa_ctrl_command(ctrl, cmd);
+}
 
 
 static int wpa_cli_cmd_identity(struct wpa_ctrl *ctrl, int argc, char *argv[])
@@ -1002,6 +1392,58 @@ static int wpa_cli_cmd_bssid(struct wpa_ctrl *ctrl, int argc, char *argv[])
 }
 
 
+static int wpa_cli_cmd_blacklist(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       char cmd[256], *pos, *end;
+       int i, ret;
+
+       end = cmd + sizeof(cmd);
+       pos = cmd;
+       ret = os_snprintf(pos, end - pos, "BLACKLIST");
+       if (ret < 0 || ret >= end - pos) {
+               printf("Too long BLACKLIST command.\n");
+               return -1;
+       }
+       pos += ret;
+       for (i = 0; i < argc; i++) {
+               ret = os_snprintf(pos, end - pos, " %s", argv[i]);
+               if (ret < 0 || ret >= end - pos) {
+                       printf("Too long BLACKLIST command.\n");
+                       return -1;
+               }
+               pos += ret;
+       }
+
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_log_level(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       char cmd[256], *pos, *end;
+       int i, ret;
+
+       end = cmd + sizeof(cmd);
+       pos = cmd;
+       ret = os_snprintf(pos, end - pos, "LOG_LEVEL");
+       if (ret < 0 || ret >= end - pos) {
+               printf("Too long LOG_LEVEL command.\n");
+               return -1;
+       }
+       pos += ret;
+       for (i = 0; i < argc; i++) {
+               ret = os_snprintf(pos, end - pos, " %s", argv[i]);
+               if (ret < 0 || ret >= end - pos) {
+                       printf("Too long LOG_LEVEL command.\n");
+                       return -1;
+               }
+               pos += ret;
+       }
+
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
 static int wpa_cli_cmd_list_networks(struct wpa_ctrl *ctrl, int argc,
                                     char *argv[])
 {
@@ -1229,6 +1671,21 @@ static int wpa_cli_cmd_bss(struct wpa_ctrl *ctrl, int argc, char *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[])
 {
@@ -1393,73 +1850,826 @@ static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd,
                return -1;
        }
 
-       buf[len] = '\0';
-       if (memcmp(buf, "FAIL", 4) == 0)
+       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 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;
+}
+#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");
+}
+
+
+static int wpa_cli_cmd_drop_sa(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       return wpa_ctrl_command(ctrl, "DROP_SA");
+}
+
+
+static int wpa_cli_cmd_roam(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       char cmd[128];
+       int res;
+
+       if (argc != 1) {
+               printf("Invalid ROAM command: needs one argument "
+                      "(target AP's BSSID)\n");
+               return -1;
+       }
+
+       res = os_snprintf(cmd, sizeof(cmd), "ROAM %s", argv[0]);
+       if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+               printf("Too long ROAM command.\n");
+               return -1;
+       }
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+#ifdef CONFIG_P2P
+
+static int wpa_cli_cmd_p2p_find(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       char cmd[128];
+       int res;
+
+       if (argc == 0)
+               return wpa_ctrl_command(ctrl, "P2P_FIND");
+
+       if (argc > 2)
+               res = os_snprintf(cmd, sizeof(cmd), "P2P_FIND %s %s %s",
+                                 argv[0], argv[1], argv[2]);
+       else if (argc > 1)
+               res = os_snprintf(cmd, sizeof(cmd), "P2P_FIND %s %s",
+                                 argv[0], argv[1]);
+       else
+               res = os_snprintf(cmd, sizeof(cmd), "P2P_FIND %s", argv[0]);
+       if (res < 0 || (size_t) res >= sizeof(cmd))
+               return -1;
+       cmd[sizeof(cmd) - 1] = '\0';
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+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_connect(struct wpa_ctrl *ctrl, int argc,
+                                  char *argv[])
+{
+       char cmd[128];
+       int res;
+
+       if (argc < 2) {
+               printf("Invalid P2P_CONNECT command: needs at least two "
+                      "arguments (address and pbc/PIN)\n");
+               return -1;
+       }
+
+       if (argc > 4)
+               res = os_snprintf(cmd, sizeof(cmd),
+                                 "P2P_CONNECT %s %s %s %s %s",
+                                 argv[0], argv[1], argv[2], argv[3],
+                                 argv[4]);
+       else if (argc > 3)
+               res = os_snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s %s %s",
+                                 argv[0], argv[1], argv[2], argv[3]);
+       else if (argc > 2)
+               res = os_snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s %s",
+                                 argv[0], argv[1], argv[2]);
+       else
+               res = os_snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s",
+                                 argv[0], argv[1]);
+       if (res < 0 || (size_t) res >= sizeof(cmd))
+               return -1;
+       cmd[sizeof(cmd) - 1] = '\0';
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+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[])
+{
+       char cmd[128];
+       int res;
+
+       if (argc == 0)
+               return wpa_ctrl_command(ctrl, "P2P_LISTEN");
+
+       res = os_snprintf(cmd, sizeof(cmd), "P2P_LISTEN %s", argv[0]);
+       if (res < 0 || (size_t) res >= sizeof(cmd))
+               return -1;
+       cmd[sizeof(cmd) - 1] = '\0';
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_p2p_group_remove(struct wpa_ctrl *ctrl, int argc,
+                                       char *argv[])
+{
+       char cmd[128];
+       int res;
+
+       if (argc != 1) {
+               printf("Invalid P2P_GROUP_REMOVE command: needs one argument "
+                      "(interface name)\n");
+               return -1;
+       }
+
+       res = os_snprintf(cmd, sizeof(cmd), "P2P_GROUP_REMOVE %s", argv[0]);
+       if (res < 0 || (size_t) res >= sizeof(cmd))
+               return -1;
+       cmd[sizeof(cmd) - 1] = '\0';
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+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[])
+{
+       char cmd[128];
+       int res;
+
+       if (argc == 0)
+               return wpa_ctrl_command(ctrl, "P2P_GROUP_ADD");
+
+       if (argc > 1)
+               res = os_snprintf(cmd, sizeof(cmd), "P2P_GROUP_ADD %s %s",
+                                 argv[0], argv[1]);
+       else
+               res = os_snprintf(cmd, sizeof(cmd), "P2P_GROUP_ADD %s",
+                                 argv[0]);
+       if (res < 0 || (size_t) res >= sizeof(cmd))
+               return -1;
+       cmd[sizeof(cmd) - 1] = '\0';
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_p2p_prov_disc(struct wpa_ctrl *ctrl, int argc,
+                                    char *argv[])
+{
+       char cmd[128];
+       int res;
+
+       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;
+       }
+
+       if (argc == 3)
+               res = os_snprintf(cmd, sizeof(cmd), "P2P_PROV_DISC %s %s %s",
+                                 argv[0], argv[1], argv[2]);
+       else
+               res = os_snprintf(cmd, sizeof(cmd), "P2P_PROV_DISC %s %s",
+                                 argv[0], argv[1]);
+       if (res < 0 || (size_t) res >= sizeof(cmd))
+               return -1;
+       cmd[sizeof(cmd) - 1] = '\0';
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+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];
+       int res;
+
+       if (argc != 2 && argc != 4) {
+               printf("Invalid P2P_SERV_DISC_REQ command: needs two "
+                      "arguments (address and TLVs) or four arguments "
+                      "(address, \"upnp\", version, search target "
+                      "(SSDP ST:)\n");
+               return -1;
+       }
+
+       if (argc == 4)
+               res = os_snprintf(cmd, sizeof(cmd),
+                                 "P2P_SERV_DISC_REQ %s %s %s %s",
+                                 argv[0], argv[1], argv[2], argv[3]);
+       else
+               res = os_snprintf(cmd, sizeof(cmd), "P2P_SERV_DISC_REQ %s %s",
+                                 argv[0], argv[1]);
+       if (res < 0 || (size_t) res >= sizeof(cmd))
+               return -1;
+       cmd[sizeof(cmd) - 1] = '\0';
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_p2p_serv_disc_cancel_req(struct wpa_ctrl *ctrl,
+                                               int argc, char *argv[])
+{
+       char cmd[128];
+       int res;
+
+       if (argc != 1) {
+               printf("Invalid P2P_SERV_DISC_CANCEL_REQ command: needs one "
+                      "argument (pending request identifier)\n");
+               return -1;
+       }
+
+       res = os_snprintf(cmd, sizeof(cmd), "P2P_SERV_DISC_CANCEL_REQ %s",
+                         argv[0]);
+       if (res < 0 || (size_t) res >= sizeof(cmd))
+               return -1;
+       cmd[sizeof(cmd) - 1] = '\0';
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+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 (res < 0 || (size_t) res >= sizeof(cmd))
+               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[])
+{
+       char cmd[128];
+       int res;
+
+       if (argc != 1) {
+               printf("Invalid P2P_SERV_DISC_EXTERNAL command: needs one "
+                      "argument (external processing: 0/1)\n");
+               return -1;
+       }
+
+       res = os_snprintf(cmd, sizeof(cmd), "P2P_SERV_DISC_EXTERNAL %s",
+                         argv[0]);
+       if (res < 0 || (size_t) res >= sizeof(cmd))
+               return -1;
+       cmd[sizeof(cmd) - 1] = '\0';
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+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[])
+{
+       char cmd[4096];
+       int res;
+
+       if (argc != 3 && argc != 4) {
+               printf("Invalid P2P_SERVICE_ADD command: needs three or four "
+                      "arguments\n");
+               return -1;
+       }
+
+       if (argc == 4)
+               res = os_snprintf(cmd, sizeof(cmd),
+                                 "P2P_SERVICE_ADD %s %s %s %s",
+                                 argv[0], argv[1], argv[2], argv[3]);
+       else
+               res = os_snprintf(cmd, sizeof(cmd),
+                                 "P2P_SERVICE_ADD %s %s %s",
+                                 argv[0], argv[1], argv[2]);
+       if (res < 0 || (size_t) res >= sizeof(cmd))
+               return -1;
+       cmd[sizeof(cmd) - 1] = '\0';
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+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 (res < 0 || (size_t) res >= sizeof(cmd))
+               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[])
+{
+       char cmd[128];
+       int res;
+
+       if (argc != 1) {
+               printf("Invalid P2P_REJECT command: needs one argument "
+                      "(peer address)\n");
+               return -1;
+       }
+
+       res = os_snprintf(cmd, sizeof(cmd), "P2P_REJECT %s", argv[0]);
+       if (res < 0 || (size_t) res >= sizeof(cmd))
+               return -1;
+       cmd[sizeof(cmd) - 1] = '\0';
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_p2p_invite(struct wpa_ctrl *ctrl,
+                                 int argc, char *argv[])
+{
+       char cmd[128];
+       int res;
+
+       if (argc < 1) {
+               printf("Invalid P2P_INVITE command: needs at least one "
+                      "argument\n");
+               return -1;
+       }
+
+       if (argc > 2)
+               res = os_snprintf(cmd, sizeof(cmd), "P2P_INVITE %s %s %s",
+                                 argv[0], argv[1], argv[2]);
+       else if (argc > 1)
+               res = os_snprintf(cmd, sizeof(cmd), "P2P_INVITE %s %s",
+                                 argv[0], argv[1]);
+       else
+               res = os_snprintf(cmd, sizeof(cmd), "P2P_INVITE %s", argv[0]);
+       if (res < 0 || (size_t) res >= sizeof(cmd))
+               return -1;
+       cmd[sizeof(cmd) - 1] = '\0';
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_p2p_peer(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       char buf[64];
+       if (argc != 1) {
+               printf("Invalid 'p2p_peer' command - exactly one argument, "
+                      "P2P peer device address, is required.\n");
+               return -1;
+       }
+       os_snprintf(buf, sizeof(buf), "P2P_PEER %s", argv[0]);
+       return wpa_ctrl_command(ctrl, buf);
+}
+
+
+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, 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 (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[])
+{
+       char cmd[100];
+       int res;
+
+       if (argc != 2) {
+               printf("Invalid P2P_SET command: needs two arguments (field, "
+                      "value)\n");
+               return -1;
+       }
+
+       res = os_snprintf(cmd, sizeof(cmd), "P2P_SET %s %s", argv[0], argv[1]);
+       if (res < 0 || (size_t) res >= sizeof(cmd))
+               return -1;
+       cmd[sizeof(cmd) - 1] = '\0';
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+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[])
+{
+       char cmd[100];
+       int res;
+
+       if (argc != 1) {
+               printf("Invalid P2P_UNAUTHORIZE command: needs one argument "
+                      "(peer address)\n");
+               return -1;
+       }
+
+       res = os_snprintf(cmd, sizeof(cmd), "P2P_UNAUTHORIZE %s", argv[0]);
+
+       if (res < 0 || (size_t) res >= sizeof(cmd))
+               return -1;
+
+       cmd[sizeof(cmd) - 1] = '\0';
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_p2p_presence_req(struct wpa_ctrl *ctrl, int argc,
+                                       char *argv[])
+{
+       char cmd[100];
+       int res;
+
+       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;
+       }
+
+       if (argc == 4)
+               res = os_snprintf(cmd, sizeof(cmd),
+                                 "P2P_PRESENCE_REQ %s %s %s %s",
+                                 argv[0], argv[1], argv[2], argv[3]);
+       else if (argc == 2)
+               res = os_snprintf(cmd, sizeof(cmd), "P2P_PRESENCE_REQ %s %s",
+                                 argv[0], argv[1]);
+       else
+               res = os_snprintf(cmd, sizeof(cmd), "P2P_PRESENCE_REQ");
+       if (res < 0 || (size_t) res >= sizeof(cmd))
+               return -1;
+       cmd[sizeof(cmd) - 1] = '\0';
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_p2p_ext_listen(struct wpa_ctrl *ctrl, int argc,
+                                     char *argv[])
+{
+       char cmd[100];
+       int res;
+
+       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;
+       }
+
+       if (argc == 2)
+               res = os_snprintf(cmd, sizeof(cmd), "P2P_EXT_LISTEN %s %s",
+                                 argv[0], argv[1]);
+       else
+               res = os_snprintf(cmd, sizeof(cmd), "P2P_EXT_LISTEN");
+       if (res < 0 || (size_t) res >= sizeof(cmd))
+               return -1;
+       cmd[sizeof(cmd) - 1] = '\0';
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+#endif /* CONFIG_P2P */
+
+
+#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[])
+{
+       char cmd[100];
+       int res;
+
+       if (argc == 0)
+               return wpa_ctrl_command(ctrl, "INTERWORKING_SELECT");
+
+       res = os_snprintf(cmd, sizeof(cmd), "INTERWORKING_SELECT %s", argv[0]);
+       if (res < 0 || (size_t) res >= sizeof(cmd))
+               return -1;
+       cmd[sizeof(cmd) - 1] = '\0';
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_interworking_connect(struct wpa_ctrl *ctrl, int argc,
+                                           char *argv[])
+{
+       char cmd[100];
+       int res;
+
+       if (argc != 1) {
+               printf("Invalid INTERWORKING_CONNECT commands: needs one "
+                      "argument (BSSID)\n");
+               return -1;
+       }
+
+       res = os_snprintf(cmd, sizeof(cmd), "INTERWORKING_CONNECT %s",
+                         argv[0]);
+       if (res < 0 || (size_t) res >= sizeof(cmd))
                return -1;
-       printf("%s", buf);
-
-       pos = buf;
-       while (*pos != '\0' && *pos != '\n')
-               pos++;
-       *pos = '\0';
-       os_strlcpy(addr, buf, addr_len);
-       return 0;
+       cmd[sizeof(cmd) - 1] = '\0';
+       return wpa_ctrl_command(ctrl, cmd);
 }
 
 
-static int wpa_cli_cmd_all_sta(struct wpa_ctrl *ctrl, int argc, char *argv[])
+static int wpa_cli_cmd_anqp_get(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
-       char addr[32], cmd[64];
+       char cmd[100];
+       int res;
 
-       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);
+       if (argc != 2) {
+               printf("Invalid ANQP_GET command: needs two arguments "
+                      "(addr and info id list)\n");
+               return -1;
+       }
 
-       return -1;
+       res = os_snprintf(cmd, sizeof(cmd), "ANQP_GET %s %s",
+                         argv[0], argv[1]);
+       if (res < 0 || (size_t) res >= sizeof(cmd))
+               return -1;
+       cmd[sizeof(cmd) - 1] = '\0';
+       return wpa_ctrl_command(ctrl, cmd);
 }
-#endif /* CONFIG_AP */
+#endif /* CONFIG_INTERWORKING */
 
 
-static int wpa_cli_cmd_suspend(struct wpa_ctrl *ctrl, int argc, char *argv[])
+static int wpa_cli_cmd_sta_autoconnect(struct wpa_ctrl *ctrl, int argc,
+                                      char *argv[])
 {
-       return wpa_ctrl_command(ctrl, "SUSPEND");
+       char cmd[256];
+       int res;
+
+       if (argc != 1) {
+               printf("Invalid STA_AUTOCONNECT command: needs one argument "
+                      "(0/1 = disable/enable automatic reconnection)\n");
+               return -1;
+       }
+       res = os_snprintf(cmd, sizeof(cmd), "STA_AUTOCONNECT %s", argv[0]);
+       if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+               printf("Too long STA_AUTOCONNECT command.\n");
+               return -1;
+       }
+       return wpa_ctrl_command(ctrl, cmd);
 }
 
 
-static int wpa_cli_cmd_resume(struct wpa_ctrl *ctrl, int argc, char *argv[])
+static int wpa_cli_cmd_tdls_discover(struct wpa_ctrl *ctrl, int argc,
+                                    char *argv[])
 {
-       return wpa_ctrl_command(ctrl, "RESUME");
+       char cmd[256];
+       int res;
+
+       if (argc != 1) {
+               printf("Invalid TDLS_DISCOVER command: needs one argument "
+                      "(Peer STA MAC address)\n");
+               return -1;
+       }
+
+       res = os_snprintf(cmd, sizeof(cmd), "TDLS_DISCOVER %s", argv[0]);
+       if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+               printf("Too long TDLS_DISCOVER command.\n");
+               return -1;
+       }
+       return wpa_ctrl_command(ctrl, cmd);
 }
 
 
-static int wpa_cli_cmd_drop_sa(struct wpa_ctrl *ctrl, int argc, char *argv[])
+static int wpa_cli_cmd_tdls_setup(struct wpa_ctrl *ctrl, int argc,
+                                 char *argv[])
 {
-       return wpa_ctrl_command(ctrl, "DROP_SA");
+       char cmd[256];
+       int res;
+
+       if (argc != 1) {
+               printf("Invalid TDLS_SETUP command: needs one argument "
+                      "(Peer STA MAC address)\n");
+               return -1;
+       }
+
+       res = os_snprintf(cmd, sizeof(cmd), "TDLS_SETUP %s", argv[0]);
+       if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+               printf("Too long TDLS_SETUP command.\n");
+               return -1;
+       }
+       return wpa_ctrl_command(ctrl, cmd);
 }
 
 
-static int wpa_cli_cmd_roam(struct wpa_ctrl *ctrl, int argc, char *argv[])
+static int wpa_cli_cmd_tdls_teardown(struct wpa_ctrl *ctrl, int argc,
+                                    char *argv[])
 {
-       char cmd[128];
+       char cmd[256];
        int res;
 
        if (argc != 1) {
-               printf("Invalid ROAM command: needs one argument "
-                      "(target AP's BSSID)\n");
+               printf("Invalid TDLS_TEARDOWN command: needs one argument "
+                      "(Peer STA MAC address)\n");
                return -1;
        }
 
-       res = os_snprintf(cmd, sizeof(cmd), "ROAM %s", argv[0]);
+       res = os_snprintf(cmd, sizeof(cmd), "TDLS_TEARDOWN %s", argv[0]);
        if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
-               printf("Too long ROAM command.\n");
+               printf("Too long TDLS_TEARDOWN command.\n");
                return -1;
        }
        return wpa_ctrl_command(ctrl, cmd);
 }
 
 
+static int wpa_cli_cmd_signal_poll(struct wpa_ctrl *ctrl, int argc,
+                                  char *argv[])
+{
+       return wpa_ctrl_command(ctrl, "SIGNAL_POLL");
+}
+
+
 enum wpa_cli_cmd_flags {
        cli_cmd_flag_none               = 0x00,
        cli_cmd_flag_sensitive          = 0x01
@@ -1479,6 +2689,12 @@ static struct wpa_cli_cmd wpa_cli_commands[] = {
        { "ping", wpa_cli_cmd_ping,
          cli_cmd_flag_none,
          "= pings wpa_supplicant" },
+       { "relog", wpa_cli_cmd_relog,
+         cli_cmd_flag_none,
+         "= re-open log-file (allow rolling logs)" },
+       { "note", wpa_cli_cmd_note,
+         cli_cmd_flag_none,
+         "<text> = add a note to wpa_supplicant debug log" },
        { "mib", wpa_cli_cmd_mib,
          cli_cmd_flag_none,
          "= get MIB variables (dot1x, dot11)" },
@@ -1501,6 +2717,9 @@ static struct wpa_cli_cmd wpa_cli_commands[] = {
          cli_cmd_flag_none,
          "= set variables (shows list of variables when run without "
          "arguments)" },
+       { "get", wpa_cli_cmd_get,
+         cli_cmd_flag_none,
+         "<name> = get information" },
        { "logon", wpa_cli_cmd_logon,
          cli_cmd_flag_none,
          "= IEEE 802.1X EAPOL state machine logon" },
@@ -1539,6 +2758,15 @@ static struct wpa_cli_cmd wpa_cli_commands[] = {
        { "bssid", wpa_cli_cmd_bssid,
          cli_cmd_flag_none,
          "<network id> <BSSID> = set preferred BSSID for an SSID" },
+       { "blacklist", wpa_cli_cmd_blacklist,
+         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,
+         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,
          cli_cmd_flag_none,
          "= list configured networks" },
@@ -1607,6 +2835,15 @@ static struct wpa_cli_cmd wpa_cli_commands[] = {
        { "ap_scan", wpa_cli_cmd_ap_scan,
          cli_cmd_flag_none,
          "<value> = set ap_scan parameter" },
+       { "scan_interval", wpa_cli_cmd_scan_interval,
+         cli_cmd_flag_none,
+         "<value> = set scan_interval parameter (in seconds)" },
+       { "bss_expire_age", wpa_cli_cmd_bss_expire_age,
+         cli_cmd_flag_none,
+         "<value> = set BSS expiration age parameter" },
+       { "bss_expire_count", wpa_cli_cmd_bss_expire_count,
+         cli_cmd_flag_none,
+         "<value> = set BSS expiration scan count parameter" },
        { "stkstart", wpa_cli_cmd_stkstart,
          cli_cmd_flag_none,
          "<addr> = request STK negotiation with <addr>" },
@@ -1620,6 +2857,11 @@ static struct wpa_cli_cmd wpa_cli_commands[] = {
          cli_cmd_flag_sensitive,
          "<BSSID> [PIN] = start WPS PIN method (returns PIN, if not "
          "hardcoded)" },
+       { "wps_check_pin", wpa_cli_cmd_wps_check_pin,
+         cli_cmd_flag_sensitive,
+         "<PIN> = verify PIN checksum" },
+       { "wps_cancel", wpa_cli_cmd_wps_cancel, cli_cmd_flag_none,
+         "Cancels the pending WPS operation" },
 #ifdef CONFIG_WPS_OOB
        { "wps_oob", wpa_cli_cmd_wps_oob,
          cli_cmd_flag_sensitive,
@@ -1628,9 +2870,12 @@ static struct wpa_cli_cmd wpa_cli_commands[] = {
        { "wps_reg", wpa_cli_cmd_wps_reg,
          cli_cmd_flag_sensitive,
          "<BSSID> <AP PIN> = start WPS Registrar to configure an AP" },
+       { "wps_ap_pin", wpa_cli_cmd_wps_ap_pin,
+         cli_cmd_flag_sensitive,
+         "[params..] = enable/disable AP PIN" },
        { "wps_er_start", wpa_cli_cmd_wps_er_start,
          cli_cmd_flag_none,
-         "= start Wi-Fi Protected Setup External Registrar" },
+         "[IP address] = start Wi-Fi Protected Setup External Registrar" },
        { "wps_er_stop", wpa_cli_cmd_wps_er_stop,
          cli_cmd_flag_none,
          "= stop Wi-Fi Protected Setup External Registrar" },
@@ -1643,6 +2888,12 @@ static struct wpa_cli_cmd wpa_cli_commands[] = {
        { "wps_er_learn", wpa_cli_cmd_wps_er_learn,
          cli_cmd_flag_sensitive,
          "<UUID> <PIN> = learn AP configuration" },
+       { "wps_er_set_config", wpa_cli_cmd_wps_er_set_config,
+         cli_cmd_flag_none,
+         "<UUID> <network id> = set AP configuration for enrolling" },
+       { "wps_er_config", wpa_cli_cmd_wps_er_config,
+         cli_cmd_flag_sensitive,
+         "<UUID> <PIN> <SSID> <auth> <encr> <key> = configure AP" },
        { "ibss_rsn", wpa_cli_cmd_ibss_rsn,
          cli_cmd_flag_none,
          "<addr> = request RSN authentication with <addr> in IBSS" },
@@ -1663,6 +2914,104 @@ static struct wpa_cli_cmd wpa_cli_commands[] = {
        { "roam", wpa_cli_cmd_roam,
          cli_cmd_flag_none,
          "<addr> = roam to the specified BSS" },
+#ifdef CONFIG_P2P
+       { "p2p_find", wpa_cli_cmd_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, cli_cmd_flag_none,
+         "= stop P2P Devices search" },
+       { "p2p_connect", wpa_cli_cmd_p2p_connect, cli_cmd_flag_none,
+         "<addr> <\"pbc\"|PIN> = connect to a P2P Devices" },
+       { "p2p_listen", wpa_cli_cmd_p2p_listen, cli_cmd_flag_none,
+         "[timeout] = listen for P2P Devices for up-to timeout seconds" },
+       { "p2p_group_remove", wpa_cli_cmd_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, cli_cmd_flag_none,
+         "= add a new P2P group (local end as GO)" },
+       { "p2p_prov_disc", wpa_cli_cmd_p2p_prov_disc, cli_cmd_flag_none,
+         "<addr> <method> = request provisioning discovery" },
+       { "p2p_get_passphrase", wpa_cli_cmd_p2p_get_passphrase,
+         cli_cmd_flag_none,
+         "= get the passphrase for a group (GO only)" },
+       { "p2p_serv_disc_req", wpa_cli_cmd_p2p_serv_disc_req,
+         cli_cmd_flag_none,
+         "<addr> <TLVs> = schedule service discovery request" },
+       { "p2p_serv_disc_cancel_req", wpa_cli_cmd_p2p_serv_disc_cancel_req,
+         cli_cmd_flag_none,
+         "<id> = cancel pending service discovery request" },
+       { "p2p_serv_disc_resp", wpa_cli_cmd_p2p_serv_disc_resp,
+         cli_cmd_flag_none,
+         "<freq> <addr> <dialog token> <TLVs> = service discovery response" },
+       { "p2p_service_update", wpa_cli_cmd_p2p_service_update,
+         cli_cmd_flag_none,
+         "= indicate change in local services" },
+       { "p2p_serv_disc_external", wpa_cli_cmd_p2p_serv_disc_external,
+         cli_cmd_flag_none,
+         "<external> = set external processing of service discovery" },
+       { "p2p_service_flush", wpa_cli_cmd_p2p_service_flush,
+         cli_cmd_flag_none,
+         "= remove all stored service entries" },
+       { "p2p_service_add", wpa_cli_cmd_p2p_service_add,
+         cli_cmd_flag_none,
+         "<bonjour|upnp> <query|version> <response|service> = add a local "
+         "service" },
+       { "p2p_service_del", wpa_cli_cmd_p2p_service_del,
+         cli_cmd_flag_none,
+         "<bonjour|upnp> <query|version> [|service] = remove a local "
+         "service" },
+       { "p2p_reject", wpa_cli_cmd_p2p_reject,
+         cli_cmd_flag_none,
+         "<addr> = reject connection attempts from a specific peer" },
+       { "p2p_invite", wpa_cli_cmd_p2p_invite,
+         cli_cmd_flag_none,
+         "<cmd> [peer=addr] = invite peer" },
+       { "p2p_peers", wpa_cli_cmd_p2p_peers, cli_cmd_flag_none,
+         "[discovered] = list known (optionally, only fully discovered) P2P "
+         "peers" },
+       { "p2p_peer", wpa_cli_cmd_p2p_peer, cli_cmd_flag_none,
+         "<address> = show information about known P2P peer" },
+       { "p2p_set", wpa_cli_cmd_p2p_set, cli_cmd_flag_none,
+         "<field> <value> = set a P2P parameter" },
+       { "p2p_flush", wpa_cli_cmd_p2p_flush, cli_cmd_flag_none,
+         "= flush P2P state" },
+       { "p2p_cancel", wpa_cli_cmd_p2p_cancel, cli_cmd_flag_none,
+         "= cancel P2P group formation" },
+       { "p2p_unauthorize", wpa_cli_cmd_p2p_unauthorize, cli_cmd_flag_none,
+         "<address> = unauthorize a peer" },
+       { "p2p_presence_req", wpa_cli_cmd_p2p_presence_req, cli_cmd_flag_none,
+         "[<duration> <interval>] [<duration> <interval>] = request GO "
+         "presence" },
+       { "p2p_ext_listen", wpa_cli_cmd_p2p_ext_listen, cli_cmd_flag_none,
+         "[<period> <interval>] = set extended listen timing" },
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_INTERWORKING
+       { "fetch_anqp", wpa_cli_cmd_fetch_anqp, cli_cmd_flag_none,
+         "= fetch ANQP information for all APs" },
+       { "stop_fetch_anqp", wpa_cli_cmd_stop_fetch_anqp, cli_cmd_flag_none,
+         "= stop fetch_anqp operation" },
+       { "interworking_select", wpa_cli_cmd_interworking_select,
+         cli_cmd_flag_none,
+         "[auto] = perform Interworking network selection" },
+       { "interworking_connect", wpa_cli_cmd_interworking_connect,
+         cli_cmd_flag_none,
+         "<BSSID> = connect using Interworking credentials" },
+       { "anqp_get", wpa_cli_cmd_anqp_get, cli_cmd_flag_none,
+         "<addr> <info id>[,<info id>]... = request ANQP information" },
+#endif /* CONFIG_INTERWORKING */
+       { "sta_autoconnect", wpa_cli_cmd_sta_autoconnect, cli_cmd_flag_none,
+         "<0/1> = disable/enable automatic reconnection" },
+       { "tdls_discover", wpa_cli_cmd_tdls_discover,
+         cli_cmd_flag_none,
+         "<addr> = request TDLS discovery with <addr>" },
+       { "tdls_setup", wpa_cli_cmd_tdls_setup,
+         cli_cmd_flag_none,
+         "<addr> = request TDLS setup with <addr>" },
+       { "tdls_teardown", wpa_cli_cmd_tdls_teardown,
+         cli_cmd_flag_none,
+         "<addr> = tear down TDLS with <addr>" },
+       { "signal_poll", wpa_cli_cmd_signal_poll,
+         cli_cmd_flag_none,
+         "= get signal parameters" },
        { NULL, NULL, cli_cmd_flag_none, NULL }
 };
 
@@ -1694,8 +3043,7 @@ static void print_help(void)
 }
 
 
-#ifdef CONFIG_READLINE
-static int cmd_has_sensitive_data(const char *cmd)
+static int wpa_cli_edit_filter_history_cb(void *ctx, const char *cmd)
 {
        const char *c, *delim;
        int n;
@@ -1714,7 +3062,76 @@ static int cmd_has_sensitive_data(const char *cmd)
        }
        return 0;
 }
-#endif /* CONFIG_READLINE */
+
+
+static char ** wpa_list_cmd_list(void)
+{
+       char **res;
+       int i, count;
+
+       count = sizeof(wpa_cli_commands) / sizeof(wpa_cli_commands[0]);
+       res = os_zalloc(count * 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;
+       }
+
+       return res;
+}
+
+
+static char ** wpa_cli_cmd_completion(const char *cmd, const char *str,
+                                     int pos)
+{
+       int i;
+
+       if (os_strcasecmp(cmd, "bss") == 0)
+               return wpa_cli_complete_bss(str, pos);
+#ifdef CONFIG_P2P
+       if (os_strcasecmp(cmd, "p2p_connect") == 0)
+               return wpa_cli_complete_p2p_connect(str, pos);
+       if (os_strcasecmp(cmd, "p2p_peer") == 0)
+               return wpa_cli_complete_p2p_peer(str, pos);
+       if (os_strcasecmp(cmd, "p2p_group_remove") == 0)
+               return wpa_cli_complete_p2p_group_remove(str, pos);
+#endif /* CONFIG_P2P */
+
+       for (i = 0; wpa_cli_commands[i].cmd; i++) {
+               if (os_strcasecmp(wpa_cli_commands[i].cmd, cmd) == 0) {
+                       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;
+
+       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[])
@@ -1852,6 +3269,18 @@ static void wpa_cli_action_process(const char *msg)
                        wpa_cli_connected = 0;
                        wpa_cli_exec(action_file, ctrl_ifname, "DISCONNECTED");
                }
+       } else if (str_match(pos, P2P_EVENT_GROUP_STARTED)) {
+               wpa_cli_exec(action_file, ctrl_ifname, pos);
+       } else if (str_match(pos, P2P_EVENT_GROUP_REMOVED)) {
+               wpa_cli_exec(action_file, ctrl_ifname, pos);
+       } else if (str_match(pos, P2P_EVENT_CROSS_CONNECT_ENABLE)) {
+               wpa_cli_exec(action_file, ctrl_ifname, pos);
+       } else if (str_match(pos, P2P_EVENT_CROSS_CONNECT_DISABLE)) {
+               wpa_cli_exec(action_file, ctrl_ifname, pos);
+       } else if (str_match(pos, WPS_EVENT_SUCCESS)) {
+               wpa_cli_exec(action_file, ctrl_ifname, pos);
+       } else if (str_match(pos, WPS_EVENT_FAIL)) {
+               wpa_cli_exec(action_file, ctrl_ifname, pos);
        } else if (str_match(pos, WPA_EVENT_TERMINATING)) {
                printf("wpa_supplicant is terminating - stop monitoring\n");
                wpa_cli_quit = 1;
@@ -1874,10 +3303,76 @@ static void wpa_cli_reconnect(void)
 }
 
 
-static void wpa_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read,
-                                int action_monitor)
+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_add_word(&p2p_groups, s + 1);
+               return;
+       }
+
+       if (str_starts(start, P2P_EVENT_GROUP_REMOVED)) {
+               s = os_strchr(start, ' ');
+               if (s == NULL)
+                       return;
+               cli_txt_list_del_word(&p2p_groups, s + 1);
+               return;
+       }
+#endif /* CONFIG_P2P */
+}
+
+
+static void wpa_cli_recv_pending(struct wpa_ctrl *ctrl, int action_monitor)
 {
-       int first = 1;
        if (ctrl_conn == NULL) {
                wpa_cli_reconnect();
                return;
@@ -1890,14 +3385,12 @@ static void wpa_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read,
                        if (action_monitor)
                                wpa_cli_action_process(buf);
                        else {
-                               if (in_read && first)
-                                       printf("\r");
-                               first = 0;
-                               printf("%s\n", buf);
-#ifdef CONFIG_READLINE
-                               rl_on_new_line();
-                               rl_redisplay();
-#endif /* CONFIG_READLINE */
+                               cli_event(buf);
+                               if (wpa_cli_show_event(buf)) {
+                                       edit_clear_line();
+                                       printf("\r%s\n", buf);
+                                       edit_redraw();
+                               }
                        }
                } else {
                        printf("Could not read pending message.\n");
@@ -1912,214 +3405,108 @@ static void wpa_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read,
        }
 }
 
+#define max_args 10
 
-#ifdef CONFIG_READLINE
-static char * wpa_cli_cmd_gen(const char *text, int state)
+static int tokenize_cmd(char *cmd, char *argv[])
 {
-       static int i, len;
-       const char *cmd;
-
-       if (state == 0) {
-               i = 0;
-               len = os_strlen(text);
-       }
+       char *pos;
+       int argc = 0;
 
-       while ((cmd = wpa_cli_commands[i].cmd)) {
-               i++;
-               if (os_strncasecmp(cmd, text, len) == 0)
-                       return strdup(cmd);
+       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 NULL;
+       return argc;
 }
 
 
-static char * wpa_cli_dummy_gen(const char *text, int state)
+static void wpa_cli_ping(void *eloop_ctx, void *timeout_ctx)
 {
-       int i;
-
-       for (i = 0; wpa_cli_commands[i].cmd; i++) {
-               const char *cmd = wpa_cli_commands[i].cmd;
-               size_t len = os_strlen(cmd);
-               if (os_strncasecmp(rl_line_buffer, cmd, len) == 0 &&
-                   rl_line_buffer[len] == ' ') {
-                       printf("\n%s\n", wpa_cli_commands[i].usage);
-                       rl_on_new_line();
-                       rl_redisplay();
-                       break;
-               }
+       if (ctrl_conn && _wpa_ctrl_command(ctrl_conn, "PING", 0)) {
+               printf("Connection to wpa_supplicant lost - trying to "
+                      "reconnect\n");
+               wpa_cli_close_connection();
        }
-
-       rl_attempted_completion_over = 1;
-       return NULL;
+       if (!ctrl_conn)
+               wpa_cli_reconnect();
+       eloop_register_timeout(ping_interval, 0, wpa_cli_ping, NULL, NULL);
 }
 
 
-static char * wpa_cli_status_gen(const char *text, int state)
+static void wpa_cli_eloop_terminate(int sig, void *signal_ctx)
 {
-       static int i, len;
-       char *options[] = {
-               "verbose", NULL
-       };
-       char *t;
-
-       if (state == 0) {
-               i = 0;
-               len = os_strlen(text);
-       }
+       eloop_terminate();
+}
 
-       while ((t = options[i])) {
-               i++;
-               if (os_strncasecmp(t, text, len) == 0)
-                       return strdup(t);
-       }
 
-       rl_attempted_completion_over = 1;
-       return NULL;
+static void wpa_cli_mon_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+       wpa_cli_recv_pending(mon_conn, 0);
 }
 
 
-static char ** wpa_cli_completion(const char *text, int start, int end)
+static void wpa_cli_edit_cmd_cb(void *ctx, char *cmd)
 {
-       char * (*func)(const char *text, int state);
+       char *argv[max_args];
+       int argc;
+       argc = tokenize_cmd(cmd, argv);
+       if (argc)
+               wpa_request(ctrl_conn, argc, argv);
+}
 
-       if (start == 0)
-               func = wpa_cli_cmd_gen;
-       else if (os_strncasecmp(rl_line_buffer, "status ", 7) == 0)
-               func = wpa_cli_status_gen;
-       else
-               func = wpa_cli_dummy_gen;
-       return rl_completion_matches(text, func);
+
+static void wpa_cli_edit_eof_cb(void *ctx)
+{
+       eloop_terminate();
 }
-#endif /* CONFIG_READLINE */
 
 
 static void wpa_cli_interactive(void)
 {
-#define max_args 10
-       char cmdbuf[256], *cmd, *argv[max_args], *pos;
-       int argc;
-#ifdef CONFIG_READLINE
        char *home, *hfile = NULL;
-#endif /* CONFIG_READLINE */
 
        printf("\nInteractive mode\n\n");
 
-#ifdef CONFIG_READLINE
-       rl_attempted_completion_function = wpa_cli_completion;
        home = getenv("HOME");
        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) {
-                       int res;
-                       res = os_snprintf(hfile, hfile_len, "%s/%s", home,
-                                         fname);
-                       if (res >= 0 && res < hfile_len) {
-                               hfile[hfile_len - 1] = '\0';
-                               read_history(hfile);
-                               stifle_history(100);
-                       }
-               }
+               if (hfile)
+                       os_snprintf(hfile, hfile_len, "%s/%s", home, fname);
        }
-#endif /* CONFIG_READLINE */
 
-       do {
-               wpa_cli_recv_pending(mon_conn, 0, 0);
-#ifndef CONFIG_NATIVE_WINDOWS
-               alarm(ping_interval);
-#endif /* CONFIG_NATIVE_WINDOWS */
-#ifdef CONFIG_WPA_CLI_FORK
-               if (mon_pid)
-                       kill(mon_pid, SIGUSR1);
-#endif /* CONFIG_WPA_CLI_FORK */
-#ifdef CONFIG_READLINE
-               cmd = readline("> ");
-               if (cmd && *cmd) {
-                       HIST_ENTRY *h;
-                       while (next_history())
-                               ;
-                       h = previous_history();
-                       if (h == NULL || os_strcmp(cmd, h->line) != 0)
-                               add_history(cmd);
-                       next_history();
-               }
-#else /* CONFIG_READLINE */
-               printf("> ");
-               cmd = fgets(cmdbuf, sizeof(cmdbuf), stdin);
-#endif /* CONFIG_READLINE */
-#ifndef CONFIG_NATIVE_WINDOWS
-               alarm(0);
-#endif /* CONFIG_NATIVE_WINDOWS */
-               if (cmd == NULL)
-                       break;
-               wpa_cli_recv_pending(mon_conn, 0, 0);
-               pos = cmd;
-               while (*pos != '\0') {
-                       if (*pos == '\n') {
-                               *pos = '\0';
-                               break;
-                       }
-                       pos++;
-               }
-               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';
-               }
-               if (argc)
-                       wpa_request(ctrl_conn, argc, argv);
-
-               if (cmd != cmdbuf)
-                       free(cmd);
-#ifdef CONFIG_WPA_CLI_FORK
-               if (mon_pid)
-                       kill(mon_pid, SIGUSR2);
-#endif /* CONFIG_WPA_CLI_FORK */
-       } while (!wpa_cli_quit);
-
-#ifdef CONFIG_READLINE
-       if (hfile) {
-               /* Save command history, excluding lines that may contain
-                * passwords. */
-               HIST_ENTRY *h;
-               history_set_pos(0);
-               while ((h = current_history())) {
-                       char *p = h->line;
-                       while (*p == ' ' || *p == '\t')
-                               p++;
-                       if (cmd_has_sensitive_data(p)) {
-                               h = remove_history(where_history());
-                               if (h) {
-                                       os_free(h->line);
-                                       os_free(h->data);
-                                       os_free(h);
-                               } else
-                                       next_history();
-                       } else
-                               next_history();
-               }
-               write_history(hfile);
-               os_free(hfile);
-       }
-#endif /* CONFIG_READLINE */
+       eloop_register_signal_terminate(wpa_cli_eloop_terminate, NULL);
+       edit_init(wpa_cli_edit_cmd_cb, wpa_cli_edit_eof_cb,
+                 wpa_cli_edit_completion_cb, NULL, hfile);
+       eloop_register_timeout(ping_interval, 0, wpa_cli_ping, NULL, NULL);
+
+       eloop_run();
+
+       cli_txt_list_flush(&p2p_peers);
+       cli_txt_list_flush(&p2p_groups);
+       cli_txt_list_flush(&bsses);
+       edit_deinit(hfile, wpa_cli_edit_filter_history_cb);
+       os_free(hfile);
+       eloop_cancel_timeout(wpa_cli_ping, NULL, NULL);
+       wpa_cli_close_connection();
 }
 
 
@@ -2149,7 +3536,7 @@ static void wpa_cli_action(struct wpa_ctrl *ctrl)
                }
 
                if (FD_ISSET(fd, &rfds))
-                       wpa_cli_recv_pending(ctrl, 0, 1);
+                       wpa_cli_recv_pending(ctrl, 1);
                else {
                        /* verify that connection is still working */
                        len = sizeof(buf) - 1;
@@ -2182,34 +3569,6 @@ static void wpa_cli_terminate(int sig)
 }
 
 
-#ifdef CONFIG_WPA_CLI_FORK
-static void wpa_cli_usr1(int sig)
-{
-#ifdef CONFIG_READLINE
-       rl_on_new_line();
-       rl_redisplay();
-#endif /* CONFIG_READLINE */
-}
-#endif /* CONFIG_WPA_CLI_FORK */
-
-
-#ifndef CONFIG_NATIVE_WINDOWS
-static void wpa_cli_alarm(int sig)
-{
-       if (ctrl_conn && _wpa_ctrl_command(ctrl_conn, "PING", 0)) {
-               printf("Connection to wpa_supplicant lost - trying to "
-                      "reconnect\n");
-               wpa_cli_close_connection();
-       }
-       if (!ctrl_conn)
-               wpa_cli_reconnect();
-       if (mon_conn)
-               wpa_cli_recv_pending(mon_conn, 1, 0);
-       alarm(ping_interval);
-}
-#endif /* CONFIG_NATIVE_WINDOWS */
-
-
 static char * wpa_cli_get_default_ifname(void)
 {
        char *ifname = NULL;
@@ -2217,8 +3576,17 @@ static char * wpa_cli_get_default_ifname(void)
 #ifdef CONFIG_CTRL_IFACE_UNIX
        struct dirent *dent;
        DIR *dir = opendir(ctrl_iface_dir);
-       if (!dir)
+       if (!dir) {
+#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 */
                return NULL;
+       }
        while ((dent = readdir(dir))) {
 #ifdef _DIRENT_HAVE_D_TYPE
                /*
@@ -2320,6 +3688,9 @@ int main(int argc, char *argv[])
        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);
@@ -2337,12 +3708,6 @@ int main(int argc, char *argv[])
        signal(SIGINT, wpa_cli_terminate);
        signal(SIGTERM, wpa_cli_terminate);
 #endif /* _WIN32_WCE */
-#ifndef CONFIG_NATIVE_WINDOWS
-       signal(SIGALRM, wpa_cli_alarm);
-#endif /* CONFIG_NATIVE_WINDOWS */
-#ifdef CONFIG_WPA_CLI_FORK
-       signal(SIGUSR1, wpa_cli_usr1);
-#endif /* CONFIG_WPA_CLI_FORK */
 
        if (ctrl_ifname == NULL)
                ctrl_ifname = wpa_cli_get_default_ifname();
@@ -2393,6 +3758,7 @@ int main(int argc, char *argv[])
                ret = wpa_request(ctrl_conn, argc - optind, &argv[optind]);
 
        os_free(ctrl_ifname);
+       eloop_destroy();
        wpa_cli_cleanup();
 
        return ret;
index 316ee89..dd72c7e 100644 (file)
@@ -3,5 +3,7 @@
   <file alias="wpa_gui.svg">icons/wpa_gui.svg</file>
   <file alias="ap.svg">icons/ap.svg</file>
   <file alias="laptop.svg">icons/laptop.svg</file>
+  <file alias="group.svg">icons/group.svg</file>
+  <file alias="invitation.svg">icons/invitation.svg</file>
  </qresource>
 </RCC>
diff --git a/wpa_supplicant/wpa_gui-qt4/icons/Makefile b/wpa_supplicant/wpa_gui-qt4/icons/Makefile
new file mode 100644 (file)
index 0000000..709514c
--- /dev/null
@@ -0,0 +1,23 @@
+#!/usr/bin/make -f
+
+NAMES := wpa_gui ap laptop group invitation
+SIZES := 16x16 22x22 32x32 48x48 64x64 128x128
+ICONS := $(addsuffix .png, $(foreach name, $(NAMES), $(foreach size, $(SIZES), $(size)/$(name))))
+ICONS += $(addsuffix .xpm, $(NAMES))
+
+all: $(ICONS)
+
+%.png:
+       mkdir -p hicolor/$(word 1, $(subst /, ,$(@)))/apps/
+       inkscape $(subst .png,.svg, $(word 2, $(subst /, , $(@)))) --without-gui \
+               --export-width=$(word 1, $(subst x, , $(@)))  \
+               --export-height=$(word 2, $(subst x, , $(subst /, , $(@)))) \
+               --export-png=hicolor/$(word 1, $(subst /, ,$(@)))/apps/$(word 2, $(subst /, , $@))
+
+%.xpm:
+       mkdir -p pixmaps/
+       convert hicolor/16x16/apps/$(@:.xpm=.png) pixmaps/$(@:.xpm=-16.xpm)
+       convert hicolor/32x32/apps/$(@:.xpm=.png) pixmaps/$@
+
+clean:
+       $(RM) -r pixmaps hicolor
index d73eed5..3953238 100644 (file)
@@ -37,3 +37,38 @@ by:      metalmarious
 last change:    May 18, 2008 07:04 pm (File added)
 date:   August 27, 2007 04:44 am
 license: PD
+
+
+group.svg
+---------
+
+http://www.openclipart.org/detail/25428
+http://www.openclipart.org/people/Anonymous/Anonymous_Network.svg
+Uploader:
+    Anonymous
+Drawn by:
+    Andrew Fitzsimon / Anonymous
+Created:
+    2009-04-29 04:07:37
+Description:
+    A network icon by Andrew Fitzsimon. Etiquette Icon set.
+    From 0.18 OCAL database.
+
+Public Domain
+
+
+
+invitation.svg
+--------------
+
+http://www.openclipart.org/detail/974
+http://www.openclipart.org/people/jean_victor_balin/jean_victor_balin_unknown_green.svg
+Uploader:
+    jean_victor_balin
+Drawn by:
+    jean_victor_balin
+Created:
+    2006-10-27 02:12:13
+Description:
+
+Public Domain
diff --git a/wpa_supplicant/wpa_gui-qt4/icons/group.svg b/wpa_supplicant/wpa_gui-qt4/icons/group.svg
new file mode 100644 (file)
index 0000000..4ea959b
--- /dev/null
@@ -0,0 +1,616 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   height="160.00000"
+   id="Andysvg"
+   inkscape:version="0.46"
+   sodipodi:docbase="/home/andy/Desktop/etiquette-icons-0.4/scalable/filesystems"
+   sodipodi:docname="gnome-fs-network.svg"
+   sodipodi:version="0.32"
+   version="1.0"
+   width="160.00000"
+   x="0.00000000"
+   y="0.00000000"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   inkscape:export-filename="C:\Documents and Settings\All Users\Documents\Ubuntu Brig\Andy Fitzsimon\gnome-fs-network.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <metadata
+     id="metadata3">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:title>Etiquette Icons</dc:title>
+        <dc:description />
+        <dc:subject>
+          <rdf:Bag>
+            <rdf:li>hash</rdf:li>
+            <rdf:li />
+            <rdf:li>filesystem</rdf:li>
+            <rdf:li>computer</rdf:li>
+            <rdf:li>icons</rdf:li>
+          </rdf:Bag>
+        </dc:subject>
+        <dc:publisher>
+          <cc:Agent
+             rdf:about="http://www.openclipart.org">
+            <dc:title>Andy Fitzsimon</dc:title>
+          </cc:Agent>
+        </dc:publisher>
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Andy Fitzsimon</dc:title>
+          </cc:Agent>
+        </dc:creator>
+        <dc:rights>
+          <cc:Agent>
+            <dc:title>Andy Fitzsimon</dc:title>
+          </cc:Agent>
+        </dc:rights>
+        <dc:date />
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <cc:license
+           rdf:resource="http://web.resource.org/cc/PublicDomain" />
+        <dc:language>en</dc:language>
+      </cc:Work>
+      <cc:License
+         rdf:about="http://web.resource.org/cc/PublicDomain">
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Reproduction" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Distribution" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+      </cc:License>
+    </rdf:RDF>
+  </metadata>
+  <defs
+     id="defs3">
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 80 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="160 : 80 : 1"
+       inkscape:persp3d-origin="80 : 53.333333 : 1"
+       id="perspective97" />
+    <linearGradient
+       id="linearGradient4894">
+      <stop
+         id="stop4895"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop4896"
+         offset="0.47000000"
+         style="stop-color:#ffffff;stop-opacity:0.85567009;" />
+      <stop
+         id="stop4897"
+         offset="1.0000000"
+         style="stop-color:#ffffff;stop-opacity:0.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient1853">
+      <stop
+         id="stop1854"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop1855"
+         offset="0.47000000"
+         style="stop-color:#ffffff;stop-opacity:0.85567009;" />
+      <stop
+         id="stop1856"
+         offset="1.0000000"
+         style="stop-color:#ffffff;stop-opacity:0.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient1806">
+      <stop
+         id="stop1807"
+         offset="0.0000000"
+         style="stop-color:#000000;stop-opacity:0.35051546;" />
+      <stop
+         id="stop3276"
+         offset="0.64999998"
+         style="stop-color:#000000;stop-opacity:0.13402061;" />
+      <stop
+         id="stop1808"
+         offset="1.0000000"
+         style="stop-color:#000000;stop-opacity:0.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient893">
+      <stop
+         id="stop895"
+         offset="0"
+         style="stop-color:#000;stop-opacity:1;" />
+      <stop
+         id="stop896"
+         offset="1"
+         style="stop-color:#fff;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient1317">
+      <stop
+         id="stop1318"
+         offset="0.00000000"
+         style="stop-color:#000000;stop-opacity:0.52892560;" />
+      <stop
+         id="stop1320"
+         offset="0.50000000"
+         style="stop-color:#000000;stop-opacity:0.17355372;" />
+      <stop
+         id="stop1319"
+         offset="1.0000000"
+         style="stop-color:#000000;stop-opacity:0.00000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient1133">
+      <stop
+         id="stop1134"
+         offset="0.00000000"
+         style="stop-color:#8bb7df;stop-opacity:1.0000000;" />
+      <stop
+         id="stop1136"
+         offset="0.76209301"
+         style="stop-color:#2a6092;stop-opacity:1.0000000;" />
+      <stop
+         id="stop1135"
+         offset="1.0000000"
+         style="stop-color:#375e82;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient1098">
+      <stop
+         id="stop1099"
+         offset="0.00000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop1101"
+         offset="0.50000000"
+         style="stop-color:#ffffff;stop-opacity:0.22314049;" />
+      <stop
+         id="stop1102"
+         offset="0.59930235"
+         style="stop-color:#ffffff;stop-opacity:0.00000000;" />
+      <stop
+         id="stop1100"
+         offset="1.0000000"
+         style="stop-color:#ffffff;stop-opacity:0.60330576;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient902">
+      <stop
+         id="stop903"
+         offset="0.00000000"
+         style="stop-color:#000000;stop-opacity:0.00000000;" />
+      <stop
+         id="stop904"
+         offset="1.0000000"
+         style="stop-color:#000000;stop-opacity:0.22000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient892">
+      <stop
+         id="stop893"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:0.0000000;" />
+      <stop
+         id="stop894"
+         offset="1"
+         style="stop-color:#fff;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient888">
+      <stop
+         id="stop889"
+         offset="0.0000000"
+         style="stop-color:#626262;stop-opacity:1.0000000;" />
+      <stop
+         id="stop890"
+         offset="1"
+         style="stop-color:#fff;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient891"
+       x1="1.3485916"
+       x2="0.024647888"
+       xlink:href="#linearGradient888"
+       y1="-0.85185188"
+       y2="1.0899471" />
+    <linearGradient
+       id="linearGradient901"
+       spreadMethod="pad"
+       x1="1.5803921"
+       x2="0.14117648"
+       xlink:href="#linearGradient888"
+       y1="2.4285715"
+       y2="-0.38571429" />
+    <linearGradient
+       id="linearGradient905"
+       x1="-1.5389611"
+       x2="1.0909091"
+       xlink:href="#linearGradient888"
+       y1="2.7890625"
+       y2="-0.19531250" />
+    <radialGradient
+       cx="0.10362694"
+       cy="0.093750000"
+       fx="0.10362694"
+       fy="0.093750000"
+       id="radialGradient1132"
+       r="1.2958785"
+       xlink:href="#linearGradient1133" />
+    <linearGradient
+       id="linearGradient1138"
+       xlink:href="#linearGradient4894" />
+    <linearGradient
+       id="linearGradient1140"
+       x1="0.54117650"
+       x2="0.57647061"
+       xlink:href="#linearGradient888"
+       y1="-2.4210527"
+       y2="4.6315789" />
+    <linearGradient
+       id="linearGradient1141"
+       x1="1.8281938"
+       x2="-0.0088105723"
+       xlink:href="#linearGradient888"
+       y1="3.0546875"
+       y2="-0.44531250" />
+    <linearGradient
+       id="linearGradient1144"
+       x1="0.21960784"
+       x2="0.59607846"
+       xlink:href="#linearGradient1853"
+       y1="-11.111111"
+       y2="5.2777777" />
+    <linearGradient
+       id="linearGradient1146"
+       x1="0.51351351"
+       x2="-0.076576576"
+       xlink:href="#linearGradient892"
+       y1="0.55468750"
+       y2="1.1875000" />
+    <linearGradient
+       id="linearGradient1148"
+       x1="0.23245615"
+       x2="1.0789474"
+       xlink:href="#linearGradient892"
+       y1="0.15625000"
+       y2="-0.64843750" />
+    <linearGradient
+       id="linearGradient1150"
+       x1="0.25221238"
+       x2="-0.57522124"
+       xlink:href="#linearGradient892"
+       y1="0.57812500"
+       y2="1.4765625" />
+    <linearGradient
+       id="linearGradient1156"
+       x1="0.48260871"
+       x2="0.48260871"
+       xlink:href="#linearGradient888"
+       y1="-0.40000001"
+       y2="1.8750000" />
+    <linearGradient
+       id="linearGradient1157"
+       x1="1.5528169"
+       x2="-1.2077465"
+       xlink:href="#linearGradient888"
+       y1="3.3265307"
+       y2="-0.48979592" />
+    <linearGradient
+       id="linearGradient1166"
+       x1="0.52941179"
+       x2="0.57647061"
+       xlink:href="#linearGradient1317"
+       y1="-3.5714285"
+       y2="4.6315789" />
+    <linearGradient
+       id="linearGradient1167"
+       x1="1.6111112"
+       x2="-0.083333336"
+       xlink:href="#linearGradient888"
+       y1="3.0703125"
+       y2="0.046875000" />
+    <linearGradient
+       id="linearGradient1169"
+       x1="1.4780220"
+       x2="-0.13028169"
+       xlink:href="#linearGradient893"
+       y1="2.9218750"
+       y2="-0.26732674" />
+    <linearGradient
+       gradientTransform="scale(0.998371,1.001632)"
+       id="linearGradient1170"
+       x1="0.47284532"
+       x2="0.48655096"
+       xlink:href="#linearGradient902"
+       y1="-0.016295359"
+       y2="1.8378206" />
+    <linearGradient
+       id="linearGradient1171"
+       x1="0.83050847"
+       x2="0.56355929"
+       xlink:href="#linearGradient902"
+       y1="0.57812500"
+       y2="0.36718750" />
+    <radialGradient
+       cx="0.088082902"
+       cy="0.093750000"
+       fx="0.090673581"
+       fy="0.10937500"
+       id="radialGradient1315"
+       r="1.1765809"
+       xlink:href="#linearGradient1133" />
+    <radialGradient
+       cx="0.50000000"
+       cy="0.50000006"
+       fx="0.50352114"
+       fy="0.18269235"
+       id="radialGradient1316"
+       r="0.34964636"
+       xlink:href="#linearGradient1317" />
+    <linearGradient
+       id="linearGradient1404"
+       x1="0.53169012"
+       x2="0.54577464"
+       xlink:href="#linearGradient892"
+       y1="0.28888890"
+       y2="1.1000000" />
+    <linearGradient
+       gradientTransform="scale(0.997825,1.002180)"
+       id="linearGradient1505"
+       x1="0.47157744"
+       x2="0.48548824"
+       xlink:href="#linearGradient902"
+       y1="-0.024853170"
+       y2="1.8570156" />
+    <linearGradient
+       gradientTransform="scale(0.995847,1.004170)"
+       id="linearGradient1506"
+       x1="0.47042510"
+       x2="0.48481107"
+       xlink:href="#linearGradient902"
+       y1="-0.043652620"
+       y2="1.9025002" />
+    <linearGradient
+       gradientTransform="scale(0.997153,1.002855)"
+       id="linearGradient2740"
+       x1="0.47041038"
+       x2="0.48453596"
+       xlink:href="#linearGradient902"
+       y1="-0.033741195"
+       y2="1.8771822" />
+    <linearGradient
+       id="linearGradient4283"
+       x1="-0.77314812"
+       x2="0.99074072"
+       xlink:href="#linearGradient893"
+       y1="2.0837989"
+       y2="-0.033519555" />
+    <linearGradient
+       id="linearGradient4284"
+       x1="-2.3960868e-17"
+       x2="0.92957747"
+       xlink:href="#linearGradient893"
+       y1="3.3012049"
+       y2="-0.45783132" />
+    <radialGradient
+       cx="0.50000000"
+       cy="0.50000000"
+       fx="0.50000000"
+       fy="0.50000000"
+       id="radialGradient1977"
+       r="0.50000000"
+       xlink:href="#linearGradient1853" />
+  </defs>
+  <sodipodi:namedview
+     bordercolor="#666666"
+     borderopacity="1.0"
+     id="base"
+     inkscape:cx="62.122256"
+     inkscape:cy="81.091465"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:window-height="667"
+     inkscape:window-width="573"
+     inkscape:window-x="380"
+     inkscape:window-y="151"
+     inkscape:zoom="2"
+     pagecolor="#ffffff"
+     showborder="true"
+     showgrid="false"
+     inkscape:current-layer="Andysvg" />
+  <path
+     d="M 26.564473,83.749649 L 26.564473,121.41271 L 57.756286,121.41271"
+     id="path3723"
+     sodipodi:nodetypes="ccc"
+     style="fill:none;fill-rule:evenodd;stroke:#9c9c9c;stroke-width:5.7184987;stroke-linecap:round;stroke-linejoin:round;" />
+  <g
+     id="g2843"
+     transform="matrix(0.999379,0.000000,0.000000,0.999379,1.227893e-3,3.986513)">
+    <rect
+       height="8.3153667"
+       id="rect1906"
+       style="fill:url(#linearGradient1156);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient1157);stroke-width:1.4473482pt;"
+       transform="matrix(0.716224,0.000000,0.000000,0.716224,-12.57051,-9.652832)"
+       width="57.567924"
+       x="33.326111"
+       y="78.658051" />
+    <rect
+       height="60.126495"
+       id="rect1907"
+       rx="5.4369707"
+       ry="5.4369707"
+       style="fill:url(#linearGradient905);fill-opacity:1;fill-rule:evenodd;stroke-width:1.6282668;"
+       transform="matrix(0.716224,0.000000,0.000000,0.716224,-12.57051,-9.652832)"
+       width="72.279724"
+       x="26.015469"
+       y="22.413721" />
+    <rect
+       height="38.044163"
+       id="rect1908"
+       style="fill:url(#radialGradient1315);fill-rule:evenodd;stroke:url(#linearGradient891);stroke-width:1.4649456pt;"
+       transform="matrix(0.716224,0.000000,0.000000,0.716224,-12.57051,-9.652832)"
+       width="58.178177"
+       x="33.386066"
+       y="31.695871" />
+    <path
+       d="M 27.690431,52.841444 L 27.370609,74.749236 C 27.319624,78.241665 29.310209,80.477938 32.807578,80.506029 L 72.625393,80.825852 L 76.463254,71.870840 L 32.008024,71.551020 L 31.688202,52.681533 L 27.690431,52.841444 z "
+       id="path1909"
+       sodipodi:nodetypes="czzccccc"
+       style="fill:url(#linearGradient1146);fill-opacity:1;fill-rule:evenodd;stroke-width:1.0000000pt;"
+       transform="matrix(0.716224,0.000000,0.000000,0.716224,-12.57051,-9.652832)" />
+    <rect
+       height="26.147448"
+       id="rect1913"
+       rx="7.4449978"
+       ry="7.4449978"
+       style="fill:url(#linearGradient901);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient1157);stroke-width:2.3625000;"
+       transform="matrix(0.571582,0.000000,0.000000,0.571582,-77.72566,72.35541)"
+       width="104.09673"
+       x="140.62315"
+       y="-34.316952" />
+    <rect
+       height="15.829688"
+       id="rect1914"
+       rx="3.7576280"
+       ry="3.7576280"
+       style="fill:url(#linearGradient901);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient1157);stroke-width:1.3591428;"
+       transform="matrix(0.571582,0.000000,0.000000,0.571582,-77.72566,72.35541)"
+       width="56.908955"
+       x="184.04552"
+       y="-28.539845" />
+    <rect
+       height="15.829688"
+       id="rect1915"
+       rx="2.9970589"
+       ry="2.9970589"
+       style="fill:url(#linearGradient1141);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient1157);stroke-width:0.96249998;"
+       transform="matrix(0.571582,0.000000,0.000000,0.571582,-77.72566,72.35541)"
+       width="28.796961"
+       x="145.28902"
+       y="-28.227346" />
+    <rect
+       height="3.3627598"
+       id="rect1916"
+       rx="1.6813799"
+       ry="1.6813799"
+       style="fill-opacity:0.13836475;fill-rule:evenodd;stroke-width:0.46326005;"
+       transform="matrix(0.571582,0.000000,0.000000,0.571582,-77.72566,72.35541)"
+       width="49.231453"
+       x="187.88426"
+       y="-21.681381" />
+  </g>
+  <path
+     style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:helvetica"
+     d="M 7.0612345,-14.660837 L 7.0612345,-23.250681 L 8.2272501,-23.250681 L 12.738969,-16.50654 L 12.738969,-23.250681 L 13.828813,-23.250681 L 13.828813,-14.660837 L 12.662797,-14.660837 L 8.1510782,-21.410837 L 8.1510782,-14.660837 L 7.0612345,-14.660837 z M 19.869828,-16.664743 L 20.959672,-16.529978 C 20.787791,-15.893258 20.469432,-15.399118 20.004594,-15.047556 C 19.539745,-14.695993 18.945996,-14.520212 18.223344,-14.520212 C 17.313185,-14.520212 16.591506,-14.800485 16.058305,-15.361032 C 15.525101,-15.921578 15.2585,-16.70771 15.2585,-17.719431 C 15.2585,-18.766302 15.528031,-19.578801 16.067094,-20.156931 C 16.606155,-20.73505 17.305373,-21.024112 18.16475,-21.024118 C 18.996777,-21.024112 19.676464,-20.740909 20.203813,-20.174509 C 20.73115,-19.608098 20.994822,-18.811224 20.994828,-17.783884 C 20.994822,-17.721381 20.992869,-17.627631 20.988969,-17.502634 L 16.348344,-17.502634 C 16.387405,-16.819038 16.580764,-16.295601 16.928422,-15.932322 C 17.276076,-15.569039 17.709669,-15.387399 18.229203,-15.3874 C 18.615918,-15.387399 18.945996,-15.488961 19.219438,-15.692087 C 19.49287,-15.895211 19.709667,-16.219429 19.869828,-16.664743 L 19.869828,-16.664743 z M 16.406938,-18.369822 L 19.881547,-18.369822 C 19.834667,-18.893255 19.701855,-19.285833 19.483109,-19.547556 C 19.147168,-19.953801 18.711621,-20.156925 18.176469,-20.156931 C 17.692091,-20.156925 17.284865,-19.994816 16.954789,-19.670603 C 16.624709,-19.346379 16.442092,-18.912786 16.406938,-18.369822 L 16.406938,-18.369822 z M 24.592484,-15.604197 L 24.744828,-14.672556 C 24.44795,-14.610056 24.182326,-14.578806 23.947953,-14.578806 C 23.565139,-14.578806 23.268264,-14.639353 23.057328,-14.760447 C 22.846389,-14.88154 22.697952,-15.04072 22.612016,-15.237986 C 22.526077,-15.43525 22.483108,-15.850289 22.483109,-16.483103 L 22.483109,-20.063181 L 21.709672,-20.063181 L 21.709672,-20.883493 L 22.483109,-20.883493 L 22.483109,-22.424509 L 23.531938,-23.057322 L 23.531938,-20.883493 L 24.592484,-20.883493 L 24.592484,-20.063181 L 23.531938,-20.063181 L 23.531938,-16.424509 C 23.531936,-16.123726 23.55049,-15.930367 23.587602,-15.844431 C 23.624709,-15.758492 23.685256,-15.690133 23.769242,-15.639353 C 23.853224,-15.588571 23.973341,-15.56318 24.129594,-15.563181 C 24.246779,-15.56318 24.401075,-15.576852 24.592484,-15.604197 L 24.592484,-15.604197 z M 26.766313,-14.660837 L 24.862016,-20.883493 L 25.951859,-20.883493 L 26.942094,-17.291697 L 27.311234,-15.955759 C 27.326857,-16.022164 27.434279,-16.449898 27.6335,-17.238962 L 28.623734,-20.883493 L 29.707719,-20.883493 L 30.639359,-17.274118 L 30.949906,-16.084665 L 31.307328,-17.285837 L 32.373734,-20.883493 L 33.399125,-20.883493 L 31.453813,-14.660837 L 30.358109,-14.660837 L 29.367875,-18.3874 L 29.127641,-19.447947 L 27.867875,-14.660837 L 26.766313,-14.660837 z M 33.897172,-17.772165 C 33.897172,-18.924505 34.217484,-19.77802 34.858109,-20.332712 C 35.393264,-20.793644 36.045607,-21.024112 36.815141,-21.024118 C 37.670605,-21.024112 38.369823,-20.743839 38.912797,-20.183298 C 39.45576,-19.622746 39.727244,-18.848333 39.72725,-17.860056 C 39.727244,-17.059272 39.607127,-16.42939 39.366899,-15.970407 C 39.126659,-15.511422 38.77705,-15.154977 38.31807,-14.901072 C 37.859082,-14.647165 37.358106,-14.520212 36.815141,-14.520212 C 35.944045,-14.520212 35.239944,-14.799509 34.702836,-15.358103 C 34.165726,-15.916695 33.897172,-16.721382 33.897172,-17.772165 L 33.897172,-17.772165 z M 34.981156,-17.772165 C 34.981155,-16.975288 35.154983,-16.378609 35.502641,-15.982126 C 35.850295,-15.585641 36.287794,-15.387399 36.815141,-15.3874 C 37.338574,-15.387399 37.774121,-15.586617 38.121781,-15.985056 C 38.469433,-16.383492 38.643261,-16.990913 38.643266,-17.807322 C 38.643261,-18.576849 38.468456,-19.159856 38.118852,-19.556345 C 37.769238,-19.952824 37.334668,-20.151066 36.815141,-20.151072 C 36.287794,-20.151066 35.850295,-19.953801 35.502641,-19.559275 C 35.154983,-19.164739 34.981155,-18.569036 34.981156,-17.772165 L 34.981156,-17.772165 z M 40.957719,-14.660837 L 40.957719,-20.883493 L 41.906938,-20.883493 L 41.906938,-19.940134 C 42.149123,-20.381535 42.372756,-20.67255 42.577836,-20.813181 C 42.782912,-20.9538 43.008497,-21.024112 43.254594,-21.024118 C 43.610059,-21.024112 43.971387,-20.910831 44.338578,-20.684275 L 43.975297,-19.705759 C 43.717481,-19.858098 43.459669,-19.934269 43.201859,-19.934275 C 42.971388,-19.934269 42.764357,-19.864934 42.580766,-19.726267 C 42.39717,-19.58759 42.266311,-19.395207 42.188188,-19.149118 C 42.070998,-18.774114 42.012405,-18.363958 42.012406,-17.91865 L 42.012406,-14.660837 L 40.957719,-14.660837 z M 44.983109,-14.660837 L 44.983109,-23.250681 L 46.037797,-23.250681 L 46.037797,-18.352243 L 48.533891,-20.883493 L 49.899125,-20.883493 L 47.520219,-18.5749 L 50.139359,-14.660837 L 48.838578,-14.660837 L 46.781938,-17.842478 L 46.037797,-17.127634 L 46.037797,-14.660837 L 44.983109,-14.660837 z"
+     id="text1232" />
+  <path
+     transform="scale(0.246729,0.246729)"
+     style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:helvetica"
+     d="M 91.619637,-37.962852 L 92.756355,-37.675743 C 92.518066,-36.742148 92.089356,-36.030234 91.470222,-35.540001 C 90.851076,-35.049766 90.09424,-34.804649 89.199715,-34.804649 C 88.27393,-34.804649 87.521001,-34.993126 86.940926,-35.370079 C 86.360846,-35.747031 85.91944,-36.292929 85.616707,-37.007774 C 85.313972,-37.722615 85.162605,-38.490193 85.162605,-39.310509 C 85.162605,-40.205035 85.333503,-40.985307 85.675301,-41.651329 C 86.017096,-42.317337 86.503424,-42.823196 87.134285,-43.168907 C 87.765141,-43.514602 88.459476,-43.687453 89.217293,-43.687462 C 90.076662,-43.687453 90.799318,-43.468703 91.385262,-43.031212 C 91.971192,-42.593704 92.379394,-41.97847 92.609871,-41.185509 L 91.49073,-40.921837 C 91.291505,-41.54683 91.002443,-42.001908 90.623543,-42.287071 C 90.244631,-42.57222 89.768069,-42.714798 89.193855,-42.714806 C 88.533695,-42.714798 87.981938,-42.556595 87.538582,-42.240196 C 87.09522,-41.923783 86.783697,-41.498979 86.604012,-40.965782 C 86.424322,-40.432574 86.334479,-39.882769 86.33448,-39.316368 C 86.334479,-38.585896 86.440924,-37.948201 86.653816,-37.403282 C 86.866705,-36.858358 87.197759,-36.451132 87.64698,-36.181602 C 88.096196,-35.91207 88.582523,-35.777305 89.105965,-35.777306 C 89.742678,-35.777305 90.28174,-35.960898 90.723152,-36.328087 C 91.164552,-36.695273 91.46338,-37.240194 91.619637,-37.962852 L 91.619637,-37.962852 z M 94.016121,-34.951134 L 94.016121,-41.17379 L 94.96534,-41.17379 L 94.96534,-40.230431 C 95.207525,-40.671831 95.431158,-40.962846 95.636238,-41.103477 C 95.841314,-41.244096 96.066899,-41.314409 96.312996,-41.314415 C 96.668461,-41.314409 97.029789,-41.201127 97.39698,-40.974571 L 97.033699,-39.996056 C 96.775883,-40.148394 96.518071,-40.224566 96.260262,-40.224571 C 96.02979,-40.224566 95.822759,-40.15523 95.639168,-40.016563 C 95.455572,-39.877887 95.324713,-39.685504 95.24659,-39.439415 C 95.1294,-39.064411 95.070807,-38.654255 95.070808,-38.208946 L 95.070808,-34.951134 L 94.016121,-34.951134 z M 102.29542,-36.95504 L 103.38526,-36.820274 C 103.21338,-36.183554 102.89502,-35.689414 102.43018,-35.337852 C 101.96533,-34.98629 101.37159,-34.810509 100.64893,-34.810509 C 99.738775,-34.810509 99.017096,-35.090782 98.483894,-35.651329 C 97.950691,-36.211875 97.684089,-36.998007 97.68409,-38.009727 C 97.684089,-39.056598 97.95362,-39.869098 98.492683,-40.447227 C 99.031744,-41.025346 99.730962,-41.314409 100.59034,-41.314415 C 101.42237,-41.314409 102.10205,-41.031206 102.6294,-40.464806 C 103.15674,-39.898394 103.42041,-39.10152 103.42042,-38.074181 C 103.42041,-38.011678 103.41846,-37.917928 103.41456,-37.792931 L 98.773933,-37.792931 C 98.812994,-37.109335 99.006354,-36.585898 99.354012,-36.222618 C 99.701665,-35.859336 100.13526,-35.677696 100.65479,-35.677696 C 101.04151,-35.677696 101.37159,-35.779258 101.64503,-35.982384 C 101.91846,-36.185507 102.13526,-36.509726 102.29542,-36.95504 L 102.29542,-36.95504 z M 98.832527,-38.660118 L 102.30714,-38.660118 C 102.26026,-39.183551 102.12744,-39.576129 101.9087,-39.837852 C 101.57276,-40.244097 101.13721,-40.447222 100.60206,-40.447227 C 100.11768,-40.447222 99.710454,-40.285113 99.380379,-39.960899 C 99.050299,-39.636676 98.867682,-39.203083 98.832527,-38.660118 L 98.832527,-38.660118 z M 108.77589,-35.718712 C 108.38526,-35.38668 108.00928,-35.152305 107.64796,-35.015587 C 107.28663,-34.878868 106.89893,-34.810509 106.48487,-34.810509 C 105.80128,-34.810509 105.27589,-34.977501 104.9087,-35.311485 C 104.54151,-35.645469 104.35792,-36.072226 104.35792,-36.591759 C 104.35792,-36.896444 104.42725,-37.174764 104.56593,-37.42672 C 104.7046,-37.67867 104.88624,-37.880818 105.11085,-38.033165 C 105.33546,-38.185505 105.58838,-38.30074 105.86964,-38.378868 C 106.07667,-38.433552 106.38917,-38.486286 106.80714,-38.537071 C 107.6587,-38.63863 108.28565,-38.759724 108.688,-38.900352 C 108.6919,-39.04488 108.69385,-39.136676 108.69386,-39.175743 C 108.69385,-39.605426 108.59424,-39.90816 108.39503,-40.083946 C 108.12549,-40.322222 107.7251,-40.441363 107.19386,-40.441368 C 106.69776,-40.441363 106.33155,-40.354449 106.09522,-40.180626 C 105.85889,-40.006793 105.68409,-39.699176 105.57081,-39.257774 L 104.53956,-39.398399 C 104.63331,-39.839801 104.7876,-40.196246 105.00245,-40.467735 C 105.21729,-40.739214 105.52784,-40.948198 105.93409,-41.094688 C 106.34034,-41.241167 106.81104,-41.314409 107.3462,-41.314415 C 107.87745,-41.314409 108.30909,-41.251909 108.64112,-41.126915 C 108.97315,-41.001909 109.21729,-40.844683 109.37354,-40.655235 C 109.52979,-40.465777 109.63916,-40.226519 109.70167,-39.937462 C 109.73682,-39.75777 109.7544,-39.433551 109.7544,-38.964806 L 109.7544,-37.558556 C 109.7544,-36.578085 109.77686,-35.957969 109.82178,-35.698204 C 109.8667,-35.438438 109.95557,-35.189415 110.08839,-34.951134 L 108.98682,-34.951134 C 108.87744,-35.169884 108.80713,-35.425743 108.77589,-35.718712 L 108.77589,-35.718712 z M 108.688,-38.074181 C 108.30518,-37.917928 107.73096,-37.785115 106.96534,-37.675743 C 106.53174,-37.61324 106.2251,-37.542928 106.04542,-37.464806 C 105.86573,-37.386678 105.72706,-37.27242 105.6294,-37.122032 C 105.53174,-36.97164 105.48292,-36.804647 105.48292,-36.621056 C 105.48292,-36.339804 105.58936,-36.105429 105.80225,-35.917931 C 106.01514,-35.73043 106.32667,-35.63668 106.73682,-35.636681 C 107.14307,-35.63668 107.5044,-35.725547 107.82081,-35.903282 C 108.13721,-36.081015 108.36963,-36.324179 108.51807,-36.632774 C 108.63135,-36.871054 108.68799,-37.222616 108.688,-37.687462 L 108.688,-38.074181 z M 113.69776,-35.894493 L 113.85011,-34.962852 C 113.55323,-34.900353 113.2876,-34.869103 113.05323,-34.869102 C 112.67042,-34.869103 112.37354,-34.929649 112.16261,-35.050743 C 111.95167,-35.171837 111.80323,-35.331016 111.71729,-35.528282 C 111.63135,-35.725547 111.58839,-36.140586 111.58839,-36.773399 L 111.58839,-40.353477 L 110.81495,-40.353477 L 110.81495,-41.17379 L 111.58839,-41.17379 L 111.58839,-42.714806 L 112.63721,-43.347618 L 112.63721,-41.17379 L 113.69776,-41.17379 L 113.69776,-40.353477 L 112.63721,-40.353477 L 112.63721,-36.714806 C 112.63721,-36.414023 112.65577,-36.220664 112.69288,-36.134727 C 112.72999,-36.048789 112.79053,-35.98043 112.87452,-35.929649 C 112.9585,-35.878867 113.07862,-35.853477 113.23487,-35.853477 C 113.35206,-35.853477 113.50635,-35.867148 113.69776,-35.894493 L 113.69776,-35.894493 z M 118.98292,-36.95504 L 120.07276,-36.820274 C 119.90088,-36.183554 119.58252,-35.689414 119.11768,-35.337852 C 118.65283,-34.98629 118.05909,-34.810509 117.33643,-34.810509 C 116.42627,-34.810509 115.7046,-35.090782 115.17139,-35.651329 C 114.63819,-36.211875 114.37159,-36.998007 114.37159,-38.009727 C 114.37159,-39.056598 114.64112,-39.869098 115.18018,-40.447227 C 115.71924,-41.025346 116.41846,-41.314409 117.27784,-41.314415 C 118.10987,-41.314409 118.78955,-41.031206 119.3169,-40.464806 C 119.84424,-39.898394 120.10791,-39.10152 120.10792,-38.074181 C 120.10791,-38.011678 120.10596,-37.917928 120.10206,-37.792931 L 115.46143,-37.792931 C 115.50049,-37.109335 115.69385,-36.585898 116.04151,-36.222618 C 116.38917,-35.859336 116.82276,-35.677696 117.34229,-35.677696 C 117.72901,-35.677696 118.05909,-35.779258 118.33253,-35.982384 C 118.60596,-36.185507 118.82276,-36.509726 118.98292,-36.95504 L 118.98292,-36.95504 z M 115.52003,-38.660118 L 118.99464,-38.660118 C 118.94776,-39.183551 118.81494,-39.576129 118.5962,-39.837852 C 118.26026,-40.244097 117.82471,-40.447222 117.28956,-40.447227 C 116.80518,-40.447222 116.39795,-40.285113 116.06788,-39.960899 C 115.7378,-39.636676 115.55518,-39.203083 115.52003,-38.660118 L 115.52003,-38.660118 z M 125.43995,-34.951134 L 125.43995,-35.73629 C 125.04541,-35.119102 124.46534,-34.810509 123.69971,-34.810509 C 123.20362,-34.810509 122.74756,-34.947227 122.33155,-35.220665 C 121.91553,-35.494102 121.59327,-35.875937 121.36475,-36.366173 C 121.13624,-36.856405 121.02198,-37.419881 121.02198,-38.056602 C 121.02198,-38.677693 121.1255,-39.241169 121.33253,-39.747032 C 121.53956,-40.252886 121.8501,-40.640581 122.26417,-40.910118 C 122.67823,-41.179643 123.14112,-41.314409 123.65284,-41.314415 C 124.02784,-41.314409 124.36182,-41.235307 124.65479,-41.07711 C 124.94776,-40.918901 125.18604,-40.712847 125.36964,-40.458946 L 125.36964,-43.540977 L 126.41846,-43.540977 L 126.41846,-34.951134 L 125.43995,-34.951134 z M 122.10596,-38.056602 C 122.10596,-37.259725 122.27393,-36.664023 122.60987,-36.269493 C 122.94581,-35.874961 123.34229,-35.677696 123.79932,-35.677696 C 124.26026,-35.677696 124.65186,-35.866172 124.97413,-36.243126 C 125.29639,-36.620077 125.45752,-37.195272 125.45753,-37.968712 C 125.45752,-38.82027 125.29346,-39.44527 124.96534,-39.843712 C 124.63721,-40.242144 124.23291,-40.441363 123.75245,-40.441368 C 123.2837,-40.441363 122.8921,-40.249957 122.57764,-39.867149 C 122.26319,-39.484332 122.10596,-38.880817 122.10596,-38.056602 L 122.10596,-38.056602 z M 132.38331,-34.951134 L 131.40479,-34.951134 L 131.40479,-43.540977 L 132.45948,-43.540977 L 132.45948,-40.476524 C 132.90479,-41.035112 133.47315,-41.314409 134.16456,-41.314415 C 134.54737,-41.314409 134.90967,-41.23726 135.25147,-41.08297 C 135.59326,-40.928667 135.87451,-40.71187 136.09522,-40.432579 C 136.31592,-40.153277 136.48877,-39.816363 136.61378,-39.421837 C 136.73877,-39.027302 136.80127,-38.605427 136.80128,-38.156212 C 136.80127,-37.089803 136.5376,-36.265586 136.01026,-35.683556 C 135.48291,-35.101524 134.8501,-34.810509 134.11182,-34.810509 C 133.37745,-34.810509 132.80127,-35.117149 132.38331,-35.730431 L 132.38331,-34.951134 z M 132.37159,-38.109337 C 132.37159,-37.363241 132.47315,-36.824179 132.67628,-36.492149 C 133.00831,-35.94918 133.45752,-35.677696 134.02393,-35.677696 C 134.48487,-35.677696 134.8833,-35.877891 135.21925,-36.278282 C 135.55518,-36.678671 135.72315,-37.27535 135.72315,-38.068321 C 135.72315,-38.880817 135.56201,-39.480426 135.23975,-39.867149 C 134.91748,-40.253863 134.52784,-40.447222 134.07081,-40.447227 C 133.60987,-40.447222 133.21143,-40.247027 132.8755,-39.846642 C 132.53956,-39.446246 132.37159,-38.867145 132.37159,-38.109337 L 132.37159,-38.109337 z M 138.04346,-32.554649 L 137.92628,-33.544884 C 138.15675,-33.482385 138.35792,-33.451135 138.52979,-33.451134 C 138.76417,-33.451135 138.95167,-33.490198 139.09229,-33.568321 C 139.23292,-33.646448 139.34815,-33.755822 139.438,-33.896446 C 139.5044,-34.001916 139.61182,-34.263634 139.76026,-34.681602 C 139.77979,-34.740196 139.81104,-34.826134 139.85401,-34.939415 L 137.49268,-41.17379 L 138.6294,-41.17379 L 139.92432,-37.570274 C 140.09229,-37.113241 140.24268,-36.632773 140.3755,-36.128868 C 140.49659,-36.613241 140.64112,-37.085897 140.80909,-37.546837 L 142.13917,-41.17379 L 143.19386,-41.17379 L 140.82667,-34.845665 C 140.57276,-34.162072 140.37549,-33.691369 140.23487,-33.433556 C 140.04737,-33.085901 139.83252,-32.831019 139.59034,-32.668907 C 139.34815,-32.5068 139.05909,-32.425746 138.72315,-32.425743 C 138.52003,-32.425746 138.29346,-32.468714 138.04346,-32.554649 L 138.04346,-32.554649 z M 146.60987,-34.951134 L 149.9087,-43.540977 L 151.13331,-43.540977 L 154.64893,-34.951134 L 153.35401,-34.951134 L 152.35206,-37.552696 L 148.76026,-37.552696 L 147.8169,-34.951134 L 146.60987,-34.951134 z M 149.08839,-38.478477 L 152.0005,-38.478477 L 151.10401,-40.857384 C 150.83057,-41.580033 150.62745,-42.173783 150.49464,-42.638634 C 150.38526,-42.087845 150.23096,-41.540971 150.03175,-40.998009 L 149.08839,-38.478477 z M 155.43409,-34.951134 L 155.43409,-41.17379 L 156.38331,-41.17379 L 156.38331,-40.289024 C 156.84034,-40.972612 157.50049,-41.314409 158.36378,-41.314415 C 158.73877,-41.314409 159.0835,-41.247026 159.39796,-41.112267 C 159.7124,-40.977495 159.94776,-40.800737 160.10401,-40.581993 C 160.26026,-40.363238 160.36963,-40.103472 160.43214,-39.802696 C 160.47119,-39.607379 160.49072,-39.265583 160.49073,-38.777306 L 160.49073,-34.951134 L 159.43604,-34.951134 L 159.43604,-38.73629 C 159.43604,-39.165973 159.39502,-39.487262 159.313,-39.700157 C 159.23096,-39.913043 159.08545,-40.082965 158.87647,-40.209923 C 158.66748,-40.336871 158.42237,-40.400347 158.14112,-40.400352 C 157.6919,-40.400347 157.3042,-40.257769 156.97803,-39.972618 C 156.65186,-39.687457 156.48878,-39.146442 156.48878,-38.349571 L 156.48878,-34.951134 L 155.43409,-34.951134 z M 166.15089,-34.951134 L 166.15089,-35.73629 C 165.75635,-35.119102 165.17627,-34.810509 164.41065,-34.810509 C 163.91456,-34.810509 163.4585,-34.947227 163.04249,-35.220665 C 162.62647,-35.494102 162.30421,-35.875937 162.07569,-36.366173 C 161.84718,-36.856405 161.73292,-37.419881 161.73292,-38.056602 C 161.73292,-38.677693 161.83643,-39.241169 162.04346,-39.747032 C 162.25049,-40.252886 162.56104,-40.640581 162.97511,-40.910118 C 163.38917,-41.179643 163.85206,-41.314409 164.36378,-41.314415 C 164.73877,-41.314409 165.07276,-41.235307 165.36573,-41.07711 C 165.65869,-40.918901 165.89698,-40.712847 166.08057,-40.458946 L 166.08057,-43.540977 L 167.1294,-43.540977 L 167.1294,-34.951134 L 166.15089,-34.951134 z M 162.8169,-38.056602 C 162.8169,-37.259725 162.98487,-36.664023 163.32081,-36.269493 C 163.65674,-35.874961 164.05323,-35.677696 164.51026,-35.677696 C 164.9712,-35.677696 165.3628,-35.866172 165.68507,-36.243126 C 166.00733,-36.620077 166.16846,-37.195272 166.16846,-37.968712 C 166.16846,-38.82027 166.0044,-39.44527 165.67628,-39.843712 C 165.34815,-40.242144 164.94385,-40.441363 164.46339,-40.441368 C 163.99463,-40.441363 163.60303,-40.249957 163.28858,-39.867149 C 162.97413,-39.484332 162.8169,-38.880817 162.8169,-38.056602 L 162.8169,-38.056602 z M 168.78175,-34.951134 L 168.78175,-41.17379 L 169.73096,-41.17379 L 169.73096,-40.230431 C 169.97315,-40.671831 170.19678,-40.962846 170.40186,-41.103477 C 170.60694,-41.244096 170.83252,-41.314409 171.07862,-41.314415 C 171.43409,-41.314409 171.79541,-41.201127 172.16261,-40.974571 L 171.79932,-39.996056 C 171.54151,-40.148394 171.2837,-40.224566 171.02589,-40.224571 C 170.79541,-40.224566 170.58838,-40.15523 170.40479,-40.016563 C 170.2212,-39.877887 170.09034,-39.685504 170.01221,-39.439415 C 169.89503,-39.064411 169.83643,-38.654255 169.83643,-38.208946 L 169.83643,-34.951134 L 168.78175,-34.951134 z M 177.06104,-36.95504 L 178.15089,-36.820274 C 177.97901,-36.183554 177.66065,-35.689414 177.19581,-35.337852 C 176.73096,-34.98629 176.13721,-34.810509 175.41456,-34.810509 C 174.5044,-34.810509 173.78272,-35.090782 173.24952,-35.651329 C 172.71632,-36.211875 172.44971,-36.998007 172.44971,-38.009727 C 172.44971,-39.056598 172.71925,-39.869098 173.25831,-40.447227 C 173.79737,-41.025346 174.49659,-41.314409 175.35596,-41.314415 C 176.18799,-41.314409 176.86768,-41.031206 177.39503,-40.464806 C 177.92236,-39.898394 178.18604,-39.10152 178.18604,-38.074181 C 178.18604,-38.011678 178.18408,-37.917928 178.18018,-37.792931 L 173.53956,-37.792931 C 173.57862,-37.109335 173.77198,-36.585898 174.11964,-36.222618 C 174.46729,-35.859336 174.90088,-35.677696 175.42042,-35.677696 C 175.80713,-35.677696 176.13721,-35.779258 176.41065,-35.982384 C 176.68408,-36.185507 176.90088,-36.509726 177.06104,-36.95504 L 177.06104,-36.95504 z M 173.59815,-38.660118 L 177.07276,-38.660118 C 177.02588,-39.183551 176.89307,-39.576129 176.67432,-39.837852 C 176.33838,-40.244097 175.90284,-40.447222 175.36768,-40.447227 C 174.88331,-40.447222 174.47608,-40.285113 174.146,-39.960899 C 173.81592,-39.636676 173.63331,-39.203083 173.59815,-38.660118 L 173.59815,-38.660118 z M 180.6294,-34.951134 L 178.72511,-41.17379 L 179.81495,-41.17379 L 180.80518,-37.581993 L 181.17432,-36.246056 C 181.18995,-36.31246 181.29737,-36.740194 181.49659,-37.529259 L 182.48682,-41.17379 L 183.57081,-41.17379 L 184.50245,-37.564415 L 184.813,-36.374962 L 185.17042,-37.576134 L 186.23682,-41.17379 L 187.26221,-41.17379 L 185.3169,-34.951134 L 184.2212,-34.951134 L 183.23096,-38.677696 L 182.99073,-39.738243 L 181.73096,-34.951134 L 180.6294,-34.951134 z M 191.67432,-34.951134 L 191.67432,-43.540977 L 197.46925,-43.540977 L 197.46925,-42.527306 L 192.81104,-42.527306 L 192.81104,-39.867149 L 196.84229,-39.867149 L 196.84229,-38.853477 L 192.81104,-38.853477 L 192.81104,-34.951134 L 191.67432,-34.951134 z M 198.82276,-42.328087 L 198.82276,-43.540977 L 199.87745,-43.540977 L 199.87745,-42.328087 L 198.82276,-42.328087 z M 198.82276,-34.951134 L 198.82276,-41.17379 L 199.87745,-41.17379 L 199.87745,-34.951134 L 198.82276,-34.951134 z M 203.79151,-35.894493 L 203.94386,-34.962852 C 203.64698,-34.900353 203.38135,-34.869103 203.14698,-34.869102 C 202.76417,-34.869103 202.46729,-34.929649 202.25636,-35.050743 C 202.04542,-35.171837 201.89698,-35.331016 201.81104,-35.528282 C 201.7251,-35.725547 201.68214,-36.140586 201.68214,-36.773399 L 201.68214,-40.353477 L 200.9087,-40.353477 L 200.9087,-41.17379 L 201.68214,-41.17379 L 201.68214,-42.714806 L 202.73096,-43.347618 L 202.73096,-41.17379 L 203.79151,-41.17379 L 203.79151,-40.353477 L 202.73096,-40.353477 L 202.73096,-36.714806 C 202.73096,-36.414023 202.74952,-36.220664 202.78663,-36.134727 C 202.82374,-36.048789 202.88428,-35.98043 202.96827,-35.929649 C 203.05225,-35.878867 203.17237,-35.853477 203.32862,-35.853477 C 203.44581,-35.853477 203.6001,-35.867148 203.79151,-35.894493 L 203.79151,-35.894493 z M 204.26026,-34.951134 L 204.26026,-35.806602 L 208.2212,-40.353477 C 207.77198,-40.330035 207.37549,-40.318316 207.03175,-40.318321 L 204.49464,-40.318321 L 204.49464,-41.17379 L 209.58057,-41.17379 L 209.58057,-40.476524 L 206.21143,-36.527306 L 205.56104,-35.806602 C 206.0337,-35.841758 206.47706,-35.859336 206.89112,-35.859337 L 209.76807,-35.859337 L 209.76807,-34.951134 L 204.26026,-34.951134 z M 210.39503,-36.808556 L 211.438,-36.972618 C 211.49659,-36.554648 211.65967,-36.234336 211.92725,-36.011681 C 212.19483,-35.789024 212.56885,-35.677696 213.04932,-35.677696 C 213.5337,-35.677696 213.89307,-35.776328 214.12745,-35.973595 C 214.36182,-36.170859 214.47901,-36.402304 214.47901,-36.667931 C 214.47901,-36.90621 214.37549,-37.09371 214.16846,-37.230431 C 214.02393,-37.324178 213.66455,-37.443319 213.09034,-37.587852 C 212.3169,-37.783162 211.78077,-37.952107 211.48194,-38.094688 C 211.18311,-38.237263 210.95655,-38.434529 210.80225,-38.686485 C 210.64796,-38.938434 210.57081,-39.216754 210.57081,-39.521446 C 210.57081,-39.798785 210.63428,-40.055621 210.76124,-40.291954 C 210.88819,-40.528277 211.06104,-40.724565 211.27979,-40.880821 C 211.44385,-41.001909 211.66749,-41.104448 211.95069,-41.188438 C 212.23389,-41.272416 212.5376,-41.314409 212.86182,-41.314415 C 213.3501,-41.314409 213.77881,-41.244096 214.14796,-41.103477 C 214.51709,-40.962846 214.78955,-40.772417 214.96534,-40.532188 C 215.14112,-40.291949 215.26221,-39.97066 215.32862,-39.568321 L 214.29737,-39.427696 C 214.25049,-39.748004 214.11475,-39.998004 213.89014,-40.177696 C 213.66553,-40.357378 213.34815,-40.447222 212.938,-40.447227 C 212.45362,-40.447222 212.10792,-40.367144 211.90089,-40.206993 C 211.69385,-40.046832 211.59034,-39.859332 211.59034,-39.644493 C 211.59034,-39.50777 211.63331,-39.384723 211.71925,-39.275352 C 211.80518,-39.162067 211.93995,-39.068317 212.12354,-38.994102 C 212.22901,-38.955036 212.53956,-38.865192 213.05518,-38.724571 C 213.80127,-38.525349 214.32178,-38.362263 214.61671,-38.235313 C 214.91162,-38.108357 215.14307,-37.923787 215.31104,-37.681602 C 215.47901,-37.439412 215.56299,-37.138632 215.563,-36.779259 C 215.56299,-36.427695 215.46045,-36.09664 215.25538,-35.786095 C 215.0503,-35.475547 214.7544,-35.235313 214.36768,-35.065392 C 213.98096,-34.89547 213.54346,-34.810509 213.05518,-34.810509 C 212.24659,-34.810509 211.63038,-34.978477 211.20655,-35.314415 C 210.78272,-35.650352 210.51221,-36.148398 210.39503,-36.808556 L 210.39503,-36.808556 z M 216.82276,-42.328087 L 216.82276,-43.540977 L 217.87745,-43.540977 L 217.87745,-42.328087 L 216.82276,-42.328087 z M 216.82276,-34.951134 L 216.82276,-41.17379 L 217.87745,-41.17379 L 217.87745,-34.951134 L 216.82276,-34.951134 z M 219.48878,-34.951134 L 219.48878,-41.17379 L 220.43214,-41.17379 L 220.43214,-40.300743 C 220.62745,-40.605425 220.88721,-40.850542 221.21143,-41.036095 C 221.53565,-41.221635 221.90479,-41.314409 222.31886,-41.314415 C 222.77979,-41.314409 223.15772,-41.218706 223.45264,-41.027306 C 223.74756,-40.835893 223.95557,-40.568316 224.07667,-40.224571 C 224.56885,-40.951128 225.20947,-41.314409 225.99854,-41.314415 C 226.61572,-41.314409 227.09033,-41.14351 227.42237,-40.80172 C 227.75439,-40.459917 227.92041,-39.933551 227.92042,-39.222618 L 227.92042,-34.951134 L 226.87159,-34.951134 L 226.87159,-38.871056 C 226.87158,-39.292926 226.8374,-39.596637 226.76905,-39.782188 C 226.70068,-39.96773 226.57666,-40.117144 226.39698,-40.230431 C 226.21729,-40.343706 226.00635,-40.400347 225.76417,-40.400352 C 225.32666,-40.400347 224.96338,-40.254839 224.67432,-39.963829 C 224.38526,-39.672809 224.24072,-39.206989 224.24073,-38.566368 L 224.24073,-34.951134 L 223.18604,-34.951134 L 223.18604,-38.994102 C 223.18604,-39.462848 223.1001,-39.81441 222.92823,-40.04879 C 222.75635,-40.28316 222.4751,-40.400347 222.08448,-40.400352 C 221.7876,-40.400347 221.51319,-40.322222 221.26124,-40.165977 C 221.00928,-40.009722 220.82667,-39.781207 220.71339,-39.480431 C 220.6001,-39.179645 220.54346,-38.746052 220.54346,-38.179649 L 220.54346,-34.951134 L 219.48878,-34.951134 z M 229.10401,-38.062462 C 229.10401,-39.214801 229.42432,-40.068316 230.06495,-40.623009 C 230.6001,-41.08394 231.25245,-41.314409 232.02198,-41.314415 C 232.87744,-41.314409 233.57666,-41.034135 234.11964,-40.473595 C 234.6626,-39.913043 234.93408,-39.13863 234.93409,-38.150352 C 234.93408,-37.349569 234.81397,-36.719687 234.57374,-36.260704 C 234.3335,-35.801719 233.98389,-35.445274 233.52491,-35.191368 C 233.06592,-34.937462 232.56495,-34.810509 232.02198,-34.810509 C 231.15088,-34.810509 230.44678,-35.089805 229.90968,-35.648399 C 229.37257,-36.206992 229.10401,-37.011679 229.10401,-38.062462 L 229.10401,-38.062462 z M 230.188,-38.062462 C 230.18799,-37.265585 230.36182,-36.668905 230.70948,-36.272423 C 231.05713,-35.875937 231.49463,-35.677696 232.02198,-35.677696 C 232.54541,-35.677696 232.98096,-35.876914 233.32862,-36.275352 C 233.67627,-36.673788 233.8501,-37.28121 233.85011,-38.097618 C 233.8501,-38.867145 233.6753,-39.450153 233.32569,-39.846642 C 232.97608,-40.243121 232.54151,-40.441363 232.02198,-40.441368 C 231.49463,-40.441363 231.05713,-40.244097 230.70948,-39.849571 C 230.36182,-39.455035 230.18799,-38.859333 230.188,-38.062462 L 230.188,-38.062462 z M 236.17628,-34.951134 L 236.17628,-41.17379 L 237.1255,-41.17379 L 237.1255,-40.289024 C 237.58252,-40.972612 238.24268,-41.314409 239.10596,-41.314415 C 239.48096,-41.314409 239.82569,-41.247026 240.14014,-41.112267 C 240.45459,-40.977495 240.68994,-40.800737 240.8462,-40.581993 C 241.00244,-40.363238 241.11182,-40.103472 241.17432,-39.802696 C 241.21338,-39.607379 241.23291,-39.265583 241.23292,-38.777306 L 241.23292,-34.951134 L 240.17823,-34.951134 L 240.17823,-38.73629 C 240.17823,-39.165973 240.13721,-39.487262 240.05518,-39.700157 C 239.97315,-39.913043 239.82764,-40.082965 239.61866,-40.209923 C 239.40967,-40.336871 239.16455,-40.400347 238.88331,-40.400352 C 238.43409,-40.400347 238.04639,-40.257769 237.72022,-39.972618 C 237.39405,-39.687457 237.23096,-39.146442 237.23096,-38.349571 L 237.23096,-34.951134 L 236.17628,-34.951134 z"
+     id="text1235" />
+  <g
+     id="g2852"
+     transform="matrix(1.018857,0.000000,0.000000,1.018857,-4.481650,2.131177)">
+    <rect
+       height="8.3153667"
+       id="rect1866"
+       style="fill:url(#linearGradient1156);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient1157);stroke-width:1.4473482pt;"
+       transform="matrix(1.150066,0.000000,0.000000,1.150066,38.98882,26.86863)"
+       width="57.567924"
+       x="33.326111"
+       y="78.658051" />
+    <rect
+       height="60.126495"
+       id="rect1867"
+       rx="5.4369707"
+       ry="5.4369707"
+       style="fill:url(#linearGradient905);fill-opacity:1;fill-rule:evenodd;stroke-width:1.6282668;"
+       transform="matrix(1.150066,0.000000,0.000000,1.150066,38.98882,26.86863)"
+       width="72.279724"
+       x="26.015469"
+       y="22.413721" />
+    <rect
+       height="38.044163"
+       id="rect1868"
+       style="fill:url(#radialGradient1132);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient891);stroke-width:1.4649456pt;"
+       transform="matrix(1.150066,0.000000,0.000000,1.150066,38.98882,26.86863)"
+       width="58.178177"
+       x="33.386066"
+       y="31.695871" />
+    <path
+       d="M 27.690431,52.841444 L 27.370609,74.749236 C 27.319624,78.241665 29.310209,80.477938 32.807578,80.506029 L 72.625393,80.825852 L 76.463254,71.870840 L 32.008024,71.551020 L 31.688202,52.681533 L 27.690431,52.841444 z "
+       id="path1869"
+       sodipodi:nodetypes="czzccccc"
+       style="fill:url(#linearGradient1146);fill-opacity:1;fill-rule:evenodd;stroke-width:1.0000000pt;"
+       transform="matrix(1.150066,0.000000,0.000000,1.150066,38.98882,26.86863)" />
+    <g
+       id="g1870"
+       transform="matrix(1.150066,0.000000,0.000000,1.150066,38.98882,26.86863)">
+      <path
+         d="M 42.062098,33.460351 L 77.341205,33.008055 C 82.787126,32.938235 89.553204,38.416797 89.553204,43.863165 L 89.553204,60.145830 L 41.609801,59.693534 L 42.062098,33.460351 z "
+         id="path1871"
+         sodipodi:nodetypes="czzccc"
+         style="fill:url(#linearGradient1148);fill-opacity:1;fill-rule:evenodd;stroke-width:1.0000000pt;" />
+      <path
+         d="M 78.337784,67.629235 L 46.723745,67.724544 C 41.843589,67.739257 35.829319,62.771024 35.877168,57.891081 L 36.020221,43.301821 L 78.973514,44.128288 L 78.337784,67.629235 z "
+         id="path1872"
+         sodipodi:nodetypes="czzccc"
+         style="fill:url(#linearGradient1150);fill-opacity:1;fill-rule:evenodd;stroke-width:1.0000000pt;" />
+    </g>
+    <rect
+       height="26.147448"
+       id="rect1888"
+       rx="7.4449978"
+       ry="7.4449978"
+       style="fill:url(#linearGradient901);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient1157);stroke-width:2.3625000;"
+       transform="matrix(0.917809,0.000000,0.000000,0.917809,-65.63305,158.5521)"
+       width="104.09673"
+       x="140.62315"
+       y="-34.316952" />
+    <rect
+       height="15.829688"
+       id="rect1889"
+       rx="3.7576280"
+       ry="3.7576280"
+       style="fill:url(#linearGradient901);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient1157);stroke-width:1.3591428;"
+       transform="matrix(0.917809,0.000000,0.000000,0.917809,-65.63305,158.5521)"
+       width="56.908955"
+       x="184.04552"
+       y="-28.539845" />
+    <rect
+       height="15.829688"
+       id="rect1890"
+       rx="2.9970589"
+       ry="2.9970589"
+       style="fill:url(#linearGradient1141);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient1157);stroke-width:0.96249998;"
+       transform="matrix(0.917809,0.000000,0.000000,0.917809,-65.63305,158.5521)"
+       width="28.796961"
+       x="145.28902"
+       y="-28.227346" />
+    <rect
+       height="3.3627598"
+       id="rect1891"
+       rx="1.6813799"
+       ry="1.6813799"
+       style="fill-opacity:0.16981132;fill-rule:evenodd;stroke-width:0.46326005;"
+       transform="matrix(0.917809,0.000000,0.000000,0.917809,-65.63305,158.5521)"
+       width="49.231453"
+       x="187.88426"
+       y="-21.681381" />
+  </g>
+</svg>
diff --git a/wpa_supplicant/wpa_gui-qt4/icons/invitation.svg b/wpa_supplicant/wpa_gui-qt4/icons/invitation.svg
new file mode 100644 (file)
index 0000000..1a02d13
--- /dev/null
@@ -0,0 +1,374 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://inkscape.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="64.000000px"
+   height="64.000000px"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.42"
+   sodipodi:docbase="G:\Projs\Cliparts Stocker\released"
+   sodipodi:docname="unknown_green.svg"
+   inkscape:export-filename="/datas/wiki/unknown_green.png"
+   inkscape:export-xdpi="90.000000"
+   inkscape:export-ydpi="90.000000">
+  <defs
+     id="defs4">
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2842"
+       id="linearGradient1363"
+       x1="25.403513"
+       y1="19.175573"
+       x2="35.541985"
+       y2="49.068703"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-2.402975,4.759656e-3)" />
+    <linearGradient
+       id="linearGradient2900">
+      <stop
+         id="stop2902"
+         offset="0.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2904"
+         offset="1.0000000"
+         style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2842">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2844" />
+      <stop
+         style="stop-color:#c8c8c8;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop2846" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2814">
+      <stop
+         id="stop2816"
+         offset="0.0000000"
+         style="stop-color:#e6e6e6;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2818"
+         offset="1.0000000"
+         style="stop-color:#11661d;stop-opacity:0.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2171">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2173" />
+      <stop
+         style="stop-color:#a3a5ee;stop-opacity:0.0000000;"
+         offset="1.0000000"
+         id="stop2175" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2160">
+      <stop
+         id="stop2162"
+         offset="0.0000000"
+         style="stop-color:#d3cece;stop-opacity:1.0000000;" />
+      <stop
+         id="stop2164"
+         offset="1.0000000"
+         style="stop-color:#474240;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient1367">
+      <stop
+         id="stop1369"
+         offset="0.0000000"
+         style="stop-color:#f67e36;stop-opacity:1.0000000;" />
+      <stop
+         id="stop1371"
+         offset="1.0000000"
+         style="stop-color:#602604;stop-opacity:1.0000000;" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient1347">
+      <stop
+         style="stop-color:#f0da27;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop1349" />
+      <stop
+         style="stop-color:#bf4d09;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop1351" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient1315">
+      <stop
+         style="stop-color:#97ff82;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop1317" />
+      <stop
+         style="stop-color:#ceff24;stop-opacity:0.0000000;"
+         offset="1.0000000"
+         id="stop1319" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2122">
+      <stop
+         style="stop-color:#2edc32;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop2124" />
+      <stop
+         style="stop-color:#11661d;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop2126" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient1364">
+      <stop
+         style="stop-color:#236b0d;stop-opacity:1.0000000;"
+         offset="0.00000000"
+         id="stop1366" />
+      <stop
+         style="stop-color:#0a2205;stop-opacity:1.0000000;"
+         offset="1.0000000"
+         id="stop1368" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient1367"
+       id="radialGradient1402"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.211118e-16,1.330643,-1.347411,2.027373e-5,44.09678,-13.39507)"
+       cx="21.959658"
+       cy="14.921703"
+       fx="21.959658"
+       fy="14.921703"
+       r="27.500000" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2122"
+       id="radialGradient1404"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.211118e-16,1.330643,-1.347411,2.027373e-5,44.09678,-13.39507)"
+       cx="21.959658"
+       cy="14.921703"
+       fx="21.959658"
+       fy="14.921703"
+       r="27.500000" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient1364"
+       id="linearGradient1419"
+       gradientUnits="userSpaceOnUse"
+       x1="74.910713"
+       y1="32.362179"
+       x2="84.910713"
+       y2="47.451466" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2122"
+       id="linearGradient1421"
+       gradientUnits="userSpaceOnUse"
+       x1="73.839287"
+       y1="34.428566"
+       x2="76.875000"
+       y2="43.714283" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient1315"
+       id="linearGradient1423"
+       gradientUnits="userSpaceOnUse"
+       x1="72.946426"
+       y1="35.589287"
+       x2="85.000000"
+       y2="47.375000" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2171"
+       id="linearGradient2177"
+       x1="24.916031"
+       y1="28.824427"
+       x2="39.816792"
+       y2="49.099239"
+       gradientUnits="userSpaceOnUse" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2122"
+       id="radialGradient2184"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(9.909149e-17,1.088708,-1.102427,1.658760e-5,41.48828,-4.732338)"
+       cx="21.959658"
+       cy="14.921703"
+       fx="21.959658"
+       fy="14.921703"
+       r="27.500000" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient1364"
+       id="linearGradient2189"
+       x1="10.018247"
+       y1="8.6306763"
+       x2="63.487556"
+       y2="63.660282"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2171"
+       id="linearGradient1339"
+       gradientUnits="userSpaceOnUse"
+       x1="24.916031"
+       y1="28.824427"
+       x2="39.816792"
+       y2="49.099239" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2122"
+       id="radialGradient1343"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.521415e-2,1.026125,-0.978137,2.404729e-2,38.83024,-3.575704)"
+       cx="24.764277"
+       cy="16.361967"
+       fx="24.764277"
+       fy="16.361967"
+       r="27.500000" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient1364"
+       id="linearGradient1346"
+       gradientUnits="userSpaceOnUse"
+       x1="10.018247"
+       y1="8.6306763"
+       x2="63.487556"
+       y2="63.660282" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2814"
+       id="radialGradient2812"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.142398e-2,1.098850,-1.843995,1.878760e-2,52.15051,-5.667446)"
+       cx="18.387238"
+       cy="14.046815"
+       fx="18.387238"
+       fy="14.046815"
+       r="27.500000" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient1364"
+       id="linearGradient2832"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-2.841000e-3,-2.841000e-3)"
+       x1="10.018247"
+       y1="8.6306763"
+       x2="63.487556"
+       y2="63.660282" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2842"
+       id="linearGradient2848"
+       x1="-0.56685609"
+       y1="22.651009"
+       x2="-0.33713850"
+       y2="23.858734"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient2842"
+       id="linearGradient2864"
+       gradientUnits="userSpaceOnUse"
+       x1="-0.82287467"
+       y1="22.444542"
+       x2="-0.33713850"
+       y2="23.858734" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="8.2031250"
+     inkscape:cx="32.000000"
+     inkscape:cy="32.000000"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:grid-bbox="true"
+     inkscape:grid-points="true"
+     inkscape:window-width="1156"
+     inkscape:window-height="693"
+     inkscape:window-x="0"
+     inkscape:window-y="25"
+     showguides="false" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title>Green Unknown</dc:title>
+        <dc:date>2005-11-01</dc:date>
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Jean-Victor Balin</dc:title>
+          </cc:Agent>
+        </dc:creator>
+        <dc:description>jean.victor.balin@gmail.com</dc:description>
+        <cc:license
+           rdf:resource="http://web.resource.org/cc/PublicDomain" />
+        <dc:subject>
+          <rdf:Bag>
+            <rdf:li>icon</rdf:li>
+          </rdf:Bag>
+        </dc:subject>
+      </cc:Work>
+      <cc:License
+         rdf:about="http://web.resource.org/cc/PublicDomain">
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Reproduction" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Distribution" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+      </cc:License>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Calque 1"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <g
+       id="g1354">
+      <path
+         id="path1373"
+         d="M 32.000000,8.6306766 C 19.113097,8.6306766 8.6306766,19.113097 8.6306766,32.000000 C 8.6306766,44.886903 19.113097,55.369323 32.000000,55.369323 C 44.886903,55.369323 55.369323,44.886903 55.369323,32.000000 C 55.369323,19.113097 44.886903,8.6306766 32.000000,8.6306766 z "
+         style="fill:url(#linearGradient1346);fill-opacity:1.0000000;stroke:none;stroke-width:2.0000000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000" />
+      <path
+         id="path1339"
+         d="M 54.500005,32.000000 C 54.500005,44.420003 44.420003,54.500005 32.000000,54.500005 C 19.579997,54.500005 9.4999950,44.420003 9.4999950,32.000000 C 9.4999950,19.579997 19.579997,9.4999950 32.000000,9.4999950 C 44.420003,9.4999950 54.500005,19.579997 54.500005,32.000000 z "
+         style="fill:url(#radialGradient1343);fill-opacity:1.0000000;stroke:none;stroke-width:2.0000000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000" />
+      <path
+         id="path1341"
+         d="M 32.016991,9.1562500 C 22.574792,9.1562500 14.505423,14.865048 11.062500,22.968750 C 16.006322,25.801817 21.393258,27.855853 27.181339,27.593750 C 32.755311,27.279922 37.553510,23.530916 43.236968,23.812500 C 47.451058,23.716455 52.244330,25.294372 54.488550,29.000000 C 53.142630,17.846718 43.657640,9.1562500 32.016991,9.1562500 z "
+         style="fill:url(#radialGradient2812);fill-opacity:1.0000000;stroke:none;stroke-width:2.0000000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000" />
+      <path
+         id="path2827"
+         d="M 32.000000,8.6250000 C 19.113098,8.6250000 8.6250000,19.113097 8.6250000,32.000000 C 8.6250000,44.886904 19.113097,55.375000 32.000000,55.375000 C 44.886904,55.375000 55.375000,44.886903 55.375000,32.000000 C 55.375000,19.113098 44.886903,8.6250000 32.000000,8.6250000 z M 32.000000,9.5000000 C 44.420004,9.4999998 54.500000,19.579997 54.500000,32.000000 C 54.499998,44.420004 44.420003,54.500000 32.000000,54.500000 C 19.579998,54.499998 9.5000000,44.420003 9.5000000,32.000000 C 9.5000000,19.579998 19.579997,9.5000000 32.000000,9.5000000 z "
+         style="fill:url(#linearGradient2832);fill-opacity:1.0000000;stroke:none;stroke-width:2.0000000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000" />
+      <path
+         id="text1353"
+         d="M 32.556888,39.006317 C 32.692760,35.835967 33.100380,35.066018 35.908404,32.892064 C 39.395790,30.219911 39.803410,29.902873 40.120445,29.631129 C 41.705621,28.272407 42.611437,26.189029 42.611437,24.015074 C 42.611437,19.078386 38.625844,15.953318 32.285143,15.953318 C 26.306768,15.953318 22.094721,18.851931 22.094721,23.018677 C 22.094721,25.464376 23.906354,27.230718 26.397344,27.230718 C 28.707171,27.230718 30.292350,25.736121 30.292350,23.607457 C 30.292350,22.384608 29.794150,21.388209 28.843045,20.663558 C 28.027812,20.029488 27.982521,19.984196 27.982521,19.667161 C 27.982521,19.033091 28.978919,18.534892 30.382931,18.534892 C 33.100374,18.534892 34.640263,20.346525 34.640263,23.516876 C 34.640263,25.373795 33.960900,27.683628 32.828632,29.721710 C 30.337643,34.160201 29.975314,35.066023 29.975314,37.104105 C 29.975314,37.557012 30.020605,38.281665 30.111187,39.006317 L 32.556888,39.006317 M 31.424619,41.497309 C 29.069501,41.497309 27.167287,43.399523 27.167287,45.754641 C 27.167287,48.064467 29.069501,50.011973 31.379328,50.011973 C 33.779736,50.011973 35.681951,48.109758 35.681951,45.754641 C 35.681951,43.399523 33.779736,41.497309 31.424619,41.497309"
+         style="font-size:45.290764px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125.00000%;writing-mode:lr-tb;text-anchor:start;fill:url(#linearGradient1363);fill-opacity:1.0000000;stroke:none;stroke-width:1.0000000px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;font-family:Century Schoolbook L" />
+    </g>
+  </g>
+</svg>
index f262217..9a30b7f 100644 (file)
@@ -3,5 +3,7 @@
   <file alias="wpa_gui.png">icons/hicolor/16x16/apps/wpa_gui.png</file>
   <file alias="ap.png">icons/hicolor/32x32/apps/ap.png</file>
   <file alias="laptop.png">icons/hicolor/32x32/apps/laptop.png</file>
+  <file alias="group.png">icons/hicolor/32x32/apps/group.png</file>
+  <file alias="invitation.png">icons/hicolor/32x32/apps/invitation.png</file>
  </qresource>
 </RCC>
index 7a99299..65bb17d 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * wpa_gui - Peers class
- * Copyright (c) 2009, Atheros Communications
+ * Copyright (c) 2009-2010, Atheros Communications
  *
  * 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
@@ -27,11 +27,22 @@ enum {
        peer_role_type,
        peer_role_uuid,
        peer_role_details,
+       peer_role_ifname,
        peer_role_pri_dev_type,
        peer_role_ssid,
        peer_role_config_methods,
        peer_role_dev_passwd_id,
-       peer_role_bss_id
+       peer_role_bss_id,
+       peer_role_selected_method,
+       peer_role_selected_pin,
+       peer_role_requested_method,
+       peer_role_network_id
+};
+
+enum selected_method {
+       SEL_METHOD_NONE,
+       SEL_METHOD_PIN_PEER_DISPLAY,
+       SEL_METHOD_PIN_LOCAL_DISPLAY
 };
 
 /*
@@ -44,6 +55,12 @@ enum peer_type {
        PEER_TYPE_AP,
        PEER_TYPE_AP_WPS,
        PEER_TYPE_WPS_PIN_NEEDED,
+       PEER_TYPE_P2P,
+       PEER_TYPE_P2P_CLIENT,
+       PEER_TYPE_P2P_GROUP,
+       PEER_TYPE_P2P_PERSISTENT_GROUP_GO,
+       PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT,
+       PEER_TYPE_P2P_INVITATION,
        PEER_TYPE_WPS_ER_AP,
        PEER_TYPE_WPS_ER_AP_UNCONFIGURED,
        PEER_TYPE_WPS_ER_ENROLLEE,
@@ -61,20 +78,27 @@ Peers::Peers(QWidget *parent, const char *, bool, Qt::WFlags)
                default_icon = new QIcon(":/icons/wpa_gui.svg");
                ap_icon = new QIcon(":/icons/ap.svg");
                laptop_icon = new QIcon(":/icons/laptop.svg");
+               group_icon = new QIcon(":/icons/group.svg");
+               invitation_icon = new QIcon(":/icons/invitation.svg");
        } else {
                default_icon = new QIcon(":/icons/wpa_gui.png");
                ap_icon = new QIcon(":/icons/ap.png");
                laptop_icon = new QIcon(":/icons/laptop.png");
+               group_icon = new QIcon(":/icons/group.png");
+               invitation_icon = new QIcon(":/icons/invitation.png");
        }
 
        peers->setModel(&model);
        peers->setResizeMode(QListView::Adjust);
+       peers->setDragEnabled(false);
+       peers->setSelectionMode(QAbstractItemView::NoSelection);
 
        peers->setContextMenuPolicy(Qt::CustomContextMenu);
        connect(peers, SIGNAL(customContextMenuRequested(const QPoint &)),
                this, SLOT(context_menu(const QPoint &)));
 
        wpagui = NULL;
+       hide_ap = false;
 }
 
 
@@ -90,6 +114,8 @@ Peers::~Peers()
        delete default_icon;
        delete ap_icon;
        delete laptop_icon;
+       delete group_icon;
+       delete invitation_icon;
 }
 
 
@@ -115,6 +141,24 @@ QString Peers::ItemType(int type)
        case PEER_TYPE_WPS_PIN_NEEDED:
                title = tr("WPS PIN needed");
                break;
+       case PEER_TYPE_P2P:
+               title = tr("P2P Device");
+               break;
+       case PEER_TYPE_P2P_CLIENT:
+               title = tr("P2P Device (group client)");
+               break;
+       case PEER_TYPE_P2P_GROUP:
+               title = tr("P2P Group");
+               break;
+       case PEER_TYPE_P2P_PERSISTENT_GROUP_GO:
+               title = tr("P2P Persistent Group (GO)");
+               break;
+       case PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT:
+               title = tr("P2P Persistent Group (client)");
+               break;
+       case PEER_TYPE_P2P_INVITATION:
+               title = tr("P2P Invitation");
+               break;
        case PEER_TYPE_WPS_ER_AP:
                title = tr("ER: WPS AP");
                break;
@@ -150,6 +194,11 @@ void Peers::context_menu(const QPoint &pos)
                if (var.isValid())
                        config_methods = var.toInt();
 
+               enum selected_method method = SEL_METHOD_NONE;
+               var = ctx_item->data(peer_role_selected_method);
+               if (var.isValid())
+                       method = (enum selected_method) var.toInt();
+
                if ((type == PEER_TYPE_ASSOCIATED_STATION ||
                     type == PEER_TYPE_AP_WPS ||
                     type == PEER_TYPE_WPS_PIN_NEEDED ||
@@ -160,6 +209,60 @@ void Peers::context_menu(const QPoint &pos)
                                        SLOT(enter_pin()));
                }
 
+               if (type == PEER_TYPE_P2P || type == PEER_TYPE_P2P_CLIENT) {
+                       menu->addAction(tr("P2P Connect"), this,
+                                       SLOT(ctx_p2p_connect()));
+                       if (method == SEL_METHOD_NONE &&
+                           config_methods > -1 &&
+                           config_methods & 0x0080 /* PBC */ &&
+                           config_methods != 0x0080)
+                               menu->addAction(tr("P2P Connect (PBC)"), this,
+                                               SLOT(connect_pbc()));
+                       if (method == SEL_METHOD_NONE) {
+                               menu->addAction(tr("P2P Request PIN"), this,
+                                               SLOT(ctx_p2p_req_pin()));
+                               menu->addAction(tr("P2P Show PIN"), this,
+                                               SLOT(ctx_p2p_show_pin()));
+                       }
+
+                       if (config_methods > -1 && (config_methods & 0x0100)) {
+                               /* Peer has Keypad */
+                               menu->addAction(tr("P2P Display PIN"), this,
+                                               SLOT(ctx_p2p_display_pin()));
+                       }
+
+                       if (config_methods > -1 && (config_methods & 0x000c)) {
+                               /* Peer has Label or Display */
+                               menu->addAction(tr("P2P Enter PIN"), this,
+                                               SLOT(ctx_p2p_enter_pin()));
+                       }
+               }
+
+               if (type == PEER_TYPE_P2P_GROUP) {
+                       menu->addAction(tr("Show passphrase"), this,
+                                       SLOT(ctx_p2p_show_passphrase()));
+                       menu->addAction(tr("Remove P2P Group"), this,
+                                       SLOT(ctx_p2p_remove_group()));
+               }
+
+               if (type == PEER_TYPE_P2P_PERSISTENT_GROUP_GO ||
+                   type == PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT ||
+                   type == PEER_TYPE_P2P_INVITATION) {
+                       menu->addAction(tr("Start group"), this,
+                                       SLOT(ctx_p2p_start_persistent()));
+               }
+
+               if (type == PEER_TYPE_P2P_PERSISTENT_GROUP_GO ||
+                   type == PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT) {
+                       menu->addAction(tr("Invite"), this,
+                                       SLOT(ctx_p2p_invite()));
+               }
+
+               if (type == PEER_TYPE_P2P_INVITATION) {
+                       menu->addAction(tr("Ignore"), this,
+                                       SLOT(ctx_p2p_delete()));
+               }
+
                if (type == PEER_TYPE_AP_WPS) {
                        menu->addAction(tr("Connect (PBC)"), this,
                                        SLOT(connect_pbc()));
@@ -183,6 +286,20 @@ void Peers::context_menu(const QPoint &pos)
                ctx_item = NULL;
                menu->addAction(QString(tr("Refresh")), this,
                                SLOT(ctx_refresh()));
+               menu->addAction(tr("Start P2P discovery"), this,
+                               SLOT(ctx_p2p_start()));
+               menu->addAction(tr("Stop P2P discovery"), this,
+                               SLOT(ctx_p2p_stop()));
+               menu->addAction(tr("P2P listen only"), this,
+                               SLOT(ctx_p2p_listen()));
+               menu->addAction(tr("Start P2P group"), this,
+                               SLOT(ctx_p2p_start_group()));
+               if (hide_ap)
+                       menu->addAction(tr("Show AP entries"), this,
+                                       SLOT(ctx_show_ap()));
+               else
+                       menu->addAction(tr("Hide AP entries"), this,
+                                       SLOT(ctx_hide_ap()));
        }
 
        menu->exec(peers->mapToGlobal(pos));
@@ -197,10 +314,9 @@ void Peers::enter_pin()
        int peer_type = ctx_item->data(peer_role_type).toInt();
        QString uuid;
        QString addr;
+       addr = ctx_item->data(peer_role_address).toString();
        if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE)
                uuid = ctx_item->data(peer_role_uuid).toString();
-       else
-               addr = ctx_item->data(peer_role_address).toString();
 
        StringQuery input(tr("PIN:"));
        input.setWindowTitle(tr("PIN for ") + ctx_item->text());
@@ -212,9 +328,10 @@ void Peers::enter_pin()
        size_t reply_len;
 
        if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE) {
-               snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s",
+               snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s %s",
                         uuid.toAscii().constData(),
-                        input.get_string().toAscii().constData());
+                        input.get_string().toAscii().constData(),
+                        addr.toAscii().constData());
        } else {
                snprintf(cmd, sizeof(cmd), "WPS_PIN %s %s",
                         addr.toAscii().constData(),
@@ -236,6 +353,60 @@ void Peers::ctx_refresh()
 }
 
 
+void Peers::ctx_p2p_start()
+{
+       char reply[20];
+       size_t reply_len;
+       reply_len = sizeof(reply) - 1;
+       if (wpagui->ctrlRequest("P2P_FIND", reply, &reply_len) < 0 ||
+           memcmp(reply, "FAIL", 4) == 0) {
+               QMessageBox msg;
+               msg.setIcon(QMessageBox::Warning);
+               msg.setText("Failed to start P2P discovery.");
+               msg.exec();
+       }
+}
+
+
+void Peers::ctx_p2p_stop()
+{
+       char reply[20];
+       size_t reply_len;
+       reply_len = sizeof(reply) - 1;
+       wpagui->ctrlRequest("P2P_STOP_FIND", reply, &reply_len);
+}
+
+
+void Peers::ctx_p2p_listen()
+{
+       char reply[20];
+       size_t reply_len;
+       reply_len = sizeof(reply) - 1;
+       if (wpagui->ctrlRequest("P2P_LISTEN 3600", reply, &reply_len) < 0 ||
+           memcmp(reply, "FAIL", 4) == 0) {
+               QMessageBox msg;
+               msg.setIcon(QMessageBox::Warning);
+               msg.setText("Failed to start P2P listen.");
+               msg.exec();
+       }
+}
+
+
+void Peers::ctx_p2p_start_group()
+{
+       char reply[20];
+       size_t reply_len;
+       reply_len = sizeof(reply) - 1;
+       if (wpagui->ctrlRequest("P2P_GROUP_ADD", reply, &reply_len) < 0 ||
+           memcmp(reply, "FAIL", 4) == 0) {
+               QMessageBox msg;
+               msg.setIcon(QMessageBox::Warning);
+               msg.setText("Failed to start P2P group.");
+               msg.exec();
+       }
+}
+
+
 void Peers::add_station(QString info)
 {
        QStringList lines = info.split(QRegExp("\\n"));
@@ -249,6 +420,8 @@ void Peers::add_station(QString info)
 
                if ((*it).startsWith("wpsDeviceName="))
                        name = (*it).mid(pos);
+               else if ((*it).startsWith("p2p_device_name="))
+                       name = (*it).mid(pos);
        }
 
        if (name.isEmpty())
@@ -256,6 +429,24 @@ void Peers::add_station(QString info)
 
        QStandardItem *item = new QStandardItem(*laptop_icon, name);
        if (item) {
+               /* Remove WPS enrollee entry if one is still pending */
+               if (model.rowCount() > 0) {
+                       QModelIndexList lst = model.match(model.index(0, 0),
+                                                         peer_role_address,
+                                                         lines[0]);
+                       for (int i = 0; i < lst.size(); i++) {
+                               QStandardItem *item;
+                               item = model.itemFromIndex(lst[i]);
+                               if (item == NULL)
+                                       continue;
+                               int type = item->data(peer_role_type).toInt();
+                               if (type == PEER_TYPE_WPS_ENROLLEE) {
+                                       model.removeRow(lst[i].row());
+                                       break;
+                               }
+                       }
+               }
+
                item->setData(lines[0], peer_role_address);
                item->setData(PEER_TYPE_ASSOCIATED_STATION,
                              peer_role_type);
@@ -322,6 +513,55 @@ void Peers::add_single_station(const char *addr)
 }
 
 
+void Peers::add_p2p_group_client(QStandardItem * /*parent*/, QString params)
+{
+       /*
+        * dev=02:b5:64:63:30:63 iface=02:b5:64:63:30:63 dev_capab=0x0
+        * dev_type=1-0050f204-1 dev_name='Wireless Client'
+        * config_methods=0x8c
+        */
+
+       QStringList items =
+               params.split(QRegExp(" (?=[^']*('[^']*'[^']*)*$)"));
+       QString addr = "";
+       QString name = "";
+       int config_methods = 0;
+       QString dev_type;
+
+       for (int i = 0; i < items.size(); i++) {
+               QString str = items.at(i);
+               int pos = str.indexOf('=') + 1;
+               if (str.startsWith("dev_name='"))
+                       name = str.section('\'', 1, -2);
+               else if (str.startsWith("config_methods="))
+                       config_methods =
+                               str.section('=', 1).toInt(0, 0);
+               else if (str.startsWith("dev="))
+                       addr = str.mid(pos);
+               else if (str.startsWith("dev_type=") && dev_type.isEmpty())
+                       dev_type = str.mid(pos);
+       }
+
+       QStandardItem *item = find_addr(addr);
+       if (item)
+               return;
+
+       item = new QStandardItem(*default_icon, name);
+       if (item) {
+               /* TODO: indicate somehow the relationship to the group owner
+                * (parent) */
+               item->setData(addr, peer_role_address);
+               item->setData(config_methods, peer_role_config_methods);
+               item->setData(PEER_TYPE_P2P_CLIENT, peer_role_type);
+               if (!dev_type.isEmpty())
+                       item->setData(dev_type, peer_role_pri_dev_type);
+               item->setData(items.join(QString("\n")), peer_role_details);
+               item->setToolTip(ItemType(PEER_TYPE_P2P_CLIENT));
+               model.appendRow(item);
+       }
+}
+
+
 void Peers::remove_bss(int id)
 {
        if (model.rowCount() == 0)
@@ -340,6 +580,9 @@ bool Peers::add_bss(const char *cmd)
        char reply[2048];
        size_t reply_len;
 
+       if (hide_ap)
+               return false;
+
        reply_len = sizeof(reply) - 1;
        if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
                return false;
@@ -403,6 +646,14 @@ bool Peers::add_bss(const char *cmd)
                if (!ssid.isEmpty())
                        item->setData(ssid, peer_role_ssid);
                model.appendRow(item);
+
+               lines = bss.split(QRegExp("\\n"));
+               for (QStringList::Iterator it = lines.begin();
+                    it != lines.end(); it++) {
+                       if ((*it).startsWith("p2p_group_client:"))
+                               add_p2p_group_client(item,
+                                                    (*it).mid(18));
+               }
        }
 
        return true;
@@ -426,6 +677,95 @@ void Peers::add_scan_results()
 }
 
 
+void Peers::add_persistent(int id, const char *ssid, const char *bssid)
+{
+       char cmd[100];
+       char reply[100];
+       size_t reply_len;
+       int mode;
+
+       snprintf(cmd, sizeof(cmd), "GET_NETWORK %d mode", id);
+       if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
+               return;
+       reply[reply_len] = '\0';
+       mode = atoi(reply);
+
+       QString name = ssid;
+       name = '[' + name + ']';
+
+       QStandardItem *item = new QStandardItem(*group_icon, name);
+       if (!item)
+               return;
+
+       int type;
+       if (mode == 3)
+               type = PEER_TYPE_P2P_PERSISTENT_GROUP_GO;
+       else
+               type = PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT;
+       item->setData(type, peer_role_type);
+       item->setToolTip(ItemType(type));
+       item->setData(ssid, peer_role_ssid);
+       if (bssid && strcmp(bssid, "any") == 0)
+               bssid = NULL;
+       if (bssid)
+               item->setData(bssid, peer_role_address);
+       item->setData(id, peer_role_network_id);
+       item->setBackground(Qt::BDiagPattern);
+
+       model.appendRow(item);
+}
+
+
+void Peers::add_persistent_groups()
+{
+       char buf[2048], *start, *end, *id, *ssid, *bssid, *flags;
+       size_t len;
+
+       len = sizeof(buf) - 1;
+       if (wpagui->ctrlRequest("LIST_NETWORKS", buf, &len) < 0)
+               return;
+
+       buf[len] = '\0';
+       start = strchr(buf, '\n');
+       if (start == NULL)
+               return;
+       start++;
+
+       while (*start) {
+               bool last = false;
+               end = strchr(start, '\n');
+               if (end == NULL) {
+                       last = true;
+                       end = start;
+                       while (end[0] && end[1])
+                               end++;
+               }
+               *end = '\0';
+
+               id = start;
+               ssid = strchr(id, '\t');
+               if (ssid == NULL)
+                       break;
+               *ssid++ = '\0';
+               bssid = strchr(ssid, '\t');
+               if (bssid == NULL)
+                       break;
+               *bssid++ = '\0';
+               flags = strchr(bssid, '\t');
+               if (flags == NULL)
+                       break;
+               *flags++ = '\0';
+
+               if (strstr(flags, "[DISABLED][P2P-PERSISTENT]"))
+                       add_persistent(atoi(id), ssid, bssid);
+
+               if (last)
+                       break;
+               start = end + 1;
+       }
+}
+
+
 void Peers::update_peers()
 {
        model.clear();
@@ -438,6 +778,7 @@ void Peers::update_peers()
 
        add_stations();
        add_scan_results();
+       add_persistent_groups();
 }
 
 
@@ -454,6 +795,22 @@ QStandardItem * Peers::find_addr(QString addr)
 }
 
 
+QStandardItem * Peers::find_addr_type(QString addr, int type)
+{
+       if (model.rowCount() == 0)
+               return NULL;
+
+       QModelIndexList lst = model.match(model.index(0, 0), peer_role_address,
+                                         addr);
+       for (int i = 0; i < lst.size(); i++) {
+               QStandardItem *item = model.itemFromIndex(lst[i]);
+               if (item->data(peer_role_type).toInt() == type)
+                       return item;
+       }
+       return NULL;
+}
+
+
 QStandardItem * Peers::find_uuid(QString uuid)
 {
        if (model.rowCount() == 0)
@@ -530,16 +887,225 @@ void Peers::event_notify(WpaMsg msg)
                        return;
 
                QModelIndexList lst = model.match(model.index(0, 0),
-                                                 peer_role_address, addr);
+                                                 peer_role_address, addr, -1);
                for (int i = 0; i < lst.size(); i++) {
                        QStandardItem *item = model.itemFromIndex(lst[i]);
                        if (item && item->data(peer_role_type).toInt() ==
-                           PEER_TYPE_ASSOCIATED_STATION)
+                           PEER_TYPE_ASSOCIATED_STATION) {
                                model.removeRow(lst[i].row());
+                               break;
+                       }
+               }
+               return;
+       }
+
+       if (text.startsWith(P2P_EVENT_DEVICE_FOUND)) {
+               /*
+                * P2P-DEVICE-FOUND 02:b5:64:63:30:63
+                * p2p_dev_addr=02:b5:64:63:30:63 pri_dev_type=1-0050f204-1
+                * name='Wireless Client' config_methods=0x84 dev_capab=0x21
+                * group_capab=0x0
+                */
+               QStringList items =
+                       text.split(QRegExp(" (?=[^']*('[^']*'[^']*)*$)"));
+               QString addr = items[1];
+               QString name = "";
+               QString pri_dev_type;
+               int config_methods = 0;
+               for (int i = 0; i < items.size(); i++) {
+                       QString str = items.at(i);
+                       if (str.startsWith("name='"))
+                               name = str.section('\'', 1, -2);
+                       else if (str.startsWith("config_methods="))
+                               config_methods =
+                                       str.section('=', 1).toInt(0, 0);
+                       else if (str.startsWith("pri_dev_type="))
+                               pri_dev_type = str.section('=', 1);
+               }
+
+               QStandardItem *item = find_addr(addr);
+               if (item) {
+                       int type = item->data(peer_role_type).toInt();
+                       if (type == PEER_TYPE_P2P)
+                               return;
+               }
+
+               item = new QStandardItem(*default_icon, name);
+               if (item) {
+                       item->setData(addr, peer_role_address);
+                       item->setData(config_methods,
+                                     peer_role_config_methods);
+                       item->setData(PEER_TYPE_P2P, peer_role_type);
+                       if (!pri_dev_type.isEmpty())
+                               item->setData(pri_dev_type,
+                                             peer_role_pri_dev_type);
+                       item->setData(items.join(QString("\n")),
+                                     peer_role_details);
+                       item->setToolTip(ItemType(PEER_TYPE_P2P));
+                       model.appendRow(item);
+               }
+
+               item = find_addr_type(addr,
+                                     PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT);
+               if (item)
+                       item->setBackground(Qt::NoBrush);
+       }
+
+       if (text.startsWith(P2P_EVENT_GROUP_STARTED)) {
+               /* P2P-GROUP-STARTED wlan0-p2p-0 GO ssid="DIRECT-3F"
+                * passphrase="YOyTkxID" go_dev_addr=02:40:61:c2:f3:b7
+                * [PERSISTENT] */
+               QStringList items = text.split(' ');
+               if (items.size() < 4)
+                       return;
+
+               int pos = text.indexOf(" ssid=\"");
+               if (pos < 0)
+                       return;
+               QString ssid = text.mid(pos + 7);
+               pos = ssid.indexOf(" passphrase=\"");
+               if (pos < 0)
+                       pos = ssid.indexOf(" psk=");
+               if (pos >= 0)
+                       ssid.truncate(pos);
+               pos = ssid.lastIndexOf('"');
+               if (pos >= 0)
+                       ssid.truncate(pos);
+
+               QStandardItem *item = new QStandardItem(*group_icon, ssid);
+               if (item) {
+                       item->setData(PEER_TYPE_P2P_GROUP, peer_role_type);
+                       item->setData(items[1], peer_role_ifname);
+                       QString details;
+                       if (items[2] == "GO") {
+                               details = tr("P2P GO for interface ") +
+                                       items[1];
+                       } else {
+                               details = tr("P2P client for interface ") +
+                                       items[1];
+                       }
+                       if (text.contains(" [PERSISTENT]"))
+                               details += "\nPersistent group";
+                       item->setData(details, peer_role_details);
+                       item->setToolTip(ItemType(PEER_TYPE_P2P_GROUP));
+                       model.appendRow(item);
+               }
+       }
+
+       if (text.startsWith(P2P_EVENT_GROUP_REMOVED)) {
+               /* P2P-GROUP-REMOVED wlan0-p2p-0 GO */
+               QStringList items = text.split(' ');
+               if (items.size() < 2)
+                       return;
+
+               if (model.rowCount() == 0)
+                       return;
+
+               QModelIndexList lst = model.match(model.index(0, 0),
+                                                 peer_role_ifname, items[1]);
+               for (int i = 0; i < lst.size(); i++)
+                       model.removeRow(lst[i].row());
+               return;
+       }
+
+       if (text.startsWith(P2P_EVENT_PROV_DISC_SHOW_PIN)) {
+               /* P2P-PROV-DISC-SHOW-PIN 02:40:61:c2:f3:b7 12345670 */
+               QStringList items = text.split(' ');
+               if (items.size() < 3)
+                       return;
+               QString addr = items[1];
+               QString pin = items[2];
+
+               QStandardItem *item = find_addr_type(addr, PEER_TYPE_P2P);
+               if (item == NULL)
+                       return;
+               item->setData(SEL_METHOD_PIN_LOCAL_DISPLAY,
+                             peer_role_selected_method);
+               item->setData(pin, peer_role_selected_pin);
+               QVariant var = item->data(peer_role_requested_method);
+               if (var.isValid() &&
+                   var.toInt() == SEL_METHOD_PIN_LOCAL_DISPLAY) {
+                       ctx_item = item;
+                       ctx_p2p_display_pin_pd();
+               }
+               return;
+       }
+
+       if (text.startsWith(P2P_EVENT_PROV_DISC_ENTER_PIN)) {
+               /* P2P-PROV-DISC-ENTER-PIN 02:40:61:c2:f3:b7 */
+               QStringList items = text.split(' ');
+               if (items.size() < 2)
+                       return;
+               QString addr = items[1];
+
+               QStandardItem *item = find_addr_type(addr, PEER_TYPE_P2P);
+               if (item == NULL)
+                       return;
+               item->setData(SEL_METHOD_PIN_PEER_DISPLAY,
+                             peer_role_selected_method);
+               QVariant var = item->data(peer_role_requested_method);
+               if (var.isValid() &&
+                   var.toInt() == SEL_METHOD_PIN_PEER_DISPLAY) {
+                       ctx_item = item;
+                       ctx_p2p_connect();
                }
                return;
        }
 
+       if (text.startsWith(P2P_EVENT_INVITATION_RECEIVED)) {
+               /* P2P-INVITATION-RECEIVED sa=02:f0:bc:44:87:62 persistent=4 */
+               QStringList items = text.split(' ');
+               if (items.size() < 3)
+                       return;
+               if (!items[1].startsWith("sa=") ||
+                   !items[2].startsWith("persistent="))
+                       return;
+               QString addr = items[1].mid(3);
+               int id = items[2].mid(11).toInt();
+
+               char cmd[100];
+               char reply[100];
+               size_t reply_len;
+
+               snprintf(cmd, sizeof(cmd), "GET_NETWORK %d ssid", id);
+               reply_len = sizeof(reply) - 1;
+               if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
+                       return;
+               reply[reply_len] = '\0';
+               QString name;
+               char *pos = strrchr(reply, '"');
+               if (pos && reply[0] == '"') {
+                       *pos = '\0';
+                       name = reply + 1;
+               } else
+                       name = reply;
+
+               QStandardItem *item;
+               item = find_addr_type(addr, PEER_TYPE_P2P_INVITATION);
+               if (item)
+                       model.removeRow(item->row());
+
+               item = new QStandardItem(*invitation_icon, name);
+               if (!item)
+                       return;
+               item->setData(PEER_TYPE_P2P_INVITATION, peer_role_type);
+               item->setToolTip(ItemType(PEER_TYPE_P2P_INVITATION));
+               item->setData(addr, peer_role_address);
+               item->setData(id, peer_role_network_id);
+
+               model.appendRow(item);
+
+               enable_persistent(id);
+
+               return;
+       }
+
+       if (text.startsWith(P2P_EVENT_INVITATION_RESULT)) {
+               /* P2P-INVITATION-RESULT status=1 */
+               /* TODO */
+               return;
+       }
+
        if (text.startsWith(WPS_EVENT_ER_AP_ADD)) {
                /*
                 * WPS-ER-AP-ADD 87654321-9abc-def0-1234-56789abc0002
@@ -700,6 +1266,13 @@ void Peers::event_notify(WpaMsg msg)
                int dev_passwd_id = items[5].toInt();
                QString name;
 
+               QStandardItem *item = find_addr(addr);
+               if (item) {
+                       int type = item->data(peer_role_type).toInt();
+                       if (type == PEER_TYPE_ASSOCIATED_STATION)
+                               return; /* already associated */
+               }
+
                int pos = text.indexOf('[');
                if (pos >= 0) {
                        int pos2 = text.lastIndexOf(']');
@@ -713,8 +1286,6 @@ void Peers::event_notify(WpaMsg msg)
                if (name.isEmpty())
                        name = addr;
 
-               QStandardItem *item;
-
                item = find_uuid(uuid);
                if (item) {
                        QVariant var = item->data(peer_role_config_methods);
@@ -767,6 +1338,221 @@ void Peers::event_notify(WpaMsg msg)
 }
 
 
+void Peers::ctx_p2p_connect()
+{
+       if (ctx_item == NULL)
+               return;
+       QString addr = ctx_item->data(peer_role_address).toString();
+       QString arg;
+       int config_methods =
+               ctx_item->data(peer_role_config_methods).toInt();
+       enum selected_method method = SEL_METHOD_NONE;
+       QVariant var = ctx_item->data(peer_role_selected_method);
+       if (var.isValid())
+               method = (enum selected_method) var.toInt();
+       if (method == SEL_METHOD_PIN_LOCAL_DISPLAY) {
+               arg = ctx_item->data(peer_role_selected_pin).toString();
+               char cmd[100];
+               char reply[100];
+               size_t reply_len;
+               snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s display",
+                        addr.toAscii().constData(),
+                        arg.toAscii().constData());
+               reply_len = sizeof(reply) - 1;
+               if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
+                       QMessageBox msg;
+                       msg.setIcon(QMessageBox::Warning);
+                       msg.setText("Failed to initiate P2P connect.");
+                       msg.exec();
+                       return;
+               }
+               QMessageBox::information(this,
+                                        tr("PIN for ") + ctx_item->text(),
+                                        tr("Enter the following PIN on the\n"
+                                           "peer device: ") + arg);
+       } else if (method == SEL_METHOD_PIN_PEER_DISPLAY) {
+               StringQuery input(tr("PIN from peer display:"));
+               input.setWindowTitle(tr("PIN for ") + ctx_item->text());
+               if (input.exec() != QDialog::Accepted)
+                       return;
+               arg = input.get_string();
+       } else if (config_methods == 0x0080 /* PBC */) {
+               arg = "pbc";
+       } else {
+               StringQuery input(tr("PIN:"));
+               input.setWindowTitle(tr("PIN for ") + ctx_item->text());
+               if (input.exec() != QDialog::Accepted)
+                       return;
+               arg = input.get_string();
+       }
+
+       char cmd[100];
+       char reply[100];
+       size_t reply_len;
+       snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s",
+                addr.toAscii().constData(),
+                arg.toAscii().constData());
+       reply_len = sizeof(reply) - 1;
+       if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
+               QMessageBox msg;
+               msg.setIcon(QMessageBox::Warning);
+               msg.setText("Failed to initiate P2P connect.");
+               msg.exec();
+       }
+}
+
+
+void Peers::ctx_p2p_req_pin()
+{
+       if (ctx_item == NULL)
+               return;
+       QString addr = ctx_item->data(peer_role_address).toString();
+       ctx_item->setData(SEL_METHOD_PIN_PEER_DISPLAY,
+                         peer_role_requested_method);
+
+       char cmd[100];
+       char reply[100];
+       size_t reply_len;
+       snprintf(cmd, sizeof(cmd), "P2P_PROV_DISC %s display",
+                addr.toAscii().constData());
+       reply_len = sizeof(reply) - 1;
+       if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
+               QMessageBox msg;
+               msg.setIcon(QMessageBox::Warning);
+               msg.setText(tr("Failed to request PIN from peer."));
+               msg.exec();
+       }
+}
+
+
+void Peers::ctx_p2p_show_pin()
+{
+       if (ctx_item == NULL)
+               return;
+       QString addr = ctx_item->data(peer_role_address).toString();
+       ctx_item->setData(SEL_METHOD_PIN_LOCAL_DISPLAY,
+                         peer_role_requested_method);
+
+       char cmd[100];
+       char reply[100];
+       size_t reply_len;
+       snprintf(cmd, sizeof(cmd), "P2P_PROV_DISC %s keypad",
+                addr.toAscii().constData());
+       reply_len = sizeof(reply) - 1;
+       if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
+               QMessageBox msg;
+               msg.setIcon(QMessageBox::Warning);
+               msg.setText(tr("Failed to request peer to enter PIN."));
+               msg.exec();
+       }
+}
+
+
+void Peers::ctx_p2p_display_pin()
+{
+       if (ctx_item == NULL)
+               return;
+       QString addr = ctx_item->data(peer_role_address).toString();
+
+       char cmd[100];
+       char reply[100];
+       size_t reply_len;
+       snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s pin",
+                addr.toAscii().constData());
+       reply_len = sizeof(reply) - 1;
+       if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
+               QMessageBox msg;
+               msg.setIcon(QMessageBox::Warning);
+               msg.setText("Failed to initiate P2P connect.");
+               msg.exec();
+               return;
+       }
+       reply[reply_len] = '\0';
+       QMessageBox::information(this,
+                                tr("PIN for ") + ctx_item->text(),
+                                tr("Enter the following PIN on the\n"
+                                   "peer device: ") + reply);
+}
+
+
+void Peers::ctx_p2p_display_pin_pd()
+{
+       if (ctx_item == NULL)
+               return;
+       QString addr = ctx_item->data(peer_role_address).toString();
+       QString arg = ctx_item->data(peer_role_selected_pin).toString();
+
+       char cmd[100];
+       char reply[100];
+       size_t reply_len;
+       snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s display",
+                addr.toAscii().constData(),
+                arg.toAscii().constData());
+       reply_len = sizeof(reply) - 1;
+       if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
+               QMessageBox msg;
+               msg.setIcon(QMessageBox::Warning);
+               msg.setText("Failed to initiate P2P connect.");
+               msg.exec();
+               return;
+       }
+       reply[reply_len] = '\0';
+       QMessageBox::information(this,
+                                tr("PIN for ") + ctx_item->text(),
+                                tr("Enter the following PIN on the\n"
+                                   "peer device: ") + arg);
+}
+
+
+void Peers::ctx_p2p_enter_pin()
+{
+       if (ctx_item == NULL)
+               return;
+       QString addr = ctx_item->data(peer_role_address).toString();
+       QString arg;
+
+       StringQuery input(tr("PIN from peer:"));
+       input.setWindowTitle(tr("PIN for ") + ctx_item->text());
+       if (input.exec() != QDialog::Accepted)
+               return;
+       arg = input.get_string();
+
+       char cmd[100];
+       char reply[100];
+       size_t reply_len;
+       snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s keypad",
+                addr.toAscii().constData(),
+                arg.toAscii().constData());
+       reply_len = sizeof(reply) - 1;
+       if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
+               QMessageBox msg;
+               msg.setIcon(QMessageBox::Warning);
+               msg.setText("Failed to initiate P2P connect.");
+               msg.exec();
+       }
+}
+
+
+void Peers::ctx_p2p_remove_group()
+{
+       if (ctx_item == NULL)
+               return;
+       char cmd[100];
+       char reply[100];
+       size_t reply_len;
+       snprintf(cmd, sizeof(cmd), "P2P_GROUP_REMOVE %s",
+                ctx_item->data(peer_role_ifname).toString().toAscii().
+                constData());
+       reply_len = sizeof(reply) - 1;
+       if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
+               QMessageBox msg;
+               msg.setIcon(QMessageBox::Warning);
+               msg.setText("Failed to remove P2P Group.");
+               msg.exec();
+       }
+}
+
+
 void Peers::closeEvent(QCloseEvent *)
 {
        if (wpagui) {
@@ -864,6 +1650,27 @@ void Peers::properties()
                info += "\n";
        }
 
+       var = ctx_item->data(peer_role_selected_method);
+       if (var.isValid()) {
+               enum selected_method method =
+                       (enum selected_method) var.toInt();
+               switch (method) {
+               case SEL_METHOD_NONE:
+                       break;
+               case SEL_METHOD_PIN_PEER_DISPLAY:
+                       info += tr("Selected Method: PIN on peer display\n");
+                       break;
+               case SEL_METHOD_PIN_LOCAL_DISPLAY:
+                       info += tr("Selected Method: PIN on local display\n");
+                       break;
+               }
+       }
+
+       var = ctx_item->data(peer_role_selected_pin);
+       if (var.isValid()) {
+               info += tr("PIN to enter on peer: ") + var.toString() + "\n";
+       }
+
        var = ctx_item->data(peer_role_dev_passwd_id);
        if (var.isValid()) {
                info += tr("Device Password ID: ") + var.toString();
@@ -914,6 +1721,11 @@ void Peers::connect_pbc()
                snprintf(cmd, sizeof(cmd), "WPS_ER_PBC %s",
                         ctx_item->data(peer_role_uuid).toString().toAscii().
                         constData());
+       } else if (peer_type == PEER_TYPE_P2P ||
+                  peer_type == PEER_TYPE_P2P_CLIENT) {
+               snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s pbc",
+                        ctx_item->data(peer_role_address).toString().
+                        toAscii().constData());
        } else {
                snprintf(cmd, sizeof(cmd), "WPS_PBC");
        }
@@ -954,3 +1766,124 @@ void Peers::learn_ap_config()
                msg.exec();
        }
 }
+
+
+void Peers::ctx_hide_ap()
+{
+       hide_ap = true;
+
+       if (model.rowCount() == 0)
+               return;
+
+       do {
+               QModelIndexList lst;
+               lst = model.match(model.index(0, 0),
+                                 peer_role_type, PEER_TYPE_AP);
+               if (lst.size() == 0) {
+                       lst = model.match(model.index(0, 0),
+                                         peer_role_type, PEER_TYPE_AP_WPS);
+                       if (lst.size() == 0)
+                               break;
+               }
+
+               model.removeRow(lst[0].row());
+       } while (1);
+}
+
+
+void Peers::ctx_show_ap()
+{
+       hide_ap = false;
+       add_scan_results();
+}
+
+
+void Peers::ctx_p2p_show_passphrase()
+{
+       char reply[64];
+       size_t reply_len;
+
+       reply_len = sizeof(reply) - 1;
+       if (wpagui->ctrlRequest("P2P_GET_PASSPHRASE", reply, &reply_len) < 0 ||
+           memcmp(reply, "FAIL", 4) == 0) {
+               QMessageBox msg;
+               msg.setIcon(QMessageBox::Warning);
+               msg.setText("Failed to get P2P group passphrase.");
+               msg.exec();
+       } else {
+               reply[reply_len] = '\0';
+               QMessageBox::information(this, tr("Passphrase"),
+                                        tr("P2P group passphrase:\n") +
+                                        reply);
+       }
+}
+
+
+void Peers::ctx_p2p_start_persistent()
+{
+       if (ctx_item == NULL)
+               return;
+
+       char cmd[100];
+       char reply[100];
+       size_t reply_len;
+
+       snprintf(cmd, sizeof(cmd), "P2P_GROUP_ADD persistent=%d",
+                ctx_item->data(peer_role_network_id).toInt());
+       if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0 ||
+           memcmp(reply, "FAIL", 4) == 0) {
+               QMessageBox msg;
+               msg.setIcon(QMessageBox::Warning);
+               msg.setText(tr("Failed to start persistent P2P Group."));
+               msg.exec();
+       } else if (ctx_item->data(peer_role_type).toInt() ==
+                  PEER_TYPE_P2P_INVITATION)
+               model.removeRow(ctx_item->row());
+}
+
+
+void Peers::ctx_p2p_invite()
+{
+       if (ctx_item == NULL)
+               return;
+
+       char cmd[100];
+       char reply[100];
+       size_t reply_len;
+
+       snprintf(cmd, sizeof(cmd), "P2P_INVITE persistent=%d",
+                ctx_item->data(peer_role_network_id).toInt());
+       if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0 ||
+           memcmp(reply, "FAIL", 4) == 0) {
+               QMessageBox msg;
+               msg.setIcon(QMessageBox::Warning);
+               msg.setText(tr("Failed to invite peer to start persistent "
+                              "P2P Group."));
+               msg.exec();
+       }
+}
+
+
+void Peers::ctx_p2p_delete()
+{
+       if (ctx_item == NULL)
+               return;
+       model.removeRow(ctx_item->row());
+}
+
+
+void Peers::enable_persistent(int id)
+{
+       if (model.rowCount() == 0)
+               return;
+
+       QModelIndexList lst = model.match(model.index(0, 0),
+                                         peer_role_network_id, id);
+       for (int i = 0; i < lst.size(); i++) {
+               QStandardItem *item = model.itemFromIndex(lst[i]);
+               int type = item->data(peer_role_type).toInt();
+               if (type == PEER_TYPE_P2P_PERSISTENT_GROUP_GO ||
+                   type == PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT)
+                       item->setBackground(Qt::NoBrush);
+       }
+}
index 89b7b5b..a715395 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * wpa_gui - Peers class
- * Copyright (c) 2009, Atheros Communications
+ * Copyright (c) 2009-2010, Atheros Communications
  *
  * 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
@@ -39,7 +39,24 @@ public slots:
        virtual void connect_pbc();
        virtual void learn_ap_config();
        virtual void ctx_refresh();
+       virtual void ctx_p2p_start();
+       virtual void ctx_p2p_stop();
+       virtual void ctx_p2p_listen();
+       virtual void ctx_p2p_start_group();
+       virtual void ctx_p2p_remove_group();
+       virtual void ctx_p2p_connect();
+       virtual void ctx_p2p_req_pin();
+       virtual void ctx_p2p_show_pin();
+       virtual void ctx_p2p_display_pin();
+       virtual void ctx_p2p_display_pin_pd();
+       virtual void ctx_p2p_enter_pin();
        virtual void properties();
+       virtual void ctx_hide_ap();
+       virtual void ctx_show_ap();
+       virtual void ctx_p2p_show_passphrase();
+       virtual void ctx_p2p_start_persistent();
+       virtual void ctx_p2p_invite();
+       virtual void ctx_p2p_delete();
 
 protected slots:
        virtual void languageChange();
@@ -52,19 +69,28 @@ private:
        bool add_bss(const char *cmd);
        void remove_bss(int id);
        void add_scan_results();
+       void add_persistent(int id, const char *ssid, const char *bssid);
+       void add_persistent_groups();
        void update_peers();
        QStandardItem * find_addr(QString addr);
+       QStandardItem * find_addr_type(QString addr, int type);
+       void add_p2p_group_client(QStandardItem *parent, QString params);
        QStandardItem * find_uuid(QString uuid);
        void done(int r);
        void remove_enrollee_uuid(QString uuid);
        QString ItemType(int type);
+       void enable_persistent(int id);
 
        WpaGui *wpagui;
        QStandardItemModel model;
        QIcon *default_icon;
        QIcon *ap_icon;
        QIcon *laptop_icon;
+       QIcon *group_icon;
+       QIcon *invitation_icon;
        QStandardItem *ctx_item;
+
+       bool hide_ap;
 };
 
 #endif /* PEERS_H */
index 2057d67..97bf5ac 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * wpa_gui - WpaGui class
- * Copyright (c) 2005-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2005-2011, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -659,6 +659,13 @@ void WpaGui::updateNetworks()
                        break;
                *flags++ = '\0';
 
+               if (strstr(flags, "[DISABLED][P2P-PERSISTENT]")) {
+                       if (last)
+                               break;
+                       start = end + 1;
+                       continue;
+               }
+
                QString network(id);
                network.append(": ");
                network.append(ssid);
@@ -712,7 +719,7 @@ void WpaGui::helpContents()
 void WpaGui::helpAbout()
 {
        QMessageBox::about(this, "wpa_gui for wpa_supplicant",
-                          "Copyright (c) 2003-2010,\n"
+                          "Copyright (c) 2003-2011,\n"
                           "Jouni Malinen <j@w1.fi>\n"
                           "and contributors.\n"
                           "\n"
@@ -899,6 +906,15 @@ void WpaGui::processMsg(char *msg)
                if (textStatus->text() == "INACTIVE" ||
                    textStatus->text() == "DISCONNECTED")
                        wpaguiTab->setCurrentWidget(wpsTab);
+       } else if (str_match(pos, WPS_EVENT_AP_AVAILABLE_AUTH)) {
+               showTrayMessage(QSystemTrayIcon::Information, 3,
+                               "Wi-Fi Protected Setup (WPS) AP\n"
+                               "indicating this client is authorized.");
+               wpsStatusText->setText("WPS AP indicating this client is "
+                                      "authorized");
+               if (textStatus->text() == "INACTIVE" ||
+                   textStatus->text() == "DISCONNECTED")
+                       wpaguiTab->setCurrentWidget(wpsTab);
        } else if (str_match(pos, WPS_EVENT_AP_AVAILABLE)) {
                wpsStatusText->setText(tr("WPS AP detected"));
        } else if (str_match(pos, WPS_EVENT_OVERLAP)) {
index d2a991b..c8854da 100644 (file)
@@ -825,7 +825,7 @@ static void wpa_priv_send_ft_response(struct wpa_priv_interface *iface,
 }
 
 
-void wpa_supplicant_event(void *ctx, wpa_event_type event,
+void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                          union wpa_event_data *data)
 {
        struct wpa_priv_interface *iface = ctx;
@@ -915,7 +915,7 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
 }
 
 
-static void wpa_priv_terminate(int sig, void *eloop_ctx, void *signal_ctx)
+static void wpa_priv_terminate(int sig, void *signal_ctx)
 {
        wpa_printf(MSG_DEBUG, "wpa_priv termination requested");
        eloop_terminate();
index 37a539d..cddc694 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant
- * Copyright (c) 2003-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -19,6 +19,8 @@
 #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_server/eap_methods.h"
 #include "rsn_supp/preauth.h"
 #include "rsn_supp/pmksa_cache.h"
 #include "common/wpa_ctrl.h"
-#include "mlme.h"
 #include "common/ieee802_11_defs.h"
+#include "p2p/p2p.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 "notify.h"
 #include "bgscan.h"
 #include "bss.h"
 #include "scan.h"
+#include "offchannel.h"
 
 const char *wpa_supplicant_version =
 "wpa_supplicant v" VERSION_STR "\n"
-"Copyright (c) 2003-2010, Jouni Malinen <j@w1.fi> and contributors";
+"Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi> and contributors";
 
 const char *wpa_supplicant_license =
 "This program is free software. You can distribute it and/or modify it\n"
@@ -130,9 +135,8 @@ int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
                        continue;
 
                set = 1;
-               wpa_drv_set_key(wpa_s, WPA_ALG_WEP,
-                               (u8 *) "\xff\xff\xff\xff\xff\xff",
-                               i, i == ssid->wep_tx_keyidx, (u8 *) "", 0,
+               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]);
        }
 
@@ -152,13 +156,14 @@ static int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s,
         * sending unicast and multicast packets. */
 
        if (ssid->mode != WPAS_MODE_IBSS) {
-               wpa_printf(MSG_INFO, "WPA: Invalid mode %d (not IBSS/ad-hoc) "
-                          "for WPA-None", ssid->mode);
+               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_printf(MSG_INFO, "WPA: No PSK configured for WPA-None");
+               wpa_msg(wpa_s, MSG_INFO, "WPA: No PSK configured for "
+                       "WPA-None");
                return -1;
        }
 
@@ -176,16 +181,15 @@ static int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s,
                alg = WPA_ALG_TKIP;
                break;
        default:
-               wpa_printf(MSG_INFO, "WPA: Invalid group cipher %d for "
-                          "WPA-None", wpa_s->group_cipher);
+               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.. */
 
-       return wpa_drv_set_key(wpa_s, alg, (u8 *) "\xff\xff\xff\xff\xff\xff",
-                              0, 1, seq, 6, key, keylen);
+       return wpa_drv_set_key(wpa_s, alg, NULL, 0, 1, seq, 6, key, keylen);
 }
 
 
@@ -201,7 +205,12 @@ static void wpa_supplicant_timeout(void *eloop_ctx, void *timeout_ctx)
        wpa_sm_notify_disassoc(wpa_s->wpa);
        wpa_supplicant_disassociate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
        wpa_s->reassociate = 1;
-       wpa_supplicant_req_scan(wpa_s, 0, 0);
+
+       /*
+        * 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);
 }
 
 
@@ -221,7 +230,7 @@ void wpa_supplicant_req_auth_timeout(struct wpa_supplicant *wpa_s,
            (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED))
                return;
 
-       wpa_msg(wpa_s, MSG_DEBUG, "Setting authentication timeout: %d sec "
+       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);
@@ -238,7 +247,7 @@ void wpa_supplicant_req_auth_timeout(struct wpa_supplicant *wpa_s,
  */
 void wpa_supplicant_cancel_auth_timeout(struct wpa_supplicant *wpa_s)
 {
-       wpa_msg(wpa_s, MSG_DEBUG, "Cancelling authentication timeout");
+       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);
 }
@@ -361,6 +370,22 @@ void wpa_supplicant_set_non_wpa_policy(struct wpa_supplicant *wpa_s,
 }
 
 
+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)
 {
        bgscan_deinit(wpa_s);
@@ -396,6 +421,10 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
 
        rsn_preauth_deinit(wpa_s->wpa);
 
+#ifdef CONFIG_TDLS
+       wpa_tdls_deinit(wpa_s->wpa);
+#endif /* CONFIG_TDLS */
+
        pmksa_candidate_free(wpa_s->wpa);
        wpa_sm_deinit(wpa_s->wpa);
        wpa_s->wpa = NULL;
@@ -405,8 +434,11 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
 
        wpa_supplicant_cancel_scan(wpa_s);
        wpa_supplicant_cancel_auth_timeout(wpa_s);
-
-       ieee80211_sta_deinit(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 */
 
        wpas_wps_deinit(wpa_s);
 
@@ -418,15 +450,29 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
        wpa_s->ibss_rsn = NULL;
 #endif /* CONFIG_IBSS_RSN */
 
-#ifdef CONFIG_SME
-       os_free(wpa_s->sme.ft_ies);
-       wpa_s->sme.ft_ies = NULL;
-       wpa_s->sme.ft_ies_len = 0;
-#endif /* CONFIG_SME */
+       sme_deinit(wpa_s);
 
 #ifdef CONFIG_AP
        wpa_supplicant_ap_deinit(wpa_s);
 #endif /* CONFIG_AP */
+
+#ifdef CONFIG_P2P
+       wpas_p2p_deinit(wpa_s);
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_OFFCHANNEL
+       offchannel_deinit(wpa_s);
+#endif /* CONFIG_OFFCHANNEL */
+
+       wpa_supplicant_cancel_sched_scan(wpa_s);
+
+       os_free(wpa_s->next_scan_freqs);
+       wpa_s->next_scan_freqs = NULL;
+
+       gas_query_deinit(wpa_s->gas);
+       wpa_s->gas = NULL;
+
+       free_hw_features(wpa_s);
 }
 
 
@@ -440,8 +486,6 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
  */
 void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr)
 {
-       u8 *bcast = (u8 *) "\xff\xff\xff\xff\xff\xff";
-
        if (wpa_s->keys_cleared) {
                /* Some drivers (e.g., ndiswrapper & NDIS drivers) seem to have
                 * timing issues with keys being cleared just before new keys
@@ -450,19 +494,19 @@ void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr)
                 * client not receiving the first encrypted packets correctly.
                 * Skipping some of the extra key clearing steps seems to help
                 * in completing group key handshake more reliably. */
-               wpa_printf(MSG_DEBUG, "No keys have been configured - "
-                          "skip key clearing");
+               wpa_dbg(wpa_s, MSG_DEBUG, "No keys have been configured - "
+                       "skip key clearing");
                return;
        }
 
        /* MLME-DELETEKEYS.request */
-       wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 0, 0, NULL, 0, NULL, 0);
-       wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 1, 0, NULL, 0, NULL, 0);
-       wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 2, 0, NULL, 0, NULL, 0);
-       wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 3, 0, NULL, 0, NULL, 0);
+       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, bcast, 4, 0, NULL, 0, NULL, 0);
-       wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 5, 0, NULL, 0, NULL, 0);
+       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 */
        if (addr) {
                wpa_drv_set_key(wpa_s, WPA_ALG_NONE, addr, 0, 0, NULL, 0, NULL,
@@ -489,6 +533,8 @@ const char * wpa_supplicant_state_txt(enum wpa_states state)
                return "DISCONNECTED";
        case WPA_INACTIVE:
                return "INACTIVE";
+       case WPA_INTERFACE_DISABLED:
+               return "INTERFACE_DISABLED";
        case WPA_SCANNING:
                return "SCANNING";
        case WPA_AUTHENTICATING:
@@ -509,6 +555,43 @@ const char * wpa_supplicant_state_txt(enum wpa_states state)
 }
 
 
+#ifdef CONFIG_BGSCAN
+
+static void wpa_supplicant_start_bgscan(struct wpa_supplicant *wpa_s)
+{
+       if (wpas_driver_bss_selection(wpa_s))
+               return;
+       if (wpa_s->current_ssid == wpa_s->bgscan_ssid)
+               return;
+
+       bgscan_deinit(wpa_s);
+       if (wpa_s->current_ssid && wpa_s->current_ssid->bgscan) {
+               if (bgscan_init(wpa_s, wpa_s->current_ssid)) {
+                       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
+                       wpa_s->bgscan_ssid = wpa_s->current_ssid;
+       } 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 */
+
+
 /**
  * wpa_supplicant_set_state - Set current connection state
  * @wpa_s: Pointer to wpa_supplicant data
@@ -522,9 +605,9 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s,
 {
        enum wpa_states old_state = wpa_s->wpa_state;
 
-       wpa_printf(MSG_DEBUG, "State: %s -> %s",
-                  wpa_supplicant_state_txt(wpa_s->wpa_state),
-                  wpa_supplicant_state_txt(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_SCANNING)
                wpa_supplicant_notify_scanning(wpa_s, 0);
@@ -542,16 +625,37 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s,
                wpa_s->new_connection = 0;
                wpa_s->reassociated_connection = 1;
                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;
+#ifdef CONFIG_P2P
+               wpas_p2p_completed(wpa_s);
+#endif /* CONFIG_P2P */
        } 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 */
        }
        wpa_s->wpa_state = state;
 
-       if (wpa_s->wpa_state != old_state)
+#ifdef CONFIG_BGSCAN
+       if (state == WPA_COMPLETED)
+               wpa_supplicant_start_bgscan(wpa_s);
+       else
+               wpa_supplicant_stop_bgscan(wpa_s);
+#endif /* CONFIG_BGSCAN */
+
+       if (wpa_s->wpa_state != old_state) {
                wpas_notify_state_changed(wpa_s, wpa_s->wpa_state, old_state);
+
+               if (wpa_s->wpa_state == WPA_COMPLETED ||
+                   old_state == WPA_COMPLETED)
+                       wpas_notify_auth_changed(wpa_s);
+       }
 }
 
 
@@ -584,7 +688,7 @@ static void wpa_supplicant_terminate(int sig, void *signal_ctx)
 }
 
 
-static void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s)
+void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s)
 {
        enum wpa_states old_state = wpa_s->wpa_state;
 
@@ -592,7 +696,8 @@ static void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s)
        wpa_s->group_cipher = 0;
        wpa_s->mgmt_group_cipher = 0;
        wpa_s->key_mgmt = 0;
-       wpa_s->wpa_state = WPA_DISCONNECTED;
+       if (wpa_s->wpa_state != WPA_INTERFACE_DISABLED)
+               wpa_s->wpa_state = WPA_DISCONNECTED;
 
        if (wpa_s->wpa_state != old_state)
                wpas_notify_state_changed(wpa_s, wpa_s->wpa_state, old_state);
@@ -613,7 +718,6 @@ static void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s)
 int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s)
 {
        struct wpa_config *conf;
-       struct wpa_ssid *old_ssid;
        int reconf_ctrl;
        int old_ap_scan;
 
@@ -625,6 +729,7 @@ int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s)
                        "file '%s' - exiting", wpa_s->confname);
                return -1;
        }
+       conf->changed_parameters = (unsigned int) -1;
 
        reconf_ctrl = !!conf->ctrl_interface != !!wpa_s->conf->ctrl_interface
                || (conf->ctrl_interface && wpa_s->conf->ctrl_interface &&
@@ -637,10 +742,10 @@ int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s)
        }
 
        eapol_sm_invalidate_cached_session(wpa_s->eapol);
-       old_ssid = wpa_s->current_ssid;
-       wpa_s->current_ssid = NULL;
-       if (old_ssid != wpa_s->current_ssid)
-               wpas_notify_network_changed(wpa_s);
+       if (wpa_s->current_ssid) {
+               wpa_supplicant_deauthenticate(wpa_s,
+                                             WLAN_REASON_DEAUTH_LEAVING);
+       }
 
        /*
         * TODO: should notify EAPOL SM about changes in opensc_engine_path,
@@ -655,6 +760,7 @@ int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s)
        }
        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);
 
@@ -667,10 +773,14 @@ int wpa_supplicant_reload_configuration(struct wpa_supplicant *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);
-       wpa_s->reassociate = 1;
-       wpa_supplicant_req_scan(wpa_s, 0, 0);
-       wpa_msg(wpa_s, MSG_DEBUG, "Reconfiguration completed");
+       if (wpa_supplicant_enabled_networks(wpa_s->conf)) {
+               wpa_s->reassociate = 1;
+               wpa_supplicant_req_scan(wpa_s, 0, 0);
+       }
+       wpa_dbg(wpa_s, MSG_DEBUG, "Reconfiguration completed");
        return 0;
 }
 
@@ -679,8 +789,9 @@ static void wpa_supplicant_reconfig(int sig, void *signal_ctx)
 {
        struct wpa_global *global = signal_ctx;
        struct wpa_supplicant *wpa_s;
-       wpa_printf(MSG_DEBUG, "Signal %d received - reconfiguring", sig);
        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);
                }
@@ -747,8 +858,8 @@ static int wpa_supplicant_suites_from_ai(struct wpa_supplicant *wpa_s,
                return -1;
        }
 
-       wpa_printf(MSG_DEBUG, "WPA: Using WPA IE from AssocReq to set cipher "
-                  "suites");
+       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",
@@ -815,14 +926,14 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
            (ie.group_cipher & ssid->group_cipher) &&
            (ie.pairwise_cipher & ssid->pairwise_cipher) &&
            (ie.key_mgmt & ssid->key_mgmt)) {
-               wpa_msg(wpa_s, MSG_DEBUG, "RSN: using IEEE 802.11i/D9.0");
+               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_msg(wpa_s, MSG_DEBUG, "WPA: using IEEE 802.11i/D3.0");
+               wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using IEEE 802.11i/D3.0");
                proto = WPA_PROTO_WPA;
        } else if (bss) {
                wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select WPA/RSN");
@@ -842,22 +953,23 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
                                ssid->ieee80211w != NO_MGMT_FRAME_PROTECTION ?
                                WPA_CIPHER_AES_128_CMAC : 0;
 #endif /* CONFIG_IEEE80211W */
-                       wpa_printf(MSG_DEBUG, "WPA: Set cipher suites based "
-                                  "on configuration");
+                       wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Set cipher suites "
+                               "based on configuration");
                } else
                        proto = ie.proto;
        }
 
-       wpa_printf(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);
+       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_printf(MSG_DEBUG, "WPA: Selected mgmt group cipher %d",
-                          ie.mgmt_group_cipher);
+               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));
@@ -873,34 +985,35 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
        sel = ie.group_cipher & ssid->group_cipher;
        if (sel & WPA_CIPHER_CCMP) {
                wpa_s->group_cipher = WPA_CIPHER_CCMP;
-               wpa_msg(wpa_s, MSG_DEBUG, "WPA: using GTK CCMP");
+               wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK CCMP");
        } else if (sel & WPA_CIPHER_TKIP) {
                wpa_s->group_cipher = WPA_CIPHER_TKIP;
-               wpa_msg(wpa_s, MSG_DEBUG, "WPA: using GTK TKIP");
+               wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK TKIP");
        } else if (sel & WPA_CIPHER_WEP104) {
                wpa_s->group_cipher = WPA_CIPHER_WEP104;
-               wpa_msg(wpa_s, MSG_DEBUG, "WPA: using GTK WEP104");
+               wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK WEP104");
        } else if (sel & WPA_CIPHER_WEP40) {
                wpa_s->group_cipher = WPA_CIPHER_WEP40;
-               wpa_msg(wpa_s, MSG_DEBUG, "WPA: using GTK WEP40");
+               wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK WEP40");
        } else {
-               wpa_printf(MSG_WARNING, "WPA: Failed to select group cipher.");
+               wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select group "
+                       "cipher");
                return -1;
        }
 
        sel = ie.pairwise_cipher & ssid->pairwise_cipher;
        if (sel & WPA_CIPHER_CCMP) {
                wpa_s->pairwise_cipher = WPA_CIPHER_CCMP;
-               wpa_msg(wpa_s, MSG_DEBUG, "WPA: using PTK CCMP");
+               wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK CCMP");
        } else if (sel & WPA_CIPHER_TKIP) {
                wpa_s->pairwise_cipher = WPA_CIPHER_TKIP;
-               wpa_msg(wpa_s, MSG_DEBUG, "WPA: using PTK TKIP");
+               wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK TKIP");
        } else if (sel & WPA_CIPHER_NONE) {
                wpa_s->pairwise_cipher = WPA_CIPHER_NONE;
-               wpa_msg(wpa_s, MSG_DEBUG, "WPA: using PTK NONE");
+               wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK NONE");
        } else {
-               wpa_printf(MSG_WARNING, "WPA: Failed to select pairwise "
-                          "cipher.");
+               wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select pairwise "
+                       "cipher");
                return -1;
        }
 
@@ -909,33 +1022,33 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
 #ifdef CONFIG_IEEE80211R
        } else if (sel & WPA_KEY_MGMT_FT_IEEE8021X) {
                wpa_s->key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X;
-               wpa_msg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/802.1X");
+               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_msg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/PSK");
+               wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/PSK");
 #endif /* CONFIG_IEEE80211R */
 #ifdef CONFIG_IEEE80211W
        } else if (sel & WPA_KEY_MGMT_IEEE8021X_SHA256) {
                wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_SHA256;
-               wpa_msg(wpa_s, MSG_DEBUG,
+               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_msg(wpa_s, MSG_DEBUG,
+               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_msg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT 802.1X");
+               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_msg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT WPA-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_msg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT WPA-NONE");
+               wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT WPA-NONE");
        } else {
-               wpa_printf(MSG_WARNING, "WPA: Failed to select authenticated "
-                          "key management type.");
+               wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select "
+                       "authenticated key management type");
                return -1;
        }
 
@@ -951,11 +1064,11 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
                sel = 0;
        if (sel & WPA_CIPHER_AES_128_CMAC) {
                wpa_s->mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC;
-               wpa_msg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher "
+               wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher "
                        "AES-128-CMAC");
        } else {
                wpa_s->mgmt_group_cipher = 0;
-               wpa_msg(wpa_s, MSG_DEBUG, "WPA: not using MGMT group cipher");
+               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);
@@ -963,14 +1076,26 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
 #endif /* CONFIG_IEEE80211W */
 
        if (wpa_sm_set_assoc_wpa_ie_default(wpa_s->wpa, wpa_ie, wpa_ie_len)) {
-               wpa_printf(MSG_WARNING, "WPA: Failed to generate WPA IE.");
+               wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to generate WPA IE");
                return -1;
        }
 
        if (ssid->key_mgmt &
            (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_FT_PSK | WPA_KEY_MGMT_PSK_SHA256))
+       {
                wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN);
-       else
+#ifndef CONFIG_NO_PBKDF2
+               if (bss && ssid->bssid_set && ssid->ssid_len == 0 &&
+                   ssid->passphrase) {
+                       u8 psk[PMK_LEN];
+                       pbkdf2_sha1(ssid->passphrase, (char *) 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);
+               }
+#endif /* CONFIG_NO_PBKDF2 */
+       } else
                wpa_sm_set_pmk_from_pmksa(wpa_s->wpa);
 
        return 0;
@@ -988,7 +1113,7 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
 void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
                              struct wpa_bss *bss, struct wpa_ssid *ssid)
 {
-       u8 wpa_ie[80];
+       u8 wpa_ie[200];
        size_t wpa_ie_len;
        int use_crypt, ret, i, bssid_changed;
        int algs = WPA_AUTH_ALG_OPEN;
@@ -999,30 +1124,43 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
        int assoc_failed = 0;
        struct wpa_ssid *old_ssid;
 
-       if (ssid->mode == WPAS_MODE_AP) {
+#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_printf(MSG_INFO, "Driver does not support AP "
-                                  "mode");
+                       wpa_msg(wpa_s, MSG_INFO, "Driver does not support AP "
+                               "mode");
                        return;
                }
                wpa_supplicant_create_ap(wpa_s, ssid);
                wpa_s->current_bss = bss;
 #else /* CONFIG_AP */
-               wpa_printf(MSG_ERROR, "AP mode support not included in the "
-                          "build");
+               wpa_msg(wpa_s, MSG_ERROR, "AP mode support not included in "
+                       "the build");
 #endif /* CONFIG_AP */
                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;
        }
 
+       os_memset(&params, 0, sizeof(params));
        wpa_s->reassociate = 0;
-       if (bss) {
+       if (bss && !wpas_driver_bss_selection(wpa_s)) {
 #ifdef CONFIG_IEEE80211R
                const u8 *ie, *md = NULL;
 #endif /* CONFIG_IEEE80211R */
@@ -1060,6 +1198,7 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
                        wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
                os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
        }
+       wpa_supplicant_cancel_sched_scan(wpa_s);
        wpa_supplicant_cancel_scan(wpa_s);
 
        /* Starting new association, so clear the possibly used WPA IE from the
@@ -1076,11 +1215,11 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
                }
        }
 #endif /* IEEE8021X_EAPOL */
-       wpa_printf(MSG_DEBUG, "Automatic auth_alg selection: 0x%x", algs);
+       wpa_dbg(wpa_s, MSG_DEBUG, "Automatic auth_alg selection: 0x%x", algs);
        if (ssid->auth_alg) {
                algs = ssid->auth_alg;
-               wpa_printf(MSG_DEBUG, "Overriding auth_alg selection: 0x%x",
-                          algs);
+               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) ||
@@ -1100,8 +1239,8 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
                wpa_ie_len = sizeof(wpa_ie);
                if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
                                              wpa_ie, &wpa_ie_len)) {
-                       wpa_printf(MSG_WARNING, "WPA: Failed to set WPA key "
-                                  "management and encryption suites");
+                       wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA "
+                               "key management and encryption suites");
                        return;
                }
        } else if (ssid->key_mgmt &
@@ -1112,9 +1251,9 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
                wpa_ie_len = sizeof(wpa_ie);
                if (wpa_supplicant_set_suites(wpa_s, NULL, ssid,
                                              wpa_ie, &wpa_ie_len)) {
-                       wpa_printf(MSG_WARNING, "WPA: Failed to set WPA key "
-                                  "management and encryption suites (no scan "
-                                  "results)");
+                       wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA "
+                               "key management and encryption suites (no "
+                               "scan results)");
                        return;
                }
 #ifdef CONFIG_WPS
@@ -1128,12 +1267,63 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
                        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");
+               }
+       }
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_INTERWORKING
+       if (wpa_s->conf->interworking) {
+               u8 *pos = wpa_ie;
+               if (wpa_ie_len > 0 && pos[0] == WLAN_EID_RSN)
+                       pos += 2 + pos[1];
+               os_memmove(pos + 6, pos, wpa_ie_len - (pos - wpa_ie));
+               wpa_ie_len += 6;
+               *pos++ = WLAN_EID_EXT_CAPAB;
+               *pos++ = 4;
+               *pos++ = 0x00;
+               *pos++ = 0x00;
+               *pos++ = 0x00;
+               *pos++ = 0x80; /* Bit 31 - Interworking */
+       }
+#endif /* CONFIG_INTERWORKING */
+
        wpa_clear_keys(wpa_s, bss ? bss->bssid : NULL);
        use_crypt = 1;
        cipher_pairwise = cipher_suite2driver(wpa_s->pairwise_cipher);
@@ -1172,12 +1362,13 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
        }
 
        wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATING);
-       os_memset(&params, 0, sizeof(params));
        if (bss) {
-               params.bssid = bss->bssid;
                params.ssid = bss->ssid;
                params.ssid_len = bss->ssid_len;
-               params.freq = bss->freq;
+               if (!wpas_driver_bss_selection(wpa_s)) {
+                       params.bssid = bss->bssid;
+                       params.freq = bss->freq;
+               }
        } else {
                params.ssid = ssid->ssid;
                params.ssid_len = ssid->ssid_len;
@@ -1190,6 +1381,7 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
        params.pairwise_suite = cipher_pairwise;
        params.group_suite = cipher_group;
        params.key_mgmt_suite = key_mgmt2driver(wpa_s->key_mgmt);
+       params.wpa_proto = wpa_s->wpa_proto;
        params.auth_alg = algs;
        params.mode = ssid->mode;
        for (i = 0; i < NUM_WEP_KEYS; i++) {
@@ -1217,21 +1409,35 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
                if (rsn && wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ie) == 0 &&
                    ie.capabilities &
                    (WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR)) {
-                       wpa_printf(MSG_DEBUG, "WPA: Selected AP supports MFP: "
-                                  "require MFP");
+                       wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Selected AP supports "
+                               "MFP: require MFP");
                        params.mgmt_frame_protection =
                                MGMT_FRAME_PROTECTION_REQUIRED;
                }
        }
 #endif /* CONFIG_IEEE80211W */
 
-       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
-               ret = ieee80211_sta_associate(wpa_s, &params);
+       params.p2p = ssid->p2p_group;
+
+       if (wpa_s->parent->set_sta_uapsd)
+               params.uapsd = wpa_s->parent->sta_uapsd;
        else
-               ret = wpa_drv_associate(wpa_s, &params);
+               params.uapsd = -1;
+
+       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);
+                       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;
@@ -1249,7 +1455,6 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
        } else if (ssid->mode == WPAS_MODE_IBSS &&
                   wpa_s->key_mgmt != WPA_KEY_MGMT_NONE &&
                   wpa_s->key_mgmt != WPA_KEY_MGMT_WPA_NONE) {
-               ibss_rsn_set_psk(wpa_s->ibss_rsn, ssid->psk);
                /*
                 * RSN IBSS authentication is per-STA and we can disable the
                 * per-BSSID authentication.
@@ -1293,6 +1498,24 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
 }
 
 
+static void wpa_supplicant_clear_connection(struct wpa_supplicant *wpa_s,
+                                           const u8 *addr)
+{
+       struct wpa_ssid *old_ssid;
+
+       wpa_clear_keys(wpa_s, addr);
+       wpa_supplicant_mark_disassoc(wpa_s);
+       old_ssid = wpa_s->current_ssid;
+       wpa_s->current_ssid = NULL;
+       wpa_s->current_bss = NULL;
+       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_disassociate - Disassociate the current connection
  * @wpa_s: Pointer to wpa_supplicant data
@@ -1304,26 +1527,14 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
 void wpa_supplicant_disassociate(struct wpa_supplicant *wpa_s,
                                 int reason_code)
 {
-       struct wpa_ssid *old_ssid;
        u8 *addr = NULL;
 
        if (!is_zero_ether_addr(wpa_s->bssid)) {
-               if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
-                       ieee80211_sta_disassociate(wpa_s, reason_code);
-               else
-                       wpa_drv_disassociate(wpa_s, wpa_s->bssid, reason_code);
+               wpa_drv_disassociate(wpa_s, wpa_s->bssid, reason_code);
                addr = wpa_s->bssid;
        }
-       wpa_clear_keys(wpa_s, addr);
-       wpa_supplicant_mark_disassoc(wpa_s);
-       old_ssid = wpa_s->current_ssid;
-       wpa_s->current_ssid = NULL;
-       wpa_s->current_bss = NULL;
-       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_clear_connection(wpa_s, addr);
 }
 
 
@@ -1338,27 +1549,14 @@ void wpa_supplicant_disassociate(struct wpa_supplicant *wpa_s,
 void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s,
                                   int reason_code)
 {
-       struct wpa_ssid *old_ssid;
        u8 *addr = NULL;
 
        if (!is_zero_ether_addr(wpa_s->bssid)) {
-               if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
-                       ieee80211_sta_deauthenticate(wpa_s, reason_code);
-               else
-                       wpa_drv_deauthenticate(wpa_s, wpa_s->bssid,
-                                              reason_code);
+               wpa_drv_deauthenticate(wpa_s, wpa_s->bssid, reason_code);
                addr = wpa_s->bssid;
        }
-       wpa_clear_keys(wpa_s, addr);
-       wpa_supplicant_mark_disassoc(wpa_s);
-       old_ssid = wpa_s->current_ssid;
-       wpa_s->current_ssid = NULL;
-       wpa_s->current_bss = NULL;
-       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_clear_connection(wpa_s, addr);
 }
 
 
@@ -1376,8 +1574,11 @@ void wpa_supplicant_enable_network(struct wpa_supplicant *wpa_s,
        int was_disabled;
 
        if (ssid == NULL) {
-               other_ssid = wpa_s->conf->ssid;
-               while (other_ssid) {
+               for (other_ssid = wpa_s->conf->ssid; other_ssid;
+                    other_ssid = other_ssid->next) {
+                       if (other_ssid->disabled == 2)
+                               continue; /* do not change persistent P2P group
+                                          * data */
                        if (other_ssid == wpa_s->current_ssid &&
                            other_ssid->disabled)
                                wpa_s->reassociate = 1;
@@ -1389,12 +1590,10 @@ void wpa_supplicant_enable_network(struct wpa_supplicant *wpa_s,
                        if (was_disabled != other_ssid->disabled)
                                wpas_notify_network_enabled_changed(
                                        wpa_s, other_ssid);
-
-                       other_ssid = other_ssid->next;
                }
                if (wpa_s->reassociate)
                        wpa_supplicant_req_scan(wpa_s, 0, 0);
-       } else if (ssid->disabled) {
+       } else if (ssid->disabled && ssid->disabled != 2) {
                if (wpa_s->current_ssid == NULL) {
                        /*
                         * Try to reassociate since there is no current
@@ -1428,22 +1627,23 @@ void wpa_supplicant_disable_network(struct wpa_supplicant *wpa_s,
        int was_disabled;
 
        if (ssid == NULL) {
-               other_ssid = wpa_s->conf->ssid;
-               while (other_ssid) {
+               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);
-
-                       other_ssid = other_ssid->next;
                }
                if (wpa_s->current_ssid)
                        wpa_supplicant_disassociate(
                                wpa_s, WLAN_REASON_DEAUTH_LEAVING);
-       } else {
+       } else if (ssid->disabled != 2) {
                if (ssid == wpa_s->current_ssid)
                        wpa_supplicant_disassociate(
                                wpa_s, WLAN_REASON_DEAUTH_LEAVING);
@@ -1468,29 +1668,41 @@ void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s,
 {
 
        struct wpa_ssid *other_ssid;
+       int disconnected = 0;
 
-       if (ssid && ssid != wpa_s->current_ssid && wpa_s->current_ssid)
+       if (ssid && ssid != wpa_s->current_ssid && wpa_s->current_ssid) {
                wpa_supplicant_disassociate(
                        wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+               disconnected = 1;
+       }
 
        /*
         * Mark all other networks disabled or mark all networks enabled if no
         * network specified.
         */
-       other_ssid = wpa_s->conf->ssid;
-       while (other_ssid) {
+       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_notify_network_enabled_changed(wpa_s, other_ssid);
+       }
 
-               other_ssid = other_ssid->next;
+       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;
        }
+
+       wpa_s->connect_without_scan = NULL;
        wpa_s->disconnected = 0;
        wpa_s->reassociate = 1;
-       wpa_supplicant_req_scan(wpa_s, 0, 0);
+       wpa_supplicant_req_scan(wpa_s, 0, disconnected ? 100000 : 0);
 
        if (ssid)
                wpas_notify_network_selected(wpa_s, ssid);
@@ -1512,6 +1724,16 @@ int wpa_supplicant_set_ap_scan(struct wpa_supplicant *wpa_s, int ap_scan)
        if (ap_scan < 0 || ap_scan > 2)
                return -1;
 
+#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;
 
@@ -1523,6 +1745,52 @@ int wpa_supplicant_set_ap_scan(struct wpa_supplicant *wpa_s, int ap_scan)
 
 
 /**
+ * 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_debug_params - Set global debug params
  * @global: wpa_global structure
  * @debug_level: debug level
@@ -1537,7 +1805,8 @@ int wpa_supplicant_set_debug_params(struct wpa_global *global, int debug_level,
        int old_level, old_timestamp, old_show_keys;
 
        /* check for allowed debuglevels */
-       if (debug_level != MSG_MSGDUMP &&
+       if (debug_level != MSG_EXCESSIVE &&
+           debug_level != MSG_MSGDUMP &&
            debug_level != MSG_DEBUG &&
            debug_level != MSG_INFO &&
            debug_level != MSG_WARNING &&
@@ -1577,26 +1846,17 @@ struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s)
        u8 bssid[ETH_ALEN];
        int wired;
 
-       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) {
-               if (ieee80211_sta_get_ssid(wpa_s, ssid, &ssid_len)) {
-                       wpa_printf(MSG_WARNING, "Could not read SSID from "
-                                  "MLME.");
-                       return NULL;
-               }
-       } else {
-               res = wpa_drv_get_ssid(wpa_s, ssid);
-               if (res < 0) {
-                       wpa_printf(MSG_WARNING, "Could not read SSID from "
-                                  "driver.");
-                       return NULL;
-               }
-               ssid_len = res;
+       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_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
-               os_memcpy(bssid, wpa_s->bssid, ETH_ALEN);
-       else if (wpa_drv_get_bssid(wpa_s, bssid) < 0) {
-               wpa_printf(MSG_WARNING, "Could not read BSSID from driver.");
+       if (wpa_drv_get_bssid(wpa_s, bssid) < 0) {
+               wpa_msg(wpa_s, MSG_WARNING, "Could not read BSSID from "
+                       "driver");
                return NULL;
        }
 
@@ -1619,6 +1879,12 @@ struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s)
                     os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0))
                        return entry;
 #endif /* CONFIG_WPS */
+
+               if (!entry->disabled && entry->bssid_set &&
+                   entry->ssid_len == 0 &&
+                   os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0)
+                       return entry;
+
                entry = entry->next;
        }
 
@@ -1626,45 +1892,68 @@ struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s)
 }
 
 
+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;
+       const char *pos, *driver = name;
 
        if (wpa_s == NULL)
                return -1;
 
        if (wpa_drivers[0] == NULL) {
-               wpa_printf(MSG_ERROR, "No driver interfaces build into "
-                          "wpa_supplicant.");
+               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 */
-               wpa_s->driver = wpa_drivers[0];
-               wpa_s->global_drv_priv = wpa_s->global->drv_priv[0];
-               return 0;
+               return select_driver(wpa_s, 0);
        }
 
-       pos = os_strchr(name, ',');
-       if (pos)
-               len = pos - name;
-       else
-               len = os_strlen(name);
-       for (i = 0; wpa_drivers[i]; i++) {
-               if (os_strlen(wpa_drivers[i]->name) == len &&
-                   os_strncmp(name, wpa_drivers[i]->name, len) ==
-                   0) {
-                       wpa_s->driver = wpa_drivers[i];
-                       wpa_s->global_drv_priv = wpa_s->global->drv_priv[i];
-                       return 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;
+                       }
                }
-       }
 
-       wpa_printf(MSG_ERROR, "Unsupported driver '%s'.", name);
+               driver = pos + 1;
+       } while (pos);
+
+       wpa_msg(wpa_s, MSG_ERROR, "Unsupported driver '%s'", name);
        return -1;
 }
 
@@ -1688,7 +1977,7 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
 {
        struct wpa_supplicant *wpa_s = ctx;
 
-       wpa_printf(MSG_DEBUG, "RX EAPOL from " MACSTR, MAC2STR(src_addr));
+       wpa_dbg(wpa_s, MSG_DEBUG, "RX EAPOL from " MACSTR, MAC2STR(src_addr));
        wpa_hexdump(MSG_MSGDUMP, "RX EAPOL", buf, len);
 
        if (wpa_s->wpa_state < WPA_ASSOCIATED) {
@@ -1700,8 +1989,8 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
                 * association information, lets queue it for processing until
                 * the association event is received.
                 */
-               wpa_printf(MSG_DEBUG, "Not associated - Delay processing of "
-                          "received EAPOL frame");
+               wpa_dbg(wpa_s, MSG_DEBUG, "Not associated - Delay processing "
+                       "of received EAPOL frame");
                wpabuf_free(wpa_s->pending_eapol_rx);
                wpa_s->pending_eapol_rx = wpabuf_alloc_copy(buf, len);
                if (wpa_s->pending_eapol_rx) {
@@ -1720,8 +2009,8 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
 #endif /* CONFIG_AP */
 
        if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE) {
-               wpa_printf(MSG_DEBUG, "Ignored received EAPOL frame since "
-                          "no key management is configured");
+               wpa_dbg(wpa_s, MSG_DEBUG, "Ignored received EAPOL frame since "
+                       "no key management is configured");
                return;
        }
 
@@ -1742,8 +2031,8 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
        wpa_s->eapol_received++;
 
        if (wpa_s->countermeasures) {
-               wpa_printf(MSG_INFO, "WPA: Countermeasures - dropped EAPOL "
-                          "packet");
+               wpa_msg(wpa_s, MSG_INFO, "WPA: Countermeasures - dropped "
+                       "EAPOL packet");
                return;
        }
 
@@ -1780,52 +2069,68 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
 }
 
 
-/**
- * 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)
+int wpa_supplicant_update_mac_addr(struct wpa_supplicant *wpa_s)
 {
-       static int interface_count = 0;
-
        if (wpa_s->driver->send_eapol) {
                const u8 *addr = wpa_drv_get_mac_addr(wpa_s);
                if (addr)
                        os_memcpy(wpa_s->own_addr, addr, ETH_ALEN);
-       } else {
+       } else if (!(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_printf(MSG_ERROR, "Failed to get own L2 address");
+               wpa_msg(wpa_s, MSG_ERROR, "Failed to get own L2 address");
                return -1;
        }
 
-       wpa_printf(MSG_DEBUG, "Own MAC address: " MACSTR,
-                  MAC2STR(wpa_s->own_addr));
+       wpa_dbg(wpa_s, MSG_DEBUG, "Own MAC address: " MACSTR,
+               MAC2STR(wpa_s->own_addr));
+       wpa_sm_set_own_addr(wpa_s->wpa, wpa_s->own_addr);
+
+       return 0;
+}
+
+
+/**
+ * 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;
 
        if (wpa_s->bridge_ifname[0]) {
-               wpa_printf(MSG_DEBUG, "Receiving packets from bridge interface"
-                          " '%s'", wpa_s->bridge_ifname);
+               wpa_dbg(wpa_s, MSG_DEBUG, "Receiving packets from bridge "
+                       "interface '%s'", wpa_s->bridge_ifname);
                wpa_s->l2_br = l2_packet_init(wpa_s->bridge_ifname,
                                              wpa_s->own_addr,
                                              ETH_P_EAPOL,
                                              wpa_supplicant_rx_eapol, wpa_s,
                                              0);
                if (wpa_s->l2_br == NULL) {
-                       wpa_printf(MSG_ERROR, "Failed to open l2_packet "
-                                  "connection for the bridge interface '%s'",
-                                  wpa_s->bridge_ifname);
+                       wpa_msg(wpa_s, MSG_ERROR, "Failed to open l2_packet "
+                               "connection for the bridge interface '%s'",
+                               wpa_s->bridge_ifname);
                        return -1;
                }
        }
@@ -1836,12 +2141,15 @@ int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s)
         * happen if wpa_supplicant is killed during countermeasures. */
        wpa_drv_set_countermeasures(wpa_s, 0);
 
-       wpa_printf(MSG_DEBUG, "RSN: flushing PMKID list in the driver");
+       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;
        if (wpa_supplicant_enabled_networks(wpa_s->conf)) {
-               wpa_supplicant_req_scan(wpa_s, interface_count, 100000);
+               if (wpa_supplicant_delayed_sched_scan(wpa_s, interface_count,
+                                                     100000))
+                       wpa_supplicant_req_scan(wpa_s, interface_count,
+                                               100000);
                interface_count++;
        } else
                wpa_supplicant_set_state(wpa_s, WPA_INACTIVE);
@@ -1865,7 +2173,10 @@ static struct wpa_supplicant * wpa_supplicant_alloc(void)
        if (wpa_s == NULL)
                return NULL;
        wpa_s->scan_req = 1;
+       wpa_s->scan_interval = 5;
        wpa_s->new_connection = 1;
+       wpa_s->parent = wpa_s;
+       wpa_s->sched_scanning = 0;
 
        return wpa_s;
 }
@@ -1969,24 +2280,25 @@ next_driver:
                const char *pos;
                pos = driver ? os_strchr(driver, ',') : NULL;
                if (pos) {
-                       wpa_printf(MSG_DEBUG, "Failed to initialize driver "
-                                  "interface - try next driver wrapper");
+                       wpa_dbg(wpa_s, MSG_DEBUG, "Failed to initialize "
+                               "driver interface - try next driver wrapper");
                        driver = pos + 1;
                        goto next_driver;
                }
-               wpa_printf(MSG_ERROR, "Failed to initialize driver interface");
+               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_printf(MSG_ERROR, "Driver interface rejected "
-                          "driver_param '%s'", wpa_s->conf->driver_param);
+               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_printf(MSG_DEBUG, "Driver interface replaced interface "
-                          "name with '%s'", ifname);
+               wpa_dbg(wpa_s, MSG_DEBUG, "Driver interface replaced "
+                       "interface name with '%s'", ifname);
                os_strlcpy(wpa_s->ifname, ifname, sizeof(wpa_s->ifname));
        }
 
@@ -2001,15 +2313,15 @@ next_driver:
        if (wpa_s->conf->dot11RSNAConfigPMKLifetime &&
            wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_LIFETIME,
                             wpa_s->conf->dot11RSNAConfigPMKLifetime)) {
-               wpa_printf(MSG_ERROR, "Invalid WPA parameter value for "
-                          "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_printf(MSG_ERROR, "Invalid WPA parameter value for "
+               wpa_msg(wpa_s, MSG_ERROR, "Invalid WPA parameter value for "
                        "dot11RSNAConfigPMKReauthThreshold");
                return -1;
        }
@@ -2017,19 +2329,24 @@ next_driver:
        if (wpa_s->conf->dot11RSNAConfigSATimeout &&
            wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT,
                             wpa_s->conf->dot11RSNAConfigSATimeout)) {
-               wpa_printf(MSG_ERROR, "Invalid WPA parameter value for "
-                          "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_drv_get_capa(wpa_s, &capa) == 0) {
+               wpa_s->drv_capa_known = 1;
                wpa_s->drv_flags = capa.flags;
-               if (capa.flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) {
-                       if (ieee80211_sta_init(wpa_s))
-                               return -1;
-               }
                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;
        }
        if (wpa_s->max_remain_on_chan == 0)
                wpa_s->max_remain_on_chan = 1000;
@@ -2037,14 +2354,17 @@ next_driver:
        if (wpa_supplicant_driver_init(wpa_s) < 0)
                return -1;
 
+#ifdef CONFIG_TDLS
+       if (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_printf(MSG_DEBUG, "Failed to set country");
+               wpa_dbg(wpa_s, MSG_DEBUG, "Failed to set country");
                return -1;
        }
 
-       wpa_sm_set_own_addr(wpa_s->wpa, wpa_s->own_addr);
-
        if (wpas_wps_init(wpa_s))
                return -1;
 
@@ -2066,13 +2386,18 @@ next_driver:
                return -1;
        }
 
-#ifdef CONFIG_IBSS_RSN
-       wpa_s->ibss_rsn = ibss_rsn_init(wpa_s);
-       if (!wpa_s->ibss_rsn) {
-               wpa_printf(MSG_DEBUG, "Failed to init IBSS RSN");
+       wpa_s->gas = gas_query_init(wpa_s);
+       if (wpa_s->gas == NULL) {
+               wpa_printf(MSG_ERROR, "Failed to initialize GAS query");
                return -1;
        }
-#endif /* CONFIG_IBSS_RSN */
+
+#ifdef CONFIG_P2P
+       if (wpas_p2p_init(wpa_s->global, wpa_s) < 0) {
+               wpa_msg(wpa_s, MSG_ERROR, "Failed to init P2P");
+               return -1;
+       }
+#endif /* CONFIG_P2P */
 
        if (wpa_bss_init(wpa_s) < 0)
                return -1;
@@ -2166,7 +2491,7 @@ struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global,
        wpa_s->next = global->ifaces;
        global->ifaces = wpa_s;
 
-       wpa_printf(MSG_DEBUG, "Added interface %s", wpa_s->ifname);
+       wpa_dbg(wpa_s, MSG_DEBUG, "Added interface %s", wpa_s->ifname);
 
        return wpa_s;
 }
@@ -2200,8 +2525,10 @@ int wpa_supplicant_remove_iface(struct wpa_global *global,
                prev->next = wpa_s->next;
        }
 
-       wpa_printf(MSG_DEBUG, "Removing interface %s", wpa_s->ifname);
+       wpa_dbg(wpa_s, MSG_DEBUG, "Removing interface %s", wpa_s->ifname);
 
+       if (global->p2p_group_formation == wpa_s)
+               global->p2p_group_formation = NULL;
        wpa_supplicant_deinit_iface(wpa_s, 1);
        os_free(wpa_s);
 
@@ -2210,6 +2537,28 @@ int wpa_supplicant_remove_iface(struct wpa_global *global,
 
 
 /**
+ * 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
@@ -2228,6 +2577,17 @@ struct wpa_supplicant * wpa_supplicant_get_iface(struct wpa_global *global,
 }
 
 
+#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 */
+
+
 /**
  * wpa_supplicant_init - Initialize %wpa_supplicant
  * @params: Parameters for %wpa_supplicant
@@ -2245,6 +2605,17 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params)
        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 */
+
        wpa_debug_open_file(params->wpa_debug_file_path);
        if (params->wpa_debug_syslog)
                wpa_debug_open_syslog();
@@ -2261,6 +2632,8 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params)
        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;
@@ -2282,12 +2655,16 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params)
        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);
@@ -2311,17 +2688,6 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params)
                wpa_supplicant_deinit(global);
                return NULL;
        }
-       for (i = 0; wpa_drivers[i]; i++) {
-               if (!wpa_drivers[i]->global_init)
-                       continue;
-               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);
-                       wpa_supplicant_deinit(global);
-                       return NULL;
-               }
-       }
 
        return global;
 }
@@ -2374,6 +2740,10 @@ void wpa_supplicant_deinit(struct wpa_global *global)
        if (global == NULL)
                return;
 
+#ifdef CONFIG_P2P
+       wpas_p2p_deinit_global(global);
+#endif /* CONFIG_P2P */
+
        while (global->ifaces)
                wpa_supplicant_remove_iface(global, global->ifaces);
 
@@ -2394,6 +2764,8 @@ void wpa_supplicant_deinit(struct wpa_global *global)
        }
        os_free(global->drv_priv);
 
+       random_deinit();
+
        eloop_destroy();
 
        if (global->params.pid_file) {
@@ -2408,3 +2780,149 @@ void wpa_supplicant_deinit(struct wpa_global *global)
        wpa_debug_close_syslog();
        wpa_debug_close_file();
 }
+
+
+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);
+               }
+       }
+
+#ifdef CONFIG_WPS
+       wpas_wps_update_config(wpa_s);
+#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_P2P
+       wpas_p2p_update_config(wpa_s);
+#endif /* CONFIG_P2P */
+
+       wpa_s->conf->changed_parameters = 0;
+}
+
+
+static void add_freq(int *freqs, int *num_freqs, int freq)
+{
+       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_zalloc(sizeof(int) * (max_freqs + 1));
+       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;
+
+       /*
+        * Remove possible authentication timeout since the connection failed.
+        */
+       eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
+
+       /*
+        * 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;
+               }
+       }
+
+       switch (count) {
+       case 1:
+               timeout = 100;
+               break;
+       case 2:
+               timeout = 500;
+               break;
+       case 3:
+               timeout = 1000;
+               break;
+       default:
+               timeout = 5000;
+       }
+
+       /*
+        * 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);
+}
index 0cd5b02..d393015 100644 (file)
@@ -28,7 +28,7 @@
 # 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 existance of this parameter
+# interface mechanism is used. For all cases, the existence of this parameter
 # in configuration is used to determine whether the control interface is
 # enabled.
 #
@@ -199,8 +199,12 @@ fast_reauth=1
 # 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
+#      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)
@@ -224,6 +228,35 @@ fast_reauth=1
 #filter_ssids=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
+
+# Home Realm for Interworking
+#home_realm=example.com
+
+# Username for Interworking network selection
+#home_username=user
+
+# Password for Interworking network selection
+#home_password=secret
+
+# CA certificate for Interworking network selection
+#home_ca_cert=/etc/cert/ca.pem
+
+# IMSI in <MCC> | <MNC> | '-' | <MSIN> format
+#home_imsi=232010000000000
+
+# Milenage parameters for SIM/USIM simulator in <Ki>:<OPc>:<SQN> format
+#home_milenage=90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123
+
 # network block
 #
 # Each network (usually AP's sharing the same SSID) is configured as a separate
@@ -350,7 +383,7 @@ fast_reauth=1
 #
 # 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 form scan results.
+# SSID, are allowed when selecting a BSS from scan results.
 # 0 = disabled (default)
 # 1 = enabled
 #
index 6c36a1a..9698c68 100644 (file)
@@ -17,6 +17,7 @@
 
 #include "utils/list.h"
 #include "common/defs.h"
+#include "config_ssid.h"
 
 extern const char *wpa_supplicant_version;
 extern const char *wpa_supplicant_license;
@@ -34,6 +35,7 @@ struct ibss_rsn;
 struct scan_info;
 struct wpa_bss;
 struct wpa_scan_results;
+struct hostapd_hw_modes;
 
 /*
  * Forward declarations of private structures used within the ctrl_iface
@@ -180,6 +182,26 @@ struct wpa_params {
         * 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;
+};
+
+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;
 };
 
 /**
@@ -196,101 +218,21 @@ struct wpa_global {
        void **drv_priv;
        size_t drv_count;
        struct os_time suspend_time;
+       struct p2p_data *p2p;
+       struct wpa_supplicant *p2p_group_formation;
+       u8 p2p_dev_addr[ETH_ALEN];
+       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_client_mlme {
-#ifdef CONFIG_CLIENT_MLME
-       enum {
-               IEEE80211_DISABLED, IEEE80211_AUTHENTICATE,
-               IEEE80211_ASSOCIATE, IEEE80211_ASSOCIATED,
-               IEEE80211_IBSS_SEARCH, IEEE80211_IBSS_JOINED
-       } state;
-       u8 prev_bssid[ETH_ALEN];
-       u8 ssid[32];
-       size_t ssid_len;
-       u16 aid;
-       u16 ap_capab, capab;
-       u8 *extra_ie; /* to be added to the end of AssocReq */
-       size_t extra_ie_len;
-       u8 *extra_probe_ie; /* to be added to the end of ProbeReq */
-       size_t extra_probe_ie_len;
-       enum wpa_key_mgmt key_mgmt;
-
-       /* The last AssocReq/Resp IEs */
-       u8 *assocreq_ies, *assocresp_ies;
-       size_t assocreq_ies_len, assocresp_ies_len;
-
-       int auth_tries, assoc_tries;
-
-       unsigned int ssid_set:1;
-       unsigned int bssid_set:1;
-       unsigned int prev_bssid_set:1;
-       unsigned int authenticated:1;
-       unsigned int associated:1;
-       unsigned int probereq_poll:1;
-       unsigned int use_protection:1;
-       unsigned int create_ibss:1;
-       unsigned int mixed_cell:1;
-       unsigned int wmm_enabled:1;
-
-       struct os_time last_probe;
-
-       unsigned int auth_algs; /* bitfield of allowed auth algs
-                                * (WPA_AUTH_ALG_*) */
-       int auth_alg; /* currently used IEEE 802.11 authentication algorithm */
-       int auth_transaction;
-
-       struct os_time ibss_join_req;
-       u8 *probe_resp; /* ProbeResp template for IBSS */
-       size_t probe_resp_len;
-       u32 supp_rates_bits;
-
-       int wmm_last_param_set;
-
-       int sta_scanning;
-       int scan_hw_mode_idx;
-       int scan_channel_idx;
-       enum { SCAN_SET_CHANNEL, SCAN_SEND_PROBE } scan_state;
-       struct os_time last_scan_completed;
-       int scan_oper_channel;
-       int scan_oper_freq;
-       int scan_oper_phymode;
-       u8 scan_ssid[32];
-       size_t scan_ssid_len;
-       int scan_skip_11b;
-       int *scan_freqs;
-
-       struct ieee80211_sta_bss *sta_bss_list;
-#define STA_HASH_SIZE 256
-#define STA_HASH(sta) (sta[5])
-       struct ieee80211_sta_bss *sta_bss_hash[STA_HASH_SIZE];
-
-       int cts_protect_erp_frames;
-
-       enum hostapd_hw_mode phymode; /* current mode */
-       struct hostapd_hw_modes *modes;
-       size_t num_modes;
-       unsigned int hw_modes; /* bitfield of allowed hardware modes;
-                               * (1 << HOSTAPD_MODE_*) */
-       int num_curr_rates;
-       int *curr_rates;
-       int freq; /* The current frequency in MHz */
-       int channel; /* The current IEEE 802.11 channel number */
-
-#ifdef CONFIG_IEEE80211R
-       u8 current_md[6];
-       u8 *ft_ies;
-       size_t ft_ies_len;
-#endif /* CONFIG_IEEE80211R */
-
-       void (*public_action_cb)(void *ctx, const u8 *buf, size_t len,
-                                int freq);
-       void *public_action_cb_ctx;
-
-#else /* CONFIG_CLIENT_MLME */
-       int dummy; /* to keep MSVC happy */
-#endif /* CONFIG_CLIENT_MLME */
+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 */
 };
 
 /**
@@ -303,6 +245,7 @@ struct wpa_client_mlme {
  */
 struct wpa_supplicant {
        struct wpa_global *global;
+       struct wpa_supplicant *parent;
        struct wpa_supplicant *next;
        struct l2_packet_data *l2;
        struct l2_packet_data *l2_br;
@@ -313,6 +256,7 @@ struct wpa_supplicant {
 #endif /* CONFIG_CTRL_IFACE_DBUS */
 #ifdef CONFIG_CTRL_IFACE_DBUS_NEW
        char *dbus_new_path;
+       char *dbus_groupobj_path;
 #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
        char bridge_ifname[16];
 
@@ -322,7 +266,7 @@ struct wpa_supplicant {
        os_time_t last_michael_mic_error;
        u8 bssid[ETH_ALEN];
        u8 pending_bssid[ETH_ALEN]; /* If wpa_state == WPA_ASSOCIATING, this
-                                    * field contains the targer BSSID. */
+                                    * field contains the target BSSID. */
        int reassociate; /* reassociation requested */
        int disconnected; /* all connections disabled; i.e., do no reassociate
                           * before this has been cleared */
@@ -335,6 +279,7 @@ struct wpa_supplicant {
        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 */
@@ -348,6 +293,12 @@ struct wpa_supplicant {
                                          */
 #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 */
@@ -366,6 +317,7 @@ struct wpa_supplicant {
 
        enum wpa_states wpa_state;
        int scanning;
+       int sched_scanning;
        int new_connection;
        int reassociated_connection;
 
@@ -383,11 +335,16 @@ struct wpa_supplicant {
        int scan_req; /* manual scan request; this forces a scan even if there
                       * are no enabled networks in the configuration */
        int scan_runs; /* number of scan runs since WPS was started */
+       int *next_scan_freqs;
+       int scan_interval; /* time in sec between scans to find suitable AP */
 
-       struct wpa_client_mlme mlme;
        unsigned int drv_flags;
        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;
@@ -404,12 +361,17 @@ struct wpa_supplicant {
 
        struct ibss_rsn *ibss_rsn;
 
+       int set_sta_uapsd;
+       int sta_uapsd;
+       int set_ap_uapsd;
+       int ap_uapsd;
+
 #ifdef CONFIG_SME
        struct {
                u8 ssid[32];
                size_t ssid_len;
                int freq;
-               u8 assoc_req_ie[80];
+               u8 assoc_req_ie[200];
                size_t assoc_req_ie_len;
                int mfp;
                int ft_used;
@@ -419,6 +381,15 @@ struct wpa_supplicant {
                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_time sa_query_start;
        } sme;
 #endif /* CONFIG_SME */
 
@@ -429,14 +400,116 @@ struct wpa_supplicant {
        void *ap_configured_cb_data;
 #endif /* CONFIG_AP */
 
+       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;
+       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;
+
+#ifdef CONFIG_P2P
+       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 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;
+       int p2p_join_scan_count;
+       int force_long_sd;
+
+       /*
+        * 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];
+
+       enum {
+               P2P_GROUP_REMOVAL_UNKNOWN,
+               P2P_GROUP_REMOVAL_REQUESTED,
+               P2P_GROUP_REMOVAL_IDLE_TIMEOUT,
+               P2P_GROUP_REMOVAL_UNAVAILABLE
+       } removal_reason;
+
+       unsigned int p2p_cb_on_scan_complete:1;
+#endif /* CONFIG_P2P */
+
        struct wpa_ssid *bgscan_ssid;
        const struct bgscan_ops *bgscan;
        void *bgscan_priv;
 
-       int connect_without_scan;
+       struct wpa_ssid *connect_without_scan;
 
        int after_wps;
        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;
+#endif /* CONFIG_INTERWORKING */
+       unsigned int drv_capa_known;
+
+       struct {
+               struct hostapd_hw_modes *modes;
+               u16 num_modes;
+               u16 flags;
+       } hw;
 };
 
 
@@ -446,6 +519,7 @@ int wpa_set_wep_keys(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,
@@ -462,6 +536,7 @@ void wpa_supplicant_req_auth_timeout(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);
@@ -476,9 +551,14 @@ void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s,
                                   struct wpa_ssid *ssid);
 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_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);
 
@@ -499,14 +579,30 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
                             const u8 *buf, size_t len);
 enum wpa_key_mgmt key_mgmt2driver(int key_mgmt);
 enum wpa_cipher cipher_suite2driver(int cipher);
+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);
 
 /* events.c */
 void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s);
-void wpa_supplicant_connect(struct wpa_supplicant *wpa_s,
-                           struct wpa_bss *selected,
-                           struct wpa_ssid *ssid);
+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);
 
 /* eap_register.c */
 int eap_register_methods(void);
 
+/**
+ * Utility method to tell if a given network is a persistent group
+ * @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);
+}
+
 #endif /* WPA_SUPPLICANT_I_H */
index 4af0cd0..69b0cf8 100644 (file)
@@ -24,7 +24,6 @@
 #include "wpa_supplicant_i.h"
 #include "driver_i.h"
 #include "rsn_supp/pmksa_cache.h"
-#include "mlme.h"
 #include "sme.h"
 #include "common/ieee802_11_defs.h"
 #include "common/wpa_ctrl.h"
@@ -32,6 +31,7 @@
 #include "wps_supplicant.h"
 #include "bss.h"
 #include "scan.h"
+#include "notify.h"
 
 
 #ifndef CONFIG_NO_CONFIG_BLOBS
@@ -210,9 +210,8 @@ static int wpa_eapol_set_wep_key(void *ctx, int unicast, int keyidx,
                        wpa_s->group_cipher = cipher;
        }
        return wpa_drv_set_key(wpa_s, WPA_ALG_WEP,
-                              unicast ? wpa_s->bssid :
-                              (u8 *) "\xff\xff\xff\xff\xff\xff",
-                              keyidx, unicast, (u8 *) "", 0, key, keylen);
+                              unicast ? wpa_s->bssid : NULL,
+                              keyidx, unicast, NULL, 0, key, keylen);
 }
 
 
@@ -255,14 +254,29 @@ static void wpa_supplicant_eapol_cb(struct eapol_sm *eapol, int success,
                   "handshake");
 
        pmk_len = PMK_LEN;
-       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 (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) {
@@ -271,6 +285,9 @@ static void wpa_supplicant_eapol_cb(struct eapol_sm *eapol, int success,
                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");
@@ -420,10 +437,6 @@ static void * wpa_supplicant_get_network_ctx(void *wpa_s)
 static int wpa_supplicant_get_bssid(void *ctx, u8 *bssid)
 {
        struct wpa_supplicant *wpa_s = ctx;
-       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) {
-               os_memcpy(bssid, wpa_s->bssid, ETH_ALEN);
-               return 0;
-       }
        return wpa_drv_get_bssid(wpa_s, bssid);
 }
 
@@ -471,8 +484,6 @@ 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_USER_SPACE_MLME)
-               return ieee80211_sta_update_ft_ies(wpa_s, md, ies, ies_len);
        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);
@@ -484,9 +495,6 @@ static int wpa_supplicant_send_ft_action(void *ctx, u8 action,
                                         const u8 *ies, size_t ies_len)
 {
        struct wpa_supplicant *wpa_s = ctx;
-       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
-               return ieee80211_sta_send_ft_action(wpa_s, action, target_ap,
-                                                   ies, ies_len);
        return wpa_drv_send_ft_action(wpa_s, action, target_ap, ies, ies_len);
 }
 
@@ -497,9 +505,6 @@ static int wpa_supplicant_mark_authenticated(void *ctx, const u8 *target_ap)
        struct wpa_driver_auth_params params;
        struct wpa_bss *bss;
 
-       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
-               return -1;
-
        bss = wpa_bss_get_bssid(wpa_s, target_ap);
        if (bss == NULL)
                return -1;
@@ -518,13 +523,142 @@ static int wpa_supplicant_mark_authenticated(void *ctx, const u8 *target_ap)
 #endif /* CONFIG_NO_WPA */
 
 
+#ifdef CONFIG_TDLS
+
+static int wpa_supplicant_tdls_get_capa(void *ctx, int *tdls_supported,
+                                       int *tdls_ext_setup)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+
+       *tdls_supported = 0;
+       *tdls_ext_setup = 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;
+
+       return 0;
+}
+
+
+static int wpa_supplicant_send_tdls_mgmt(void *ctx, const u8 *dst,
+                                        u8 action_code, u8 dialog_token,
+                                        u16 status_code, const u8 *buf,
+                                        size_t len)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       return wpa_drv_send_tdls_mgmt(wpa_s, dst, action_code, dialog_token,
+                                     status_code, buf, len);
+}
+
+
+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 capability,
+       const u8 *supp_rates, size_t supp_rates_len)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       struct hostapd_sta_add_params params;
+
+       params.addr = peer;
+       params.aid = 1;
+       params.capability = capability;
+       params.flags = WPA_STA_TDLS_PEER | WPA_STA_AUTHORIZED;
+       params.ht_capabilities = NULL;
+       params.listen_interval = 0;
+       params.supp_rates = supp_rates;
+       params.supp_rates_len = supp_rates_len;
+       params.set = !add;
+
+       return wpa_drv_sta_add(wpa_s, &params);
+}
+
+#endif /* CONFIG_TDLS */
+
+
+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;
+       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;
+       default:
+               break;
+       }
+
+       /* txt needs to be something */
+       if (*txt == NULL) {
+               wpa_printf(MSG_WARNING, "No message for request %d", field);
+               ret = NULL;
+       }
+
+       return ret;
+}
+
 #ifdef IEEE8021X_EAPOL
 #if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
-static void wpa_supplicant_eap_param_needed(void *ctx, const char *field,
-                                           const char *txt)
+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;
        char *buf;
        size_t buflen;
        int len;
@@ -532,13 +666,23 @@ static void wpa_supplicant_eap_param_needed(void *ctx, const char *field,
        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;
+       }
+
        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, ssid->id, txt);
+                         field_name, ssid->id, txt);
        if (len < 0 || (size_t) len >= buflen) {
                os_free(buf);
                return;
@@ -572,6 +716,16 @@ static void wpa_supplicant_port_cb(void *ctx, int authorized)
                   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 *cert_hash,
+                                  const struct wpabuf *cert)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+
+       wpas_notify_certification(wpa_s, depth, subject, cert_hash, cert);
+}
 #endif /* IEEE8021X_EAPOL */
 
 
@@ -602,6 +756,7 @@ int wpa_supplicant_init_eapol(struct wpa_supplicant *wpa_s)
        ctx->eap_param_needed = wpa_supplicant_eap_param_needed;
        ctx->port_cb = wpa_supplicant_port_cb;
        ctx->cb = wpa_supplicant_eapol_cb;
+       ctx->cert_cb = wpa_supplicant_cert_cb;
        ctx->cb_ctx = wpa_s;
        wpa_s->eapol = eapol_sm_init(ctx);
        if (wpa_s->eapol == NULL) {
@@ -616,6 +771,16 @@ int wpa_supplicant_init_eapol(struct wpa_supplicant *wpa_s)
 }
 
 
+static void wpa_supplicant_set_rekey_offload(void *ctx, const u8 *kek,
+                                            const u8 *kck,
+                                            const u8 *replay_ctr)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+
+       wpa_drv_set_rekey_info(wpa_s, kek, kck, replay_ctr);
+}
+
+
 int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s)
 {
 #ifndef CONFIG_NO_WPA
@@ -651,6 +816,13 @@ int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s)
        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;
+#endif /* CONFIG_TDLS */
+       ctx->set_rekey_offload = wpa_supplicant_set_rekey_offload;
 
        wpa_s->wpa = wpa_sm_init(ctx);
        if (wpa_s->wpa == NULL) {
@@ -674,6 +846,7 @@ void wpa_supplicant_rsn_supp_set_config(struct wpa_supplicant *wpa_s,
                conf.peerkey_enabled = ssid->peerkey;
                conf.allowed_pairwise_cipher = ssid->pairwise_cipher;
 #ifdef IEEE8021X_EAPOL
+               conf.proactive_key_caching = ssid->proactive_key_caching;
                conf.eap_workaround = ssid->eap_workaround;
                conf.eap_conf_ctx = &ssid->eap;
 #endif /* IEEE8021X_EAPOL */
index b571e4d..78c1b3d 100644 (file)
 #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);
+
 #endif /* WPAS_GLUE_H */
index ba94d33..b0dafe4 100644 (file)
@@ -24,6 +24,7 @@
 #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 "config.h"
 #include "wpa_supplicant_i.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 */
 
 static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx);
 static void wpas_clear_wps(struct wpa_supplicant *wpa_s);
@@ -65,15 +71,25 @@ int wpas_wps_eapol_cb(struct wpa_supplicant *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;
                wpa_printf(MSG_DEBUG, "WPS: Network configuration replaced - "
-                          "try to associate with the received credential");
+                          "try to associate with the received credential "
+                          "(freq=%u)", freq);
                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 = wpa_s->assoc_freq;
+               wpa_s->wps_freq = freq;
                wpa_s->reassociate = 1;
                wpa_supplicant_req_scan(wpa_s, 0, 0);
                return 1;
@@ -113,6 +129,8 @@ static void wpas_wps_security_workaround(struct wpa_supplicant *wpa_s,
        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 "
@@ -178,6 +196,9 @@ static int wpa_supplicant_wps_cred(void *ctx,
        struct wpa_ssid *ssid = wpa_s->current_ssid;
        u8 key_idx = 0;
        u16 auth_type;
+#ifdef CONFIG_WPS_REG_DISABLE_OPEN
+       int registrar = 0;
+#endif /* CONFIG_WPS_REG_DISABLE_OPEN */
 
        if ((wpa_s->conf->wps_cred_processing == 1 ||
             wpa_s->conf->wps_cred_processing == 2) && cred->cred_attr) {
@@ -231,6 +252,13 @@ static int wpa_supplicant_wps_cred(void *ctx,
        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;
@@ -238,6 +266,8 @@ static int wpa_supplicant_wps_cred(void *ctx,
                ssid->eap.phase1 = NULL;
                os_free(ssid->eap.eap_methods);
                ssid->eap.eap_methods = NULL;
+               if (!ssid->p2p_group)
+                       ssid->temporary = 0;
        } else {
                wpa_printf(MSG_DEBUG, "WPS: Create a new network based on the "
                           "received credential");
@@ -304,6 +334,16 @@ static int wpa_supplicant_wps_cred(void *ctx,
                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_SHARED:
                ssid->auth_alg = WPA_AUTH_ALG_SHARED;
@@ -341,6 +381,7 @@ static int wpa_supplicant_wps_cred(void *ctx,
                                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);
@@ -349,6 +390,7 @@ static int wpa_supplicant_wps_cred(void *ctx,
                        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",
@@ -371,6 +413,15 @@ static int wpa_supplicant_wps_cred(void *ctx,
 }
 
 
+#ifdef CONFIG_P2P
+static void wpas_wps_pbc_overlap_cb(void *eloop_ctx, void *timeout_ctx)
+{
+       struct wpa_supplicant *wpa_s = eloop_ctx;
+       wpas_p2p_notif_pbc_overlap(wpa_s);
+}
+#endif /* CONFIG_P2P */
+
+
 static void wpa_supplicant_wps_event_m2d(struct wpa_supplicant *wpa_s,
                                         struct wps_event_m2d *m2d)
 {
@@ -378,15 +429,59 @@ static void wpa_supplicant_wps_event_m2d(struct wpa_supplicant *wpa_s,
                "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_wps_pbc_overlap_cb, wpa_s,
+                                      NULL);
+       }
+#endif /* CONFIG_P2P */
 }
 
 
+static const char * wps_event_fail_reason[NUM_WPS_EI_VALUES] = {
+       "No Error", /* WPS_EI_NO_ERROR */
+       "TKIP Only Prohibited", /* WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED */
+       "WEP Prohibited" /* WPS_EI_SECURITY_WEP_PROHIBITED */
+};
+
 static void wpa_supplicant_wps_event_fail(struct wpa_supplicant *wpa_s,
                                          struct wps_event_fail *fail)
 {
-       wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_FAIL "msg=%d", fail->msg);
+       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_event_fail_reason[fail->error_indication]);
+               if (wpa_s->parent && wpa_s->parent != wpa_s)
+                       wpa_msg(wpa_s->parent, MSG_INFO, WPS_EVENT_FAIL
+                               "msg=%d config_error=%d reason=%d (%s)",
+                               fail->msg, fail->config_error,
+                               fail->error_indication,
+                               wps_event_fail_reason[fail->error_indication]);
+       } 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);
+       }
        wpas_clear_wps(wpa_s);
        wpas_notify_wps_event_fail(wpa_s, fail);
+#ifdef CONFIG_P2P
+       wpas_p2p_wps_failed(wpa_s, fail);
+#endif /* CONFIG_P2P */
 }
 
 
@@ -395,6 +490,9 @@ 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);
+#ifdef CONFIG_P2P
+       wpas_p2p_wps_success(wpa_s, wpa_s->bssid, 0);
+#endif /* CONFIG_P2P */
 }
 
 
@@ -468,6 +566,59 @@ static void wpa_supplicant_wps_event_er_enrollee_remove(
 }
 
 
+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)
 {
@@ -483,6 +634,10 @@ static void wpa_supplicant_wps_event(void *ctx, enum wps_event event,
                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;
@@ -502,6 +657,16 @@ static void wpa_supplicant_wps_event(void *ctx, enum wps_event event,
                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;
        }
 }
 
@@ -519,7 +684,9 @@ enum wps_request_type wpas_wps_get_req_type(struct wpa_ssid *ssid)
 static void wpas_clear_wps(struct wpa_supplicant *wpa_s)
 {
        int id;
-       struct wpa_ssid *ssid, *remove_ssid = NULL;
+       struct wpa_ssid *ssid, *remove_ssid = NULL, *prev_current;
+
+       prev_current = wpa_s->current_ssid;
 
        eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL);
 
@@ -538,6 +705,11 @@ static void wpas_clear_wps(struct wpa_supplicant *wpa_s)
                        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);
                }
@@ -548,8 +720,8 @@ static void wpas_clear_wps(struct wpa_supplicant *wpa_s)
 static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx)
 {
        struct wpa_supplicant *wpa_s = eloop_ctx;
-       wpa_printf(MSG_INFO, WPS_EVENT_TIMEOUT "Requested operation timed "
-                  "out");
+       wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_TIMEOUT "Requested operation timed "
+               "out");
        wpas_clear_wps(wpa_s);
 }
 
@@ -564,6 +736,7 @@ static struct wpa_ssid * wpas_wps_add_network(struct wpa_supplicant *wpa_s,
                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 ?
@@ -575,12 +748,20 @@ static struct wpa_ssid * wpas_wps_add_network(struct wpa_supplicant *wpa_s,
        }
 
        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;
@@ -604,6 +785,7 @@ static struct wpa_ssid * wpas_wps_add_network(struct wpa_supplicant *wpa_s,
                        ssid->ssid = NULL;
                        ssid->ssid_len = 0;
                }
+#endif /* CONFIG_P2P */
        }
 
        return ssid;
@@ -615,13 +797,26 @@ static void wpas_wps_reassoc(struct wpa_supplicant *wpa_s,
 {
        struct wpa_ssid *ssid;
 
+       if (wpa_s->current_ssid)
+               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 = ssid != selected;
-               if (was_disabled != ssid->disabled)
-                       wpas_notify_network_enabled_changed(wpa_s, ssid);
+               /*
+                * 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)
+                               wpas_notify_network_enabled_changed(wpa_s,
+                                                                   ssid);
+               }
                ssid = ssid->next;
        }
        wpa_s->disconnected = 0;
@@ -633,14 +828,31 @@ static void wpas_wps_reassoc(struct wpa_supplicant *wpa_s,
 }
 
 
-int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid)
+int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid,
+                      int p2p_group)
 {
        struct wpa_ssid *ssid;
        wpas_clear_wps(wpa_s);
        ssid = wpas_wps_add_network(wpa_s, 0, 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 */
        wpa_config_set(ssid, "phase1", "\"pbc=1\"", 0);
+       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);
@@ -649,7 +861,7 @@ int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid)
 
 
 int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
-                      const char *pin)
+                      const char *pin, int p2p_group, u16 dev_pw_id)
 {
        struct wpa_ssid *ssid;
        char val[128];
@@ -659,13 +871,31 @@ int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
        ssid = wpas_wps_add_network(wpa_s, 0, 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 (pin)
-               os_snprintf(val, sizeof(val), "\"pin=%s\"", pin);
+               os_snprintf(val, sizeof(val), "\"pin=%s dev_pw_id=%u\"",
+                           pin, dev_pw_id);
        else {
                rpin = wps_generate_pin();
-               os_snprintf(val, sizeof(val), "\"pin=%08d\"", rpin);
+               os_snprintf(val, sizeof(val), "\"pin=%08d dev_pw_id=%u\"",
+                           rpin, dev_pw_id);
        }
        wpa_config_set(ssid, "phase1", val, 0);
+       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);
@@ -673,6 +903,32 @@ int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
 }
 
 
+/* 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_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_supplicant_deauthenticate(wpa_s,
+                                             WLAN_REASON_DEAUTH_LEAVING);
+               wpas_clear_wps(wpa_s);
+       }
+
+       return 0;
+}
+
+
 #ifdef CONFIG_WPS_OOB
 int wpas_wps_start_oob(struct wpa_supplicant *wpa_s, char *device_type,
                       char *path, char *method, char *name)
@@ -715,7 +971,8 @@ int wpas_wps_start_oob(struct wpa_supplicant *wpa_s, char *device_type,
        if ((wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_E ||
             wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_R) &&
            wpas_wps_start_pin(wpa_s, NULL,
-                              wpabuf_head(wps->oob_conf.dev_password)) < 0)
+                              wpabuf_head(wps->oob_conf.dev_password), 0,
+                              DEV_PW_DEFAULT) < 0)
                        return -1;
 
        return 0;
@@ -737,6 +994,7 @@ int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid,
        ssid = wpas_wps_add_network(wpa_s, 1, bssid);
        if (ssid == NULL)
                return -1;
+       ssid->temporary = 1;
        pos = val;
        end = pos + sizeof(val);
        res = os_snprintf(pos, end - pos, "\"pin=%s", pin);
@@ -756,6 +1014,8 @@ int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid,
        if (res < 0 || res >= end - pos)
                return -1;
        wpa_config_set(ssid, "phase1", val, 0);
+       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);
@@ -805,16 +1065,71 @@ static void wpas_wps_set_sel_reg_cb(void *ctx, int sel_reg, u16 dev_passwd_id,
 
        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)
+{
+#ifdef CONFIG_WPS2
+       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;
+       }
+#endif /* CONFIG_WPS2 */
+
+       return config_methods;
+}
+
+
+static void wpas_wps_set_uuid(struct wpa_supplicant *wpa_s,
+                             struct wps_context *wps)
+{
+       wpa_printf(MSG_DEBUG, "WPS: Set UUID for interface %s", wpa_s->ifname);
+       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) {
+                       os_memcpy(wps->uuid, wpa_s->global->ifaces->wps->uuid,
+                                 WPS_UUID_LEN);
+                       wpa_hexdump(MSG_DEBUG, "WPS: UUID from the first "
+                                   "interface", wps->uuid, WPS_UUID_LEN);
+               } else {
+                       uuid_gen_mac_addr(wpa_s->own_addr, wps->uuid);
+                       wpa_hexdump(MSG_DEBUG, "WPS: UUID based on MAC "
+                                   "address", wps->uuid, WPS_UUID_LEN);
+               }
+       } else {
+               os_memcpy(wps->uuid, wpa_s->conf->uuid, WPS_UUID_LEN);
+               wpa_hexdump(MSG_DEBUG, "WPS: UUID based on configuration",
+                           wps->uuid, WPS_UUID_LEN);
+       }
+}
+
+
 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)
@@ -831,22 +1146,42 @@ int wpas_wps_init(struct wpa_supplicant *wpa_s)
        wps->dev.serial_number = wpa_s->conf->serial_number;
        wps->config_methods =
                wps_config_methods_str2bin(wpa_s->conf->config_methods);
-       if (wpa_s->conf->device_type &&
-           wps_dev_type_str2bin(wpa_s->conf->device_type,
-                                wps->dev.pri_dev_type) < 0) {
-               wpa_printf(MSG_ERROR, "WPS: Invalid device_type");
+       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);
+
        wps->dev.os_version = WPA_GET_BE32(wpa_s->conf->os_version);
-       wps->dev.rf_bands = WPS_RF_24GHZ | WPS_RF_50GHZ; /* TODO: config */
+       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;
+               }
+       }
+       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);
-       if (is_nil_uuid(wpa_s->conf->uuid)) {
-               uuid_gen_mac_addr(wpa_s->own_addr, wps->uuid);
-               wpa_hexdump(MSG_DEBUG, "WPS: UUID based on MAC address",
-                           wps->uuid, WPS_UUID_LEN);
-       } else
-               os_memcpy(wps->uuid, wpa_s->conf->uuid, WPS_UUID_LEN);
+       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;
@@ -929,12 +1264,13 @@ int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s,
                }
 
                /*
-                * Start with WPS APs that advertise active PIN Registrar and
-                * allow any WPS AP after third scan since some APs do not set
-                * Selected Registrar attribute properly when using external
-                * Registrar.
+                * 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_selected_pin_registrar(wps_ie)) {
+               if (!wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1)) {
                        if (wpa_s->scan_runs < WPS_PIN_SCAN_IGNORE_SEL_REG) {
                                wpa_printf(MSG_DEBUG, "   skip - WPS AP "
                                           "without active PIN Registrar");
@@ -944,7 +1280,7 @@ int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s,
                        wpa_printf(MSG_DEBUG, "   selected based on WPS IE");
                } else {
                        wpa_printf(MSG_DEBUG, "   selected based on WPS IE "
-                                  "(Active PIN)");
+                                  "(Authorized MAC or Active PIN)");
                }
                wpabuf_free(wps_ie);
                return 1;
@@ -976,7 +1312,7 @@ int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s,
        } else if (eap_is_wps_pin_enrollee(&ssid->eap)) {
                wps_ie = wpa_scan_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
                if (wps_ie &&
-                   (wps_is_selected_pin_registrar(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;
@@ -989,6 +1325,28 @@ int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s,
                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_scan_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;
@@ -1006,12 +1364,21 @@ int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s,
        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)
+       if (wps_ie) {
                sel_uuid = wps_get_uuid_e(wps_ie);
-       else
+               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;
+       }
 
        dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
                struct wpabuf *ie;
@@ -1024,10 +1391,18 @@ int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s,
                        wpabuf_free(ie);
                        continue;
                }
+               wpa_printf(MSG_DEBUG, "WPS: Another BSS in active PBC mode: "
+                          MACSTR, MAC2STR(bss->bssid));
                uuid = wps_get_uuid_e(ie);
+               wpa_hexdump(MSG_DEBUG, "WPS: UUID of the other BSS",
+                           uuid, UUID_LEN);
                if (sel_uuid == NULL || uuid == NULL ||
-                   os_memcmp(sel_uuid, uuid, 16) != 0) {
+                   os_memcmp(sel_uuid, uuid, UUID_LEN) != 0) {
                        ret = 1; /* PBC overlap */
+                       wpa_msg(wpa_s, MSG_INFO, "WPS: PBC overlap detected: "
+                               MACSTR " and " MACSTR,
+                               MAC2STR(selected->bssid),
+                               MAC2STR(bss->bssid));
                        wpabuf_free(ie);
                        break;
                }
@@ -1046,6 +1421,7 @@ int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s,
 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;
@@ -1056,17 +1432,24 @@ void wpas_wps_notify_scan_results(struct wpa_supplicant *wpa_s)
                if (!ie)
                        continue;
                if (wps_is_selected_pbc_registrar(ie))
-                       wpa_msg_ctrl(wpa_s, MSG_INFO,
-                                    WPS_EVENT_AP_AVAILABLE_PBC);
+                       pbc++;
+               else if (wps_is_addr_authorized(ie, wpa_s->own_addr, 0))
+                       auth++;
                else if (wps_is_selected_pin_registrar(ie))
-                       wpa_msg_ctrl(wpa_s, MSG_INFO,
-                                    WPS_EVENT_AP_AVAILABLE_PIN);
+                       pin++;
                else
-                       wpa_msg_ctrl(wpa_s, MSG_INFO,
-                                    WPS_EVENT_AP_AVAILABLE);
+                       wps++;
                wpabuf_free(ie);
-               break;
        }
+
+       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);
 }
 
 
@@ -1099,14 +1482,14 @@ int wpas_wps_scan_result_text(const u8 *ies, size_t ies_len, char *buf,
 }
 
 
-int wpas_wps_er_start(struct wpa_supplicant *wpa_s)
+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);
+       wpa_s->wps_er = wps_er_init(wpa_s->wps, wpa_s->ifname, filter);
        if (wpa_s->wps_er == NULL)
                return -1;
        return 0;
@@ -1127,8 +1510,8 @@ int wpas_wps_er_stop(struct wpa_supplicant *wpa_s)
 
 
 #ifdef CONFIG_WPS_ER
-int wpas_wps_er_add_pin(struct wpa_supplicant *wpa_s, const char *uuid,
-                       const char *pin)
+int wpas_wps_er_add_pin(struct wpa_supplicant *wpa_s, const u8 *addr,
+                       const char *uuid, const char *pin)
 {
        u8 u[UUID_LEN];
        int any = 0;
@@ -1137,7 +1520,8 @@ int wpas_wps_er_add_pin(struct wpa_supplicant *wpa_s, const char *uuid,
                any = 1;
        else if (uuid_str2bin(uuid, u))
                return -1;
-       return wps_registrar_add_pin(wpa_s->wps->registrar, any ? NULL : u,
+       return wps_registrar_add_pin(wpa_s->wps->registrar, addr,
+                                    any ? NULL : u,
                                     (const u8 *) pin, os_strlen(pin), 300);
 }
 
@@ -1164,10 +1548,107 @@ int wpas_wps_er_learn(struct wpa_supplicant *wpa_s, const char *uuid,
 }
 
 
+int wpas_wps_er_set_config(struct wpa_supplicant *wpa_s, const char *uuid,
+                          int id)
+{
+       u8 u[UUID_LEN];
+       struct wpa_ssid *ssid;
+       struct wps_credential cred;
+
+       if (uuid_str2bin(uuid, u))
+               return -1;
+       ssid = wpa_config_get_network(wpa_s->conf, id);
+       if (ssid == NULL || ssid->ssid == NULL)
+               return -1;
+
+       os_memset(&cred, 0, sizeof(cred));
+       if (ssid->ssid_len > 32)
+               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 wps_er_set_config(wpa_s->wps_er, u, &cred);
+}
+
+
+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];
+       struct wps_credential cred;
+       size_t len;
+
+       if (uuid_str2bin(uuid, u))
+               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;
+       else if (os_strcmp(settings->encr, "WEP") == 0)
+               cred.encr_type = WPS_ENCR_WEP;
+       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, u, (const u8 *) pin,
+                            os_strlen(pin), &cred);
+}
+
+
+static int callbacks_pending = 0;
+
 static void wpas_wps_terminate_cb(void *ctx)
 {
        wpa_printf(MSG_DEBUG, "WPS ER: Terminated");
-       eloop_terminate();
+       if (--callbacks_pending <= 0)
+               eloop_terminate();
 }
 #endif /* CONFIG_WPS_ER */
 
@@ -1176,6 +1657,7 @@ 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;
@@ -1183,3 +1665,65 @@ int wpas_wps_terminate_pending(struct wpa_supplicant *wpa_s)
 #endif /* CONFIG_WPS_ER */
        return 0;
 }
+
+
+int wpas_wps_in_progress(struct wpa_supplicant *wpa_s)
+{
+       struct wpa_ssid *ssid;
+
+       for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+               if (!ssid->disabled && ssid->key_mgmt == WPA_KEY_MGMT_WPS)
+                       return 1;
+       }
+
+       return 0;
+}
+
+
+void wpas_wps_update_config(struct wpa_supplicant *wpa_s)
+{
+       struct wps_context *wps = wpa_s->wps;
+
+       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);
+
+       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_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;
+       }
+}
index ba2fb16..b38c091 100644 (file)
@@ -35,9 +35,11 @@ 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 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);
+                      const char *pin, int p2p_group, u16 dev_pw_id);
+int wpas_wps_cancel(struct wpa_supplicant *wpa_s);
 int wpas_wps_start_oob(struct wpa_supplicant *wpa_s, char *device_type,
                       char *path, char *method, char *name);
 int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid,
@@ -52,14 +54,20 @@ 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);
+int wpas_wps_er_start(struct wpa_supplicant *wpa_s, const char *filter);
 int wpas_wps_er_stop(struct wpa_supplicant *wpa_s);
-int wpas_wps_er_add_pin(struct wpa_supplicant *wpa_s, const char *uuid,
-                       const char *pin);
+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);
 int wpas_wps_terminate_pending(struct wpa_supplicant *wpa_s);
+int wpas_wps_in_progress(struct wpa_supplicant *wpa_s);
+void wpas_wps_update_config(struct wpa_supplicant *wpa_s);
 
 #else /* CONFIG_WPS */