diff options
Diffstat (limited to 'contrib/wpa/wpa_supplicant')
134 files changed, 40590 insertions, 8597 deletions
diff --git a/contrib/wpa/wpa_supplicant/.gitignore b/contrib/wpa/wpa_supplicant/.gitignore index e7e034c..0e3ad1b 100644 --- a/contrib/wpa/wpa_supplicant/.gitignore +++ b/contrib/wpa/wpa_supplicant/.gitignore @@ -1,8 +1 @@ -*.d -.config -eapol_test -preauth_test -wpa_cli -wpa_passphrase -wpa_supplicant -wpa_priv +*.service diff --git a/contrib/wpa/wpa_supplicant/ChangeLog b/contrib/wpa/wpa_supplicant/ChangeLog index 56046c3..8abafb2 100644 --- a/contrib/wpa/wpa_supplicant/ChangeLog +++ b/contrib/wpa/wpa_supplicant/ChangeLog @@ -1,23 +1,408 @@ 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 +2013-01-12 - v2.0 + * removed Qt3-based wpa_gui (obsoleted by wpa_qui-qt4) + * removed unmaintained driver wrappers broadcom, iphone, osx, ralink, + hostap, madwifi (hostap and madwifi remain available for hostapd; + their wpa_supplicant functionality is obsoleted by wext) + * improved debug logging (human readable event names, interface name + included in more entries) + * changed AP mode behavior to enable WPS only for open and + WPA/WPA2-Personal configuration + * improved P2P concurrency operations + - better coordination of concurrent scan and P2P search operations + - avoid concurrent remain-on-channel operation requests by canceling + previous operations prior to starting a new one + - reject operations that would require multi-channel concurrency if + the driver does not support it + - add parameter to select whether STA or P2P connection is preferred + if the driver cannot support both at the same time + - allow driver to indicate channel changes + - added optional delay=<search delay in milliseconds> parameter for + p2p_find to avoid taking all radio resources + - use 500 ms p2p_find search delay by default during concurrent + operations + - allow all channels in GO Negotiation if the driver supports + multi-channel concurrency + * added number of small changes to make it easier for static analyzers + to understand the implementation + * fixed number of small bugs (see git logs for more details) + * nl80211: number of updates to use new cfg80211/nl80211 functionality + - replace monitor interface with nl80211 commands for AP mode + - additional information for driver-based AP SME + - STA entry authorization in RSN IBSS + * EAP-pwd: + - fixed KDF for group 21 and zero-padding + - added support for fragmentation + - increased maximum number of hunting-and-pecking iterations + * avoid excessive Probe Response retries for broadcast Probe Request + frames (only with drivers using wpa_supplicant AP mode SME/MLME) + * added "GET country" ctrl_iface command + * do not save an invalid network block in wpa_supplicant.conf to avoid + problems reading the file on next start + * send STA connected/disconnected ctrl_iface events to both the P2P + group and parent interfaces + * added preliminary support for using TLS v1.2 (CONFIG_TLSV12=y) + * added "SET pno <1/0>" ctrl_iface command to start/stop preferred + network offload with sched_scan driver command + * merged in number of changes from Android repository for P2P, nl80211, + and build parameters + * changed P2P GO mode configuration to use driver capabilities to + automatically enable HT operations when supported + * added "wpa_cli status wps" command to fetch WPA2-Personal passhrase + for WPS use cases in AP mode + * EAP-AKA: keep pseudonym identity across EAP exchanges to match EAP-SIM + behavior + * improved reassociation behavior in cases where association is rejected + or when an AP disconnects us to handle common load balancing + mechanisms + - try to avoid extra scans when the needed information is available + * added optional "join" argument for p2p_prov_disc ctrl_iface command + * added group ifname to P2P-PROV-DISC-* events + * added P2P Device Address to AP-STA-DISCONNECTED event and use + p2p_dev_addr parameter name with AP-STA-CONNECTED + * added workarounds for WPS PBC overlap detection for some P2P use cases + where deployed stations work incorrectly + * optimize WPS connection speed by disconnecting prior to WPS scan and + by using single channel scans when AP channel is known + * PCSC and SIM/USIM improvements: + - accept 0x67 (Wrong length) as a response to READ RECORD to fix + issues with some USIM cards + - try to read MNC length from SIM/USIM + - build realm according to 3GPP TS 23.003 with identity from the SIM + - allow T1 protocol to be enabled + * added more WPS and P2P information available through D-Bus + * improve P2P negotiation robustness + - extra waits to get ACK frames through + - longer timeouts for cases where deployed devices have been + identified have issues meeting the specification requirements + - more retries for some P2P frames + - handle race conditions in GO Negotiation start by both devices + - ignore unexpected GO Negotiation Response frame + * added support for libnl 3.2 and newer + * added P2P persistent group info to P2P_PEER data + * maintain a list of P2P Clients for persistent group on GO + * AP: increased initial group key handshake retransmit timeout to 500 ms + * added optional dev_id parameter for p2p_find + * added P2P-FIND-STOPPED ctrl_iface event + * fixed issues in WPA/RSN element validation when roaming with ap_scan=1 + and driver-based BSS selection + * do not expire P2P peer entries while connected with the peer in a + group + * fixed WSC element inclusion in cases where P2P is disabled + * AP: added a WPS workaround for mixed mode AP Settings with Windows 7 + * EAP-SIM: fixed AT_COUNTER_TOO_SMALL use + * EAP-SIM/AKA: append realm to pseudonym identity + * EAP-SIM/AKA: store pseudonym identity in network configuration to + allow it to persist over multiple EAP sessions and wpa_supplicant + restarts + * EAP-AKA': updated to RFC 5448 (username prefixes changed); note: this + breaks interoperability with older versions + * added support for WFA Hotspot 2.0 + - GAS/ANQP to fetch network information + - credential configuration and automatic network selections based on + credential match with ANQP information + * limited PMKSA cache entries to be used only with the network context + that was used to create them + * improved PMKSA cache expiration to avoid unnecessary disconnections + * adjusted bgscan_simple fast-scan backoff to avoid too frequent + background scans + * removed ctrl_iface event on P2P PD Response in join-group case + * added option to fetch BSS table entry based on P2P Device Address + ("BSS p2p_dev_addr=<P2P Device Address>") + * added BSS entry age to ctrl_iface BSS command output + * added optional MASK=0xH option for ctrl_iface BSS command to select + which fields are included in the response + * added optional RANGE=ALL|N1-N2 option for ctrl_iface BSS command to + fetch information about several BSSes in one call + * simplified licensing terms by selecting the BSD license as the only + alternative + * added "P2P_SET disallow_freq <freq list>" ctrl_iface command to + disable channels from P2P use + * added p2p_pref_chan configuration parameter to allow preferred P2P + channels to be specified + * added support for advertising immediate availability of a WPS + credential for P2P use cases + * optimized scan operations for P2P use cases (use single channel scan + for a specific SSID when possible) + * EAP-TTLS: fixed peer challenge generation for MSCHAPv2 + * SME: do not use reassociation after explicit disconnection request + (local or a notification from an AP) + * added support for sending debug info to Linux tracing (-T on command + line) + * added support for using Deauthentication reason code 3 as an + indication of P2P group termination + * added wps_vendor_ext_m1 configuration parameter to allow vendor + specific attributes to be added to WPS M1 + * started using separate TLS library context for tunneled TLS + (EAP-PEAP/TLS, EAP-TTLS/TLS, EAP-FAST/TLS) to support different CA + certificate configuration between Phase 1 and Phase 2 + * added optional "auto" parameter for p2p_connect to request automatic + GO Negotiation vs. join-a-group selection + * added disabled_scan_offload parameter to disable automatic scan + offloading (sched_scan) + * added optional persistent=<network id> parameter for p2p_connect to + allow forcing of a specific SSID/passphrase for GO Negotiation + * added support for OBSS scan requests and 20/40 BSS coexistence reports + * reject PD Request for unknown group + * removed scripts and notes related to Windows binary releases (which + have not been used starting from 1.x) + * added initial support for WNM operations + - Keep-alive based on BSS max idle period + - WNM-Sleep Mode + - minimal BSS Transition Management processing + * added autoscan module to control scanning behavior while not connected + - autoscan_periodic and autoscan_exponential modules + * added new WPS NFC ctrl_iface mechanism + - added initial support NFC connection handover + - removed obsoleted WPS_OOB command (including support for deprecated + UFD config_method) + * added optional framework for external password storage ("ext:<name>") + * wpa_cli: added optional support for controlling wpa_supplicant + remotely over UDP (CONFIG_CTRL_IFACE=udp-remote) for testing purposes + * wpa_cli: extended tab completion to more commands + * changed SSID output to use printf-escaped strings instead of masking + of non-ASCII characters + - SSID can now be configured in the same format: ssid=P"abc\x00test" + * removed default ACM=1 from AC_VO and AC_VI + * added optional "ht40" argument for P2P ctrl_iface commands to allow + 40 MHz channels to be requested on the 5 GHz band + * added optional parameters for p2p_invite command to specify channel + when reinvoking a persistent group as the GO + * improved FIPS mode builds with OpenSSL + - "make fips" with CONFIG_FIPS=y to build wpa_supplicant with the + OpenSSL FIPS object module + - replace low level OpenSSL AES API calls to use EVP + - use OpenSSL keying material exporter when possible + - do not export TLS keys in FIPS mode + - remove MD5 from CONFIG_FIPS=y builds + - use OpenSSL function for PKBDF2 passphrase-to-PSK + - use OpenSSL HMAC implementation + - mix RAND_bytes() output into random_get_bytes() to force OpenSSL + DRBG to be used in FIPS mode + - use OpenSSL CMAC implementation + * added mechanism to disable TLS Session Ticket extension + - a workaround for servers that do not support TLS extensions that + was enabled by default in recent OpenSSL versions + - tls_disable_session_ticket=1 + - automatically disable TLS Session Ticket extension by default when + using EAP-TLS/PEAP/TTLS (i.e., only use it with EAP-FAST) + * changed VENDOR-TEST EAP method to use proper private enterprise number + (this will not interoperate with older versions) + * disable network block temporarily on authentication failures + * improved WPS AP selection during WPS PIN iteration + * added support for configuring GCMP cipher for IEEE 802.11ad + * added support for Wi-Fi Display extensions + - WFD_SUBELEMENT_SET ctrl_iface command to configure WFD subelements + - SET wifi_display <0/1> to disable/enable WFD support + - WFD service discovery + - an external program is needed to manage the audio/video streaming + and codecs + * optimized scan result use for network selection + - use the internal BSS table instead of raw scan results + - allow unnecessary scans to be skipped if fresh information is + available (e.g., after GAS/ANQP round for Interworking) + * added support for 256-bit AES with internal TLS implementation + * allow peer to propose channel in P2P invitation process for a + persistent group + * added disallow_aps parameter to allow BSSIDs/SSIDs to be disallowed + from network selection + * re-enable the networks disabled during WPS operations + * allow P2P functionality to be disabled per interface (p2p_disabled=1) + * added secondary device types into P2P_PEER output + * added an option to disable use of a separate P2P group interface + (p2p_no_group_iface=1) + * fixed P2P Bonjour SD to match entries with both compressed and not + compressed domain name format and support multiple Bonjour PTR matches + for the same key + * use deauthentication instead of disassociation for all disconnection + operations; this removes the now unused disassociate() wpa_driver_ops + callback + * optimized PSK generation on P2P GO by caching results to avoid + multiple PBKDF2 operations + * added okc=1 global configuration parameter to allow OKC to be enabled + by default for all network blocks + * added a workaround for WPS PBC session overlap detection to avoid + interop issues with deployed station implementations that do not + remove active PBC indication from Probe Request frames properly + * added basic support for 60 GHz band + * extend EAPOL frames processing workaround for roaming cases + (postpone processing of unexpected EAPOL frame until association + event to handle reordered events) + +2012-05-10 - v1.0 + * bsd: Add support for setting HT values in IFM_MMASK. + * Delay STA entry removal until Deauth/Disassoc TX status in AP mode. + This allows the driver to use PS buffering of Deauthentication and + Disassociation frames when the STA is in power save sleep. Only + available with drivers that provide TX status events for Deauth/ + Disassoc frames (nl80211). + * Drop oldest unknown BSS table entries first. This makes it less + likely to hit connection issues in environments with huge number + of visible APs. + * Add systemd support. + * Add support for setting the syslog facility from the config file + at build time. + * atheros: Add support for IEEE 802.11w configuration. + * AP mode: Allow enable HT20 if driver supports it, by setting the + config parameter ieee80211n. + * Allow AP mode to disconnect STAs based on low ACK condition (when + the data connection is not working properly, e.g., due to the STA + going outside the range of the AP). Disabled by default, enable by + config option disassoc_low_ack. + * nl80211: + - Support GTK rekey offload. + - Support PMKSA candidate events. This adds support for RSN + pre-authentication with nl80211 interface and drivers that handle + roaming internally. + * dbus: + - Add a DBus signal for EAP SM requests, emitted on the Interface + object. + - Export max scan ssids supported by the driver as MaxScanSSID. + - Add signal Certification for information about server certification. + - Add BSSExpireAge and BSSExpireCount interface properties and + support set/get, which allows for setting BSS cache expiration age + and expiration scan count. + - Add ConfigFile to AddInterface properties. + - Add Interface.Country property and support to get/set the value. + - Add DBus property CurrentAuthMode. + - P2P DBus API added. + - Emit property changed events (for property BSSs) when adding/ + removing BSSs. + - Treat '' in SSIDs of Interface.Scan as a request for broadcast + scan, instead of ignoring it. + - Add DBus getter/setter for FastReauth. + - Raise PropertiesChanged on org.freedesktop.DBus.Properties. + * wpa_cli: + - Send AP-STA-DISCONNECTED event when an AP disconnects a station + due to inactivity. + - Make second argument to set command optional. This can be used to + indicate a zero length value. + - Add signal_poll command. + - Add bss_expire_age and bss_expire_count commands to set/get BSS + cache expiration age and expiration scan count. + - Add ability to set scan interval (the time in seconds wpa_s waits + before requesting a new scan after failing to find a suitable + network in scan results) using scan_interval command. + - Add event CTRL-EVENT-ASSOC-REJECT for association rejected. + - Add command get version, that returns wpa_supplicant version string. + - Add command sta_autoconnect for disabling automatic reconnection + on receiving disconnection event. + - Setting bssid parameter to an empty string "" or any can now be + used to clear the bssid_set flag in a network block, i.e., to remove + bssid filtering. + - Add tdls_testing command to add a special testing feature for + changing TDLS behavior. Build param CONFIG_TDLS_TESTING must be + enabled as well. + - For interworking, add wpa_cli commands interworking_select, + interworking_connect, anqp_get, fetch_anqp, and stop_fetch_anqp. + - Many P2P commands were added. See README-P2P. + - Many WPS/WPS ER commands - see WPS/WPS ER sections for details. + - Allow set command to change global config parameters. + - Add log_level command, which can be used to display the current + debugging level and to change the log level during run time. + - Add note command, which can be used to insert notes to the debug + log. + - Add internal line edit implementation. CONFIG_WPA_CLI_EDIT=y + can now be used to build wpa_cli with internal implementation of + line editing and history support. This can be used as a replacement + for CONFIG_READLINE=y. + * AP mode: Add max_num_sta config option, which can be used to limit + the number of stations allowed to connect to the AP. + * Add WPA_IGNORE_CONFIG_ERRORS build option to continue in case of bad + config file. + * wext: Increase scan timeout from 5 to 10 seconds. + * Add blacklist command, allowing an external program to + manage the BSS blacklist and display its current contents. + * WPS: + - Add wpa_cli wps_pin get command for generating random PINs. This can + be used in a UI to generate a PIN without starting WPS (or P2P) + operation. + - Set RF bands based on driver capabilities, instead of hardcoding + them. + - Add mechanism for indicating non-standard WPS errors. + - Add CONFIG_WPS_REG_DISABLE_OPEN=y option to disable open networks + by default. + - Add wps_ap_pin cli command for wpa_supplicant AP mode. + - Add wps_check_pin cli command for processing PIN from user input. + UIs can use this command to process a PIN entered by a user and to + validate the checksum digit (if present). + - Cancel WPS operation on PBC session overlap detection. + - New wps_cancel command in wpa_cli will cancel a pending WPS + operation. + - wpa_cli action: Add WPS_EVENT_SUCCESS and WPS_EVENT_FAIL handlers. + - Trigger WPS config update on Manufacturer, Model Name, Model + Number, and Serial Number changes. + - Fragment size is now configurable for EAP-WSC peer. Use + wpa_cli set wps_fragment_size <val>. + - Disable AP PIN after 10 consecutive failures. Slow down attacks on + failures up to 10. + - Allow AP to start in Enrollee mode without AP PIN for probing, to + be compatible with Windows 7. + - Add Config Error into WPS-FAIL events to provide more info to the + user on how to resolve the issue. + - Label and Display config methods are not allowed to be enabled + at the same time, since it is unclear which PIN to use if both + methods are advertised. + - When controlling multiple interfaces: + - apply WPS commands to all interfaces configured to use WPS + - apply WPS config changes to all interfaces that use WPS + - when an attack is detected on any interface, disable AP PIN on + all interfaces + * WPS ER: + - Add special AP Setup Locked mode to allow read only ER. + ap_setup_locked=2 can now be used to enable a special mode where + WPS ER can learn the current AP settings, but cannot change them. + - Show SetSelectedRegistrar events as ctrl_iface events + - Add wps_er_set_config to enroll a network based on a local + network configuration block instead of having to (re-)learn the + current AP settings with wps_er_learn. + - Allow AP filtering based on IP address, add ctrl_iface event for + learned AP settings, add wps_er_config command to configure an AP. + * WPS 2.0: Add support for WPS 2.0 (CONFIG_WPS2) + - Add build option CONFIG_WPS_EXTENSIBILITY_TESTING to enable tool + for testing protocol extensibility. + - Add build option CONFIG_WPS_STRICT to allow disabling of WPS + workarounds. + - Add support for AuthorizedMACs attribute. + * TDLS: + - Propogate TDLS related nl80211 capability flags from kernel and + add them as driver capability flags. If the driver doesn't support + capabilities, assume TDLS is supported internally. When TDLS is + explicitly not supported, disable all user facing TDLS operations. + - Allow TDLS to be disabled at runtime (mostly for testing). + Use set tdls_disabled. + - Honor AP TDLS settings that prohibit/allow TDLS. + - Add a special testing feature for changing TDLS behavior. Use + CONFIG_TDLS_TESTING build param to enable. Configure at runtime + with tdls_testing cli command. + - Add support for TDLS 802.11z. + * wlantest: Add a tool wlantest for IEEE802.11 protocol testing. + wlantest can be used to capture frames from a monitor interface + for realtime capturing or from pcap files for offline analysis. + * Interworking: Support added for 802.11u. Enable in .config with + CONFIG_INTERWORKING. See wpa_supplicant.conf for config parameters + for interworking. wpa_cli commands added to support this are + interworking_select, interworking_connect, anqp_get, fetch_anqp, + and stop_fetch_anqp. + * Android: Add build and runtime support for Android wpa_supplicant. + * bgscan learn: Add new bgscan that learns BSS information based on + previous scans, and uses that information to dynamically generate + the list of channels for background scans. + * Add a new debug message level for excessive information. Use + -ddd to enable. + * TLS: Add support for tls_disable_time_checks=1 in client mode. + * Internal TLS: + - Add support for TLS v1.1 (RFC 4346). Enable with build parameter + CONFIG_TLSV11. + - Add domainComponent parser for X.509 names. + * Linux: Add RFKill support by adding an interface state "disabled". + * Reorder some IEs to get closer to IEEE 802.11 standard. Move + WMM into end of Beacon, Probe Resp and (Re)Assoc Resp frames. + Move HT IEs to be later in (Re)Assoc Resp. + * Solaris: Add support for wired 802.1X client. + * Wi-Fi Direct support. See README-P2P for more information. + * Many bugfixes. 2010-04-18 - v0.7.2 * nl80211: fixed number of issues with roaming diff --git a/contrib/wpa/wpa_supplicant/Makefile b/contrib/wpa/wpa_supplicant/Makefile index 1d25623..65fef41 100644 --- a/contrib/wpa/wpa_supplicant/Makefile +++ b/contrib/wpa/wpa_supplicant/Makefile @@ -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,11 +49,17 @@ 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 +ifdef CONFIG_FIPS +CONFIG_NO_RANDOM_POOL= +CONFIG_OPENSSL_CMAC=y +endif + OBJS = config.o OBJS += notify.o OBJS += bss.o @@ -50,8 +72,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 +96,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,12 +111,21 @@ ifndef CONFIG_ELOOP CONFIG_ELOOP=eloop endif OBJS += ../src/utils/$(CONFIG_ELOOP).o +OBJS_c += ../src/utils/$(CONFIG_ELOOP).o + +ifdef CONFIG_ELOOP_POLL +CFLAGS += -DCONFIG_ELOOP_POLL +endif ifdef CONFIG_EAPOL_TEST CFLAGS += -Werror -DEAPOL_TEST endif +ifdef CONFIG_HT_OVERRIDES +CFLAGS += -DCONFIG_HT_OVERRIDES +endif + ifndef CONFIG_BACKEND CONFIG_BACKEND=file endif @@ -141,6 +172,26 @@ NEED_SHA256=y NEED_AES_OMAC1=y endif +ifdef CONFIG_SAE +CFLAGS += -DCONFIG_SAE +endif + +ifdef CONFIG_WNM +CFLAGS += -DCONFIG_WNM +OBJS += wnm_sta.o +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 +217,47 @@ 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_WIFI_DISPLAY +CFLAGS += -DCONFIG_WIFI_DISPLAY +OBJS += wifi_display.o +endif + +ifdef CONFIG_HS20 +OBJS += hs20_supplicant.o +CFLAGS += -DCONFIG_HS20 +CONFIG_INTERWORKING=y +endif + +ifdef CONFIG_INTERWORKING +OBJS += interworking.o +CFLAGS += -DCONFIG_INTERWORKING +NEED_GAS=y +endif + ifdef CONFIG_NO_WPA2 CFLAGS += -DCONFIG_NO_WPA2 endif @@ -223,6 +315,17 @@ TLS_FUNCS=y CONFIG_IEEE8021X_EAPOL=y endif +ifdef CONFIG_EAP_UNAUTH_TLS +# EAP-UNAUTH-TLS +CFLAGS += -DEAP_UNAUTH_TLS +ifndef CONFIG_EAP_UNAUTH_TLS +OBJS += ../src/eap_peer/eap_tls.o +OBJS_h += ../src/eap_server/eap_server_tls.o +TLS_FUNCS=y +endif +CONFIG_IEEE8021X_EAPOL=y +endif + ifdef CONFIG_EAP_PEAP # EAP-PEAP ifeq ($(CONFIG_EAP_PEAP), dyn) @@ -454,7 +557,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 @@ -477,25 +592,10 @@ NEED_80211_COMMON=y NEED_AES_CBC=y NEED_MODEXP=y -ifdef CONFIG_WPS_UFD -CFLAGS += -DCONFIG_WPS_UFD -OBJS += ../src/wps/wps_ufd.o -NEED_WPS_OOB=y -endif - ifdef CONFIG_WPS_NFC CFLAGS += -DCONFIG_WPS_NFC OBJS += ../src/wps/ndef.o -OBJS += ../src/wps/wps_nfc.o NEED_WPS_OOB=y -ifdef CONFIG_WPS_NFC_PN531 -PN531_PATH ?= /usr/local/src/nfc -CFLAGS += -DCONFIG_WPS_NFC_PN531 -CFLAGS += -I${PN531_PATH}/inc -OBJS += ../src/wps/wps_nfc_pn531.o -LIBS += ${PN531_PATH}/lib/wpsnfc.dll -LIBS += ${PN531_PATH}/lib/libnfc_mapping_pn53x.dll -endif endif ifdef NEED_WPS_OOB @@ -522,6 +622,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 +717,17 @@ 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 +OBJS += ../src/ap/eap_user_db.o +ifdef CONFIG_IEEE80211N +OBJS += ../src/ap/ieee802_11_ht.o +endif +ifdef CONFIG_WNM +OBJS += ../src/ap/wnm_ap.o +endif ifdef CONFIG_CTRL_IFACE OBJS += ../src/ap/ctrl_iface_ap.o endif @@ -620,14 +742,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 @@ -635,6 +753,12 @@ CFLAGS += -DEAP_SERVER_WSC OBJS += ../src/ap/wps_hostapd.o OBJS += ../src/eap_server/eap_server_wsc.o endif +ifdef CONFIG_INTERWORKING +OBJS += ../src/ap/gas_serv.o +endif +ifdef CONFIG_HS20 +OBJS += ../src/ap/hs20.o +endif endif ifdef NEED_RSN_AUTHENTICATOR @@ -707,6 +831,7 @@ endif ifdef NEED_MILENAGE OBJS += ../src/crypto/milenage.o +NEED_AES_ENCBLOCK=y endif ifdef CONFIG_PKCS12 @@ -732,13 +857,26 @@ NEED_DES=y # Shared TLS functions (needed for EAP_TLS, EAP_PEAP, EAP_TTLS, and EAP_FAST) OBJS += ../src/eap_peer/eap_tls_common.o OBJS_h += ../src/eap_server/eap_server_tls_common.o +ifndef CONFIG_FIPS NEED_TLS_PRF=y +NEED_SHA1=y +NEED_MD5=y +endif endif ifndef CONFIG_TLS CONFIG_TLS=openssl endif +ifdef CONFIG_TLSV11 +CFLAGS += -DCONFIG_TLSV11 +endif + +ifdef CONFIG_TLSV12 +CFLAGS += -DCONFIG_TLSV12 +NEED_SHA256=y +endif + ifeq ($(CONFIG_TLS), openssl) ifdef TLS_FUNCS CFLAGS += -DEAP_TLS_OPENSSL @@ -752,16 +890,16 @@ OBJS += ../src/crypto/fips_prf_openssl.o endif LIBS += -lcrypto LIBS_p += -lcrypto +ifdef CONFIG_TLS_ADD_DL +LIBS += -ldl +LIBS_p += -ldl +endif endif 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 @@ -827,6 +965,9 @@ OBJS += ../src/tls/pkcs8.o NEED_SHA256=y NEED_BASE64=y NEED_TLS_PRF=y +ifdef CONFIG_TLSV12 +NEED_TLS_PRF_SHA256=y +endif NEED_MODEXP=y NEED_CIPHER=y CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT @@ -932,8 +1073,12 @@ AESOBJS += ../src/crypto/aes-encblock.o endif ifdef NEED_AES_OMAC1 NEED_AES_ENC=y +ifdef CONFIG_OPENSSL_CMAC +CFLAGS += -DCONFIG_OPENSSL_CMAC +else AESOBJS += ../src/crypto/aes-omac1.o endif +endif ifdef NEED_AES_WRAP NEED_AES_ENC=y AESOBJS += ../src/crypto/aes-wrap.o @@ -952,16 +1097,23 @@ OBJS += $(AESOBJS) endif ifdef NEED_SHA1 +ifneq ($(CONFIG_TLS), openssl) SHA1OBJS += ../src/crypto/sha1.o +endif +SHA1OBJS += ../src/crypto/sha1-prf.o ifdef CONFIG_INTERNAL_SHA1 SHA1OBJS += ../src/crypto/sha1-internal.o 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 +ifneq ($(CONFIG_TLS), openssl) SHA1OBJS += ../src/crypto/sha1-pbkdf2.o endif +endif ifdef NEED_T_PRF SHA1OBJS += ../src/crypto/sha1-tprf.o endif @@ -970,14 +1122,13 @@ SHA1OBJS += ../src/crypto/sha1-tlsprf.o endif endif -MD5OBJS = ../src/crypto/md5.o +ifndef CONFIG_FIPS +MD5OBJS += ../src/crypto/md5.o +endif ifdef NEED_MD5 ifdef CONFIG_INTERNAL_MD5 MD5OBJS += ../src/crypto/md5-internal.o endif -ifdef CONFIG_FIPS -MD5OBJS += ../src/crypto/md5-non-fips.o -endif OBJS += $(MD5OBJS) OBJS_p += $(MD5OBJS) endif @@ -1004,10 +1155,16 @@ endif SHA256OBJS = # none by default ifdef NEED_SHA256 CFLAGS += -DCONFIG_SHA256 +ifneq ($(CONFIG_TLS), openssl) SHA256OBJS += ../src/crypto/sha256.o +endif +SHA256OBJS += ../src/crypto/sha256-prf.o ifdef CONFIG_INTERNAL_SHA256 SHA256OBJS += ../src/crypto/sha256-internal.o endif +ifdef NEED_TLS_PRF_SHA256 +SHA256OBJS += ../src/crypto/sha256-tlsprf.o +endif OBJS += $(SHA256OBJS) endif @@ -1023,6 +1180,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 @@ -1041,6 +1204,11 @@ endif ifeq ($(CONFIG_CTRL_IFACE), named_pipe) CFLAGS += -DCONFIG_CTRL_IFACE_NAMED_PIPE endif +ifeq ($(CONFIG_CTRL_IFACE), udp-remote) +CONFIG_CTRL_IFACE=udp +CFLAGS += -DCONFIG_CTRL_IFACE_UDP +CFLAGS += -DCONFIG_CTRL_IFACE_UDP_REMOTE +endif OBJS += ctrl_iface.o ctrl_iface_$(CONFIG_CTRL_IFACE).o endif @@ -1053,22 +1221,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 +DBUS_INCLUDE := $(shell $(PKG_CONFIG) --cflags dbus-1) 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) DBUS_CFLAGS += $(DBUS_INCLUDE) endif @@ -1081,11 +1238,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 +1264,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 +1306,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 +1320,13 @@ 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_LINUX_TRACING +CFLAGS += -DCONFIG_DEBUG_LINUX_TRACING endif ifdef CONFIG_DEBUG_FILE @@ -1172,11 +1339,15 @@ endif ifdef CONFIG_FIPS CFLAGS += -DCONFIG_FIPS +ifneq ($(CONFIG_TLS), openssl) +$(error CONFIG_FIPS=y requires CONFIG_TLS=openssl) +endif endif OBJS += $(SHA1OBJS) $(DESOBJS) OBJS_p += $(SHA1OBJS) +OBJS_p += $(SHA256OBJS) ifdef CONFIG_BGSCAN_SIMPLE CFLAGS += -DCONFIG_BGSCAN_SIMPLE @@ -1184,12 +1355,60 @@ 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 CONFIG_AUTOSCAN_EXPONENTIAL +CFLAGS += -DCONFIG_AUTOSCAN_EXPONENTIAL +OBJS += autoscan_exponential.o +NEED_AUTOSCAN=y +endif + +ifdef CONFIG_AUTOSCAN_PERIODIC +CFLAGS += -DCONFIG_AUTOSCAN_PERIODIC +OBJS += autoscan_periodic.o +NEED_AUTOSCAN=y +endif + +ifdef NEED_AUTOSCAN +CFLAGS += -DCONFIG_AUTOSCAN +OBJS += autoscan.o +endif + +ifdef CONFIG_EXT_PASSWORD_TEST +OBJS += ../src/utils/ext_password_test.o +CFLAGS += -DCONFIG_EXT_PASSWORD_TEST +NEED_EXT_PASSWORD=y +endif + +ifdef NEED_EXT_PASSWORD +OBJS += ../src/utils/ext_password.o +CFLAGS += -DCONFIG_EXT_PASSWORD +endif + +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 @@ -1203,6 +1422,10 @@ ifndef CONFIG_AP OBJS_t += ../src/utils/ip_addr.o endif OBJS_t2 := $(OBJS) $(OBJS_l2) preauth_test.o + +OBJS_nfc := $(OBJS) $(OBJS_l2) nfc_pw_token.o +OBJS_nfc += $(OBJS_d) ../src/drivers/drivers.o + OBJS += $(CONFIG_MAIN).o ifdef CONFIG_PRIVSEP @@ -1214,6 +1437,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 +1483,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 +1501,46 @@ 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 " $@ + +$(OBJS_c) $(OBJS_t) $(OBJS_t2) $(OBJS) $(BCHECK) $(EXTRA_progs): .config -wpa_supplicant: .config $(BCHECK) $(OBJS) $(EXTRA_progs) - $(LDO) $(LDFLAGS) -o wpa_supplicant $(OBJS) $(LIBS) $(EXTRALIBS) +wpa_supplicant: $(BCHECK) $(OBJS) $(EXTRA_progs) + $(Q)$(LDO) $(LDFLAGS) -o wpa_supplicant $(OBJS) $(LIBS) $(EXTRALIBS) + @$(E) " LD " $@ -eapol_test: .config $(OBJS_t) - $(LDO) $(LDFLAGS) -o eapol_test $(OBJS_t) $(LIBS) +eapol_test: $(OBJS_t) + $(Q)$(LDO) $(LDFLAGS) -o eapol_test $(OBJS_t) $(LIBS) + @$(E) " LD " $@ -preauth_test: .config $(OBJS_t2) - $(LDO) $(LDFLAGS) -o preauth_test $(OBJS_t2) $(LIBS) +preauth_test: $(OBJS_t2) + $(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 " $@ + +nfc_pw_token: $(OBJS_nfc) + $(Q)$(LDO) $(LDFLAGS) -o nfc_pw_token $(OBJS_nfc) $(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 +1566,16 @@ 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' $< >$@ + +%@.service: %.service.arg.in + sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@ + wpa_supplicant.exe: wpa_supplicant mv -f $< $@ wpa_cli.exe: wpa_cli @@ -1345,11 +1592,8 @@ WINALL=wpa_supplicant.exe wpa_cli.exe wpa_passphrase.exe win_if_list.exe windows-bin: $(WINALL) $(STRIP) $(WINALL) -wpa_gui/Makefile: - qmake -o wpa_gui/Makefile wpa_gui/wpa_gui.pro - -wpa_gui: wpa_gui/Makefile - $(MAKE) -C wpa_gui +wpa_gui: + @echo "wpa_gui has been removed - see wpa_gui-qt4 for replacement" wpa_gui-qt4/Makefile: qmake -o wpa_gui-qt4/Makefile wpa_gui-qt4/wpa_gui.pro @@ -1371,10 +1615,16 @@ test-eap_sim_common: $(TEST_EAP_SIM_COMMON_OBJS) tests: test-eap_sim_common +FIPSDIR=/usr/local/ssl/fips-2.0 +FIPSLD=$(FIPSDIR)/bin/fipsld +fips: + $(MAKE) CC=$(FIPSLD) FIPSLD_CC="$(CC)" + clean: $(MAKE) -C ../src clean $(MAKE) -C dbus clean rm -f core *~ *.o *.d eap_*.so $(ALL) $(WINALL) eapol_test preauth_test rm -f wpa_priv + rm -f nfc_pw_token -include $(OBJS:%.o=%.d) diff --git a/contrib/wpa/wpa_supplicant/README b/contrib/wpa/wpa_supplicant/README index 45c8bae..a06e5c1 100644 --- a/contrib/wpa/wpa_supplicant/README +++ b/contrib/wpa/wpa_supplicant/README @@ -1,37 +1,22 @@ 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 -license. Either license may be used at your option. +This program is licensed under the BSD license (the one with +advertisement clause removed). + +If you are submitting changes to the project, please see CONTRIBUTIONS +file for more instructions. 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: +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 @@ -138,55 +123,6 @@ Current hardware/software requirements: default option to start with before falling back to driver specific interface. - Host AP driver for Prism2/2.5/3 (development snapshot/v0.2.x) - (http://hostap.epitest.fi/) - Driver need to be set in Managed mode ('iwconfig wlan0 mode managed'). - Please note that station firmware version needs to be 1.7.0 or newer - to work in WPA mode. - - 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. - - Broadcom wl.o driver (old version only) - This is a generic Linux driver for Broadcom IEEE 802.11a/g cards. - However, it is proprietary driver that is not publicly available - except for couple of exceptions, mainly Broadcom-based APs/wireless - routers that use Linux. The driver binary can be downloaded, e.g., - from Linksys support site (http://www.linksys.com/support/gpl.asp) - for Linksys WRT54G. The GPL tarball includes cross-compiler and - the needed header file, wlioctl.h, for compiling wpa_supplicant. - This driver support in wpa_supplicant is expected to work also with - other devices based on Broadcom driver (assuming the driver includes - client mode support). Please note that the newer Broadcom driver - ("hybrid Linux driver") supports Linux wireless extensions and does - 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. @@ -363,7 +299,7 @@ and a list of available options and additional notes. The build time configuration can be used to select only the needed features and limit the binary size and requirements for external libraries. The main configuration parts are the selection of which -driver interfaces (e.g., hostap, madwifi, ..) and which authentication +driver interfaces (e.g., nl80211, wext, ..) and which authentication methods (e.g., EAP-TLS, EAP-PEAP, ..) are included. Following build time configuration options are used to control IEEE @@ -396,32 +332,18 @@ 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_NL80211=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 -Following example includes all features and driver interfaces that are -included in the wpa_supplicant package: +Following example includes some more 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_NL80211=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 @@ -503,7 +425,7 @@ options: -K = include keys (passwords, etc.) in debug output -t = include timestamp in debug messages -h = show this help text - -L = show license (GPL and BSD) + -L = show license (BSD) -p = driver parameters -P = PID file -q = decrease debugging verbosity (-qq even less) @@ -514,16 +436,7 @@ options: -N = start describing new interface 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.) @@ -556,15 +469,15 @@ separated with -N argument. As an example, following command would start wpa_supplicant for two interfaces: wpa_supplicant \ - -c wpa1.conf -i wlan0 -D hostap -N \ - -c wpa2.conf -i ath0 -D madwifi + -c wpa1.conf -i wlan0 -D nl80211 -N \ + -c wpa2.conf -i wlan1 -D wext If the interface is added in a Linux bridge (e.g., br0), the bridge interface needs to be configured to wpa_supplicant in addition to the main interface: -wpa_supplicant -cw.conf -Dmadwifi -iath0 -bbr0 +wpa_supplicant -cw.conf -Dwext -iwlan0 -bbr0 Configuration file @@ -894,13 +807,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/contrib/wpa/wpa_supplicant/README-HS20 b/contrib/wpa/wpa_supplicant/README-HS20 new file mode 100644 index 0000000..5669c55 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/README-HS20 @@ -0,0 +1,475 @@ +wpa_supplicant and Hotspot 2.0 +============================== + +This document describe how the IEEE 802.11u Interworking and Wi-Fi +Hotspot 2.0 (Release 1) implementation in wpa_supplicant can be +configured and how an external component on the client e.g., management +GUI or Wi-Fi framework) is used to manage this functionality. + + +Introduction to Wi-Fi Hotspot 2.0 +--------------------------------- + +Hotspot 2.0 is the name of the Wi-Fi Alliance specification that is used +in the Wi-Fi CERTIFIED Passpoint<TM> program. More information about +this is available in this white paper: + +http://www.wi-fi.org/knowledge-center/white-papers/wi-fi-certified-passpoint%E2%84%A2-new-program-wi-fi-alliance%C2%AE-enable-seamless + +The Hotspot 2.0 specification is also available from WFA: +https://www.wi-fi.org/knowledge-center/published-specifications + +The core Interworking functionality (network selection, GAS/ANQP) were +standardized in IEEE Std 802.11u-2011 which is now part of the IEEE Std +802.11-2012. + + +wpa_supplicant network selection +-------------------------------- + +Interworking support added option for configuring credentials that can +work with multiple networks as an alternative to configuration of +network blocks (e.g., per-SSID parameters). When requested to perform +network selection, wpa_supplicant picks the highest priority enabled +network block or credential. If a credential is picked (based on ANQP +information from APs), a temporary network block is created +automatically for the matching network. This temporary network block is +used similarly to the network blocks that can be configured by the user, +but it is not stored into the configuration file and is meant to be used +only for temporary period of time since a new one can be created +whenever needed based on ANQP information and the credential. + +By default, wpa_supplicant is not using automatic network selection +unless requested explicitly with the interworking_select command. This +can be changed with the auto_interworking=1 parameter to perform network +selection automatically whenever trying to find a network for connection +and none of the enabled network blocks match with the scan results. This +case works similarly to "interworking_select auto", i.e., wpa_supplicant +will internally determine which network or credential is going to be +used based on configured priorities, scan results, and ANQP information. + + +wpa_supplicant configuration +---------------------------- + +Interworking and Hotspot 2.0 functionality are optional components that +need to be enabled in the wpa_supplicant build configuration +(.config). This is done by adding following parameters into that file: + +CONFIG_INTERWORKING=y +CONFIG_HS20=y + +It should be noted that this functionality requires a driver that +supports GAS/ANQP operations. This uses the same design as P2P, i.e., +Action frame processing and building in user space within +wpa_supplicant. The Linux nl80211 driver interface provides the needed +functionality for this. + + +There are number of run-time configuration parameters (e.g., in +wpa_supplicant.conf when using the configuration file) that can be used +to control Hotspot 2.0 operations. + +# Enable Interworking +interworking=1 + +# Enable Hotspot 2.0 +hs20=1 + +# Parameters for controlling scanning + +# 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 + +# Access Network Type +# When Interworking is enabled, scans can be limited to APs that advertise the +# specified Access Network Type (0..15; with 15 indicating wildcard match). +# This value controls the Access Network Type value in Probe Request frames. +#access_network_type=15 + +# Automatic network selection behavior +# 0 = do not automatically go through Interworking network selection +# (i.e., require explicit interworking_select command for this; default) +# 1 = perform Interworking network selection if one or more +# credentials have been configured and scan did not find a +# matching network block +#auto_interworking=0 + + +Credentials can be pre-configured for automatic network selection: + +# credential block +# +# Each credential used for automatic network selection is configured as a set +# of parameters that are compared to the information advertised by the APs when +# interworking_select and interworking_connect commands are used. +# +# credential fields: +# +# priority: Priority group +# By default, all networks and credentials get the same priority group +# (0). This field can be used to give higher priority for credentials +# (and similarly in struct wpa_ssid for network blocks) to change the +# Interworking automatic networking selection behavior. The matching +# network (based on either an enabled network block or a credential) +# with the highest priority value will be selected. +# +# pcsc: Use PC/SC and SIM/USIM card +# +# realm: Home Realm for Interworking +# +# username: Username for Interworking network selection +# +# password: Password for Interworking network selection +# +# ca_cert: CA certificate for Interworking network selection +# +# client_cert: File path to client certificate file (PEM/DER) +# This field is used with Interworking networking selection for a case +# where client certificate/private key is used for authentication +# (EAP-TLS). Full path to the file should be used since working +# directory may change when wpa_supplicant is run in the background. +# +# Alternatively, a named configuration blob can be used by setting +# this to blob://blob_name. +# +# private_key: File path to client private key file (PEM/DER/PFX) +# When PKCS#12/PFX file (.p12/.pfx) is used, client_cert should be +# commented out. Both the private key and certificate will be read +# from the PKCS#12 file in this case. Full path to the file should be +# used since working directory may change when wpa_supplicant is run +# in the background. +# +# Windows certificate store can be used by leaving client_cert out and +# configuring private_key in one of the following formats: +# +# cert://substring_to_match +# +# hash://certificate_thumbprint_in_hex +# +# For example: private_key="hash://63093aa9c47f56ae88334c7b65a4" +# +# Note that when running wpa_supplicant as an application, the user +# certificate store (My user account) is used, whereas computer store +# (Computer account) is used when running wpasvc as a service. +# +# Alternatively, a named configuration blob can be used by setting +# this to blob://blob_name. +# +# private_key_passwd: Password for private key file +# +# imsi: IMSI in <MCC> | <MNC> | '-' | <MSIN> format +# +# milenage: Milenage parameters for SIM/USIM simulator in <Ki>:<OPc>:<SQN> +# format +# +# domain: Home service provider FQDN +# This is used to compare against the Domain Name List to figure out +# whether the AP is operated by the Home SP. +# +# roaming_consortium: Roaming Consortium OI +# If roaming_consortium_len is non-zero, this field contains the +# Roaming Consortium OI that can be used to determine which access +# points support authentication with this credential. This is an +# alternative to the use of the realm parameter. When using Roaming +# Consortium to match the network, the EAP parameters need to be +# pre-configured with the credential since the NAI Realm information +# may not be available or fetched. +# +# eap: Pre-configured EAP method +# This optional field can be used to specify which EAP method will be +# used with this credential. If not set, the EAP method is selected +# automatically based on ANQP information (e.g., NAI Realm). +# +# phase1: Pre-configure Phase 1 (outer authentication) parameters +# This optional field is used with like the 'eap' parameter. +# +# phase2: Pre-configure Phase 2 (inner authentication) parameters +# This optional field is used with like the 'eap' parameter. +# +# excluded_ssid: Excluded SSID +# This optional field can be used to excluded specific SSID(s) from +# matching with the network. Multiple entries can be used to specify more +# than one SSID. +# +# for example: +# +#cred={ +# realm="example.com" +# username="user@example.com" +# password="password" +# ca_cert="/etc/wpa_supplicant/ca.pem" +# domain="example.com" +#} +# +#cred={ +# imsi="310026-000000000" +# milenage="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82" +#} +# +#cred={ +# realm="example.com" +# username="user" +# password="password" +# ca_cert="/etc/wpa_supplicant/ca.pem" +# domain="example.com" +# roaming_consortium=223344 +# eap=TTLS +# phase2="auth=MSCHAPV2" +#} + + +Control interface +----------------- + +wpa_supplicant provides a control interface that can be used from +external programs to manage various operations. The included command +line tool, wpa_cli, can be used for manual testing with this interface. + +Following wpa_cli interactive mode commands show some examples of manual +operations related to Hotspot 2.0: + +Remove configured networks and credentials: + +> remove_network all +OK +> remove_cred all +OK + + +Add a username/password credential: + +> add_cred +0 +> set_cred 0 realm "mail.example.com" +OK +> set_cred 0 username "username" +OK +> set_cred 0 password "password" +OK +> set_cred 0 priority 1 +OK + +Add a SIM credential using a simulated SIM/USIM card for testing: + +> add_cred +1 +> set_cred 1 imsi "23456-0000000000" +OK +> set_cred 1 milenage "90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123" +OK +> set_cred 1 priority 1 +OK + +Note: the return value of add_cred is used as the first argument to +the following set_cred commands. + + +Add a WPA2-Enterprise network: + +> add_network +0 +> set_network 0 key_mgmt WPA-EAP +OK +> set_network 0 ssid "enterprise" +OK +> set_network 0 eap TTLS +OK +> set_network 0 anonymous_identity "anonymous" +OK +> set_network 0 identity "user" +OK +> set_network 0 password "password" +OK +> set_network 0 priority 0 +OK +> enable_network 0 no-connect +OK + + +Add an open network: + +> add_network +3 +> set_network 3 key_mgmt NONE +OK +> set_network 3 ssid "coffee-shop" +OK +> select_network 3 +OK + +Note: the return value of add_network is used as the first argument to +the following set_network commands. + +The preferred credentials/networks can be indicated with the priority +parameter (1 is higher priority than 0). + + +Interworking network selection can be started with interworking_select +command. This instructs wpa_supplicant to run a network scan and iterate +through the discovered APs to request ANQP information from the APs that +advertise support for Interworking/Hotspot 2.0: + +> interworking_select +OK +<3>Starting ANQP fetch for 02:00:00:00:01:00 +<3>RX-ANQP 02:00:00:00:01:00 ANQP Capability list +<3>RX-ANQP 02:00:00:00:01:00 Roaming Consortium list +<3>RX-HS20-ANQP 02:00:00:00:01:00 HS Capability List +<3>ANQP fetch completed +<3>INTERWORKING-AP 02:00:00:00:01:00 type=unknown + + +INTERWORKING-AP event messages indicate the APs that support network +selection and for which there is a matching +credential. interworking_connect command can be used to select a network +to connect with: + + +> interworking_connect 02:00:00:00:01:00 +OK +<3>CTRL-EVENT-SCAN-RESULTS +<3>SME: Trying to authenticate with 02:00:00:00:01:00 (SSID='Example Network' freq=2412 MHz) +<3>Trying to associate with 02:00:00:00:01:00 (SSID='Example Network' freq=2412 MHz) +<3>Associated with 02:00:00:00:01:00 +<3>CTRL-EVENT-EAP-STARTED EAP authentication started +<3>CTRL-EVENT-EAP-PROPOSED-METHOD vendor=0 method=21 +<3>CTRL-EVENT-EAP-METHOD EAP vendor 0 method 21 (TTLS) selected +<3>CTRL-EVENT-EAP-SUCCESS EAP authentication completed successfully +<3>WPA: Key negotiation completed with 02:00:00:00:01:00 [PTK=CCMP GTK=CCMP] +<3>CTRL-EVENT-CONNECTED - Connection to 02:00:00:00:01:00 completed (auth) [id=0 id_str=] + + +wpa_supplicant creates a temporary network block for the selected +network based on the configured credential and ANQP information from the +AP: + +> list_networks +network id / ssid / bssid / flags +0 Example Network any [CURRENT] +> get_network 0 key_mgmt +WPA-EAP +> get_network 0 eap +TTLS + + +Alternatively to using an external program to select the network, +"interworking_select auto" command can be used to request wpa_supplicant +to select which network to use based on configured priorities: + + +> remove_network all +OK +<3>CTRL-EVENT-DISCONNECTED bssid=02:00:00:00:01:00 reason=1 locally_generated=1 +> interworking_select auto +OK +<3>Starting ANQP fetch for 02:00:00:00:01:00 +<3>RX-ANQP 02:00:00:00:01:00 ANQP Capability list +<3>RX-ANQP 02:00:00:00:01:00 Roaming Consortium list +<3>RX-HS20-ANQP 02:00:00:00:01:00 HS Capability List +<3>ANQP fetch completed +<3>INTERWORKING-AP 02:00:00:00:01:00 type=unknown +<3>CTRL-EVENT-SCAN-RESULTS +<3>SME: Trying to authenticate with 02:00:00:00:01:00 (SSID='Example Network' freq=2412 MHz) +<3>Trying to associate with 02:00:00:00:01:00 (SSID='Example Network' freq=2412 MHz) +<3>Associated with 02:00:00:00:01:00 +<3>CTRL-EVENT-EAP-STARTED EAP authentication started +<3>CTRL-EVENT-EAP-PROPOSED-METHOD vendor=0 method=21 +<3>CTRL-EVENT-EAP-METHOD EAP vendor 0 method 21 (TTLS) selected +<3>CTRL-EVENT-EAP-SUCCESS EAP authentication completed successfully +<3>WPA: Key negotiation completed with 02:00:00:00:01:00 [PTK=CCMP GTK=CCMP] +<3>CTRL-EVENT-CONNECTED - Connection to 02:00:00:00:01:00 completed (reauth) [id=0 id_str=] + + +The connection status can be shown with the status command: + +> status +bssid=02:00:00:00:01:00 +ssid=Example Network +id=0 +mode=station +pairwise_cipher=CCMP <--- link layer security indication +group_cipher=CCMP +key_mgmt=WPA2/IEEE 802.1X/EAP +wpa_state=COMPLETED +p2p_device_address=02:00:00:00:00:00 +address=02:00:00:00:00:00 +hs20=1 <--- HS 2.0 indication +Supplicant PAE state=AUTHENTICATED +suppPortStatus=Authorized +EAP state=SUCCESS +selectedMethod=21 (EAP-TTLS) +EAP TLS cipher=AES-128-SHA +EAP-TTLSv0 Phase2 method=PAP + + +> status +bssid=02:00:00:00:02:00 +ssid=coffee-shop +id=3 +mode=station +pairwise_cipher=NONE +group_cipher=NONE +key_mgmt=NONE +wpa_state=COMPLETED +p2p_device_address=02:00:00:00:00:00 +address=02:00:00:00:00:00 + + +Note: The Hotspot 2.0 indication is shown as "hs20=1" in the status +command output. Link layer security is indicated with the +pairwise_cipher (CCMP = secure, NONE = no encryption used). + + +Also the scan results include the Hotspot 2.0 indication: + +> scan_results +bssid / frequency / signal level / flags / ssid +02:00:00:00:01:00 2412 -30 [WPA2-EAP-CCMP][ESS][HS20] Example Network + + +ANQP information for the BSS can be fetched using the BSS command: + +> bss 02:00:00:00:01:00 +id=1 +bssid=02:00:00:00:01:00 +freq=2412 +beacon_int=100 +capabilities=0x0411 +qual=0 +noise=-92 +level=-30 +tsf=1345573286517276 +age=105 +ie=000f4578616d706c65204e6574776f726b010882848b960c1218240301012a010432043048606c30140100000fac040100000fac040100000fac0100007f04000000806b091e07010203040506076c027f006f1001531122331020304050010203040506dd05506f9a1000 +flags=[WPA2-EAP-CCMP][ESS][HS20] +ssid=Example Network +anqp_roaming_consortium=031122330510203040500601020304050603fedcba + + +ANQP queries can also be requested with the anqp_get and hs20_anqp_get +commands: + +> anqp_get 02:00:00:00:01:00 261 +OK +<3>RX-ANQP 02:00:00:00:01:00 Roaming Consortium list +> hs20_anqp_get 02:00:00:00:01:00 2 +OK +<3>RX-HS20-ANQP 02:00:00:00:01:00 HS Capability List + +In addition, fetch_anqp command can be used to request similar set of +ANQP queries to be done as is run as part of interworking_select: + +> scan +OK +<3>CTRL-EVENT-SCAN-RESULTS +> fetch_anqp +OK +<3>Starting ANQP fetch for 02:00:00:00:01:00 +<3>RX-ANQP 02:00:00:00:01:00 ANQP Capability list +<3>RX-ANQP 02:00:00:00:01:00 Roaming Consortium list +<3>RX-HS20-ANQP 02:00:00:00:01:00 HS Capability List +<3>ANQP fetch completed diff --git a/contrib/wpa/wpa_supplicant/README-P2P b/contrib/wpa/wpa_supplicant/README-P2P new file mode 100644 index 0000000..5e98c95 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/README-P2P @@ -0,0 +1,560 @@ +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>] \ + [dev_id=<addr>] [delay=<search delay in ms>] + +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. + +The optional dev_id option can be used to specify a single P2P peer to +search for. The optional delay parameter can be used to request an extra +delay to be used between search iterations (e.g., to free up radio +resources for concurrent operations). + +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|auto] + +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. The "auto" +parameter can be used to request wpa_supplicant to automatically figure +out whether the peer device is operating as a GO and if so, use +join-a-group style PD instead of GO Negotiation style PD. + +p2p_connect <peer device address> <pbc|pin|PIN#> [display|keypad] + [persistent|persistent=<network id>] [join|auth] + [go_intent=<0..15>] [freq=<in MHz>] [ht40] [provdisc] + +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. The +"persistent=<network id>" alternative can be used to pre-populate +SSID/passphrase configuration based on a previously used persistent +group where this device was the GO. The previously used parameters will +then be used if the local end becomes the GO in GO Negotiation (which +can be forced with go_intent=15). + +"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). + +"provdisc" can be used to request a Provision Discovery exchange to be +used prior to starting GO Negotiation as a workaround with some deployed +P2P implementations that require this to allow the user to accept the +connection. + +p2p_group_add [persistent|persistent=<network id>] [freq=<freq in MHz>] [ht40] + +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 and joining-a-group related +operation. This operations unauthorizes the specific peer device (if any +had been authorized to start group formation), stops P2P find (if in +progress), stops pending operations for join-a-group, and removes the +P2P group interface (if one was used) that is in the WPS provisioning +step. If the WPS provisioning step has been completed, the group is not +terminated. + +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 + +# Wi-Fi Display examples +# format: wifi-display <list of roles> <list of subelements> +p2p_serv_disc_req 00:00:00:00:00:00 wifi-display [source] 2,3,4,5 +p2p_serv_disc_req 02:01:02:03:04:05 wifi-display [pri-sink] 3 +p2p_serv_disc_req 00:00:00:00:00:00 wifi-display [sec-source] 2 +p2p_serv_disc_req 00:00:00:00:00:00 wifi-display [source+sink] 2,3,4,5 +p2p_serv_disc_req 00:00:00:00:00:00 wifi-display [source][pri-sink] 2,3,4,5 + +p2p_serv_disc_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] [freq=<freq in MHz>] [ht40] + +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 persistent 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). When reinvoking a persistent group, the GO device can specify +the frequency for the group with the freq parameter. + +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 diff --git a/contrib/wpa/wpa_supplicant/README-WPS b/contrib/wpa/wpa_supplicant/README-WPS index 8f0d0d6..1ea9843 100644 --- a/contrib/wpa/wpa_supplicant/README-WPS +++ b/contrib/wpa/wpa_supplicant/README-WPS @@ -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,21 @@ 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 + +Following parameter can be used to enable support for NFC config method: + +CONFIG_WPS_NFC=y WPS needs the Universally Unique IDentifier (UUID; see RFC 4122) for @@ -93,6 +101,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: @@ -115,6 +130,17 @@ wpa_cli wps_pin any 12345670 This starts the WPS negotiation in the same way as above with the generated PIN. +When the wps_pin command is issued for an AP (including P2P GO) mode +interface, an optional timeout parameter can be used to specify +expiration timeout for the PIN in seconds. For example: + +wpa_cli wps_pin any 12345670 300 + + +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 @@ -198,3 +224,154 @@ 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 + + +WPS with NFC +------------ + +WPS can be used with NFC-based configuration method. An NFC tag +containing a password token from the Enrollee can be used to +authenticate the connection instead of the PIN. In addition, an NFC tag +with a configuration token can be used to transfer AP settings without +going through the WPS protocol. + +When the station acts as an Enrollee, a local NFC tag with a password +token can be used by touching the NFC interface of a Registrar. + +"wps_nfc [BSSID]" command starts WPS protocol run with the local end as +the Enrollee using the NFC password token that is either pre-configured +in the configuration file (wps_nfc_dev_pw_id, wps_nfc_dh_pubkey, +wps_nfc_dh_privkey, wps_nfc_dev_pw) or generated dynamically with +"wps_nfc_token <WPS|NDEF>" command. The included nfc_pw_token tool +(build with "make nfc_pw_token") can be used to generate NFC password +tokens during manufacturing (each station needs to have its own random +keys). + +If the station includes NFC interface and reads an NFC tag with a MIME +media type "application/vnd.wfa.wsc", the NDEF message payload (with or +without NDEF encapsulation) can be delivered to wpa_supplicant using the +following wpa_cli command: + +wps_nfc_tag_read <hexdump of payload> + +If the NFC tag contains a configuration token, the network is added to +wpa_supplicant configuration. If the NFC tag contains a password token, +the token is added to the WPS Registrar component. This information can +then be used with wps_reg command (when the NFC password token was from +an AP) using a special value "nfc-pw" in place of the PIN parameter. If +the ER functionality has been started (wps_er_start), the NFC password +token is used to enable enrollment of a new station (that was the source +of the NFC password token). + +"nfc_get_handover_req <NDEF> <WPS>" command can be used to build the +contents of a Handover Request Message for connection handover. The +first argument selects the format of the output data and the second +argument selects which type of connection handover is requested (WPS = +Wi-Fi handover as specified in WSC 2.0). + +"nfc_get_handover_sel <NDEF> <WPS>" command can be used to build the +contents of a Handover Select Message for connection handover when this +does not depend on the contents of the Handover Request Message. The +first argument selects the format of the output data and the second +argument selects which type of connection handover is requested (WPS = +Wi-Fi handover as specified in WSC 2.0). + +"nfc_rx_handover_req <hexdump of payload>" is used to indicate receipt +of NFC connection handover request. The payload may include multiple +carriers the the applicable ones are matched based on the media +type. The reply data is contents for the Handover Select Message +(hexdump). + +"nfc_rx_handover_sel <hexdump of payload>" is used to indicate receipt +of NFC connection handover select. The payload may include multiple +carriers the the applicable ones are matched based on the media +type. diff --git a/contrib/wpa/wpa_supplicant/ap.c b/contrib/wpa/wpa_supplicant/ap.c index 2b93984..c1e4acf 100644 --- a/contrib/wpa/wpa_supplicant/ap.c +++ b/contrib/wpa/wpa_supplicant/ap.c @@ -3,37 +3,42 @@ * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi> * 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. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" #include "utils/common.h" +#include "utils/eloop.h" +#include "utils/uuid.h" #include "common/ieee802_11_defs.h" +#include "common/wpa_ctrl.h" #include "ap/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_common/eap_defs.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, @@ -58,39 +63,154 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, (ssid->frequency >= 5745 && ssid->frequency <= 5825)) { conf->hw_mode = HOSTAPD_MODE_IEEE80211A; conf->channel = (ssid->frequency - 5000) / 5; + } else if (ssid->frequency >= 56160 + 2160 * 1 && + ssid->frequency <= 56160 + 2160 * 4) { + conf->hw_mode = HOSTAPD_MODE_IEEE80211AD; + conf->channel = (ssid->frequency - 56160) / 2160; } else { wpa_printf(MSG_ERROR, "Unsupported AP mode frequency: %d MHz", ssid->frequency); 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 + * and a mask of allowed capabilities within conf->ht_capab. + * Using default config settings for: conf->ht_op_mode_fixed, + * conf->secondary_channel, conf->require_ht + */ + if (wpa_s->hw.modes) { + struct hostapd_hw_modes *mode = NULL; + int i, no_ht = 0; + for (i = 0; i < wpa_s->hw.num_modes; i++) { + if (wpa_s->hw.modes[i].mode == conf->hw_mode) { + mode = &wpa_s->hw.modes[i]; + break; + } + } + +#ifdef CONFIG_HT_OVERRIDES + if (ssid->disable_ht) { + conf->ieee80211n = 0; + conf->ht_capab = 0; + no_ht = 1; + } +#endif /* CONFIG_HT_OVERRIDES */ + + if (!no_ht && mode && mode->ht_capab) { + conf->ieee80211n = 1; +#ifdef CONFIG_P2P + if (conf->hw_mode == HOSTAPD_MODE_IEEE80211A && + (mode->ht_capab & + HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) && + ssid->ht40) + conf->secondary_channel = + wpas_p2p_get_ht40_mode(wpa_s, mode, + conf->channel); + if (conf->secondary_channel) + conf->ht_capab |= + HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET; +#endif /* CONFIG_P2P */ + + /* + * white-list capabilities that won't cause issues + * to connecting stations, while leaving the current + * capabilities intact (currently disabled SMPS). + */ + conf->ht_capab |= mode->ht_capab & + (HT_CAP_INFO_GREEN_FIELD | + HT_CAP_INFO_SHORT_GI20MHZ | + HT_CAP_INFO_SHORT_GI40MHZ | + HT_CAP_INFO_RX_STBC_MASK | + HT_CAP_INFO_MAX_AMSDU_SIZE); + } + } +#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; } os_memcpy(bss->ssid.ssid, ssid->ssid, ssid->ssid_len); - bss->ssid.ssid[ssid->ssid_len] = '\0'; bss->ssid.ssid_len = ssid->ssid_len; bss->ssid.ssid_set = 1; + bss->ignore_broadcast_ssid = ssid->ignore_broadcast_ssid; + + if (ssid->auth_alg) + bss->auth_algs = ssid->auth_alg; + if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt)) bss->wpa = ssid->proto; bss->wpa_key_mgmt = ssid->key_mgmt; bss->wpa_pairwise = ssid->pairwise_cipher; - if (ssid->passphrase) { - bss->ssid.wpa_passphrase = os_strdup(ssid->passphrase); - } else if (ssid->psk_set) { + if (ssid->psk_set) { os_free(bss->ssid.wpa_psk); bss->ssid.wpa_psk = os_zalloc(sizeof(struct hostapd_wpa_psk)); if (bss->ssid.wpa_psk == NULL) return -1; os_memcpy(bss->ssid.wpa_psk->psk, ssid->psk, PMK_LEN); bss->ssid.wpa_psk->group = 1; + } else if (ssid->passphrase) { + bss->ssid.wpa_passphrase = os_strdup(ssid->passphrase); + } else if (ssid->wep_key_len[0] || ssid->wep_key_len[1] || + ssid->wep_key_len[2] || ssid->wep_key_len[3]) { + struct hostapd_wep_keys *wep = &bss->ssid.wep; + int i; + for (i = 0; i < NUM_WEP_KEYS; i++) { + if (ssid->wep_key_len[i] == 0) + continue; + wep->key[i] = os_malloc(ssid->wep_key_len[i]); + if (wep->key[i] == NULL) + return -1; + os_memcpy(wep->key[i], ssid->wep_key[i], + ssid->wep_key_len[i]); + wep->len[i] = ssid->wep_key_len[i]; + } + wep->idx = ssid->wep_tx_keyidx; + wep->keys_set = 1; } + if (ssid->ap_max_inactivity) + bss->ap_max_inactivity = ssid->ap_max_inactivity; + + if (ssid->dtim_period) + bss->dtim_period = ssid->dtim_period; + /* Select group cipher based on the enabled pairwise cipher suites */ pairwise = 0; if (bss->wpa & 1) @@ -102,6 +222,9 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, } if (pairwise & WPA_CIPHER_TKIP) bss->wpa_group = WPA_CIPHER_TKIP; + else if ((pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)) == + WPA_CIPHER_GCMP) + bss->wpa_group = WPA_CIPHER_GCMP; else bss->wpa_group = WPA_CIPHER_CCMP; @@ -110,46 +233,193 @@ 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; + + if (!ssid->ignore_broadcast_ssid) + bss->wps_state = 2; + + bss->ap_setup_locked = 2; if (wpa_s->conf->config_methods) bss->config_methods = os_strdup(wpa_s->conf->config_methods); - 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); + bss->pbc_in_m1 = wpa_s->conf->pbc_in_m1; +no_wps: #endif /* CONFIG_WPS */ + if (wpa_s->max_stations && + wpa_s->max_stations < wpa_s->conf->max_num_sta) + bss->max_num_sta = wpa_s->max_stations; + else + bss->max_num_sta = wpa_s->conf->max_num_sta; + + bss->disassoc_low_ack = wpa_s->conf->disassoc_low_ack; + 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, + int ssi_signal) { +#ifdef CONFIG_P2P + struct wpa_supplicant *wpa_s = ctx; + return wpas_p2p_probe_req_rx(wpa_s, sa, da, bssid, ie, ie_len, + ssi_signal); +#else /* CONFIG_P2P */ return 0; +#endif /* CONFIG_P2P */ } static void ap_wps_reg_success_cb(void *ctx, const u8 *mac_addr, const u8 *uuid_e) { +#ifdef CONFIG_P2P + struct wpa_supplicant *wpa_s = ctx; + wpas_p2p_wps_success(wpa_s, mac_addr, 1); +#endif /* CONFIG_P2P */ +} + + +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 +452,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 @@ -195,6 +468,8 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s, if (ssid->pairwise_cipher & WPA_CIPHER_CCMP) wpa_s->pairwise_cipher = WPA_CIPHER_CCMP; + else if (ssid->pairwise_cipher & WPA_CIPHER_GCMP) + wpa_s->pairwise_cipher = WPA_CIPHER_GCMP; else if (ssid->pairwise_cipher & WPA_CIPHER_TKIP) wpa_s->pairwise_cipher = WPA_CIPHER_TKIP; else if (ssid->pairwise_cipher & WPA_CIPHER_NONE) @@ -207,6 +482,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, ¶ms) < 0) { wpa_msg(wpa_s, MSG_INFO, "Failed to start AP functionality"); return -1; @@ -216,6 +502,8 @@ 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; + hapd_iface->probe_resp_offloads = wpa_s->probe_resp_offloads; wpa_s->ap_iface->conf = conf = hostapd_config_defaults(); if (conf == NULL) { @@ -223,14 +511,31 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s, return -1; } + os_memcpy(wpa_s->ap_iface->conf->wmm_ac_params, + wpa_s->conf->wmm_ac_params, + sizeof(wpa_s->conf->wmm_ac_params)); + + if (params.uapsd > 0) { + conf->bss->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 * + hapd_iface->bss = os_calloc(conf->num_bss, sizeof(struct hostapd_data *)); if (hapd_iface->bss == NULL) { wpa_supplicant_ap_deinit(wpa_s); @@ -247,42 +552,62 @@ 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); +#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; +#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 +625,31 @@ 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_eapol_tx_status(void *ctx, const u8 *dst, + const u8 *data, size_t len, int ack) { #ifdef NEED_AP_MLME struct wpa_supplicant *wpa_s = ctx; - 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)); + hostapd_tx_status(wpa_s->ap_iface->bss[0], dst, data, len, ack); +#endif /* NEED_AP_MLME */ +} + + +void ap_client_poll_ok(void *ctx, const u8 *addr) +{ +#ifdef NEED_AP_MLME + struct wpa_supplicant *wpa_s = ctx; + if (wpa_s->ap_iface) + hostapd_client_poll_ok(wpa_s->ap_iface->bss[0], addr); +#endif /* NEED_AP_MLME */ +} + + +void ap_rx_from_unknown_sta(void *ctx, const u8 *addr, int wds) +{ +#ifdef NEED_AP_MLME + struct wpa_supplicant *wpa_s = ctx; + ieee802_11_rx_from_unknown(wpa_s->ap_iface->bss[0], addr, wds); #endif /* NEED_AP_MLME */ } @@ -346,16 +686,49 @@ 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); +} + + +int wpa_supplicant_ap_wps_cancel(struct wpa_supplicant *wpa_s) +{ + struct wps_registrar *reg; + int reg_sel = 0, wps_sta = 0; + + if (!wpa_s->ap_iface || !wpa_s->ap_iface->bss[0]->wps) + return -1; + + reg = wpa_s->ap_iface->bss[0]->wps->registrar; + reg_sel = wps_registrar_wps_cancel(reg); + wps_sta = ap_for_each_sta(wpa_s->ap_iface->bss[0], + ap_sta_wps_cancel, NULL); + + if (!reg_sel && !wps_sta) { + wpa_printf(MSG_DEBUG, "No WPS operation in progress at this " + "time"); + return -1; + } + + /* + * There are 2 cases to return wps cancel as success: + * 1. When wps cancel was initiated but no connection has been + * established with client yet. + * 2. Client is in the middle of exchanging WPS messages. + */ + + return 0; } int wpa_supplicant_ap_wps_pin(struct wpa_supplicant *wpa_s, const u8 *bssid, - const char *pin, char *buf, size_t buflen) + const char *pin, char *buf, size_t buflen, + int timeout) { int ret, ret_len = 0; @@ -364,16 +737,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, + timeout); if (ret) return -1; return ret_len; } + +static void wpas_wps_ap_pin_timeout(void *eloop_data, void *user_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_data; + wpa_printf(MSG_DEBUG, "WPS: AP PIN timed out"); + wpas_wps_ap_pin_disable(wpa_s); +} + + +static void wpas_wps_ap_pin_enable(struct wpa_supplicant *wpa_s, int timeout) +{ + struct hostapd_data *hapd; + + if (wpa_s->ap_iface == NULL) + return; + hapd = wpa_s->ap_iface->bss[0]; + wpa_printf(MSG_DEBUG, "WPS: Enabling AP PIN (timeout=%d)", timeout); + hapd->ap_pin_failures = 0; + eloop_cancel_timeout(wpas_wps_ap_pin_timeout, wpa_s, NULL); + if (timeout > 0) + eloop_register_timeout(timeout, 0, + wpas_wps_ap_pin_timeout, wpa_s, NULL); +} + + +void wpas_wps_ap_pin_disable(struct wpa_supplicant *wpa_s) +{ + struct hostapd_data *hapd; + + if (wpa_s->ap_iface == NULL) + return; + wpa_printf(MSG_DEBUG, "WPS: Disabling AP PIN"); + hapd = wpa_s->ap_iface->bss[0]; + os_free(hapd->conf->ap_pin); + hapd->conf->ap_pin = NULL; + eloop_cancel_timeout(wpas_wps_ap_pin_timeout, wpa_s, NULL); +} + + +const char * wpas_wps_ap_pin_random(struct wpa_supplicant *wpa_s, int timeout) +{ + struct hostapd_data *hapd; + unsigned int pin; + char pin_txt[9]; + + if (wpa_s->ap_iface == NULL) + return NULL; + hapd = wpa_s->ap_iface->bss[0]; + pin = wps_generate_pin(); + os_snprintf(pin_txt, sizeof(pin_txt), "%08u", pin); + os_free(hapd->conf->ap_pin); + hapd->conf->ap_pin = os_strdup(pin_txt); + if (hapd->conf->ap_pin == NULL) + return NULL; + wpas_wps_ap_pin_enable(wpa_s, timeout); + + return hapd->conf->ap_pin; +} + + +const char * wpas_wps_ap_pin_get(struct wpa_supplicant *wpa_s) +{ + struct hostapd_data *hapd; + if (wpa_s->ap_iface == NULL) + return NULL; + hapd = wpa_s->ap_iface->bss[0]; + return hapd->conf->ap_pin; +} + + +int wpas_wps_ap_pin_set(struct wpa_supplicant *wpa_s, const char *pin, + int timeout) +{ + struct hostapd_data *hapd; + char pin_txt[9]; + int ret; + + if (wpa_s->ap_iface == NULL) + return -1; + hapd = wpa_s->ap_iface->bss[0]; + ret = os_snprintf(pin_txt, sizeof(pin_txt), "%s", pin); + if (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 */ @@ -409,6 +901,26 @@ int ap_ctrl_iface_sta_next(struct wpa_supplicant *wpa_s, const char *txtaddr, } +int ap_ctrl_iface_sta_disassociate(struct wpa_supplicant *wpa_s, + const char *txtaddr) +{ + if (wpa_s->ap_iface == NULL) + return -1; + return hostapd_ctrl_iface_disassociate(wpa_s->ap_iface->bss[0], + txtaddr); +} + + +int ap_ctrl_iface_sta_deauthenticate(struct wpa_supplicant *wpa_s, + const char *txtaddr) +{ + if (wpa_s->ap_iface == NULL) + return -1; + return hostapd_ctrl_iface_deauthenticate(wpa_s->ap_iface->bss[0], + txtaddr); +} + + int ap_ctrl_iface_wpa_get_status(struct wpa_supplicant *wpa_s, char *buf, size_t buflen, int verbose) { @@ -440,6 +952,46 @@ 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; +} + + +void wpas_ap_ch_switch(struct wpa_supplicant *wpa_s, int freq, int ht, + int offset) +{ + if (!wpa_s->ap_iface) + return; + + wpa_s->assoc_freq = freq; + hostapd_event_ch_switch(wpa_s->ap_iface->bss[0], freq, ht, offset); +} + + int wpa_supplicant_ap_mac_addr_filter(struct wpa_supplicant *wpa_s, const u8 *addr) { diff --git a/contrib/wpa/wpa_supplicant/ap.h b/contrib/wpa/wpa_supplicant/ap.h index 381a432..536064f 100644 --- a/contrib/wpa/wpa_supplicant/ap.h +++ b/contrib/wpa/wpa_supplicant/ap.h @@ -3,14 +3,8 @@ * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi> * 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. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef AP_H @@ -21,23 +15,42 @@ 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); + const char *pin, char *buf, size_t buflen, + int timeout); +int wpa_supplicant_ap_wps_cancel(struct wpa_supplicant *wpa_s); +void wpas_wps_ap_pin_disable(struct wpa_supplicant *wpa_s); +const char * wpas_wps_ap_pin_random(struct wpa_supplicant *wpa_s, int timeout); +const char * wpas_wps_ap_pin_get(struct wpa_supplicant *wpa_s); +int wpas_wps_ap_pin_set(struct wpa_supplicant *wpa_s, const char *pin, + int timeout); int ap_ctrl_iface_sta_first(struct wpa_supplicant *wpa_s, char *buf, size_t buflen); int ap_ctrl_iface_sta(struct wpa_supplicant *wpa_s, const char *txtaddr, char *buf, size_t buflen); int ap_ctrl_iface_sta_next(struct wpa_supplicant *wpa_s, const char *txtaddr, char *buf, size_t buflen); +int ap_ctrl_iface_sta_deauthenticate(struct wpa_supplicant *wpa_s, + const char *txtaddr); +int ap_ctrl_iface_sta_disassociate(struct wpa_supplicant *wpa_s, + const char *txtaddr); int ap_ctrl_iface_wpa_get_status(struct wpa_supplicant *wpa_s, char *buf, size_t buflen, int verbose); void ap_tx_status(void *ctx, const u8 *addr, const u8 *buf, size_t len, int ack); -void ap_rx_from_unknown_sta(void *ctx, const u8 *frame, size_t len); +void ap_eapol_tx_status(void *ctx, const u8 *dst, + const u8 *data, size_t len, int ack); +void ap_client_poll_ok(void *ctx, const u8 *addr); +void ap_rx_from_unknown_sta(void *ctx, const u8 *addr, int wds); void ap_mgmt_rx(void *ctx, struct rx_mgmt *rx_mgmt); void ap_mgmt_tx_cb(void *ctx, const u8 *buf, size_t len, u16 stype, int ok); +int wpa_supplicant_ap_update_beacon(struct wpa_supplicant *wpa_s); int wpa_supplicant_ap_mac_addr_filter(struct wpa_supplicant *wpa_s, const u8 *addr); +void wpa_supplicant_ap_pwd_auth_fail(struct wpa_supplicant *wpa_s); +void wpas_ap_ch_switch(struct wpa_supplicant *wpa_s, int freq, int ht, + int offset); #endif /* AP_H */ diff --git a/contrib/wpa/wpa_supplicant/autoscan.c b/contrib/wpa/wpa_supplicant/autoscan.c new file mode 100644 index 0000000..a2cf7a5 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/autoscan.c @@ -0,0 +1,143 @@ +/* + * WPA Supplicant - auto scan + * Copyright (c) 2012, Intel Corporation. All rights reserved. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "config.h" +#include "wpa_supplicant_i.h" +#include "bss.h" +#include "scan.h" +#include "autoscan.h" + +#ifdef CONFIG_AUTOSCAN_EXPONENTIAL +extern const struct autoscan_ops autoscan_exponential_ops; +#endif /* CONFIG_AUTOSCAN_EXPONENTIAL */ + +#ifdef CONFIG_AUTOSCAN_PERIODIC +extern const struct autoscan_ops autoscan_periodic_ops; +#endif /* CONFIG_AUTOSCAN_PERIODIC */ + +static const struct autoscan_ops * autoscan_modules[] = { +#ifdef CONFIG_AUTOSCAN_EXPONENTIAL + &autoscan_exponential_ops, +#endif /* CONFIG_AUTOSCAN_EXPONENTIAL */ +#ifdef CONFIG_AUTOSCAN_PERIODIC + &autoscan_periodic_ops, +#endif /* CONFIG_AUTOSCAN_PERIODIC */ + NULL +}; + + +static void request_scan(struct wpa_supplicant *wpa_s) +{ + wpa_s->scan_req = MANUAL_SCAN_REQ; + + if (wpa_supplicant_req_sched_scan(wpa_s)) + wpa_supplicant_req_scan(wpa_s, wpa_s->scan_interval, 0); +} + + +int autoscan_init(struct wpa_supplicant *wpa_s, int req_scan) +{ + const char *name = wpa_s->conf->autoscan; + const char *params; + size_t nlen; + int i; + const struct autoscan_ops *ops = NULL; + + if (wpa_s->autoscan && wpa_s->autoscan_priv) + return 0; + + if (name == NULL) + return 0; + + params = os_strchr(name, ':'); + if (params == NULL) { + params = ""; + nlen = os_strlen(name); + } else { + nlen = params - name; + params++; + } + + for (i = 0; autoscan_modules[i]; i++) { + if (os_strncmp(name, autoscan_modules[i]->name, nlen) == 0) { + ops = autoscan_modules[i]; + break; + } + } + + if (ops == NULL) { + wpa_printf(MSG_ERROR, "autoscan: Could not find module " + "matching the parameter '%s'", name); + return -1; + } + + wpa_s->autoscan_params = NULL; + + wpa_s->autoscan_priv = ops->init(wpa_s, params); + if (wpa_s->autoscan_priv == NULL) + return -1; + wpa_s->autoscan = ops; + + wpa_printf(MSG_DEBUG, "autoscan: Initialized module '%s' with " + "parameters '%s'", ops->name, params); + if (!req_scan) + return 0; + + /* + * Cancelling existing scan requests, if any. + */ + wpa_supplicant_cancel_sched_scan(wpa_s); + wpa_supplicant_cancel_scan(wpa_s); + + /* + * Firing first scan, which will lead to call autoscan_notify_scan. + */ + request_scan(wpa_s); + + return 0; +} + + +void autoscan_deinit(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->autoscan && wpa_s->autoscan_priv) { + wpa_printf(MSG_DEBUG, "autoscan: Deinitializing module '%s'", + wpa_s->autoscan->name); + wpa_s->autoscan->deinit(wpa_s->autoscan_priv); + wpa_s->autoscan = NULL; + wpa_s->autoscan_priv = NULL; + + wpa_s->scan_interval = 5; + wpa_s->sched_scan_interval = 0; + } +} + + +int autoscan_notify_scan(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res) +{ + int interval; + + if (wpa_s->autoscan && wpa_s->autoscan_priv) { + interval = wpa_s->autoscan->notify_scan(wpa_s->autoscan_priv, + scan_res); + + if (interval <= 0) + return -1; + + wpa_s->scan_interval = interval; + wpa_s->sched_scan_interval = interval; + + request_scan(wpa_s); + } + + return 0; +} diff --git a/contrib/wpa/wpa_supplicant/autoscan.h b/contrib/wpa/wpa_supplicant/autoscan.h new file mode 100644 index 0000000..e2a7652 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/autoscan.h @@ -0,0 +1,49 @@ +/* + * WPA Supplicant - auto scan + * Copyright (c) 2012, Intel Corporation. All rights reserved. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef AUTOSCAN_H +#define AUTOSCAN_H + +struct wpa_supplicant; + +struct autoscan_ops { + const char *name; + + void * (*init)(struct wpa_supplicant *wpa_s, const char *params); + void (*deinit)(void *priv); + + int (*notify_scan)(void *priv, struct wpa_scan_results *scan_res); +}; + +#ifdef CONFIG_AUTOSCAN + +int autoscan_init(struct wpa_supplicant *wpa_s, int req_scan); +void autoscan_deinit(struct wpa_supplicant *wpa_s); +int autoscan_notify_scan(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res); + +#else /* CONFIG_AUTOSCAN */ + +static inline int autoscan_init(struct wpa_supplicant *wpa_s, int req_scan) +{ + return 0; +} + +static inline void autoscan_deinit(struct wpa_supplicant *wpa_s) +{ +} + +static inline int autoscan_notify_scan(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res) +{ + return 0; +} + +#endif /* CONFIG_AUTOSCAN */ + +#endif /* AUTOSCAN_H */ diff --git a/contrib/wpa/wpa_supplicant/autoscan_exponential.c b/contrib/wpa/wpa_supplicant/autoscan_exponential.c new file mode 100644 index 0000000..424477b --- /dev/null +++ b/contrib/wpa/wpa_supplicant/autoscan_exponential.c @@ -0,0 +1,104 @@ +/* + * WPA Supplicant - auto scan exponential module + * Copyright (c) 2012, Intel Corporation. All rights reserved. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "wpa_supplicant_i.h" +#include "autoscan.h" + +struct autoscan_exponential_data { + struct wpa_supplicant *wpa_s; + int base; + int limit; + int interval; +}; + + +static int +autoscan_exponential_get_params(struct autoscan_exponential_data *data, + const char *params) +{ + const char *pos; + + if (params == NULL) + return -1; + + data->base = atoi(params); + + pos = os_strchr(params, ':'); + if (pos == NULL) + return -1; + + pos++; + data->limit = atoi(pos); + + return 0; +} + + +static void * autoscan_exponential_init(struct wpa_supplicant *wpa_s, + const char *params) +{ + struct autoscan_exponential_data *data; + + data = os_zalloc(sizeof(struct autoscan_exponential_data)); + if (data == NULL) + return NULL; + + if (autoscan_exponential_get_params(data, params) < 0) { + os_free(data); + return NULL; + } + + wpa_printf(MSG_DEBUG, "autoscan exponential: base exponential is %d " + "and limit is %d", data->base, data->limit); + + data->wpa_s = wpa_s; + + return data; +} + + +static void autoscan_exponential_deinit(void *priv) +{ + struct autoscan_exponential_data *data = priv; + + os_free(data); +} + + +static int autoscan_exponential_notify_scan(void *priv, + struct wpa_scan_results *scan_res) +{ + struct autoscan_exponential_data *data = priv; + + wpa_printf(MSG_DEBUG, "autoscan exponential: scan result " + "notification"); + + if (data->interval >= data->limit) + return data->limit; + + if (data->interval <= 0) + data->interval = data->base; + else { + data->interval = data->interval * data->base; + if (data->interval > data->limit) + return data->limit; + } + + return data->interval; +} + + +const struct autoscan_ops autoscan_exponential_ops = { + .name = "exponential", + .init = autoscan_exponential_init, + .deinit = autoscan_exponential_deinit, + .notify_scan = autoscan_exponential_notify_scan, +}; diff --git a/contrib/wpa/wpa_supplicant/autoscan_periodic.c b/contrib/wpa/wpa_supplicant/autoscan_periodic.c new file mode 100644 index 0000000..102d723 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/autoscan_periodic.c @@ -0,0 +1,85 @@ +/* + * WPA Supplicant - auto scan periodic module + * Copyright (c) 2012, Intel Corporation. All rights reserved. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "wpa_supplicant_i.h" +#include "autoscan.h" + + +struct autoscan_periodic_data { + int periodic_interval; +}; + + +static int autoscan_periodic_get_params(struct autoscan_periodic_data *data, + const char *params) +{ + int interval; + + if (params == NULL) + return -1; + + interval = atoi(params); + + if (interval < 0) + return -1; + + data->periodic_interval = interval; + + return 0; +} + + +static void * autoscan_periodic_init(struct wpa_supplicant *wpa_s, + const char *params) +{ + struct autoscan_periodic_data *data; + + data = os_zalloc(sizeof(struct autoscan_periodic_data)); + if (data == NULL) + return NULL; + + if (autoscan_periodic_get_params(data, params) < 0) { + os_free(data); + return NULL; + } + + wpa_printf(MSG_DEBUG, "autoscan periodic: interval is %d", + data->periodic_interval); + + return data; +} + + +static void autoscan_periodic_deinit(void *priv) +{ + struct autoscan_periodic_data *data = priv; + + os_free(data); +} + + +static int autoscan_periodic_notify_scan(void *priv, + struct wpa_scan_results *scan_res) +{ + struct autoscan_periodic_data *data = priv; + + wpa_printf(MSG_DEBUG, "autoscan periodic: scan result notification"); + + return data->periodic_interval; +} + + +const struct autoscan_ops autoscan_periodic_ops = { + .name = "periodic", + .init = autoscan_periodic_init, + .deinit = autoscan_periodic_deinit, + .notify_scan = autoscan_periodic_notify_scan, +}; diff --git a/contrib/wpa/wpa_supplicant/bgscan.c b/contrib/wpa/wpa_supplicant/bgscan.c index 31b5d27..9a9bd52 100644 --- a/contrib/wpa/wpa_supplicant/bgscan.c +++ b/contrib/wpa/wpa_supplicant/bgscan.c @@ -2,14 +2,8 @@ * WPA Supplicant - background scan and roaming interface * 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. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -22,11 +16,17 @@ #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 +88,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 +105,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); } diff --git a/contrib/wpa/wpa_supplicant/bgscan.h b/contrib/wpa/wpa_supplicant/bgscan.h index 69e99b6..e9d15fc 100644 --- a/contrib/wpa/wpa_supplicant/bgscan.h +++ b/contrib/wpa/wpa_supplicant/bgscan.h @@ -2,14 +2,8 @@ * WPA Supplicant - background scan and roaming interface * 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. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef BGSCAN_H @@ -25,18 +19,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 +50,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 +61,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/contrib/wpa/wpa_supplicant/bgscan_learn.c b/contrib/wpa/wpa_supplicant/bgscan_learn.c new file mode 100644 index 0000000..07d31e4 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/bgscan_learn.c @@ -0,0 +1,607 @@ +/* + * WPA Supplicant - background scan and roaming module: learn + * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "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_array(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_array(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) { + if (data->probe_idx == 0) + break; + 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_array(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(¶ms, 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, ¶ms)) { + 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_array(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, +}; diff --git a/contrib/wpa/wpa_supplicant/bgscan_simple.c b/contrib/wpa/wpa_supplicant/bgscan_simple.c index 8e80b12..479f703 100644 --- a/contrib/wpa/wpa_supplicant/bgscan_simple.c +++ b/contrib/wpa/wpa_supplicant/bgscan_simple.c @@ -2,14 +2,8 @@ * WPA Supplicant - background scan and roaming module: simple * 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. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -28,6 +22,8 @@ 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 max_short_scans; /* maximum times we short-scan before back-off */ int short_interval; /* use if signal < threshold */ int long_interval; /* use if signal > threshold */ struct os_time last_bgscan; @@ -57,8 +53,30 @@ 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->max_short_scans) { + data->scan_interval = data->long_interval; + wpa_printf(MSG_DEBUG, "bgscan simple: Backing " + "off to long scan interval"); + } + } else if (data->short_scan_count > 0) { + /* + * If we lasted a long scan interval without any + * CQM triggers, decrease the short-scan count, + * which allows 1 more short-scan interval to + * occur in the future when CQM triggers. + */ + data->short_scan_count--; + } os_get_time(&data->last_bgscan); + } } @@ -122,6 +140,16 @@ static void * bgscan_simple_init(struct wpa_supplicant *wpa_s, } data->scan_interval = data->short_interval; + data->max_short_scans = data->long_interval / data->short_interval + 1; + 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 +175,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 +204,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 +218,35 @@ 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; os_get_time(&now); - if (now.sec > data->last_bgscan.sec + 1) + if (now.sec > data->last_bgscan.sec + 1 && + data->short_scan_count <= data->max_short_scans) + /* + * If we haven't just previously (<1 second ago) + * performed a scan, and we haven't depleted our + * budget for short-scans, perform a scan + * immediately. + */ 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"); diff --git a/contrib/wpa/wpa_supplicant/blacklist.c b/contrib/wpa/wpa_supplicant/blacklist.c index 4ffb220..e53dc38 100644 --- a/contrib/wpa/wpa_supplicant/blacklist.c +++ b/contrib/wpa/wpa_supplicant/blacklist.c @@ -2,14 +2,8 @@ * wpa_supplicant - Temporary BSSID blacklist * Copyright (c) 2003-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. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -29,6 +23,9 @@ struct wpa_blacklist * wpa_blacklist_get(struct wpa_supplicant *wpa_s, { struct wpa_blacklist *e; + if (wpa_s == NULL || bssid == NULL) + return NULL; + e = wpa_s->blacklist; while (e) { if (os_memcmp(e->bssid, bssid, ETH_ALEN) == 0) @@ -44,7 +41,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 @@ -60,13 +57,16 @@ int wpa_blacklist_add(struct wpa_supplicant *wpa_s, const u8 *bssid) { struct wpa_blacklist *e; + if (wpa_s == NULL || bssid == NULL) + return -1; + e = wpa_blacklist_get(wpa_s, bssid); if (e) { e->count++; 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; } @@ -93,6 +93,9 @@ int wpa_blacklist_del(struct wpa_supplicant *wpa_s, const u8 *bssid) { struct wpa_blacklist *e, *prev = NULL; + if (wpa_s == NULL || bssid == NULL) + return -1; + e = wpa_s->blacklist; while (e) { if (os_memcmp(e->bssid, bssid, ETH_ALEN) == 0) { @@ -120,14 +123,19 @@ int wpa_blacklist_del(struct wpa_supplicant *wpa_s, const u8 *bssid) void wpa_blacklist_clear(struct wpa_supplicant *wpa_s) { struct wpa_blacklist *e, *prev; + int max_count = 0; e = wpa_s->blacklist; wpa_s->blacklist = NULL; while (e) { + if (e->count > max_count) + max_count = e->count; prev = e; e = e->next; wpa_printf(MSG_DEBUG, "Removed BSSID " MACSTR " from " "blacklist (clear)", MAC2STR(prev->bssid)); os_free(prev); } + + wpa_s->extra_blacklist_count += max_count; } diff --git a/contrib/wpa/wpa_supplicant/blacklist.h b/contrib/wpa/wpa_supplicant/blacklist.h index de280cd..ae06986 100644 --- a/contrib/wpa/wpa_supplicant/blacklist.h +++ b/contrib/wpa/wpa_supplicant/blacklist.h @@ -2,14 +2,8 @@ * wpa_supplicant - Temporary BSSID blacklist * Copyright (c) 2003-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. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef BLACKLIST_H diff --git a/contrib/wpa/wpa_supplicant/bss.c b/contrib/wpa/wpa_supplicant/bss.c index e2ac230..87b7db8 100644 --- a/contrib/wpa/wpa_supplicant/bss.c +++ b/contrib/wpa/wpa_supplicant/bss.c @@ -1,15 +1,9 @@ /* * BSS table - * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi> + * Copyright (c) 2009-2012, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -30,25 +24,6 @@ */ #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) @@ -60,23 +35,185 @@ #define WPA_BSS_IES_CHANGED_FLAG BIT(8) -static void wpa_bss_remove(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) +static void wpa_bss_set_hessid(struct wpa_bss *bss) +{ +#ifdef CONFIG_INTERWORKING + const u8 *ie = wpa_bss_get_ie(bss, WLAN_EID_INTERWORKING); + if (ie == NULL || (ie[1] != 7 && ie[1] != 9)) { + os_memset(bss->hessid, 0, ETH_ALEN); + return; + } + if (ie[1] == 7) + os_memcpy(bss->hessid, ie + 3, ETH_ALEN); + else + os_memcpy(bss->hessid, ie + 5, ETH_ALEN); +#endif /* CONFIG_INTERWORKING */ +} + + +/** + * wpa_bss_anqp_alloc - Allocate ANQP data structure for a BSS entry + * Returns: Allocated ANQP data structure or %NULL on failure + * + * The allocated ANQP data structure has its users count set to 1. It may be + * shared by multiple BSS entries and each shared entry is freed with + * wpa_bss_anqp_free(). + */ +struct wpa_bss_anqp * wpa_bss_anqp_alloc(void) +{ + struct wpa_bss_anqp *anqp; + anqp = os_zalloc(sizeof(*anqp)); + if (anqp == NULL) + return NULL; + anqp->users = 1; + return anqp; +} + + +/** + * wpa_bss_anqp_clone - Clone an ANQP data structure + * @anqp: ANQP data structure from wpa_bss_anqp_alloc() + * Returns: Cloned ANQP data structure or %NULL on failure + */ +static struct wpa_bss_anqp * wpa_bss_anqp_clone(struct wpa_bss_anqp *anqp) +{ + struct wpa_bss_anqp *n; + + n = os_zalloc(sizeof(*n)); + if (n == NULL) + return NULL; + +#define ANQP_DUP(f) if (anqp->f) n->f = wpabuf_dup(anqp->f) +#ifdef CONFIG_INTERWORKING + ANQP_DUP(venue_name); + ANQP_DUP(network_auth_type); + ANQP_DUP(roaming_consortium); + ANQP_DUP(ip_addr_type_availability); + ANQP_DUP(nai_realm); + ANQP_DUP(anqp_3gpp); + ANQP_DUP(domain_name); +#endif /* CONFIG_INTERWORKING */ +#ifdef CONFIG_HS20 + ANQP_DUP(hs20_operator_friendly_name); + ANQP_DUP(hs20_wan_metrics); + ANQP_DUP(hs20_connection_capability); + ANQP_DUP(hs20_operating_class); +#endif /* CONFIG_HS20 */ +#undef ANQP_DUP + + return n; +} + + +/** + * wpa_bss_anqp_unshare_alloc - Unshare ANQP data (if shared) in a BSS entry + * @bss: BSS entry + * Returns: 0 on success, -1 on failure + * + * This function ensures the specific BSS entry has an ANQP data structure that + * is not shared with any other BSS entry. + */ +int wpa_bss_anqp_unshare_alloc(struct wpa_bss *bss) { + struct wpa_bss_anqp *anqp; + + if (bss->anqp && bss->anqp->users > 1) { + /* allocated, but shared - clone an unshared copy */ + anqp = wpa_bss_anqp_clone(bss->anqp); + if (anqp == NULL) + return -1; + anqp->users = 1; + bss->anqp->users--; + bss->anqp = anqp; + return 0; + } + + if (bss->anqp) + return 0; /* already allocated and not shared */ + + /* not allocated - allocate a new storage area */ + bss->anqp = wpa_bss_anqp_alloc(); + return bss->anqp ? 0 : -1; +} + + +/** + * wpa_bss_anqp_free - Free an ANQP data structure + * @anqp: ANQP data structure from wpa_bss_anqp_alloc() or wpa_bss_anqp_clone() + */ +static void wpa_bss_anqp_free(struct wpa_bss_anqp *anqp) +{ + if (anqp == NULL) + return; + + anqp->users--; + if (anqp->users > 0) { + /* Another BSS entry holds a pointer to this ANQP info */ + return; + } + +#ifdef CONFIG_INTERWORKING + wpabuf_free(anqp->venue_name); + wpabuf_free(anqp->network_auth_type); + wpabuf_free(anqp->roaming_consortium); + wpabuf_free(anqp->ip_addr_type_availability); + wpabuf_free(anqp->nai_realm); + wpabuf_free(anqp->anqp_3gpp); + wpabuf_free(anqp->domain_name); +#endif /* CONFIG_INTERWORKING */ +#ifdef CONFIG_HS20 + wpabuf_free(anqp->hs20_operator_friendly_name); + wpabuf_free(anqp->hs20_wan_metrics); + wpabuf_free(anqp->hs20_connection_capability); + wpabuf_free(anqp->hs20_operating_class); +#endif /* CONFIG_HS20 */ + + os_free(anqp); +} + + +static void wpa_bss_remove(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, + const char *reason) +{ + if (wpa_s->last_scan_res) { + unsigned int i; + for (i = 0; i < wpa_s->last_scan_res_used; i++) { + if (wpa_s->last_scan_res[i] == bss) { + os_memmove(&wpa_s->last_scan_res[i], + &wpa_s->last_scan_res[i + 1], + (wpa_s->last_scan_res_used - i - 1) + * sizeof(struct wpa_bss *)); + wpa_s->last_scan_res_used--; + break; + } + } + } 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' due to %s", bss->id, MAC2STR(bss->bssid), + wpa_ssid_txt(bss->ssid, bss->ssid_len), reason); wpas_notify_bss_removed(wpa_s, bss->bssid, bss->id); + wpa_bss_anqp_free(bss->anqp); os_free(bss); } +/** + * wpa_bss_get - Fetch a BSS table entry based on BSSID and SSID + * @wpa_s: Pointer to wpa_supplicant data + * @bssid: BSSID + * @ssid: SSID + * @ssid_len: Length of @ssid + * Returns: Pointer to the BSS entry or %NULL if not found + */ struct wpa_bss * wpa_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid, const u8 *ssid, size_t ssid_len) { struct wpa_bss *bss; + if (!wpa_supplicant_filter_bssid_match(wpa_s, bssid)) + return NULL; dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0 && bss->ssid_len == ssid_len && @@ -112,15 +249,79 @@ static void wpa_bss_copy_res(struct wpa_bss *dst, struct wpa_scan_res *src) } -static void wpa_bss_add(struct wpa_supplicant *wpa_s, - const u8 *ssid, size_t ssid_len, - struct wpa_scan_res *res) +static int wpa_bss_known(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) +{ + struct wpa_ssid *ssid; + + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { + if (ssid->ssid == NULL || ssid->ssid_len == 0) + continue; + if (ssid->ssid_len == bss->ssid_len && + os_memcmp(ssid->ssid, bss->ssid, ssid->ssid_len) == 0) + return 1; + } + + return 0; +} + + +static int wpa_bss_in_use(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) +{ + return bss == wpa_s->current_bss || + os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) == 0 || + os_memcmp(bss->bssid, wpa_s->pending_bssid, ETH_ALEN) == 0; +} + + +static int wpa_bss_remove_oldest_unknown(struct wpa_supplicant *wpa_s) +{ + struct wpa_bss *bss; + + dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { + if (!wpa_bss_known(wpa_s, bss)) { + wpa_bss_remove(wpa_s, bss, __func__); + return 0; + } + } + + return -1; +} + + +static int wpa_bss_remove_oldest(struct wpa_supplicant *wpa_s) +{ + struct wpa_bss *bss; + + /* + * Remove the oldest entry that does not match with any configured + * network. + */ + if (wpa_bss_remove_oldest_unknown(wpa_s) == 0) + return 0; + + /* + * Remove the oldest entry that isn't currently in use. + */ + dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { + if (!wpa_bss_in_use(wpa_s, bss)) { + wpa_bss_remove(wpa_s, bss, __func__); + return 0; + } + } + + return -1; +} + + +static struct wpa_bss * wpa_bss_add(struct wpa_supplicant *wpa_s, + const u8 *ssid, size_t ssid_len, + struct wpa_scan_res *res) { struct wpa_bss *bss; bss = os_zalloc(sizeof(*bss) + res->ie_len + res->beacon_ie_len); if (bss == NULL) - return; + return NULL; bss->id = wpa_s->bss_next_id++; bss->last_update_idx = wpa_s->bss_update_idx; wpa_bss_copy_res(bss, res); @@ -129,18 +330,23 @@ static void wpa_bss_add(struct wpa_supplicant *wpa_s, bss->ie_len = res->ie_len; bss->beacon_ie_len = res->beacon_ie_len; os_memcpy(bss + 1, res + 1, res->ie_len + res->beacon_ie_len); + wpa_bss_set_hessid(bss); 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) != 0) { + wpa_printf(MSG_ERROR, "Increasing the MAX BSS count to %d " + "because all BSSes are in use. We should normally " + "not get here!", (int) wpa_s->num_bss); + wpa_s->conf->bss_max_count = wpa_s->num_bss; } + return bss; } @@ -187,8 +393,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); @@ -269,8 +478,9 @@ static void notify_bss_changes(struct wpa_supplicant *wpa_s, u32 changes, } -static void wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, - struct wpa_scan_res *res) +static struct wpa_bss * +wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, + struct wpa_scan_res *res) { u32 changes; @@ -292,6 +502,15 @@ 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) { + unsigned int i; + for (i = 0; i < wpa_s->last_scan_res_used; i++) { + if (wpa_s->last_scan_res[i] == bss) { + wpa_s->last_scan_res[i] = nbss; + break; + } + } + if (wpa_s->current_bss == bss) + wpa_s->current_bss = nbss; bss = nbss; os_memcpy(bss + 1, res + 1, res->ie_len + res->beacon_ie_len); @@ -300,53 +519,104 @@ static void wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, } dl_list_add(prev, &bss->list_id); } + if (changes & WPA_BSS_IES_CHANGED_FLAG) + wpa_bss_set_hessid(bss); dl_list_add_tail(&wpa_s->bss, &bss->list); notify_bss_changes(wpa_s, changes, bss); -} - -static int wpa_bss_in_use(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) -{ - return bss == wpa_s->current_bss || - os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) == 0 || - os_memcmp(bss->bssid, wpa_s->pending_bssid, ETH_ALEN) == 0; + return bss; } +/** + * wpa_bss_update_start - Start a BSS table update from scan results + * @wpa_s: Pointer to wpa_supplicant data + * + * This function is called at the start of each BSS table update round for new + * scan results. The actual scan result entries are indicated with calls to + * wpa_bss_update_scan_res() and the update round is finished with a call to + * wpa_bss_update_end(). + */ void wpa_bss_update_start(struct wpa_supplicant *wpa_s) { wpa_s->bss_update_idx++; - wpa_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); + wpa_s->last_scan_res_used = 0; } +/** + * wpa_bss_update_scan_res - Update a BSS table entry based on a scan result + * @wpa_s: Pointer to wpa_supplicant data + * @res: Scan result + * + * This function updates a BSS table entry (or adds one) based on a scan result. + * This is called separately for each scan result between the calls to + * wpa_bss_update_start() and wpa_bss_update_end(). + */ void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s, struct wpa_scan_res *res) { - 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); +#ifdef CONFIG_P2P + if (p2p == NULL && + wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) { + /* + * If it's a P2P specific interface, then don't update + * the scan result without a P2P IE. + */ + wpa_printf(MSG_DEBUG, "BSS: No P2P IE - skipping BSS " MACSTR + " update for P2P interface", MAC2STR(res->bssid)); + return; + } +#endif /* CONFIG_P2P */ + if (p2p && ssid[1] == P2P_WILDCARD_SSID_LEN && + os_memcmp(ssid + 2, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) == 0) + return; /* Skip P2P listen discovery results here */ + /* TODO: add option for ignoring BSSes we are not interested in * (to save memory) */ bss = wpa_bss_get(wpa_s, res->bssid, ssid + 2, ssid[1]); if (bss == NULL) - wpa_bss_add(wpa_s, ssid + 2, ssid[1], res); + bss = wpa_bss_add(wpa_s, ssid + 2, ssid[1], res); else - wpa_bss_update(wpa_s, bss, res); + bss = wpa_bss_update(wpa_s, bss, res); + + if (bss == NULL) + return; + if (wpa_s->last_scan_res_used >= wpa_s->last_scan_res_size) { + struct wpa_bss **n; + unsigned int siz; + if (wpa_s->last_scan_res_size == 0) + siz = 32; + else + siz = wpa_s->last_scan_res_size * 2; + n = os_realloc_array(wpa_s->last_scan_res, siz, + sizeof(struct wpa_bss *)); + if (n == NULL) + return; + wpa_s->last_scan_res = n; + wpa_s->last_scan_res_size = siz; + } + + wpa_s->last_scan_res[wpa_s->last_scan_res_used++] = bss; } @@ -391,14 +661,41 @@ static int wpa_bss_included_in_scan(const struct wpa_bss *bss, } +/** + * wpa_bss_update_end - End a BSS table update from scan results + * @wpa_s: Pointer to wpa_supplicant data + * @info: Information about scan parameters + * @new_scan: Whether this update round was based on a new scan + * + * This function is called at the end of each BSS table update round for new + * scan results. The start of the update was indicated with a call to + * wpa_bss_update_start(). + */ void wpa_bss_update_end(struct wpa_supplicant *wpa_s, struct scan_info *info, int new_scan) { struct wpa_bss *bss, *n; + wpa_s->last_scan_full = 0; + os_get_time(&wpa_s->last_scan); if (!new_scan) return; /* do not expire entries without new scan */ + if (info && !info->aborted && !info->freqs) { + size_t i; + if (info->num_ssids == 0) { + wpa_s->last_scan_full = 1; + } else { + for (i = 0; i < info->num_ssids; i++) { + if (info->ssids[i].ssid == NULL || + info->ssids[i].ssid_len == 0) { + wpa_s->last_scan_full = 1; + break; + } + } + } + } + dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) { if (wpa_bss_in_use(wpa_s, bss)) continue; @@ -406,18 +703,28 @@ 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); - wpa_bss_remove(wpa_s, bss); + if (bss->scan_miss_count >= + wpa_s->conf->bss_expiration_scan_count) { + wpa_bss_remove(wpa_s, bss, "no match in scan"); } } + + wpa_printf(MSG_DEBUG, "BSS: last_scan_res_used=%u/%u " + "last_scan_full=%d", + wpa_s->last_scan_res_used, wpa_s->last_scan_res_size, + wpa_s->last_scan_full); } -static void wpa_bss_timeout(void *eloop_ctx, void *timeout_ctx) +/** + * wpa_bss_flush_by_age - Flush old BSS entries + * @wpa_s: Pointer to wpa_supplicant data + * @age: Maximum entry age in seconds + * + * Remove BSS entries that have not been updated during the last @age seconds. + */ +void wpa_bss_flush_by_age(struct wpa_supplicant *wpa_s, int age) { - struct wpa_supplicant *wpa_s = eloop_ctx; struct wpa_bss *bss, *n; struct os_time t; @@ -425,24 +732,38 @@ 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_bss_remove(wpa_s, bss); + wpa_bss_remove(wpa_s, bss, __func__); } 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); } +/** + * wpa_bss_init - Initialize BSS table + * @wpa_s: Pointer to wpa_supplicant data + * Returns: 0 on success, -1 on failure + * + * This prepares BSS table lists and timer for periodic updates. The BSS table + * is deinitialized with wpa_bss_deinit() once not needed anymore. + */ int wpa_bss_init(struct wpa_supplicant *wpa_s) { dl_list_init(&wpa_s->bss); @@ -453,22 +774,49 @@ int wpa_bss_init(struct wpa_supplicant *wpa_s) } -void wpa_bss_deinit(struct wpa_supplicant *wpa_s) +/** + * wpa_bss_flush - Flush all unused BSS entries + * @wpa_s: Pointer to wpa_supplicant data + */ +void wpa_bss_flush(struct wpa_supplicant *wpa_s) { struct wpa_bss *bss, *n; - 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) - wpa_bss_remove(wpa_s, bss); + + dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) { + if (wpa_bss_in_use(wpa_s, bss)) + continue; + wpa_bss_remove(wpa_s, bss, __func__); + } +} + + +/** + * wpa_bss_deinit - Deinitialize BSS table + * @wpa_s: Pointer to wpa_supplicant data + */ +void wpa_bss_deinit(struct wpa_supplicant *wpa_s) +{ + eloop_cancel_timeout(wpa_bss_timeout, wpa_s, NULL); + wpa_bss_flush(wpa_s); } +/** + * wpa_bss_get_bssid - Fetch a BSS table entry based on BSSID + * @wpa_s: Pointer to wpa_supplicant data + * @bssid: BSSID + * Returns: Pointer to the BSS entry or %NULL if not found + */ struct wpa_bss * wpa_bss_get_bssid(struct wpa_supplicant *wpa_s, const u8 *bssid) { struct wpa_bss *bss; - dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { + if (!wpa_supplicant_filter_bssid_match(wpa_s, bssid)) + return NULL; + dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) { if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0) return bss; } @@ -476,6 +824,35 @@ struct wpa_bss * wpa_bss_get_bssid(struct wpa_supplicant *wpa_s, } +#ifdef CONFIG_P2P +/** + * wpa_bss_get_p2p_dev_addr - Fetch a BSS table entry based on P2P Device Addr + * @wpa_s: Pointer to wpa_supplicant data + * @dev_addr: P2P Device Address of the GO + * Returns: Pointer to the BSS entry or %NULL if not found + */ +struct wpa_bss * wpa_bss_get_p2p_dev_addr(struct wpa_supplicant *wpa_s, + const u8 *dev_addr) +{ + struct wpa_bss *bss; + dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) { + u8 addr[ETH_ALEN]; + if (p2p_parse_dev_addr((const u8 *) (bss + 1), bss->ie_len, + addr) == 0 && + os_memcmp(addr, dev_addr, ETH_ALEN) == 0) + return bss; + } + return NULL; +} +#endif /* CONFIG_P2P */ + + +/** + * wpa_bss_get_id - Fetch a BSS table entry based on identifier + * @wpa_s: Pointer to wpa_supplicant data + * @id: Unique identifier (struct wpa_bss::id) assigned for the entry + * Returns: Pointer to the BSS entry or %NULL if not found + */ struct wpa_bss * wpa_bss_get_id(struct wpa_supplicant *wpa_s, unsigned int id) { struct wpa_bss *bss; @@ -487,6 +864,15 @@ struct wpa_bss * wpa_bss_get_id(struct wpa_supplicant *wpa_s, unsigned int id) } +/** + * wpa_bss_get_ie - Fetch a specified information element from a BSS entry + * @bss: BSS table entry + * @ie: Information element identitifier (WLAN_EID_*) + * Returns: Pointer to the information element (id field) or %NULL if not found + * + * This function returns the first matching information element in the BSS + * entry. + */ const u8 * wpa_bss_get_ie(const struct wpa_bss *bss, u8 ie) { const u8 *end, *pos; @@ -506,6 +892,15 @@ const u8 * wpa_bss_get_ie(const struct wpa_bss *bss, u8 ie) } +/** + * wpa_bss_get_vendor_ie - Fetch a vendor information element from a BSS entry + * @bss: BSS table entry + * @vendor_type: Vendor type (four octets starting the IE payload) + * Returns: Pointer to the information element (id field) or %NULL if not found + * + * This function returns the first matching information element in the BSS + * entry. + */ const u8 * wpa_bss_get_vendor_ie(const struct wpa_bss *bss, u32 vendor_type) { const u8 *end, *pos; @@ -526,6 +921,16 @@ const u8 * wpa_bss_get_vendor_ie(const struct wpa_bss *bss, u32 vendor_type) } +/** + * wpa_bss_get_vendor_ie_multi - Fetch vendor IE data from a BSS entry + * @bss: BSS table entry + * @vendor_type: Vendor type (four octets starting the IE payload) + * Returns: Pointer to the information element payload or %NULL if not found + * + * This function returns concatenated payload of possibly fragmented vendor + * specific information elements in the BSS entry. The caller is responsible for + * freeing the returned buffer. + */ struct wpabuf * wpa_bss_get_vendor_ie_multi(const struct wpa_bss *bss, u32 vendor_type) { @@ -557,6 +962,56 @@ struct wpabuf * wpa_bss_get_vendor_ie_multi(const struct wpa_bss *bss, } +/** + * wpa_bss_get_vendor_ie_multi_beacon - Fetch vendor IE data from a BSS entry + * @bss: BSS table entry + * @vendor_type: Vendor type (four octets starting the IE payload) + * Returns: Pointer to the information element payload or %NULL if not found + * + * This function returns concatenated payload of possibly fragmented vendor + * specific information elements in the BSS entry. The caller is responsible for + * freeing the returned buffer. + * + * This function is like wpa_bss_get_vendor_ie_multi(), but uses IE buffer only + * from Beacon frames instead of either Beacon or Probe Response frames. + */ +struct wpabuf * wpa_bss_get_vendor_ie_multi_beacon(const struct wpa_bss *bss, + u32 vendor_type) +{ + struct wpabuf *buf; + const u8 *end, *pos; + + buf = wpabuf_alloc(bss->beacon_ie_len); + if (buf == NULL) + return NULL; + + pos = (const u8 *) (bss + 1); + pos += bss->ie_len; + end = pos + bss->beacon_ie_len; + + while (pos + 1 < end) { + if (pos + 2 + pos[1] > end) + break; + if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && + vendor_type == WPA_GET_BE32(&pos[2])) + wpabuf_put_data(buf, pos + 2 + 4, pos[1] - 4); + pos += 2 + pos[1]; + } + + if (wpabuf_len(buf) == 0) { + wpabuf_free(buf); + buf = NULL; + } + + return buf; +} + + +/** + * wpa_bss_get_max_rate - Get maximum legacy TX rate supported in a BSS + * @bss: BSS table entry + * Returns: Maximum legacy rate in units of 500 kbps + */ int wpa_bss_get_max_rate(const struct wpa_bss *bss) { int rate = 0; @@ -579,6 +1034,15 @@ int wpa_bss_get_max_rate(const struct wpa_bss *bss) } +/** + * wpa_bss_get_bit_rates - Get legacy TX rates supported in a BSS + * @bss: BSS table entry + * @rates: Buffer for returning a pointer to the rates list (units of 500 kbps) + * Returns: number of legacy TX rates or -1 on failure + * + * The caller is responsible for freeing the returned buffer with os_free() in + * case of success. + */ int wpa_bss_get_bit_rates(const struct wpa_bss *bss, u8 **rates) { const u8 *ie, *ie2; diff --git a/contrib/wpa/wpa_supplicant/bss.h b/contrib/wpa/wpa_supplicant/bss.h index 1de4722..01f6c59 100644 --- a/contrib/wpa/wpa_supplicant/bss.h +++ b/contrib/wpa/wpa_supplicant/bss.h @@ -2,14 +2,8 @@ * BSS table * 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. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef BSS_H @@ -23,49 +17,79 @@ 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_anqp - ANQP data for a BSS entry (struct wpa_bss) + */ +struct wpa_bss_anqp { + /** Number of BSS entries referring to this ANQP data instance */ + unsigned int users; +#ifdef CONFIG_INTERWORKING + struct wpabuf *venue_name; + struct wpabuf *network_auth_type; + struct wpabuf *roaming_consortium; + struct wpabuf *ip_addr_type_availability; + struct wpabuf *nai_realm; + struct wpabuf *anqp_3gpp; + struct wpabuf *domain_name; +#endif /* CONFIG_INTERWORKING */ +#ifdef CONFIG_HS20 + struct wpabuf *hs20_operator_friendly_name; + struct wpabuf *hs20_wan_metrics; + struct wpabuf *hs20_connection_capability; + struct wpabuf *hs20_operating_class; +#endif /* CONFIG_HS20 */ +}; /** * struct wpa_bss - BSS table - * @list: List entry for struct wpa_supplicant::bss - * @list_id: List entry for struct wpa_supplicant::bss_id - * @id: Unique identifier for this BSS entry - * @scan_miss_count: Number of counts without seeing this BSS - * @flags: information flags about the BSS/IBSS (WPA_BSS_*) - * @last_update_idx: Index of the last scan update - * @bssid: BSSID - * @freq: frequency of the channel in MHz (e.g., 2412 = channel 1) - * @beacon_int: beacon interval in TUs (host byte order) - * @caps: capability information field in host byte order - * @qual: signal quality - * @noise: noise level - * @level: signal level - * @tsf: Timestamp of last Beacon/Probe Response frame - * @last_update: Time of the last update (i.e., Beacon or Probe Response RX) - * @ie_len: length of the following IE field in octets (from Probe Response) - * @beacon_ie_len: length of the following Beacon IE field in octets * * This structure is used to store information about neighboring BSSes in * generic format. It is mainly updated based on scan results from the driver. */ struct wpa_bss { + /** List entry for struct wpa_supplicant::bss */ struct dl_list list; + /** List entry for struct wpa_supplicant::bss_id */ struct dl_list list_id; + /** Unique identifier for this BSS entry */ unsigned int id; + /** Number of counts without seeing this BSS */ unsigned int scan_miss_count; + /** Index of the last scan update */ unsigned int last_update_idx; + /** Information flags about the BSS/IBSS (WPA_BSS_*) */ unsigned int flags; + /** BSSID */ u8 bssid[ETH_ALEN]; + /** HESSID */ + u8 hessid[ETH_ALEN]; + /** SSID */ u8 ssid[32]; + /** Length of SSID */ size_t ssid_len; + /** Frequency of the channel in MHz (e.g., 2412 = channel 1) */ int freq; + /** Beacon interval in TUs (host byte order) */ u16 beacon_int; + /** Capability information field in host byte order */ u16 caps; + /** Signal quality */ int qual; + /** Noise level */ int noise; + /** Signal level */ int level; + /** Timestamp of last Beacon/Probe Response frame */ u64 tsf; + /** Time of the last update (i.e., Beacon or Probe Response RX) */ struct os_time last_update; + /** ANQP data */ + struct wpa_bss_anqp *anqp; + /** Length of the following IE field in octets (from Probe Response) */ size_t ie_len; + /** Length of the following Beacon IE field in octets */ size_t beacon_ie_len; /* followed by ie_len octets of IEs */ /* followed by beacon_ie_len octets of IEs */ @@ -78,16 +102,24 @@ 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); struct wpabuf * wpa_bss_get_vendor_ie_multi(const struct wpa_bss *bss, u32 vendor_type); +struct wpabuf * wpa_bss_get_vendor_ie_multi_beacon(const struct wpa_bss *bss, + u32 vendor_type); int wpa_bss_get_max_rate(const struct wpa_bss *bss); int wpa_bss_get_bit_rates(const struct wpa_bss *bss, u8 **rates); +struct wpa_bss_anqp * wpa_bss_anqp_alloc(void); +int wpa_bss_anqp_unshare_alloc(struct wpa_bss *bss); #endif /* BSS_H */ diff --git a/contrib/wpa/wpa_supplicant/config.c b/contrib/wpa/wpa_supplicant/config.c index 7e2a5b4..0fab07a 100644 --- a/contrib/wpa/wpa_supplicant/config.c +++ b/contrib/wpa/wpa_supplicant/config.c @@ -1,23 +1,19 @@ /* * WPA Supplicant / Configuration parser and common functions - * Copyright (c) 2003-2008, 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 - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "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" @@ -57,42 +53,6 @@ struct parse_data { }; -static char * wpa_config_parse_string(const char *value, size_t *len) -{ - if (*value == '"') { - const char *pos; - char *str; - value++; - pos = os_strrchr(value, '"'); - if (pos == NULL || pos[1] != '\0') - return NULL; - *len = pos - value; - str = os_malloc(*len + 1); - if (str == NULL) - return NULL; - os_memcpy(str, value, *len); - str[*len] = '\0'; - return str; - } else { - u8 *str; - size_t tlen, hlen = os_strlen(value); - if (hlen & 1) - return NULL; - tlen = hlen / 2; - str = os_malloc(tlen + 1); - if (str == NULL) - return NULL; - if (hexstr2bin(value, str, tlen)) { - os_free(str); - return NULL; - } - str[tlen] = '\0'; - *len = tlen; - return (char *) str; - } -} - - static int wpa_config_parse_str(const struct parse_data *data, struct wpa_ssid *ssid, int line, const char *value) @@ -153,18 +113,6 @@ set: #ifndef NO_CONFIG_WRITE -static int is_hex(const u8 *data, size_t len) -{ - size_t i; - - for (i = 0; i < len; i++) { - if (data[i] < 32 || data[i] >= 127) - return 1; - } - return 0; -} - - static char * wpa_config_write_string_ascii(const u8 *value, size_t len) { char *buf; @@ -283,6 +231,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); @@ -322,6 +276,21 @@ static int wpa_config_parse_psk(const struct parse_data *data, struct wpa_ssid *ssid, int line, const char *value) { +#ifdef CONFIG_EXT_PASSWORD + if (os_strncmp(value, "ext:", 4) == 0) { + os_free(ssid->passphrase); + ssid->passphrase = NULL; + ssid->psk_set = 0; + os_free(ssid->ext_psk); + ssid->ext_psk = os_strdup(value + 4); + if (ssid->ext_psk == NULL) + return -1; + wpa_printf(MSG_DEBUG, "PSK: External password '%s'", + ssid->ext_psk); + return 0; + } +#endif /* CONFIG_EXT_PASSWORD */ + if (*value == '"') { #ifndef CONFIG_NO_PBKDF2 const char *pos; @@ -379,6 +348,17 @@ static int wpa_config_parse_psk(const struct parse_data *data, static char * wpa_config_write_psk(const struct parse_data *data, struct wpa_ssid *ssid) { +#ifdef CONFIG_EXT_PASSWORD + if (ssid->ext_psk) { + size_t len = 4 + os_strlen(ssid->ext_psk) + 1; + char *buf = os_malloc(len); + if (buf == NULL) + return NULL; + os_snprintf(buf, len, "ext:%s", ssid->ext_psk); + return buf; + } +#endif /* CONFIG_EXT_PASSWORD */ + if (ssid->passphrase) return wpa_config_write_string_ascii( (const u8 *) ssid->passphrase, @@ -524,6 +504,12 @@ static int wpa_config_parse_key_mgmt(const struct parse_data *data, else if (os_strcmp(start, "WPS") == 0) val |= WPA_KEY_MGMT_WPS; #endif /* CONFIG_WPS */ +#ifdef CONFIG_SAE + else if (os_strcmp(start, "SAE") == 0) + val |= WPA_KEY_MGMT_SAE; + else if (os_strcmp(start, "FT-SAE") == 0) + val |= WPA_KEY_MGMT_FT_SAE; +#endif /* CONFIG_SAE */ else { wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'", line, start); @@ -663,6 +649,8 @@ static int wpa_config_parse_cipher(int line, const char *value) *end = '\0'; if (os_strcmp(start, "CCMP") == 0) val |= WPA_CIPHER_CCMP; + else if (os_strcmp(start, "GCMP") == 0) + val |= WPA_CIPHER_GCMP; else if (os_strcmp(start, "TKIP") == 0) val |= WPA_CIPHER_TKIP; else if (os_strcmp(start, "WEP104") == 0) @@ -714,6 +702,16 @@ static char * wpa_config_write_cipher(int cipher) pos += ret; } + if (cipher & WPA_CIPHER_GCMP) { + ret = os_snprintf(pos, end - pos, "%sGCMP", + pos == buf ? "" : " "); + if (ret < 0 || ret >= end - pos) { + end[-1] = '\0'; + return buf; + } + pos += ret; + } + if (cipher & WPA_CIPHER_TKIP) { ret = os_snprintf(pos, end - pos, "%sTKIP", pos == buf ? "" : " "); @@ -767,7 +765,8 @@ static int wpa_config_parse_pairwise(const struct parse_data *data, val = wpa_config_parse_cipher(line, value); if (val == -1) return -1; - if (val & ~(WPA_CIPHER_CCMP | WPA_CIPHER_TKIP | WPA_CIPHER_NONE)) { + if (val & ~(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | + WPA_CIPHER_NONE)) { wpa_printf(MSG_ERROR, "Line %d: not allowed pairwise cipher " "(0x%x).", line, val); return -1; @@ -796,8 +795,8 @@ static int wpa_config_parse_group(const struct parse_data *data, val = wpa_config_parse_cipher(line, value); if (val == -1) return -1; - if (val & ~(WPA_CIPHER_CCMP | WPA_CIPHER_TKIP | WPA_CIPHER_WEP104 | - WPA_CIPHER_WEP40)) { + if (val & ~(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | + WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40)) { wpa_printf(MSG_ERROR, "Line %d: not allowed group cipher " "(0x%x).", line, val); return -1; @@ -927,7 +926,7 @@ static int * wpa_config_parse_freqs(const struct parse_data *data, used = 0; len = 10; - freqs = os_zalloc((len + 1) * sizeof(int)); + freqs = os_calloc(len + 1, sizeof(int)); if (freqs == NULL) return NULL; @@ -938,7 +937,7 @@ static int * wpa_config_parse_freqs(const struct parse_data *data, if (used == len) { int *n; size_t i; - n = os_realloc(freqs, (len * 2 + 1) * sizeof(int)); + n = os_realloc_array(freqs, len * 2 + 1, sizeof(int)); if (n == NULL) { os_free(freqs); return NULL; @@ -1067,8 +1066,8 @@ static int wpa_config_parse_eap(const struct parse_data *data, last = *end == '\0'; *end = '\0'; tmp = methods; - methods = os_realloc(methods, - (num_methods + 1) * sizeof(*methods)); + methods = os_realloc_array(methods, num_methods + 1, + sizeof(*methods)); if (methods == NULL) { os_free(tmp); os_free(buf); @@ -1098,7 +1097,7 @@ static int wpa_config_parse_eap(const struct parse_data *data, os_free(buf); tmp = methods; - methods = os_realloc(methods, (num_methods + 1) * sizeof(*methods)); + methods = os_realloc_array(methods, num_methods + 1, sizeof(*methods)); if (methods == NULL) { os_free(tmp); return -1; @@ -1109,6 +1108,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; } @@ -1163,6 +1163,20 @@ static int wpa_config_parse_password(const struct parse_data *data, return 0; } +#ifdef CONFIG_EXT_PASSWORD + if (os_strncmp(value, "ext:", 4) == 0) { + char *name = os_strdup(value + 4); + if (name == NULL) + return -1; + os_free(ssid->eap.password); + ssid->eap.password = (u8 *) name; + ssid->eap.password_len = os_strlen(name); + ssid->eap.flags &= ~EAP_CONFIG_FLAGS_PASSWORD_NTHASH; + ssid->eap.flags |= EAP_CONFIG_FLAGS_EXT_PASSWORD; + return 0; + } +#endif /* CONFIG_EXT_PASSWORD */ + if (os_strncmp(value, "hash:", 5) != 0) { char *tmp; size_t res_len; @@ -1180,6 +1194,7 @@ static int wpa_config_parse_password(const struct parse_data *data, ssid->eap.password = (u8 *) tmp; ssid->eap.password_len = res_len; ssid->eap.flags &= ~EAP_CONFIG_FLAGS_PASSWORD_NTHASH; + ssid->eap.flags &= ~EAP_CONFIG_FLAGS_EXT_PASSWORD; return 0; } @@ -1208,6 +1223,7 @@ static int wpa_config_parse_password(const struct parse_data *data, ssid->eap.password = hash; ssid->eap.password_len = 16; ssid->eap.flags |= EAP_CONFIG_FLAGS_PASSWORD_NTHASH; + ssid->eap.flags &= ~EAP_CONFIG_FLAGS_EXT_PASSWORD; return 0; } @@ -1221,6 +1237,17 @@ static char * wpa_config_write_password(const struct parse_data *data, if (ssid->eap.password == NULL) return NULL; +#ifdef CONFIG_EXT_PASSWORD + if (ssid->eap.flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) { + buf = os_zalloc(4 + ssid->eap.password_len + 1); + if (buf == NULL) + return NULL; + os_memcpy(buf, "ext:", 4); + os_memcpy(buf + 4, ssid->eap.password, ssid->eap.password_len); + return buf; + } +#endif /* CONFIG_EXT_PASSWORD */ + if (!(ssid->eap.flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH)) { return wpa_config_write_string( ssid->eap.password, ssid->eap.password_len); @@ -1256,6 +1283,11 @@ static int wpa_config_parse_wep_key(u8 *key, size_t *len, int line, os_free(buf); return -1; } + if (*len && *len != 5 && *len != 13 && *len != 16) { + wpa_printf(MSG_ERROR, "Line %d: Invalid WEP key length %u - " + "this network block will be ignored", + line, (unsigned int) *len); + } os_memcpy(key, buf, *len); os_free(buf); res = os_snprintf(title, sizeof(title), "wep_key%d", idx); @@ -1344,6 +1376,99 @@ 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)) { + if (count == 0) { + wpa_printf(MSG_ERROR, "Line %d: Invalid " + "p2p_client_list address '%s'.", + line, value); + os_free(buf); + return -1; + } + /* continue anyway since this could have been from a + * truncated configuration file line */ + wpa_printf(MSG_INFO, "Line %d: Ignore likely " + "truncated p2p_client_list address '%s'", + line, pos); + } else { + n = os_realloc_array(buf, count + 1, ETH_ALEN); + if (n == NULL) { + os_free(buf); + return -1; + } + buf = n; + os_memmove(buf + ETH_ALEN, buf, count * ETH_ALEN); + os_memcpy(buf, addr, ETH_ALEN); + count++; + wpa_hexdump(MSG_MSGDUMP, "p2p_client_list", + addr, ETH_ALEN); + } + + pos = os_strchr(pos, ' '); + } + + 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 = ssid->num_p2p_clients; i > 0; i--) { + res = os_snprintf(pos, end - pos, MACSTR " ", + MAC2STR(ssid->p2p_client_list + + (i - 1) * ETH_ALEN)); + if (res < 0 || res >= end - pos) { + os_free(value); + return NULL; + } + pos += res; + } + + if (pos > value) + pos[-1] = '\0'; + + return value; +} +#endif /* NO_CONFIG_WRITE */ + +#endif /* CONFIG_P2P */ + /* Helper macros for network block parser */ #ifdef OFFSET @@ -1438,6 +1563,7 @@ static const struct parse_data ssid_fields[] = { { FUNC_KEY(psk) }, { FUNC(proto) }, { FUNC(key_mgmt) }, + { INT(bg_scan_period) }, { FUNC(pairwise) }, { FUNC(group) }, { FUNC(auth_alg) }, @@ -1492,18 +1618,33 @@ 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) }, #endif /* CONFIG_IEEE80211W */ { INT_RANGE(peerkey, 0, 1) }, { INT_RANGE(mixed_cell, 0, 1) }, - { INT_RANGE(frequency, 0, 10000) }, + { INT_RANGE(frequency, 0, 65000) }, { INT(wpa_ptk_rekey) }, { STR(bgscan) }, + { INT_RANGE(ignore_broadcast_ssid, 0, 2) }, +#ifdef CONFIG_P2P + { FUNC(p2p_client_list) }, +#endif /* CONFIG_P2P */ +#ifdef CONFIG_HT_OVERRIDES + { INT_RANGE(disable_ht, 0, 1) }, + { INT_RANGE(disable_ht40, -1, 1) }, + { INT_RANGE(disable_sgi, 0, 1) }, + { INT_RANGE(disable_max_amsdu, -1, 1) }, + { INT_RANGE(ampdu_factor, -1, 3) }, + { INT_RANGE(ampdu_density, -1, 7) }, + { STR(ht_mcs) }, +#endif /* CONFIG_HT_OVERRIDES */ + { INT(ap_max_inactivity) }, + { INT(dtim_period) }, }; #undef OFFSET @@ -1557,19 +1698,20 @@ int wpa_config_add_prio_network(struct wpa_config *config, } /* First network for this priority - add a new priority list */ - nlist = os_realloc(config->pssid, - (config->num_prio + 1) * sizeof(struct wpa_ssid *)); + nlist = os_realloc_array(config->pssid, config->num_prio + 1, + sizeof(struct wpa_ssid *)); if (nlist == NULL) return -1; for (prio = 0; prio < config->num_prio; prio++) { - if (nlist[prio]->priority < ssid->priority) + if (nlist[prio]->priority < ssid->priority) { + os_memmove(&nlist[prio + 1], &nlist[prio], + (config->num_prio - prio) * + sizeof(struct wpa_ssid *)); break; + } } - os_memmove(&nlist[prio + 1], &nlist[prio], - (config->num_prio - prio) * sizeof(struct wpa_ssid *)); - nlist[prio] = ssid; config->num_prio++; config->pssid = nlist; @@ -1663,6 +1805,7 @@ void wpa_config_free_ssid(struct wpa_ssid *ssid) { os_free(ssid->ssid); os_free(ssid->passphrase); + os_free(ssid->ext_psk); #ifdef IEEE8021X_EAPOL eap_peer_config_free(&ssid->eap); #endif /* IEEE8021X_EAPOL */ @@ -1670,10 +1813,34 @@ 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); +#ifdef CONFIG_HT_OVERRIDES + os_free(ssid->ht_mcs); +#endif /* CONFIG_HT_OVERRIDES */ os_free(ssid); } +void wpa_config_free_cred(struct wpa_cred *cred) +{ + os_free(cred->realm); + os_free(cred->username); + os_free(cred->password); + os_free(cred->ca_cert); + os_free(cred->client_cert); + os_free(cred->private_key); + os_free(cred->private_key_passwd); + os_free(cred->imsi); + os_free(cred->milenage); + os_free(cred->domain); + os_free(cred->eap_method); + os_free(cred->phase1); + os_free(cred->phase2); + os_free(cred->excluded_ssid); + os_free(cred); +} + + /** * wpa_config_free - Free configuration data * @config: Configuration data from wpa_config_read() @@ -1687,6 +1854,8 @@ 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; + struct wpa_cred *cred, *cprev; + ssid = config->ssid; while (ssid) { prev = ssid; @@ -1694,6 +1863,13 @@ void wpa_config_free(struct wpa_config *config) wpa_config_free_ssid(prev); } + cred = config->cred; + while (cred) { + cprev = cred; + cred = cred->next; + wpa_config_free_cred(cprev); + } + #ifndef CONFIG_NO_CONFIG_BLOBS blob = config->blobs; prevblob = NULL; @@ -1704,25 +1880,59 @@ void wpa_config_free(struct wpa_config *config) } #endif /* CONFIG_NO_CONFIG_BLOBS */ + wpabuf_free(config->wps_vendor_ext_m1); os_free(config->ctrl_interface); os_free(config->ctrl_interface_group); os_free(config->opensc_engine_path); os_free(config->pkcs11_engine_path); os_free(config->pkcs11_module_path); + os_free(config->pcsc_reader); + os_free(config->pcsc_pin); os_free(config->driver_param); os_free(config->device_name); os_free(config->manufacturer); os_free(config->model_name); os_free(config->model_number); os_free(config->serial_number); - os_free(config->device_type); os_free(config->config_methods); + os_free(config->p2p_ssid_postfix); os_free(config->pssid); + os_free(config->p2p_pref_chan); + os_free(config->autoscan); + wpabuf_free(config->wps_nfc_dh_pubkey); + wpabuf_free(config->wps_nfc_dh_privkey); + wpabuf_free(config->wps_nfc_dev_pw); + os_free(config->ext_password_backend); os_free(config); } /** + * 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 @@ -1820,11 +2030,24 @@ void wpa_config_set_network_defaults(struct wpa_ssid *ssid) ssid->pairwise_cipher = DEFAULT_PAIRWISE; ssid->group_cipher = DEFAULT_GROUP; ssid->key_mgmt = DEFAULT_KEY_MGMT; + ssid->bg_scan_period = DEFAULT_BG_SCAN_PERIOD; #ifdef IEEE8021X_EAPOL ssid->eapol_flags = DEFAULT_EAPOL_FLAGS; ssid->eap_workaround = DEFAULT_EAP_WORKAROUND; ssid->eap.fragment_size = DEFAULT_FRAGMENT_SIZE; #endif /* IEEE8021X_EAPOL */ +#ifdef CONFIG_HT_OVERRIDES + ssid->disable_ht = DEFAULT_DISABLE_HT; + ssid->disable_ht40 = DEFAULT_DISABLE_HT40; + ssid->disable_sgi = DEFAULT_DISABLE_SGI; + ssid->disable_max_amsdu = DEFAULT_DISABLE_MAX_AMSDU; + ssid->ampdu_factor = DEFAULT_AMPDU_FACTOR; + ssid->ampdu_density = DEFAULT_AMPDU_DENSITY; +#endif /* CONFIG_HT_OVERRIDES */ + ssid->proactive_key_caching = -1; +#ifdef CONFIG_IEEE80211W + ssid->ieee80211w = MGMT_FRAME_PROTECTION_DEFAULT; +#endif /* CONFIG_IEEE80211W */ } @@ -1876,10 +2099,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,7 +2140,9 @@ char ** wpa_config_get_all(struct wpa_ssid *ssid, int get_keys) char **props; int fields_num; - props = os_zalloc(sizeof(char *) * ((2 * NUM_SSID_FIELDS) + 1)); + get_keys = get_keys && ssid->export_keys; + + props = os_calloc(2 * NUM_SSID_FIELDS + 1, sizeof(char *)); if (!props) return NULL; @@ -2023,8 +2270,7 @@ char * wpa_config_get_no_key(struct wpa_ssid *ssid, const char *var) void wpa_config_update_psk(struct wpa_ssid *ssid) { #ifndef CONFIG_NO_PBKDF2 - pbkdf2_sha1(ssid->passphrase, - (char *) ssid->ssid, ssid->ssid_len, 4096, + pbkdf2_sha1(ssid->passphrase, ssid->ssid, ssid->ssid_len, 4096, ssid->psk, PMK_LEN); wpa_hexdump_key(MSG_MSGDUMP, "PSK (from passphrase)", ssid->psk, PMK_LEN); @@ -2033,6 +2279,248 @@ void wpa_config_update_psk(struct wpa_ssid *ssid) } +int wpa_config_set_cred(struct wpa_cred *cred, const char *var, + const char *value, int line) +{ + char *val; + size_t len; + + if (os_strcmp(var, "priority") == 0) { + cred->priority = atoi(value); + return 0; + } + + if (os_strcmp(var, "pcsc") == 0) { + cred->pcsc = atoi(value); + return 0; + } + + if (os_strcmp(var, "eap") == 0) { + struct eap_method_type method; + method.method = eap_peer_get_type(value, &method.vendor); + if (method.vendor == EAP_VENDOR_IETF && + method.method == EAP_TYPE_NONE) { + wpa_printf(MSG_ERROR, "Line %d: unknown EAP type '%s' " + "for a credential", line, value); + return -1; + } + os_free(cred->eap_method); + cred->eap_method = os_malloc(sizeof(*cred->eap_method)); + if (cred->eap_method == NULL) + return -1; + os_memcpy(cred->eap_method, &method, sizeof(method)); + return 0; + } + + if (os_strcmp(var, "password") == 0 && + os_strncmp(value, "ext:", 4) == 0) { + os_free(cred->password); + cred->password = os_strdup(value); + cred->ext_password = 1; + return 0; + } + + val = wpa_config_parse_string(value, &len); + if (val == NULL) { + wpa_printf(MSG_ERROR, "Line %d: invalid field '%s' string " + "value '%s'.", line, var, value); + return -1; + } + + if (os_strcmp(var, "realm") == 0) { + os_free(cred->realm); + cred->realm = val; + return 0; + } + + if (os_strcmp(var, "username") == 0) { + os_free(cred->username); + cred->username = val; + return 0; + } + + if (os_strcmp(var, "password") == 0) { + os_free(cred->password); + cred->password = val; + cred->ext_password = 0; + return 0; + } + + if (os_strcmp(var, "ca_cert") == 0) { + os_free(cred->ca_cert); + cred->ca_cert = val; + return 0; + } + + if (os_strcmp(var, "client_cert") == 0) { + os_free(cred->client_cert); + cred->client_cert = val; + return 0; + } + + if (os_strcmp(var, "private_key") == 0) { + os_free(cred->private_key); + cred->private_key = val; + return 0; + } + + if (os_strcmp(var, "private_key_passwd") == 0) { + os_free(cred->private_key_passwd); + cred->private_key_passwd = val; + return 0; + } + + if (os_strcmp(var, "imsi") == 0) { + os_free(cred->imsi); + cred->imsi = val; + return 0; + } + + if (os_strcmp(var, "milenage") == 0) { + os_free(cred->milenage); + cred->milenage = val; + return 0; + } + + if (os_strcmp(var, "domain") == 0) { + os_free(cred->domain); + cred->domain = val; + return 0; + } + + if (os_strcmp(var, "phase1") == 0) { + os_free(cred->phase1); + cred->phase1 = val; + return 0; + } + + if (os_strcmp(var, "phase2") == 0) { + os_free(cred->phase2); + cred->phase2 = val; + return 0; + } + + if (os_strcmp(var, "roaming_consortium") == 0) { + if (len < 3 || len > sizeof(cred->roaming_consortium)) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "roaming_consortium length %d (3..15 " + "expected)", line, (int) len); + os_free(val); + return -1; + } + os_memcpy(cred->roaming_consortium, val, len); + cred->roaming_consortium_len = len; + os_free(val); + return 0; + } + + if (os_strcmp(var, "excluded_ssid") == 0) { + struct excluded_ssid *e; + + if (len > MAX_SSID_LEN) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "excluded_ssid length %d", line, (int) len); + os_free(val); + return -1; + } + + e = os_realloc_array(cred->excluded_ssid, + cred->num_excluded_ssid + 1, + sizeof(struct excluded_ssid)); + if (e == NULL) { + os_free(val); + return -1; + } + cred->excluded_ssid = e; + + e = &cred->excluded_ssid[cred->num_excluded_ssid++]; + os_memcpy(e->ssid, val, len); + e->ssid_len = len; + + os_free(val); + + return 0; + } + + if (line) { + wpa_printf(MSG_ERROR, "Line %d: unknown cred field '%s'.", + line, var); + } + + os_free(val); + + return -1; +} + + +struct wpa_cred * wpa_config_get_cred(struct wpa_config *config, int id) +{ + struct wpa_cred *cred; + + cred = config->cred; + while (cred) { + if (id == cred->id) + break; + cred = cred->next; + } + + return cred; +} + + +struct wpa_cred * wpa_config_add_cred(struct wpa_config *config) +{ + int id; + struct wpa_cred *cred, *last = NULL; + + id = -1; + cred = config->cred; + while (cred) { + if (cred->id > id) + id = cred->id; + last = cred; + cred = cred->next; + } + id++; + + cred = os_zalloc(sizeof(*cred)); + if (cred == NULL) + return NULL; + cred->id = id; + if (last) + last->next = cred; + else + config->cred = cred; + + return cred; +} + + +int wpa_config_remove_cred(struct wpa_config *config, int id) +{ + struct wpa_cred *cred, *prev = NULL; + + cred = config->cred; + while (cred) { + if (id == cred->id) + break; + prev = cred; + cred = cred->next; + } + + if (cred == NULL) + return -1; + + if (prev) + prev->next = cred->next; + else + config->cred = cred->next; + + wpa_config_free_cred(cred); + return 0; +} + + #ifndef CONFIG_NO_CONFIG_BLOBS /** * wpa_config_get_blob - Get a named configuration blob @@ -2124,6 +2612,15 @@ struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface, const char *driver_param) { struct wpa_config *config; + const int aCWmin = 4, aCWmax = 10; + const struct hostapd_wmm_ac_params ac_bk = + { aCWmin, aCWmax, 7, 0, 0 }; /* background traffic */ + const struct hostapd_wmm_ac_params ac_be = + { aCWmin, aCWmax, 3, 0, 0 }; /* best effort traffic */ + const struct hostapd_wmm_ac_params ac_vi = /* video traffic */ + { aCWmin - 1, aCWmin, 2, 3000 / 32, 0 }; + const struct hostapd_wmm_ac_params ac_vo = /* voice traffic */ + { aCWmin - 2, aCWmin - 1, 2, 1500 / 32, 0 }; config = os_zalloc(sizeof(*config)); if (config == NULL) @@ -2131,7 +2628,18 @@ 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->p2p_go_max_inactivity = DEFAULT_P2P_GO_MAX_INACTIVITY; config->bss_max_count = DEFAULT_BSS_MAX_COUNT; + config->bss_expiration_age = DEFAULT_BSS_EXPIRATION_AGE; + config->bss_expiration_scan_count = DEFAULT_BSS_EXPIRATION_SCAN_COUNT; + config->max_num_sta = DEFAULT_MAX_NUM_STA; + config->access_network_type = DEFAULT_ACCESS_NETWORK_TYPE; + config->wmm_ac_params[0] = ac_be; + config->wmm_ac_params[1] = ac_bk; + config->wmm_ac_params[2] = ac_vi; + config->wmm_ac_params[3] = ac_vo; if (ctrl_interface) config->ctrl_interface = os_strdup(ctrl_interface); @@ -2165,3 +2673,462 @@ 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_global_config_parse_bin(const struct global_parse_data *data, + struct wpa_config *config, int line, + const char *pos) +{ + size_t len; + struct wpabuf **dst, *tmp; + + len = os_strlen(pos); + if (len & 0x01) + return -1; + + tmp = wpabuf_alloc(len / 2); + if (tmp == NULL) + return -1; + + if (hexstr2bin(pos, wpabuf_put(tmp, len / 2), len / 2)) { + wpabuf_free(tmp); + return -1; + } + + dst = (struct wpabuf **) (((u8 *) config) + (long) data->param1); + wpabuf_free(*dst); + *dst = tmp; + wpa_printf(MSG_DEBUG, "%s", data->name); + + return 0; +} + + +static int wpa_config_process_country(const struct global_parse_data *data, + struct wpa_config *config, int line, + const char *pos) +{ + if (!pos[0] || !pos[1]) { + wpa_printf(MSG_DEBUG, "Invalid country set"); + return -1; + } + config->country[0] = pos[0]; + config->country[1] = pos[1]; + wpa_printf(MSG_DEBUG, "country='%c%c'", + config->country[0], config->country[1]); + return 0; +} + + +static int wpa_config_process_load_dynamic_eap( + const struct global_parse_data *data, struct wpa_config *config, + int line, const char *so) +{ + int ret; + wpa_printf(MSG_DEBUG, "load_dynamic_eap=%s", so); + ret = eap_peer_method_load(so); + if (ret == -2) { + wpa_printf(MSG_DEBUG, "This EAP type was already loaded - not " + "reloading."); + } else if (ret) { + wpa_printf(MSG_ERROR, "Line %d: Failed to load dynamic EAP " + "method '%s'.", line, so); + return -1; + } + + return 0; +} + + +#ifdef CONFIG_WPS + +static int wpa_config_process_uuid(const struct global_parse_data *data, + struct wpa_config *config, int line, + const char *pos) +{ + char buf[40]; + if (uuid_str2bin(pos, config->uuid)) { + wpa_printf(MSG_ERROR, "Line %d: invalid UUID", line); + return -1; + } + uuid_bin2str(config->uuid, buf, sizeof(buf)); + wpa_printf(MSG_DEBUG, "uuid=%s", buf); + return 0; +} + + +static int wpa_config_process_device_type( + const struct global_parse_data *data, + struct wpa_config *config, int line, const char *pos) +{ + return wps_dev_type_str2bin(pos, config->device_type); +} + + +static int wpa_config_process_os_version(const struct global_parse_data *data, + struct wpa_config *config, int line, + const char *pos) +{ + if (hexstr2bin(pos, config->os_version, 4)) { + wpa_printf(MSG_ERROR, "Line %d: invalid os_version", line); + return -1; + } + wpa_printf(MSG_DEBUG, "os_version=%08x", + WPA_GET_BE32(config->os_version)); + return 0; +} + + +static int wpa_config_process_wps_vendor_ext_m1( + const struct global_parse_data *data, + struct wpa_config *config, int line, const char *pos) +{ + struct wpabuf *tmp; + int len = os_strlen(pos) / 2; + u8 *p; + + if (!len) { + wpa_printf(MSG_ERROR, "Line %d: " + "invalid wps_vendor_ext_m1", line); + return -1; + } + + tmp = wpabuf_alloc(len); + if (tmp) { + p = wpabuf_put(tmp, len); + + if (hexstr2bin(pos, p, len)) { + wpa_printf(MSG_ERROR, "Line %d: " + "invalid wps_vendor_ext_m1", line); + wpabuf_free(tmp); + return -1; + } + + wpabuf_free(config->wps_vendor_ext_m1); + config->wps_vendor_ext_m1 = tmp; + } else { + wpa_printf(MSG_ERROR, "Can not allocate " + "memory for wps_vendor_ext_m1"); + return -1; + } + + return 0; +} + +#endif /* CONFIG_WPS */ + +#ifdef CONFIG_P2P +static int wpa_config_process_sec_device_type( + const struct global_parse_data *data, + struct wpa_config *config, int line, const char *pos) +{ + int idx; + + if (config->num_sec_device_types >= MAX_SEC_DEVICE_TYPES) { + wpa_printf(MSG_ERROR, "Line %d: too many sec_device_type " + "items", line); + return -1; + } + + idx = config->num_sec_device_types; + + if (wps_dev_type_str2bin(pos, config->sec_device_type[idx])) + return -1; + + config->num_sec_device_types++; + return 0; +} + + +static int wpa_config_process_p2p_pref_chan( + const struct global_parse_data *data, + struct wpa_config *config, int line, const char *pos) +{ + struct p2p_channel *pref = NULL, *n; + unsigned int num = 0; + const char *pos2; + u8 op_class, chan; + + /* format: class:chan,class:chan,... */ + + while (*pos) { + op_class = atoi(pos); + pos2 = os_strchr(pos, ':'); + if (pos2 == NULL) + goto fail; + pos2++; + chan = atoi(pos2); + + n = os_realloc_array(pref, num + 1, + sizeof(struct p2p_channel)); + if (n == NULL) + goto fail; + pref = n; + pref[num].op_class = op_class; + pref[num].chan = chan; + num++; + + pos = os_strchr(pos2, ','); + if (pos == NULL) + break; + pos++; + } + + os_free(config->p2p_pref_chan); + config->p2p_pref_chan = pref; + config->num_p2p_pref_chan = num; + wpa_hexdump(MSG_DEBUG, "P2P: Preferred class/channel pairs", + (u8 *) config->p2p_pref_chan, + config->num_p2p_pref_chan * sizeof(struct p2p_channel)); + + return 0; + +fail: + os_free(pref); + wpa_printf(MSG_ERROR, "Line %d: Invalid p2p_pref_chan list", line); + return -1; +} +#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 +#define BIN(f) #f, wpa_global_config_parse_bin, OFFSET(f), NULL, NULL + +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(disable_scan_offload), 0 }, + { INT(fast_reauth), 0 }, + { STR(opensc_engine_path), 0 }, + { STR(pkcs11_engine_path), 0 }, + { STR(pkcs11_module_path), 0 }, + { STR(pcsc_reader), 0 }, + { STR(pcsc_pin), 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 }, + { FUNC(wps_vendor_ext_m1), CFG_CHANGED_VENDOR_EXTENSION }, +#endif /* CONFIG_WPS */ +#ifdef CONFIG_P2P + { FUNC(sec_device_type), CFG_CHANGED_SEC_DEVICE_TYPE }, + { INT(p2p_listen_reg_class), 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 }, + { INT(p2p_go_ht40), 0 }, + { INT(p2p_disabled), 0 }, + { INT(p2p_no_group_iface), 0 }, +#endif /* CONFIG_P2P */ + { FUNC(country), CFG_CHANGED_COUNTRY }, + { INT(bss_max_count), 0 }, + { INT(bss_expiration_age), 0 }, + { INT(bss_expiration_scan_count), 0 }, + { INT_RANGE(filter_ssids, 0, 1), 0 }, + { INT_RANGE(filter_rssi, -100, 0), 0 }, + { INT(max_num_sta), 0 }, + { INT_RANGE(disassoc_low_ack, 0, 1), 0 }, +#ifdef CONFIG_HS20 + { INT_RANGE(hs20, 0, 1), 0 }, +#endif /* CONFIG_HS20 */ + { INT_RANGE(interworking, 0, 1), 0 }, + { FUNC(hessid), 0 }, + { INT_RANGE(access_network_type, 0, 15), 0 }, + { INT_RANGE(pbc_in_m1, 0, 1), 0 }, + { STR(autoscan), 0 }, + { INT_RANGE(wps_nfc_dev_pw_id, 0x10, 0xffff), 0 }, + { BIN(wps_nfc_dh_pubkey), 0 }, + { BIN(wps_nfc_dh_privkey), 0 }, + { BIN(wps_nfc_dev_pw), 0 }, + { STR(ext_password_backend), CFG_CHANGED_EXT_PW_BACKEND }, + { INT(p2p_go_max_inactivity), 0 }, + { INT_RANGE(auto_interworking, 0, 1), 0 }, + { INT(okc), 0 }, + { INT(pmf), 0 }, +}; + +#undef FUNC +#undef _INT +#undef INT +#undef INT_RANGE +#undef _STR +#undef STR +#undef STR_RANGE +#undef BIN +#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) { +#ifdef CONFIG_AP + if (os_strncmp(pos, "wmm_ac_", 7) == 0) { + char *tmp = os_strchr(pos, '='); + if (tmp == NULL) { + if (line < 0) + return -1; + wpa_printf(MSG_ERROR, "Line %d: invalid line " + "'%s'", line, pos); + return -1; + } + *tmp++ = '\0'; + if (hostapd_config_wmm_ac(config->wmm_ac_params, pos, + tmp)) { + wpa_printf(MSG_ERROR, "Line %d: invalid WMM " + "AC item", line); + return -1; + } + } +#endif /* CONFIG_AP */ + if (line < 0) + return -1; + wpa_printf(MSG_ERROR, "Line %d: unknown global field '%s'.", + line, pos); + ret = -1; + } + + return ret; +} diff --git a/contrib/wpa/wpa_supplicant/config.h b/contrib/wpa/wpa_supplicant/config.h index 754e4be..bb70b9c 100644 --- a/contrib/wpa/wpa_supplicant/config.h +++ b/contrib/wpa/wpa_supplicant/config.h @@ -1,15 +1,9 @@ /* * WPA Supplicant / Configuration file structures - * Copyright (c) 2003-2005, 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 - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef CONFIG_H @@ -22,10 +16,210 @@ #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_P2P_GO_MAX_INACTIVITY (5 * 60) #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" +#include "common/ieee802_11_common.h" + + +struct wpa_cred { + /** + * next - Next credential in the list + * + * This pointer can be used to iterate over all credentials. The head + * of this list is stored in the cred field of struct wpa_config. + */ + struct wpa_cred *next; + + /** + * id - Unique id for the credential + * + * This identifier is used as a unique identifier for each credential + * block when using the control interface. Each credential is allocated + * an id when it is being created, either when reading the + * configuration file or when a new credential is added through the + * control interface. + */ + int id; + + /** + * priority - Priority group + * + * By default, all networks and credentials get the same priority group + * (0). This field can be used to give higher priority for credentials + * (and similarly in struct wpa_ssid for network blocks) to change the + * Interworking automatic networking selection behavior. The matching + * network (based on either an enabled network block or a credential) + * with the highest priority value will be selected. + */ + int priority; + + /** + * pcsc - Use PC/SC and SIM/USIM card + */ + int pcsc; + + /** + * realm - Home Realm for Interworking + */ + char *realm; + + /** + * username - Username for Interworking network selection + */ + char *username; + + /** + * password - Password for Interworking network selection + */ + char *password; + + /** + * ext_password - Whether password is a name for external storage + */ + int ext_password; + + /** + * ca_cert - CA certificate for Interworking network selection + */ + char *ca_cert; + + /** + * client_cert - File path to client certificate file (PEM/DER) + * + * This field is used with Interworking networking selection for a case + * where client certificate/private key is used for authentication + * (EAP-TLS). Full path to the file should be used since working + * directory may change when wpa_supplicant is run in the background. + * + * Alternatively, a named configuration blob can be used by setting + * this to blob://blob_name. + */ + char *client_cert; + /** + * private_key - File path to client private key file (PEM/DER/PFX) + * + * When PKCS#12/PFX file (.p12/.pfx) is used, client_cert should be + * commented out. Both the private key and certificate will be read + * from the PKCS#12 file in this case. Full path to the file should be + * used since working directory may change when wpa_supplicant is run + * in the background. + * + * Windows certificate store can be used by leaving client_cert out and + * configuring private_key in one of the following formats: + * + * cert://substring_to_match + * + * hash://certificate_thumbprint_in_hex + * + * For example: private_key="hash://63093aa9c47f56ae88334c7b65a4" + * + * Note that when running wpa_supplicant as an application, the user + * certificate store (My user account) is used, whereas computer store + * (Computer account) is used when running wpasvc as a service. + * + * Alternatively, a named configuration blob can be used by setting + * this to blob://blob_name. + */ + char *private_key; + + /** + * private_key_passwd - Password for private key file + */ + char *private_key_passwd; + + /** + * imsi - IMSI in <MCC> | <MNC> | '-' | <MSIN> format + */ + char *imsi; + + /** + * milenage - Milenage parameters for SIM/USIM simulator in + * <Ki>:<OPc>:<SQN> format + */ + char *milenage; + + /** + * domain - Home service provider FQDN + * + * This is used to compare against the Domain Name List to figure out + * whether the AP is operated by the Home SP. + */ + char *domain; + + /** + * roaming_consortium - Roaming Consortium OI + * + * If roaming_consortium_len is non-zero, this field contains the + * Roaming Consortium OI that can be used to determine which access + * points support authentication with this credential. This is an + * alternative to the use of the realm parameter. When using Roaming + * Consortium to match the network, the EAP parameters need to be + * pre-configured with the credential since the NAI Realm information + * may not be available or fetched. + */ + u8 roaming_consortium[15]; + + /** + * roaming_consortium_len - Length of roaming_consortium + */ + size_t roaming_consortium_len; + + /** + * eap_method - EAP method to use + * + * Pre-configured EAP method to use with this credential or %NULL to + * indicate no EAP method is selected, i.e., the method will be + * selected automatically based on ANQP information. + */ + struct eap_method_type *eap_method; + + /** + * phase1 - Phase 1 (outer authentication) parameters + * + * Pre-configured EAP parameters or %NULL. + */ + char *phase1; + + /** + * phase2 - Phase 2 (inner authentication) parameters + * + * Pre-configured EAP parameters or %NULL. + */ + char *phase2; + + struct excluded_ssid { + u8 ssid[MAX_SSID_LEN]; + size_t ssid_len; + } *excluded_ssid; + size_t num_excluded_ssid; +}; + + +#define CFG_CHANGED_DEVICE_NAME BIT(0) +#define CFG_CHANGED_CONFIG_METHODS BIT(1) +#define CFG_CHANGED_DEVICE_TYPE BIT(2) +#define CFG_CHANGED_OS_VERSION BIT(3) +#define CFG_CHANGED_UUID BIT(4) +#define CFG_CHANGED_COUNTRY BIT(5) +#define CFG_CHANGED_SEC_DEVICE_TYPE BIT(6) +#define CFG_CHANGED_P2P_SSID_POSTFIX BIT(7) +#define CFG_CHANGED_WPS_STRING BIT(8) +#define CFG_CHANGED_P2P_INTRA_BSS BIT(9) +#define CFG_CHANGED_VENDOR_EXTENSION BIT(10) +#define CFG_CHANGED_P2P_LISTEN_CHANNEL BIT(11) +#define CFG_CHANGED_P2P_OPER_CHANNEL BIT(12) +#define CFG_CHANGED_P2P_PREF_CHAN BIT(13) +#define CFG_CHANGED_EXT_PW_BACKEND BIT(14) /** * struct wpa_config - wpa_supplicant configuration data @@ -57,6 +251,13 @@ struct wpa_config { int num_prio; /** + * cred - Head of the credential list + * + * This is the head for the list of all the configured credentials. + */ + struct wpa_cred *cred; + + /** * eapol_version - IEEE 802.1X/EAPOL version number * * wpa_supplicant is implemented based on IEEE Std 802.1X-2004 which @@ -98,12 +299,21 @@ struct wpa_config { int ap_scan; /** + * disable_scan_offload - Disable automatic offloading of scan requests + * + * By default, %wpa_supplicant tries to offload scanning if the driver + * indicates support for this (sched_scan). This configuration + * parameter can be used to disable this offloading mechanism. + */ + int disable_scan_offload; + + /** * ctrl_interface - Parameters for the control interface * * If this is specified, %wpa_supplicant will open a control interface * that is available for external programs to manage %wpa_supplicant. * The meaning of this string depends on which control interface - * mechanism is used. For all cases, the 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. * @@ -196,6 +406,23 @@ struct wpa_config { char *pkcs11_module_path; /** + * pcsc_reader - PC/SC reader name prefix + * + * If not %NULL, PC/SC reader with a name that matches this prefix is + * initialized for SIM/USIM access. Empty string can be used to match + * the first available reader. + */ + char *pcsc_reader; + + /** + * pcsc_pin - PIN for USIM, GSM SIM, and smartcards + * + * This field is used to configure PIN for SIM/USIM for EAP-SIM and + * EAP-AKA. If left out, this will be asked through control interface. + */ + char *pcsc_pin; + + /** * driver_param - Driver interface parameters * * This text string is passed to the selected driver interface with the @@ -285,26 +512,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 +553,250 @@ 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; + + struct wpabuf *wps_vendor_ext_m1; + +#define MAX_WPS_VENDOR_EXT 10 + /** + * wps_vendor_ext - Vendor extension attributes in WPS + */ + struct wpabuf *wps_vendor_ext[MAX_WPS_VENDOR_EXT]; + + /** + * p2p_group_idle - Maximum idle time in seconds for P2P group + * + * This value controls how long a P2P group is maintained after there + * is no other members in the group. As a GO, this means no associated + * stations in the group. As a P2P client, this means no GO seen in + * scan results. The maximum idle time is specified in seconds with 0 + * indicating no time limit, i.e., the P2P group remains in active + * state indefinitely until explicitly removed. As a P2P client, the + * maximum idle time of P2P_MAX_CLIENT_IDLE seconds is enforced, i.e., + * this parameter is mainly meant for GO use and for P2P client, it can + * only be used to reduce the default timeout to smaller value. A + * special value -1 can be used to configure immediate removal of the + * group for P2P client role on any disconnection after the data + * connection has been established. + */ + int p2p_group_idle; + /** * bss_max_count - Maximum number of BSS entries to keep in memory */ unsigned int bss_max_count; /** + * bss_expiration_age - BSS entry age after which it can be expired + * + * This value controls the time in seconds after which a BSS entry + * gets removed if it has not been updated or is not in use. + */ + unsigned int bss_expiration_age; + + /** + * bss_expiration_scan_count - Expire BSS after number of scans + * + * If the BSS entry has not been seen in this many scans, it will be + * removed. A value of 1 means that entry is removed after the first + * scan in which the BSSID is not seen. Larger values can be used + * to avoid BSS entries disappearing if they are not visible in + * every scan (e.g., low signal quality or interference). + */ + unsigned int bss_expiration_scan_count; + + /** * filter_ssids - SSID-based scan result filtering * * 0 = do not filter scan results * 1 = only include configured SSIDs in scan results/BSS table */ int filter_ssids; + + /** + * filter_rssi - RSSI-based scan result filtering + * + * 0 = do not filter scan results + * -n = filter scan results below -n dBm + */ + int filter_rssi; + + /** + * max_num_sta - Maximum number of STAs in an AP/P2P GO + */ + unsigned int max_num_sta; + + /** + * changed_parameters - Bitmap of changed parameters since last update + */ + unsigned int changed_parameters; + + /** + * disassoc_low_ack - Disassocicate stations with massive packet loss + */ + int disassoc_low_ack; + + /** + * interworking - Whether Interworking (IEEE 802.11u) is enabled + */ + int interworking; + + /** + * access_network_type - Access Network Type + * + * When Interworking is enabled, scans will be limited to APs that + * advertise the specified Access Network Type (0..15; with 15 + * indicating wildcard match). + */ + int access_network_type; + + /** + * hessid - Homogenous ESS identifier + * + * If this is set (any octet is non-zero), scans will be used to + * request response only from BSSes belonging to the specified + * Homogeneous ESS. This is used only if interworking is enabled. + */ + u8 hessid[ETH_ALEN]; + + /** + * hs20 - Hotspot 2.0 + */ + int hs20; + + /** + * pbc_in_m1 - AP mode WPS probing workaround for PBC with Windows 7 + * + * Windows 7 uses incorrect way of figuring out AP's WPS capabilities + * by acting as a Registrar and using M1 from the AP. The config + * methods attribute in that message is supposed to indicate only the + * configuration method supported by the AP in Enrollee role, i.e., to + * add an external Registrar. For that case, PBC shall not be used and + * as such, the PushButton config method is removed from M1 by default. + * If pbc_in_m1=1 is included in the configuration file, the PushButton + * config method is left in M1 (if included in config_methods + * parameter) to allow Windows 7 to use PBC instead of PIN (e.g., from + * a label in the AP). + */ + int pbc_in_m1; + + /** + * autoscan - Automatic scan parameters or %NULL if none + * + * This is an optional set of parameters for automatic scanning + * within an interface in following format: + * <autoscan module name>:<module parameters> + */ + char *autoscan; + + /** + * wps_nfc_dev_pw_id - NFC Device Password ID for password token + */ + int wps_nfc_dev_pw_id; + + /** + * wps_nfc_dh_pubkey - NFC DH Public Key for password token + */ + struct wpabuf *wps_nfc_dh_pubkey; + + /** + * wps_nfc_dh_privkey - NFC DH Private Key for password token + */ + struct wpabuf *wps_nfc_dh_privkey; + + /** + * wps_nfc_dev_pw - NFC Device Password for password token + */ + struct wpabuf *wps_nfc_dev_pw; + + /** + * ext_password_backend - External password backend or %NULL if none + * + * format: <backend name>[:<optional backend parameters>] + */ + char *ext_password_backend; + + /* + * p2p_go_max_inactivity - Timeout in seconds to detect STA inactivity + * + * This timeout value is used in P2P GO mode to clean up + * inactive stations. + * By default: 300 seconds. + */ + int p2p_go_max_inactivity; + + struct hostapd_wmm_ac_params wmm_ac_params[4]; + + /** + * auto_interworking - Whether to use network selection automatically + * + * 0 = do not automatically go through Interworking network selection + * (i.e., require explicit interworking_select command for this) + * 1 = perform Interworking network selection if one or more + * credentials have been configured and scan did not find a + * matching network block + */ + int auto_interworking; + + /** + * p2p_go_ht40 - Default mode for HT40 enable when operating as GO. + * + * This will take effect for p2p_group_add, p2p_connect, and p2p_invite. + * Note that regulatory constraints and driver capabilities are + * consulted anyway, so setting it to 1 can't do real harm. + * By default: 0 (disabled) + */ + int p2p_go_ht40; + + /** + * p2p_disabled - Whether P2P operations are disabled for this interface + */ + int p2p_disabled; + + /** + * p2p_no_group_iface - Whether group interfaces can be used + * + * By default, wpa_supplicant will create a separate interface for P2P + * group operations if the driver supports this. This functionality can + * be disabled by setting this parameter to 1. In that case, the same + * interface that was used for the P2P management operations is used + * also for the group operation. + */ + int p2p_no_group_iface; + + /** + * okc - Whether to enable opportunistic key caching by default + * + * By default, OKC is disabled unless enabled by the per-network + * proactive_key_caching=1 parameter. okc=1 can be used to change this + * default behavior. + */ + int okc; + + /** + * pmf - Whether to enable/require PMF by default + * + * By default, PMF is disabled unless enabled by the per-network + * ieee80211w=1 or ieee80211w=2 parameter. pmf=1/2 can be used to change + * this default behavior. + */ + enum mfp_options pmf; }; @@ -352,12 +804,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); @@ -372,6 +829,13 @@ void wpa_config_set_blob(struct wpa_config *config, void wpa_config_free_blob(struct wpa_config_blob *blob); int wpa_config_remove_blob(struct wpa_config *config, const char *name); +struct wpa_cred * wpa_config_get_cred(struct wpa_config *config, int id); +struct wpa_cred * wpa_config_add_cred(struct wpa_config *config); +int wpa_config_remove_cred(struct wpa_config *config, int id); +void wpa_config_free_cred(struct wpa_cred *cred); +int wpa_config_set_cred(struct wpa_cred *cred, const char *var, + const char *value, int line); + struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface, const char *driver_param); #ifndef CONFIG_NO_STDOUT_DEBUG @@ -381,6 +845,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 */ /** diff --git a/contrib/wpa/wpa_supplicant/config_file.c b/contrib/wpa/wpa_supplicant/config_file.c index 5f07045..8f32cc8 100644 --- a/contrib/wpa/wpa_supplicant/config_file.c +++ b/contrib/wpa/wpa_supplicant/config_file.c @@ -1,15 +1,9 @@ /* * WPA Supplicant / Configuration backend: text file - * Copyright (c) 2003-2008, 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 - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This file implements a configuration backend for text files. All the * configuration information is stored in a text file that uses a format @@ -22,7 +16,32 @@ #include "config.h" #include "base64.h" #include "uuid.h" +#include "p2p/p2p.h" #include "eap_peer/eap_methods.h" +#include "eap_peer/eap.h" + + +static int newline_terminated(const char *buf, size_t buflen) +{ + size_t len = os_strlen(buf); + if (len == 0) + return 0; + if (len == buflen - 1 && buf[buflen - 1] != '\r' && + buf[len - 1] != '\n') + return 0; + return 1; +} + + +static void skip_line_end(FILE *stream) +{ + char buf[100]; + while (fgets(buf, sizeof(buf), stream)) { + buf[sizeof(buf) - 1] = '\0'; + if (newline_terminated(buf, sizeof(buf))) + return; + } +} /** @@ -47,6 +66,15 @@ static char * wpa_config_get_line(char *s, int size, FILE *stream, int *line, while (fgets(s, size, stream)) { (*line)++; s[size - 1] = '\0'; + if (!newline_terminated(s, size)) { + /* + * The line was truncated - skip rest of it to avoid + * confusing error messages. + */ + wpa_printf(MSG_INFO, "Long line in configuration file " + "truncated"); + skip_line_end(stream); + } pos = s; /* Skip white space from the beginning of line. */ @@ -105,14 +133,6 @@ static int wpa_config_validate_network(struct wpa_ssid *ssid, int line) wpa_config_update_psk(ssid); } - if ((ssid->key_mgmt & (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_FT_PSK | - WPA_KEY_MGMT_PSK_SHA256)) && - !ssid->psk_set) { - wpa_printf(MSG_ERROR, "Line %d: WPA-PSK accepted for key " - "management, but no PSK configured.", line); - errors++; - } - if ((ssid->group_cipher & WPA_CIPHER_CCMP) && !(ssid->pairwise_cipher & WPA_CIPHER_CCMP) && !(ssid->pairwise_cipher & WPA_CIPHER_NONE)) { @@ -131,7 +151,7 @@ static struct wpa_ssid * wpa_config_read_network(FILE *f, int *line, int id) { struct wpa_ssid *ssid; int errors = 0, end = 0; - char buf[256], *pos, *pos2; + char buf[2000], *pos, *pos2; wpa_printf(MSG_MSGDUMP, "Line: %d - start of a new network block", *line); @@ -187,6 +207,61 @@ static struct wpa_ssid * wpa_config_read_network(FILE *f, int *line, int id) } +static struct wpa_cred * wpa_config_read_cred(FILE *f, int *line, int id) +{ + struct wpa_cred *cred; + int errors = 0, end = 0; + char buf[256], *pos, *pos2; + + wpa_printf(MSG_MSGDUMP, "Line: %d - start of a new cred block", *line); + cred = os_zalloc(sizeof(*cred)); + if (cred == NULL) + return NULL; + cred->id = id; + + while (wpa_config_get_line(buf, sizeof(buf), f, line, &pos)) { + if (os_strcmp(pos, "}") == 0) { + end = 1; + break; + } + + pos2 = os_strchr(pos, '='); + if (pos2 == NULL) { + wpa_printf(MSG_ERROR, "Line %d: Invalid cred line " + "'%s'.", *line, pos); + errors++; + continue; + } + + *pos2++ = '\0'; + if (*pos2 == '"') { + if (os_strchr(pos2 + 1, '"') == NULL) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "quotation '%s'.", *line, pos2); + errors++; + continue; + } + } + + if (wpa_config_set_cred(cred, pos, pos2, *line) < 0) + errors++; + } + + if (!end) { + wpa_printf(MSG_ERROR, "Line %d: cred block was not " + "terminated properly.", *line); + errors++; + } + + if (errors) { + wpa_config_free_cred(cred); + cred = NULL; + } + + return cred; +} + + #ifndef CONFIG_NO_CONFIG_BLOBS static struct wpa_config_blob * wpa_config_read_blob(FILE *f, int *line, const char *name) @@ -270,253 +345,29 @@ 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; - char buf[256], *pos; + char buf[512], *pos; int errors = 0, line = 0; struct wpa_ssid *ssid, *tail = NULL, *head = NULL; + struct wpa_cred *cred, *cred_tail = NULL, *cred_head = NULL; struct wpa_config *config; int id = 0; + int cred_id = 0; config = wpa_config_alloc_empty(NULL, NULL); - if (config == NULL) + if (config == NULL) { + wpa_printf(MSG_ERROR, "Failed to allocate config file " + "structure"); return NULL; + } + wpa_printf(MSG_DEBUG, "Reading configuration file '%s'", name); f = fopen(name, "r"); if (f == NULL) { + wpa_printf(MSG_ERROR, "Failed to open config file '%s', " + "error: %s", name, strerror(errno)); os_free(config); return NULL; } @@ -543,10 +394,26 @@ struct wpa_config * wpa_config_read(const char *name) errors++; continue; } + } else if (os_strcmp(pos, "cred={") == 0) { + cred = wpa_config_read_cred(f, &line, cred_id++); + if (cred == NULL) { + wpa_printf(MSG_ERROR, "Line %d: failed to " + "parse cred block.", line); + errors++; + continue; + } + if (cred_head == NULL) { + cred_head = cred_tail = cred; + } else { + cred_tail->next = cred; + cred_tail = cred; + } #ifndef CONFIG_NO_CONFIG_BLOBS } else if (os_strncmp(pos, "blob-base64-", 12) == 0) { if (wpa_config_process_blob(config, f, &line, pos + 12) < 0) { + wpa_printf(MSG_ERROR, "Line %d: failed to " + "process blob.", line); errors++; continue; } @@ -563,12 +430,15 @@ struct wpa_config * wpa_config_read(const char *name) config->ssid = head; wpa_config_debug_dump_networks(config); + config->cred = cred_head; +#ifndef WPA_IGNORE_CONFIG_ERRORS if (errors) { wpa_config_free(config); config = NULL; head = NULL; } +#endif /* WPA_IGNORE_CONFIG_ERRORS */ return config; } @@ -726,6 +596,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; @@ -742,9 +624,12 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) write_psk(f, ssid); write_proto(f, ssid); write_key_mgmt(f, ssid); + INT_DEF(bg_scan_period, DEFAULT_BG_SCAN_PERIOD); write_pairwise(f, ssid); write_group(f, ssid); write_auth_alg(f, ssid); + STR(bgscan); + STR(autoscan); #ifdef IEEE8021X_EAPOL write_eap(f, ssid); STR(identity); @@ -793,13 +678,18 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) INT_DEFe(fragment_size, DEFAULT_FRAGMENT_SIZE); #endif /* IEEE8021X_EAPOL */ INT(mode); - INT(proactive_key_caching); + INT(frequency); + write_int(f, "proactive_key_caching", ssid->proactive_key_caching, -1); INT(disabled); INT(peerkey); #ifdef CONFIG_IEEE80211W - INT(ieee80211w); + write_int(f, "ieee80211w", ssid->ieee80211w, + MGMT_FRAME_PROTECTION_DEFAULT); #endif /* CONFIG_IEEE80211W */ STR(id_str); +#ifdef CONFIG_P2P + write_p2p_client_list(f, ssid); +#endif /* CONFIG_P2P */ #undef STR #undef INT @@ -807,6 +697,65 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) } +static void wpa_config_write_cred(FILE *f, struct wpa_cred *cred) +{ + if (cred->priority) + fprintf(f, "\tpriority=%d\n", cred->priority); + if (cred->pcsc) + fprintf(f, "\tpcsc=%d\n", cred->pcsc); + if (cred->realm) + fprintf(f, "\trealm=\"%s\"\n", cred->realm); + if (cred->username) + fprintf(f, "\tusername=\"%s\"\n", cred->username); + if (cred->password && cred->ext_password) + fprintf(f, "\tpassword=ext:%s\n", cred->password); + else if (cred->password) + fprintf(f, "\tpassword=\"%s\"\n", cred->password); + if (cred->ca_cert) + fprintf(f, "\tca_cert=\"%s\"\n", cred->ca_cert); + if (cred->client_cert) + fprintf(f, "\tclient_cert=\"%s\"\n", cred->client_cert); + if (cred->private_key) + fprintf(f, "\tprivate_key=\"%s\"\n", cred->private_key); + if (cred->private_key_passwd) + fprintf(f, "\tprivate_key_passwd=\"%s\"\n", + cred->private_key_passwd); + if (cred->imsi) + fprintf(f, "\timsi=\"%s\"\n", cred->imsi); + if (cred->milenage) + fprintf(f, "\tmilenage=\"%s\"\n", cred->milenage); + if (cred->domain) + fprintf(f, "\tdomain=\"%s\"\n", cred->domain); + if (cred->roaming_consortium_len) { + size_t i; + fprintf(f, "\troaming_consortium="); + for (i = 0; i < cred->roaming_consortium_len; i++) + fprintf(f, "%02x", cred->roaming_consortium[i]); + fprintf(f, "\n"); + } + if (cred->eap_method) { + const char *name; + name = eap_get_name(cred->eap_method[0].vendor, + cred->eap_method[0].method); + fprintf(f, "\teap=%s\n", name); + } + if (cred->phase1) + fprintf(f, "\tphase1=\"%s\"\n", cred->phase1); + if (cred->phase2) + fprintf(f, "\tphase2=\"%s\"\n", cred->phase2); + if (cred->excluded_ssid) { + size_t i, j; + for (i = 0; i < cred->num_excluded_ssid; i++) { + struct excluded_ssid *e = &cred->excluded_ssid[i]; + fprintf(f, "\texcluded_ssid="); + for (j = 0; j < e->ssid_len; j++) + fprintf(f, "%02x", e->ssid[j]); + fprintf(f, "\n"); + } + } +} + + #ifndef CONFIG_NO_CONFIG_BLOBS static int wpa_config_write_blob(FILE *f, struct wpa_config_blob *blob) { @@ -823,6 +772,23 @@ static int wpa_config_write_blob(FILE *f, struct wpa_config_blob *blob) #endif /* CONFIG_NO_CONFIG_BLOBS */ +static void write_global_bin(FILE *f, const char *field, + const struct wpabuf *val) +{ + size_t i; + const u8 *pos; + + if (val == NULL) + return; + + fprintf(f, "%s=", field); + pos = wpabuf_head(val); + for (i = 0; i < wpabuf_len(val); i++) + fprintf(f, "%02X", *pos++); + fprintf(f, "\n"); +} + + static void wpa_config_write_global(FILE *f, struct wpa_config *config) { #ifdef CONFIG_CTRL_IFACE @@ -836,6 +802,9 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) fprintf(f, "eapol_version=%d\n", config->eapol_version); if (config->ap_scan != DEFAULT_AP_SCAN) fprintf(f, "ap_scan=%d\n", config->ap_scan); + if (config->disable_scan_offload) + fprintf(f, "disable_scan_offload=%d\n", + config->disable_scan_offload); if (config->fast_reauth != DEFAULT_FAST_REAUTH) fprintf(f, "fast_reauth=%d\n", config->fast_reauth); if (config->opensc_engine_path) @@ -847,6 +816,10 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) if (config->pkcs11_module_path) fprintf(f, "pkcs11_module_path=%s\n", config->pkcs11_module_path); + if (config->pcsc_reader) + fprintf(f, "pcsc_reader=%s\n", config->pcsc_reader); + if (config->pcsc_pin) + fprintf(f, "pcsc_pin=%s\n", config->pcsc_pin); if (config->driver_param) fprintf(f, "driver_param=%s\n", config->driver_param); if (config->dot11RSNAConfigPMKLifetime) @@ -876,8 +849,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)); @@ -886,15 +864,112 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) if (config->wps_cred_processing) fprintf(f, "wps_cred_processing=%d\n", config->wps_cred_processing); + if (config->wps_vendor_ext_m1) { + int i, len = wpabuf_len(config->wps_vendor_ext_m1); + const u8 *p = wpabuf_head_u8(config->wps_vendor_ext_m1); + if (len > 0) { + fprintf(f, "wps_vendor_ext_m1="); + for (i = 0; i < len; i++) + fprintf(f, "%02x", *p++); + fprintf(f, "\n"); + } + } #endif /* CONFIG_WPS */ +#ifdef CONFIG_P2P + if (config->p2p_listen_reg_class) + fprintf(f, "p2p_listen_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"); + } + if (config->p2p_go_ht40) + fprintf(f, "p2p_go_ht40=%u\n", config->p2p_go_ht40); + if (config->p2p_disabled) + fprintf(f, "p2p_disabled=%u\n", config->p2p_disabled); + if (config->p2p_no_group_iface) + fprintf(f, "p2p_no_group_iface=%u\n", + config->p2p_no_group_iface); +#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_HS20 + if (config->hs20) + fprintf(f, "hs20=1\n"); +#endif /* CONFIG_HS20 */ +#ifdef CONFIG_INTERWORKING + 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 */ + if (config->pbc_in_m1) + fprintf(f, "pbc_in_m1=%u\n", config->pbc_in_m1); + if (config->wps_nfc_dev_pw_id) + fprintf(f, "wps_nfc_dev_pw_id=%d\n", + config->wps_nfc_dev_pw_id); + write_global_bin(f, "wps_nfc_dh_pubkey", config->wps_nfc_dh_pubkey); + write_global_bin(f, "wps_nfc_dh_privkey", config->wps_nfc_dh_privkey); + write_global_bin(f, "wps_nfc_dev_pw", config->wps_nfc_dev_pw); + + if (config->ext_password_backend) + fprintf(f, "ext_password_backend=%s\n", + config->ext_password_backend); + if (config->p2p_go_max_inactivity != DEFAULT_P2P_GO_MAX_INACTIVITY) + fprintf(f, "p2p_go_max_inactivity=%d\n", + config->p2p_go_max_inactivity); + if (config->auto_interworking) + fprintf(f, "auto_interworking=%d\n", + config->auto_interworking); + if (config->okc) + fprintf(f, "okc=%d\n", config->okc); + if (config->pmf) + fprintf(f, "pmf=%d\n", config->pmf); } #endif /* CONFIG_NO_CONFIG_WRITE */ @@ -905,6 +980,7 @@ int wpa_config_write(const char *name, struct wpa_config *config) #ifndef CONFIG_NO_CONFIG_WRITE FILE *f; struct wpa_ssid *ssid; + struct wpa_cred *cred; #ifndef CONFIG_NO_CONFIG_BLOBS struct wpa_config_blob *blob; #endif /* CONFIG_NO_CONFIG_BLOBS */ @@ -920,9 +996,18 @@ int wpa_config_write(const char *name, struct wpa_config *config) wpa_config_write_global(f, config); + for (cred = config->cred; cred; cred = cred->next) { + fprintf(f, "\ncred={\n"); + wpa_config_write_cred(f, cred); + fprintf(f, "}\n"); + } + for (ssid = config->ssid; ssid; ssid = ssid->next) { - if (ssid->key_mgmt == WPA_KEY_MGMT_WPS) - 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"); diff --git a/contrib/wpa/wpa_supplicant/config_none.c b/contrib/wpa/wpa_supplicant/config_none.c index 2e9ccc0..589ea36 100644 --- a/contrib/wpa/wpa_supplicant/config_none.c +++ b/contrib/wpa/wpa_supplicant/config_none.c @@ -2,14 +2,8 @@ * WPA Supplicant / Configuration backend: empty starting point * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This file implements dummy example of a configuration backend. None of the * functions are actually implemented so this can be used as a simple diff --git a/contrib/wpa/wpa_supplicant/config_ssid.h b/contrib/wpa/wpa_supplicant/config_ssid.h index 25e87aa..9ac67c7 100644 --- a/contrib/wpa/wpa_supplicant/config_ssid.h +++ b/contrib/wpa/wpa_supplicant/config_ssid.h @@ -2,14 +2,8 @@ * WPA Supplicant / Network configuration structures * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef CONFIG_SSID_H @@ -31,6 +25,14 @@ WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40) #define DEFAULT_FRAGMENT_SIZE 1398 +#define DEFAULT_BG_SCAN_PERIOD -1 +#define DEFAULT_DISABLE_HT 0 +#define DEFAULT_DISABLE_HT40 0 +#define DEFAULT_DISABLE_SGI 0 +#define DEFAULT_DISABLE_MAX_AMSDU -1 /* no change */ +#define DEFAULT_AMPDU_FACTOR -1 /* no change */ +#define DEFAULT_AMPDU_DENSITY -1 /* no change */ + /** * struct wpa_ssid - Network configuration data * @@ -109,6 +111,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]; @@ -137,6 +142,14 @@ struct wpa_ssid { char *passphrase; /** + * ext_psk - PSK/passphrase name in external storage + * + * If this is set, PSK/passphrase will be fetched from external storage + * when requesting association with the network. + */ + char *ext_psk; + + /** * pairwise_cipher - Bitfield of allowed pairwise ciphers, WPA_CIPHER_* */ int pairwise_cipher; @@ -154,6 +167,12 @@ struct wpa_ssid { int key_mgmt; /** + * bg_scan_period - Background scan period in seconds, 0 to disable, or + * -1 to indicate no change to default driver configuration + */ + int bg_scan_period; + + /** * proto - Bitfield of allowed protocols, WPA_PROTO_* */ int proto; @@ -210,13 +229,18 @@ struct wpa_ssid { * * This field can be used to enable proactive key caching which is also * known as opportunistic PMKSA caching for WPA2. This is disabled (0) - * by default. Enable by setting this to 1. + * by default unless default value is changed with the global okc=1 + * parameter. Enable by setting this to 1. * * Proactive key caching is used to make supplicant assume that the APs * are using the same PMK and generate PMKSA cache entries without * doing RSN pre-authentication. This requires support from the AP side * and is normally used with wireless switches that co-locate the * authenticator. + * + * Internally, special value -1 is used to indicate that the parameter + * was not specified in the configuration (i.e., default behavior is + * followed). */ int proactive_key_caching; @@ -273,6 +297,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 +313,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,10 +323,20 @@ 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; /** + * disabled_for_connect - Whether this network was temporarily disabled + * + * This flag is used to reenable all the temporarily disabled networks + * after either the success or failure of a WPS connection. + */ + int disabled_for_connect; + + /** * peerkey - Whether PeerKey handshake for direct links is allowed * * This is only used when both RSN/WPA2 and IEEE 802.11e (QoS) are @@ -321,6 +362,12 @@ struct wpa_ssid { * * This value is used to configure policy for management frame * protection (IEEE 802.11w). 0 = disabled, 1 = optional, 2 = required. + * This is disabled by default unless the default value has been changed + * with the global pmf=1/2 parameter. + * + * Internally, special value 3 is used to indicate that the parameter + * was not specified in the configuration (i.e., default behavior is + * followed). */ enum mfp_options ieee80211w; #endif /* CONFIG_IEEE80211W */ @@ -337,6 +384,8 @@ struct wpa_ssid { */ int frequency; + int ht40; + /** * wpa_ptk_rekey - Maximum lifetime for PTK in seconds * @@ -365,6 +414,20 @@ struct wpa_ssid { char *bgscan; /** + * ignore_broadcast_ssid - Hide SSID in AP mode + * + * Send empty SSID in beacons and ignore probe request frames that do + * not specify full SSID, i.e., require stations to know SSID. + * default: disabled (0) + * 1 = send empty (length=0) SSID in beacon and ignore probe request + * for broadcast SSID + * 2 = clear SSID (ASCII 0), but keep the original length (this may be + * required with some clients that do not support empty SSID) and + * ignore probe requests for broadcast SSID + */ + int ignore_broadcast_ssid; + + /** * freq_list - Array of allowed frequencies or %NULL for all * * This is an optional zero-terminated array of frequencies in @@ -373,6 +436,136 @@ 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; + +#ifndef P2P_MAX_STORED_CLIENTS +#define P2P_MAX_STORED_CLIENTS 100 +#endif /* P2P_MAX_STORED_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; + +#ifdef CONFIG_HT_OVERRIDES + /** + * disable_ht - Disable HT (IEEE 802.11n) for this network + * + * By default, use it if it is available, but this can be configured + * to 1 to have it disabled. + */ + int disable_ht; + + /** + * disable_ht40 - Disable HT40 for this network + * + * By default, use it if it is available, but this can be configured + * to 1 to have it disabled. + */ + int disable_ht40; + + /** + * disable_sgi - Disable SGI (Short Guard Interval) for this network + * + * By default, use it if it is available, but this can be configured + * to 1 to have it disabled. + */ + int disable_sgi; + + /** + * disable_max_amsdu - Disable MAX A-MSDU + * + * A-MDSU will be 3839 bytes when disabled, or 7935 + * when enabled (assuming it is otherwise supported) + * -1 (default) means do not apply any settings to the kernel. + */ + int disable_max_amsdu; + + /** + * ampdu_factor - Maximum A-MPDU Length Exponent + * + * Value: 0-3, see 7.3.2.56.3 in IEEE Std 802.11n-2009. + */ + int ampdu_factor; + + /** + * ampdu_density - Minimum A-MPDU Start Spacing + * + * Value: 0-7, see 7.3.2.56.3 in IEEE Std 802.11n-2009. + */ + int ampdu_density; + + /** + * ht_mcs - Allowed HT-MCS rates, in ASCII hex: ffff0000... + * + * By default (empty string): Use whatever the OS has configured. + */ + char *ht_mcs; +#endif /* CONFIG_HT_OVERRIDES */ + + /** + * ap_max_inactivity - Timeout in seconds to detect STA's inactivity + * + * This timeout value is used in AP mode to clean up inactive stations. + * By default: 300 seconds. + */ + int ap_max_inactivity; + + /** + * dtim_period - DTIM period in Beacon intervals + * By default: 2 + */ + int dtim_period; + + /** + * auth_failures - Number of consecutive authentication failures + */ + unsigned int auth_failures; + + /** + * disabled_until - Network block disabled until this time if non-zero + */ + struct os_time disabled_until; + + /** + * parent_cred - Pointer to parent wpa_cred entry + * + * This pointer can be used to delete temporary networks when a wpa_cred + * that was used to create them is removed. This pointer should not be + * dereferences since it may not be updated in all cases. + */ + void *parent_cred; }; #endif /* CONFIG_SSID_H */ diff --git a/contrib/wpa/wpa_supplicant/ctrl_iface.c b/contrib/wpa/wpa_supplicant/ctrl_iface.c index 19fea29..864dd7d 100644 --- a/contrib/wpa/wpa_supplicant/ctrl_iface.c +++ b/contrib/wpa/wpa_supplicant/ctrl_iface.c @@ -1,22 +1,18 @@ /* * WPA Supplicant / Control interface (shared code for all backends) - * Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" #include "utils/common.h" #include "utils/eloop.h" +#include "common/version.h" #include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" #include "common/wpa_ctrl.h" #include "eap_peer/eap.h" #include "eapol_supp/eapol_supp_sm.h" @@ -31,10 +27,18 @@ #include "wps_supplicant.h" #include "ibss_rsn.h" #include "ap.h" +#include "p2p_supplicant.h" +#include "p2p/p2p.h" +#include "hs20_supplicant.h" +#include "wifi_display.h" #include "notify.h" #include "bss.h" #include "scan.h" #include "ctrl_iface.h" +#include "interworking.h" +#include "blacklist.h" +#include "autoscan.h" +#include "wnm_sta.h" extern struct wpa_driver_ops *wpa_drivers[]; @@ -44,6 +48,249 @@ static int wpa_supplicant_global_iface_interfaces(struct wpa_global *global, char *buf, int len); +static int pno_start(struct wpa_supplicant *wpa_s) +{ + int ret; + size_t i, num_ssid; + struct wpa_ssid *ssid; + struct wpa_driver_scan_params params; + + if (wpa_s->pno) + return 0; + + if (wpa_s->wpa_state == WPA_SCANNING) { + wpa_supplicant_cancel_sched_scan(wpa_s); + wpa_supplicant_cancel_scan(wpa_s); + } + + os_memset(¶ms, 0, sizeof(params)); + + num_ssid = 0; + ssid = wpa_s->conf->ssid; + while (ssid) { + if (!wpas_network_disabled(wpa_s, ssid)) + num_ssid++; + ssid = ssid->next; + } + if (num_ssid > WPAS_MAX_SCAN_SSIDS) { + wpa_printf(MSG_DEBUG, "PNO: Use only the first %u SSIDs from " + "%u", WPAS_MAX_SCAN_SSIDS, (unsigned int) num_ssid); + num_ssid = WPAS_MAX_SCAN_SSIDS; + } + + if (num_ssid == 0) { + wpa_printf(MSG_DEBUG, "PNO: No configured SSIDs"); + return -1; + } + + params.filter_ssids = os_malloc(sizeof(struct wpa_driver_scan_filter) * + num_ssid); + if (params.filter_ssids == NULL) + return -1; + i = 0; + ssid = wpa_s->conf->ssid; + while (ssid) { + if (!wpas_network_disabled(wpa_s, ssid)) { + params.ssids[i].ssid = ssid->ssid; + params.ssids[i].ssid_len = ssid->ssid_len; + params.num_ssids++; + os_memcpy(params.filter_ssids[i].ssid, ssid->ssid, + ssid->ssid_len); + params.filter_ssids[i].ssid_len = ssid->ssid_len; + params.num_filter_ssids++; + i++; + if (i == num_ssid) + break; + } + ssid = ssid->next; + } + + if (wpa_s->conf->filter_rssi) + params.filter_rssi = wpa_s->conf->filter_rssi; + + ret = wpa_drv_sched_scan(wpa_s, ¶ms, 10 * 1000); + os_free(params.filter_ssids); + if (ret == 0) + wpa_s->pno = 1; + return ret; +} + + +static int pno_stop(struct wpa_supplicant *wpa_s) +{ + int ret = 0; + + if (wpa_s->pno) { + wpa_s->pno = 0; + ret = wpa_drv_stop_sched_scan(wpa_s); + } + + if (wpa_s->wpa_state == WPA_SCANNING) + wpa_supplicant_req_scan(wpa_s, 0, 0); + + return ret; +} + + +static int set_bssid_filter(struct wpa_supplicant *wpa_s, char *val) +{ + char *pos; + u8 addr[ETH_ALEN], *filter = NULL, *n; + size_t count = 0; + + pos = val; + while (pos) { + if (*pos == '\0') + break; + if (hwaddr_aton(pos, addr)) { + os_free(filter); + return -1; + } + n = os_realloc_array(filter, count + 1, ETH_ALEN); + if (n == NULL) { + os_free(filter); + return -1; + } + filter = n; + os_memcpy(filter + count * ETH_ALEN, addr, ETH_ALEN); + count++; + + pos = os_strchr(pos, ' '); + if (pos) + pos++; + } + + wpa_hexdump(MSG_DEBUG, "bssid_filter", filter, count * ETH_ALEN); + os_free(wpa_s->bssid_filter); + wpa_s->bssid_filter = filter; + wpa_s->bssid_filter_count = count; + + return 0; +} + + +static int set_disallow_aps(struct wpa_supplicant *wpa_s, char *val) +{ + char *pos; + u8 addr[ETH_ALEN], *bssid = NULL, *n; + struct wpa_ssid_value *ssid = NULL, *ns; + size_t count = 0, ssid_count = 0; + struct wpa_ssid *c; + + /* + * disallow_list ::= <ssid_spec> | <bssid_spec> | <disallow_list> | “” + * SSID_SPEC ::= ssid <SSID_HEX> + * BSSID_SPEC ::= bssid <BSSID_HEX> + */ + + pos = val; + while (pos) { + if (*pos == '\0') + break; + if (os_strncmp(pos, "bssid ", 6) == 0) { + int res; + pos += 6; + res = hwaddr_aton2(pos, addr); + if (res < 0) { + os_free(ssid); + os_free(bssid); + wpa_printf(MSG_DEBUG, "Invalid disallow_aps " + "BSSID value '%s'", pos); + return -1; + } + pos += res; + n = os_realloc_array(bssid, count + 1, ETH_ALEN); + if (n == NULL) { + os_free(ssid); + os_free(bssid); + return -1; + } + bssid = n; + os_memcpy(bssid + count * ETH_ALEN, addr, ETH_ALEN); + count++; + } else if (os_strncmp(pos, "ssid ", 5) == 0) { + char *end; + pos += 5; + + end = pos; + while (*end) { + if (*end == '\0' || *end == ' ') + break; + end++; + } + + ns = os_realloc_array(ssid, ssid_count + 1, + sizeof(struct wpa_ssid_value)); + if (ns == NULL) { + os_free(ssid); + os_free(bssid); + return -1; + } + ssid = ns; + + if ((end - pos) & 0x01 || end - pos > 2 * 32 || + hexstr2bin(pos, ssid[ssid_count].ssid, + (end - pos) / 2) < 0) { + os_free(ssid); + os_free(bssid); + wpa_printf(MSG_DEBUG, "Invalid disallow_aps " + "SSID value '%s'", pos); + return -1; + } + ssid[ssid_count].ssid_len = (end - pos) / 2; + wpa_hexdump_ascii(MSG_DEBUG, "disallow_aps SSID", + ssid[ssid_count].ssid, + ssid[ssid_count].ssid_len); + ssid_count++; + pos = end; + } else { + wpa_printf(MSG_DEBUG, "Unexpected disallow_aps value " + "'%s'", pos); + os_free(ssid); + os_free(bssid); + return -1; + } + + pos = os_strchr(pos, ' '); + if (pos) + pos++; + } + + wpa_hexdump(MSG_DEBUG, "disallow_aps_bssid", bssid, count * ETH_ALEN); + os_free(wpa_s->disallow_aps_bssid); + wpa_s->disallow_aps_bssid = bssid; + wpa_s->disallow_aps_bssid_count = count; + + wpa_printf(MSG_DEBUG, "disallow_aps_ssid_count %d", (int) ssid_count); + os_free(wpa_s->disallow_aps_ssid); + wpa_s->disallow_aps_ssid = ssid; + wpa_s->disallow_aps_ssid_count = ssid_count; + + if (!wpa_s->current_ssid || wpa_s->wpa_state < WPA_AUTHENTICATING) + return 0; + + c = wpa_s->current_ssid; + if (c->mode != WPAS_MODE_INFRA && c->mode != WPAS_MODE_IBSS) + return 0; + + if (!disallowed_bssid(wpa_s, wpa_s->bssid) && + !disallowed_ssid(wpa_s, c->ssid, c->ssid_len)) + return 0; + + wpa_printf(MSG_DEBUG, "Disconnect and try to find another network " + "because current AP was marked disallowed"); + +#ifdef CONFIG_SME + wpa_s->sme.prev_bssid_set = 0; +#endif /* CONFIG_SME */ + wpa_s->reassociate = 1; + wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); + wpa_supplicant_req_scan(wpa_s, 0, 0); + + return 0; +} + + static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, char *cmd) { @@ -80,13 +327,148 @@ 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 if (os_strcasecmp(cmd, "pno") == 0) { + if (atoi(value)) + ret = pno_start(wpa_s); + else + ret = pno_stop(wpa_s); + } else if (os_strcasecmp(cmd, "radio_disabled") == 0) { + int disabled = atoi(value); + if (wpa_drv_radio_disable(wpa_s, disabled) < 0) + ret = -1; + else if (disabled) + wpa_supplicant_set_state(wpa_s, WPA_INACTIVE); + } else if (os_strcasecmp(cmd, "uapsd") == 0) { + if (os_strcmp(value, "disable") == 0) + wpa_s->set_sta_uapsd = 0; + else { + int be, bk, vi, vo; + char *pos; + /* format: BE,BK,VI,VO;max SP Length */ + be = atoi(value); + pos = os_strchr(value, ','); + if (pos == NULL) + return -1; + pos++; + bk = atoi(pos); + pos = os_strchr(pos, ','); + if (pos == NULL) + return -1; + pos++; + vi = atoi(pos); + pos = os_strchr(pos, ','); + if (pos == NULL) + return -1; + pos++; + vo = atoi(pos); + /* ignore max SP Length for now */ + + wpa_s->set_sta_uapsd = 1; + wpa_s->sta_uapsd = 0; + if (be) + wpa_s->sta_uapsd |= BIT(0); + if (bk) + wpa_s->sta_uapsd |= BIT(1); + if (vi) + wpa_s->sta_uapsd |= BIT(2); + if (vo) + wpa_s->sta_uapsd |= BIT(3); + } + } else if (os_strcasecmp(cmd, "ps") == 0) { + ret = wpa_drv_set_p2p_powersave(wpa_s, atoi(value), -1, -1); +#ifdef CONFIG_WIFI_DISPLAY + } else if (os_strcasecmp(cmd, "wifi_display") == 0) { + wifi_display_enable(wpa_s->global, !!atoi(value)); +#endif /* CONFIG_WIFI_DISPLAY */ + } else if (os_strcasecmp(cmd, "bssid_filter") == 0) { + ret = set_bssid_filter(wpa_s, value); + } else if (os_strcasecmp(cmd, "disallow_aps") == 0) { + ret = set_disallow_aps(wpa_s, value); + } else if (os_strcasecmp(cmd, "no_keep_alive") == 0) { + wpa_s->no_keep_alive = !!atoi(value); + } else { + value[-1] = '='; + ret = wpa_config_process_global(wpa_s->conf, cmd, -1); + if (ret == 0) + wpa_supplicant_update_config(wpa_s); + } return ret; } +static int wpa_supplicant_ctrl_iface_get(struct wpa_supplicant *wpa_s, + char *cmd, char *buf, size_t buflen) +{ + int res = -1; + + wpa_printf(MSG_DEBUG, "CTRL_IFACE GET '%s'", cmd); + + if (os_strcmp(cmd, "version") == 0) { + res = os_snprintf(buf, buflen, "%s", VERSION_STR); + } else if (os_strcasecmp(cmd, "country") == 0) { + if (wpa_s->conf->country[0] && wpa_s->conf->country[1]) + res = os_snprintf(buf, buflen, "%c%c", + wpa_s->conf->country[0], + wpa_s->conf->country[1]); +#ifdef CONFIG_WIFI_DISPLAY + } else if (os_strcasecmp(cmd, "wifi_display") == 0) { + res = os_snprintf(buf, buflen, "%d", + wpa_s->global->wifi_display); + if (res < 0 || (unsigned int) res >= buflen) + return -1; + return res; +#endif /* CONFIG_WIFI_DISPLAY */ + } + + if (res < 0 || (unsigned int) res >= buflen) + return -1; + return res; +} + + #ifdef IEEE8021X_EAPOL static int wpa_supplicant_ctrl_iface_preauth(struct wpa_supplicant *wpa_s, char *addr) @@ -131,6 +513,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 +619,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 +646,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,20 +667,36 @@ 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; } #ifdef CONFIG_AP - if (wpa_s->ap_iface) + if (wpa_s->ap_iface) { + int timeout = 0; + char *pos; + + if (pin) { + pos = os_strchr(pin, ' '); + if (pos) { + *pos++ = '\0'; + timeout = atoi(pos); + } + } + return wpa_supplicant_ap_wps_pin(wpa_s, _bssid, pin, - buf, buflen); + buf, buflen, timeout); + } #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 +705,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,35 +718,272 @@ static int wpa_supplicant_ctrl_iface_wps_pin(struct wpa_supplicant *wpa_s, } -#ifdef CONFIG_WPS_OOB -static int wpa_supplicant_ctrl_iface_wps_oob(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_NFC + +static int wpa_supplicant_ctrl_iface_wps_nfc(struct wpa_supplicant *wpa_s, char *cmd) { - char *path, *method, *name; + u8 bssid[ETH_ALEN], *_bssid = bssid; + + if (cmd == NULL || cmd[0] == '\0') + _bssid = NULL; + else if (hwaddr_aton(cmd, bssid)) + return -1; + + return wpas_wps_start_nfc(wpa_s, _bssid); +} + + +static int wpa_supplicant_ctrl_iface_wps_nfc_token( + struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len) +{ + int ndef; + struct wpabuf *buf; + int res; + + if (os_strcmp(cmd, "WPS") == 0) + ndef = 0; + else if (os_strcmp(cmd, "NDEF") == 0) + ndef = 1; + else + return -1; + + buf = wpas_wps_nfc_token(wpa_s, ndef); + if (buf == NULL) + return -1; + + res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf), + wpabuf_len(buf)); + reply[res++] = '\n'; + reply[res] = '\0'; + + wpabuf_free(buf); + + return res; +} + + +static int wpa_supplicant_ctrl_iface_wps_nfc_tag_read( + struct wpa_supplicant *wpa_s, char *pos) +{ + size_t len; + struct wpabuf *buf; + int ret; + + len = os_strlen(pos); + if (len & 0x01) + return -1; + len /= 2; + + buf = wpabuf_alloc(len); + if (buf == NULL) + return -1; + if (hexstr2bin(pos, wpabuf_put(buf, len), len) < 0) { + wpabuf_free(buf); + return -1; + } + + ret = wpas_wps_nfc_tag_read(wpa_s, buf); + wpabuf_free(buf); + + return ret; +} + + +static int wpas_ctrl_nfc_get_handover_req_wps(struct wpa_supplicant *wpa_s, + char *reply, size_t max_len) +{ + struct wpabuf *buf; + int res; + + buf = wpas_wps_nfc_handover_req(wpa_s); + if (buf == NULL) + return -1; + + res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf), + wpabuf_len(buf)); + reply[res++] = '\n'; + reply[res] = '\0'; + + wpabuf_free(buf); + + return res; +} + + +static int wpas_ctrl_nfc_get_handover_req(struct wpa_supplicant *wpa_s, + char *cmd, char *reply, + size_t max_len) +{ + char *pos; + + pos = os_strchr(cmd, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; - path = os_strchr(cmd, ' '); - if (path == NULL) + if (os_strcmp(cmd, "NDEF") != 0) return -1; - *path++ = '\0'; - method = os_strchr(path, ' '); - if (method == NULL) + if (os_strcmp(pos, "WPS") == 0) { + return wpas_ctrl_nfc_get_handover_req_wps(wpa_s, reply, + max_len); + } + + return -1; +} + + +static int wpas_ctrl_nfc_get_handover_sel_wps(struct wpa_supplicant *wpa_s, + char *reply, size_t max_len) +{ + struct wpabuf *buf; + int res; + + buf = wpas_wps_nfc_handover_sel(wpa_s); + if (buf == NULL) return -1; - *method++ = '\0'; - name = os_strchr(method, ' '); - if (name != NULL) - *name++ = '\0'; + res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf), + wpabuf_len(buf)); + reply[res++] = '\n'; + reply[res] = '\0'; - return wpas_wps_start_oob(wpa_s, cmd, path, method, name); + wpabuf_free(buf); + + return res; } -#endif /* CONFIG_WPS_OOB */ + + +static int wpas_ctrl_nfc_get_handover_sel(struct wpa_supplicant *wpa_s, + char *cmd, char *reply, + size_t max_len) +{ + char *pos; + + pos = os_strchr(cmd, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + + if (os_strcmp(cmd, "NDEF") != 0) + return -1; + + if (os_strcmp(pos, "WPS") == 0) { + return wpas_ctrl_nfc_get_handover_sel_wps(wpa_s, reply, + max_len); + } + + return -1; +} + + +static int wpas_ctrl_nfc_rx_handover_req(struct wpa_supplicant *wpa_s, + char *cmd, char *reply, + size_t max_len) +{ + size_t len; + struct wpabuf *buf; + int ret; + + len = os_strlen(cmd); + if (len & 0x01) + return -1; + len /= 2; + + buf = wpabuf_alloc(len); + if (buf == NULL) + return -1; + if (hexstr2bin(cmd, wpabuf_put(buf, len), len) < 0) { + wpabuf_free(buf); + return -1; + } + + ret = wpas_wps_nfc_rx_handover_req(wpa_s, buf); + wpabuf_free(buf); + + return ret; +} + + +static int wpas_ctrl_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s, + char *cmd) +{ + size_t len; + struct wpabuf *buf; + int ret; + + len = os_strlen(cmd); + if (len & 0x01) + return -1; + len /= 2; + + buf = wpabuf_alloc(len); + if (buf == NULL) + return -1; + if (hexstr2bin(cmd, wpabuf_put(buf, len), len) < 0) { + wpabuf_free(buf); + return -1; + } + + ret = wpas_wps_nfc_rx_handover_sel(wpa_s, buf); + wpabuf_free(buf); + + return ret; +} + +#endif /* CONFIG_WPS_NFC */ 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 +996,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 +1004,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 +1027,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 +1120,99 @@ 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); +} + + +#ifdef CONFIG_WPS_NFC +static int wpa_supplicant_ctrl_iface_wps_er_nfc_config_token( + struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len) +{ + int ndef; + struct wpabuf *buf; + int res; + char *uuid; + + uuid = os_strchr(cmd, ' '); + if (uuid == NULL) + return -1; + *uuid++ = '\0'; + + if (os_strcmp(cmd, "WPS") == 0) + ndef = 0; + else if (os_strcmp(cmd, "NDEF") == 0) + ndef = 1; + else + return -1; + + buf = wpas_wps_er_nfc_config_token(wpa_s, ndef, uuid); + if (buf == NULL) + return -1; + + res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf), + wpabuf_len(buf)); + reply[res++] = '\n'; + reply[res] = '\0'; + + wpabuf_free(buf); + + return res; +} +#endif /* CONFIG_WPS_NFC */ #endif /* CONFIG_WPS_ER */ #endif /* CONFIG_WPS */ @@ -362,7 +1245,6 @@ static int wpa_supplicant_ctrl_iface_ctrl_rsp(struct wpa_supplicant *wpa_s, char *pos, *id_pos; int id; struct wpa_ssid *ssid; - struct eap_peer_config *eap; pos = os_strchr(rsp, '-'); if (pos == NULL) @@ -384,54 +1266,9 @@ static int wpa_supplicant_ctrl_iface_ctrl_rsp(struct wpa_supplicant *wpa_s, "to update", id); return -1; } - eap = &ssid->eap; - - if (os_strcmp(rsp, "IDENTITY") == 0) { - os_free(eap->identity); - eap->identity = (u8 *) os_strdup(pos); - eap->identity_len = os_strlen(pos); - eap->pending_req_identity = 0; - if (ssid == wpa_s->current_ssid) - wpa_s->reassociate = 1; - } else if (os_strcmp(rsp, "PASSWORD") == 0) { - os_free(eap->password); - eap->password = (u8 *) os_strdup(pos); - eap->password_len = os_strlen(pos); - eap->pending_req_password = 0; - if (ssid == wpa_s->current_ssid) - wpa_s->reassociate = 1; - } else if (os_strcmp(rsp, "NEW_PASSWORD") == 0) { - os_free(eap->new_password); - eap->new_password = (u8 *) os_strdup(pos); - eap->new_password_len = os_strlen(pos); - eap->pending_req_new_password = 0; - if (ssid == wpa_s->current_ssid) - wpa_s->reassociate = 1; - } else if (os_strcmp(rsp, "PIN") == 0) { - os_free(eap->pin); - eap->pin = os_strdup(pos); - eap->pending_req_pin = 0; - if (ssid == wpa_s->current_ssid) - wpa_s->reassociate = 1; - } else if (os_strcmp(rsp, "OTP") == 0) { - os_free(eap->otp); - eap->otp = (u8 *) os_strdup(pos); - eap->otp_len = os_strlen(pos); - os_free(eap->pending_req_otp); - eap->pending_req_otp = NULL; - eap->pending_req_otp_len = 0; - } else if (os_strcmp(rsp, "PASSPHRASE") == 0) { - os_free(eap->private_key_passwd); - eap->private_key_passwd = (u8 *) os_strdup(pos); - 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); - return -1; - } - return 0; + 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; @@ -444,9 +1281,10 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) { char *pos, *end, tmp[30]; - int res, verbose, ret; + int res, verbose, wps, ret; verbose = os_strcmp(params, "-VERBOSE") == 0; + wps = os_strcmp(params, "-WPS") == 0; pos = buf; end = buf + buflen; if (wpa_s->wpa_state >= WPA_ASSOCIATED) { @@ -475,6 +1313,17 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, return pos - buf; pos += ret; + if (wps && ssid->passphrase && + wpa_key_mgmt_wpa_psk(ssid->key_mgmt) && + (ssid->mode == WPAS_MODE_AP || + ssid->mode == WPAS_MODE_P2P_GO)) { + ret = os_snprintf(pos, end - pos, + "passphrase=%s\n", + ssid->passphrase); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } if (ssid->id_str) { ret = os_snprintf(pos, end - pos, "id_str=%s\n", @@ -497,6 +1346,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,6 +1387,73 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, pos += ret; } +#ifdef CONFIG_P2P + if (wpa_s->global->p2p) { + ret = os_snprintf(pos, end - pos, "p2p_device_address=" MACSTR + "\n", MAC2STR(wpa_s->global->p2p_dev_addr)); + if (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; + +#ifdef CONFIG_HS20 + if (wpa_s->current_bss && + wpa_bss_get_vendor_ie(wpa_s->current_bss, HS20_IE_VENDOR_TYPE) && + wpa_s->wpa_proto == WPA_PROTO_RSN && + wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) { + ret = os_snprintf(pos, end - pos, "hs20=1\n"); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + if (wpa_s->current_ssid) { + struct wpa_cred *cred; + char *type; + + for (cred = wpa_s->conf->cred; cred; cred = cred->next) { + if (wpa_s->current_ssid->parent_cred != cred) + continue; + if (!cred->domain) + continue; + + ret = os_snprintf(pos, end - pos, "home_sp=%s\n", + cred->domain); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + if (wpa_s->current_bss == NULL || + wpa_s->current_bss->anqp == NULL) + res = -1; + else + res = interworking_home_sp_cred( + wpa_s, cred, + wpa_s->current_bss->anqp->domain_name); + if (res > 0) + type = "home"; + else if (res == 0) + type = "roaming"; + else + type = "unknown"; + + ret = os_snprintf(pos, end - pos, "sp_type=%s\n", type); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + break; + } + } +#endif /* CONFIG_HS20 */ + if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) || wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) { res = eapol_sm_get_status(wpa_s->eapol, pos, end - pos, @@ -579,6 +1504,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 +1682,14 @@ 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%s", ssid == wpa_s->current_ssid ? "[CURRENT]" : "", - ssid->disabled ? "[DISABLED]" : ""); + ssid->disabled ? "[DISABLED]" : "", + ssid->disabled_until.sec ? + "[TEMP-DISABLED]" : "", + ssid->disabled == 2 ? "[P2P-PERSISTENT]" : + ""); if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; @@ -673,6 +1748,13 @@ static char * wpa_supplicant_cipher_txt(char *pos, char *end, int cipher) pos += ret; first = 0; } + if (cipher & WPA_CIPHER_GCMP) { + ret = os_snprintf(pos, end - pos, "%sGCMP", first ? "" : "+"); + if (ret < 0 || ret >= end - pos) + return pos; + pos += ret; + first = 0; + } return pos; } @@ -774,7 +1856,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 +1867,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 +1885,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 +1901,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 +1920,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 +1928,49 @@ 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; + } +#ifdef CONFIG_HS20 + if (wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE) && ie2) { + ret = os_snprintf(pos, end - pos, "[HS20]"); + if (ret < 0 || ret >= end - pos) + return -1; + pos += ret; + } +#endif /* CONFIG_HS20 */ 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 +1993,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 +2024,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 +2057,16 @@ 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; + } + + if (os_strstr(cmd, " no-connect")) { + ssid->disabled = 0; + return 0; + } } wpa_supplicant_enable_network(wpa_s, ssid); @@ -970,6 +2094,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,10 +2148,15 @@ 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); - wpa_supplicant_disassociate(wpa_s, - WLAN_REASON_DEAUTH_LEAVING); +#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_deauthenticate( + wpa_s, WLAN_REASON_DEAUTH_LEAVING); } return 0; } @@ -1030,21 +2165,37 @@ 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 == NULL || - wpa_config_remove_network(wpa_s->conf, id) < 0) { + if (ssid) + wpas_notify_network_removed(wpa_s, ssid); + if (ssid == NULL) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find network " "id=%d", id); return -1; } - if (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); + wpa_supplicant_deauthenticate(wpa_s, + WLAN_REASON_DEAUTH_LEAVING); + } + + if (wpa_config_remove_network(wpa_s->conf, id) < 0) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Not able to remove the " + "network id=%d", id); + return -1; } return 0; @@ -1088,10 +2239,14 @@ static int wpa_supplicant_ctrl_iface_set_network( return -1; } - if (wpa_s->current_ssid == ssid) { + if (os_strcmp(name, "bssid") != 0 && + os_strcmp(name, "priority") != 0) + wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid); + + if (wpa_s->current_ssid == ssid || wpa_s->current_ssid == NULL) { /* * Invalidate the EAP session cache if anything in the current - * configuration changes. + * or previously used configuration changes. */ eapol_sm_invalidate_cached_session(wpa_s->eapol); } @@ -1151,6 +2306,167 @@ static int wpa_supplicant_ctrl_iface_get_network( } +static int wpa_supplicant_ctrl_iface_list_creds(struct wpa_supplicant *wpa_s, + char *buf, size_t buflen) +{ + char *pos, *end; + struct wpa_cred *cred; + int ret; + + pos = buf; + end = buf + buflen; + ret = os_snprintf(pos, end - pos, + "cred id / realm / username / domain / imsi\n"); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + cred = wpa_s->conf->cred; + while (cred) { + ret = os_snprintf(pos, end - pos, "%d\t%s\t%s\t%s\t%s\n", + cred->id, cred->realm ? cred->realm : "", + cred->username ? cred->username : "", + cred->domain ? cred->domain : "", + cred->imsi ? cred->imsi : ""); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + cred = cred->next; + } + + return pos - buf; +} + + +static int wpa_supplicant_ctrl_iface_add_cred(struct wpa_supplicant *wpa_s, + char *buf, size_t buflen) +{ + struct wpa_cred *cred; + int ret; + + wpa_printf(MSG_DEBUG, "CTRL_IFACE: ADD_CRED"); + + cred = wpa_config_add_cred(wpa_s->conf); + if (cred == NULL) + return -1; + + ret = os_snprintf(buf, buflen, "%d\n", cred->id); + if (ret < 0 || (size_t) ret >= buflen) + return -1; + return ret; +} + + +static int wpas_ctrl_remove_cred(struct wpa_supplicant *wpa_s, + struct wpa_cred *cred) +{ + struct wpa_ssid *ssid; + char str[20]; + + if (cred == NULL || wpa_config_remove_cred(wpa_s->conf, cred->id) < 0) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find cred"); + return -1; + } + + /* Remove any network entry created based on the removed credential */ + ssid = wpa_s->conf->ssid; + while (ssid) { + if (ssid->parent_cred == cred) { + wpa_printf(MSG_DEBUG, "Remove network id %d since it " + "used the removed credential", ssid->id); + os_snprintf(str, sizeof(str), "%d", ssid->id); + ssid = ssid->next; + wpa_supplicant_ctrl_iface_remove_network(wpa_s, str); + } else + ssid = ssid->next; + } + + return 0; +} + + +static int wpa_supplicant_ctrl_iface_remove_cred(struct wpa_supplicant *wpa_s, + char *cmd) +{ + int id; + struct wpa_cred *cred, *prev; + + /* cmd: "<cred id>", "all", or "sp_fqdn=<FQDN>" */ + if (os_strcmp(cmd, "all") == 0) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_CRED all"); + cred = wpa_s->conf->cred; + while (cred) { + prev = cred; + cred = cred->next; + wpas_ctrl_remove_cred(wpa_s, prev); + } + return 0; + } + + if (os_strncmp(cmd, "sp_fqdn=", 8) == 0) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_CRED SP FQDN '%s'", + cmd + 8); + cred = wpa_s->conf->cred; + while (cred) { + prev = cred; + cred = cred->next; + if (prev->domain && + os_strcmp(prev->domain, cmd + 8) == 0) + wpas_ctrl_remove_cred(wpa_s, prev); + } + return 0; + } + + id = atoi(cmd); + wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_CRED id=%d", id); + + cred = wpa_config_get_cred(wpa_s->conf, id); + return wpas_ctrl_remove_cred(wpa_s, cred); +} + + +static int wpa_supplicant_ctrl_iface_set_cred(struct wpa_supplicant *wpa_s, + char *cmd) +{ + int id; + struct wpa_cred *cred; + char *name, *value; + + /* cmd: "<cred id> <variable name> <value>" */ + name = os_strchr(cmd, ' '); + if (name == NULL) + return -1; + *name++ = '\0'; + + value = os_strchr(name, ' '); + if (value == NULL) + return -1; + *value++ = '\0'; + + id = atoi(cmd); + wpa_printf(MSG_DEBUG, "CTRL_IFACE: SET_CRED id=%d name='%s'", + id, name); + wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: value", + (u8 *) value, os_strlen(value)); + + cred = wpa_config_get_cred(wpa_s->conf, id); + if (cred == NULL) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find cred id=%d", + id); + return -1; + } + + if (wpa_config_set_cred(cred, name, value, 0) < 0) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to set cred " + "variable '%s'", name); + return -1; + } + + return 0; +} + + #ifndef CONFIG_NO_CONFIG_WRITE static int wpa_supplicant_ctrl_iface_save_config(struct wpa_supplicant *wpa_s) { @@ -1204,6 +2520,14 @@ static int ctrl_iface_get_capability_pairwise(int res, char *strict, first = 0; } + if (capa->enc & WPA_DRIVER_CAPA_ENC_GCMP) { + ret = os_snprintf(pos, end - pos, "%sGCMP", first ? "" : " "); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + first = 0; + } + if (capa->enc & WPA_DRIVER_CAPA_ENC_TKIP) { ret = os_snprintf(pos, end - pos, "%sTKIP", first ? "" : " "); if (ret < 0 || ret >= end - pos) @@ -1252,6 +2576,14 @@ static int ctrl_iface_get_capability_group(int res, char *strict, first = 0; } + if (capa->enc & WPA_DRIVER_CAPA_ENC_GCMP) { + ret = os_snprintf(pos, end - pos, "%sGCMP", first ? "" : " "); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + first = 0; + } + if (capa->enc & WPA_DRIVER_CAPA_ENC_TKIP) { ret = os_snprintf(pos, end - pos, "%sTKIP", first ? "" : " "); if (ret < 0 || ret >= end - pos) @@ -1425,6 +2757,56 @@ static int ctrl_iface_get_capability_auth_alg(int res, char *strict, } +static int ctrl_iface_get_capability_channels(struct wpa_supplicant *wpa_s, + char *buf, size_t buflen) +{ + struct hostapd_channel_data *chnl; + int ret, i, j; + char *pos, *end, *hmode; + + pos = buf; + end = pos + buflen; + + for (j = 0; j < wpa_s->hw.num_modes; j++) { + switch (wpa_s->hw.modes[j].mode) { + case HOSTAPD_MODE_IEEE80211B: + hmode = "B"; + break; + case HOSTAPD_MODE_IEEE80211G: + hmode = "G"; + break; + case HOSTAPD_MODE_IEEE80211A: + hmode = "A"; + break; + case HOSTAPD_MODE_IEEE80211AD: + hmode = "AD"; + break; + default: + continue; + } + ret = os_snprintf(pos, end - pos, "Mode[%s] Channels:", hmode); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + chnl = wpa_s->hw.modes[j].channels; + for (i = 0; i < wpa_s->hw.modes[j].num_channels; i++) { + if (chnl[i].flag & HOSTAPD_CHAN_DISABLED) + continue; + ret = os_snprintf(pos, end - pos, " %d", chnl[i].chan); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + ret = os_snprintf(pos, end - pos, "\n"); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + return pos - buf; +} + + static int wpa_supplicant_ctrl_iface_get_capability( struct wpa_supplicant *wpa_s, const char *_field, char *buf, size_t buflen) @@ -1475,6 +2857,9 @@ static int wpa_supplicant_ctrl_iface_get_capability( return ctrl_iface_get_capability_auth_alg(res, strict, &capa, buf, buflen); + if (os_strcmp(field, "channels") == 0) + return ctrl_iface_get_capability_channels(wpa_s, buf, buflen); + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'", field); @@ -1482,6 +2867,288 @@ 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 print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, + unsigned long mask, char *buf, size_t buflen) +{ + size_t i; + int ret; + char *pos, *end; + const u8 *ie, *ie2; + + pos = buf; + end = buf + buflen; + + if (mask & WPA_BSS_MASK_ID) { + ret = os_snprintf(pos, end - pos, "id=%u\n", bss->id); + if (ret < 0 || ret >= end - pos) + return 0; + pos += ret; + } + + if (mask & WPA_BSS_MASK_BSSID) { + ret = os_snprintf(pos, end - pos, "bssid=" MACSTR "\n", + MAC2STR(bss->bssid)); + if (ret < 0 || ret >= end - pos) + return 0; + pos += ret; + } + + if (mask & WPA_BSS_MASK_FREQ) { + ret = os_snprintf(pos, end - pos, "freq=%d\n", bss->freq); + if (ret < 0 || ret >= end - pos) + return 0; + pos += ret; + } + + if (mask & WPA_BSS_MASK_BEACON_INT) { + ret = os_snprintf(pos, end - pos, "beacon_int=%d\n", + bss->beacon_int); + if (ret < 0 || ret >= end - pos) + return 0; + pos += ret; + } + + if (mask & WPA_BSS_MASK_CAPABILITIES) { + ret = os_snprintf(pos, end - pos, "capabilities=0x%04x\n", + bss->caps); + if (ret < 0 || ret >= end - pos) + return 0; + pos += ret; + } + + if (mask & WPA_BSS_MASK_QUAL) { + ret = os_snprintf(pos, end - pos, "qual=%d\n", bss->qual); + if (ret < 0 || ret >= end - pos) + return 0; + pos += ret; + } + + if (mask & WPA_BSS_MASK_NOISE) { + ret = os_snprintf(pos, end - pos, "noise=%d\n", bss->noise); + if (ret < 0 || ret >= end - pos) + return 0; + pos += ret; + } + + if (mask & WPA_BSS_MASK_LEVEL) { + ret = os_snprintf(pos, end - pos, "level=%d\n", bss->level); + if (ret < 0 || ret >= end - pos) + return 0; + pos += ret; + } + + if (mask & WPA_BSS_MASK_TSF) { + ret = os_snprintf(pos, end - pos, "tsf=%016llu\n", + (unsigned long long) bss->tsf); + if (ret < 0 || ret >= end - pos) + return 0; + pos += ret; + } + + if (mask & WPA_BSS_MASK_AGE) { + struct os_time now; + + os_get_time(&now); + ret = os_snprintf(pos, end - pos, "age=%d\n", + (int) (now.sec - bss->last_update.sec)); + if (ret < 0 || ret >= end - pos) + return 0; + pos += ret; + } + + if (mask & WPA_BSS_MASK_IE) { + ret = os_snprintf(pos, end - pos, "ie="); + if (ret < 0 || ret >= end - pos) + return 0; + pos += ret; + + ie = (const u8 *) (bss + 1); + for (i = 0; i < bss->ie_len; i++) { + ret = os_snprintf(pos, end - pos, "%02x", *ie++); + if (ret < 0 || ret >= end - pos) + return 0; + pos += ret; + } + + ret = os_snprintf(pos, end - pos, "\n"); + if (ret < 0 || ret >= end - pos) + return 0; + pos += ret; + } + + if (mask & WPA_BSS_MASK_FLAGS) { + ret = os_snprintf(pos, end - pos, "flags="); + if (ret < 0 || ret >= end - pos) + return 0; + pos += ret; + + ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); + if (ie) + pos = wpa_supplicant_ie_txt(pos, end, "WPA", ie, + 2 + ie[1]); + ie2 = wpa_bss_get_ie(bss, WLAN_EID_RSN); + if (ie2) + pos = wpa_supplicant_ie_txt(pos, end, "WPA2", ie2, + 2 + ie2[1]); + 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 0; + pos += ret; + } + if (bss->caps & IEEE80211_CAP_IBSS) { + ret = os_snprintf(pos, end - pos, "[IBSS]"); + if (ret < 0 || ret >= end - pos) + return 0; + pos += ret; + } + if (bss->caps & IEEE80211_CAP_ESS) { + ret = os_snprintf(pos, end - pos, "[ESS]"); + if (ret < 0 || ret >= end - pos) + return 0; + 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 0; + pos += ret; + } +#ifdef CONFIG_HS20 + if (wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE)) { + ret = os_snprintf(pos, end - pos, "[HS20]"); + if (ret < 0 || ret >= end - pos) + return -1; + pos += ret; + } +#endif /* CONFIG_HS20 */ + + ret = os_snprintf(pos, end - pos, "\n"); + if (ret < 0 || ret >= end - pos) + return 0; + pos += ret; + } + + if (mask & WPA_BSS_MASK_SSID) { + ret = os_snprintf(pos, end - pos, "ssid=%s\n", + wpa_ssid_txt(bss->ssid, bss->ssid_len)); + if (ret < 0 || ret >= end - pos) + return 0; + pos += ret; + } + +#ifdef CONFIG_WPS + if (mask & WPA_BSS_MASK_WPS_SCAN) { + ie = (const u8 *) (bss + 1); + ret = wpas_wps_scan_result_text(ie, bss->ie_len, pos, end); + if (ret < 0 || ret >= end - pos) + return 0; + pos += ret; + } +#endif /* CONFIG_WPS */ + +#ifdef CONFIG_P2P + if (mask & WPA_BSS_MASK_P2P_SCAN) { + ie = (const u8 *) (bss + 1); + ret = wpas_p2p_scan_result_text(ie, bss->ie_len, pos, end); + if (ret < 0 || ret >= end - pos) + return 0; + pos += ret; + } +#endif /* CONFIG_P2P */ + +#ifdef CONFIG_WIFI_DISPLAY + if (mask & WPA_BSS_MASK_WIFI_DISPLAY) { + struct wpabuf *wfd; + ie = (const u8 *) (bss + 1); + wfd = ieee802_11_vendor_ie_concat(ie, bss->ie_len, + WFD_IE_VENDOR_TYPE); + if (wfd) { + ret = os_snprintf(pos, end - pos, "wfd_subelems="); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + pos += wpa_snprintf_hex(pos, end - pos, + wpabuf_head(wfd), + wpabuf_len(wfd)); + wpabuf_free(wfd); + + ret = os_snprintf(pos, end - pos, "\n"); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + } +#endif /* CONFIG_WIFI_DISPLAY */ + +#ifdef CONFIG_INTERWORKING + if ((mask & WPA_BSS_MASK_INTERNETW) && bss->anqp) { + struct wpa_bss_anqp *anqp = bss->anqp; + pos = anqp_add_hex(pos, end, "anqp_venue_name", + anqp->venue_name); + pos = anqp_add_hex(pos, end, "anqp_network_auth_type", + anqp->network_auth_type); + pos = anqp_add_hex(pos, end, "anqp_roaming_consortium", + anqp->roaming_consortium); + pos = anqp_add_hex(pos, end, "anqp_ip_addr_type_availability", + anqp->ip_addr_type_availability); + pos = anqp_add_hex(pos, end, "anqp_nai_realm", + anqp->nai_realm); + pos = anqp_add_hex(pos, end, "anqp_3gpp", anqp->anqp_3gpp); + pos = anqp_add_hex(pos, end, "anqp_domain_name", + anqp->domain_name); +#ifdef CONFIG_HS20 + pos = anqp_add_hex(pos, end, "hs20_operator_friendly_name", + anqp->hs20_operator_friendly_name); + pos = anqp_add_hex(pos, end, "hs20_wan_metrics", + anqp->hs20_wan_metrics); + pos = anqp_add_hex(pos, end, "hs20_connection_capability", + anqp->hs20_connection_capability); +#endif /* CONFIG_HS20 */ + } +#endif /* CONFIG_INTERWORKING */ + + return pos - buf; +} + + static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s, const char *cmd, char *buf, size_t buflen) @@ -1489,12 +3156,55 @@ static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s, u8 bssid[ETH_ALEN]; size_t i; struct wpa_bss *bss; - int ret; - char *pos, *end; - const u8 *ie, *ie2; + struct wpa_bss *bsslast = NULL; + struct dl_list *next; + int ret = 0; + int len; + char *ctmp; + unsigned long mask = WPA_BSS_MASK_ALL; + + if (os_strncmp(cmd, "RANGE=", 6) == 0) { + if (os_strncmp(cmd + 6, "ALL", 3) == 0) { + bss = dl_list_first(&wpa_s->bss_id, struct wpa_bss, + list_id); + bsslast = dl_list_last(&wpa_s->bss_id, struct wpa_bss, + list_id); + } else { /* N1-N2 */ + unsigned int id1, id2; + + if ((ctmp = os_strchr(cmd + 6, '-')) == NULL) { + wpa_printf(MSG_INFO, "Wrong BSS range " + "format"); + return 0; + } - if (os_strcmp(cmd, "FIRST") == 0) - bss = dl_list_first(&wpa_s->bss, struct wpa_bss, list); + id1 = atoi(cmd + 6); + bss = wpa_bss_get_id(wpa_s, id1); + id2 = atoi(ctmp + 1); + if (id2 == 0) + bsslast = dl_list_last(&wpa_s->bss_id, + struct wpa_bss, + list_id); + else { + bsslast = wpa_bss_get_id(wpa_s, id2); + if (bsslast == NULL && bss && id2 > id1) { + struct wpa_bss *tmp = bss; + for (;;) { + next = tmp->list_id.next; + if (next == &wpa_s->bss_id) + break; + tmp = dl_list_entry( + next, struct wpa_bss, + list_id); + if (tmp->id > id2) + break; + bsslast = tmp; + } + } + } + } + } else if (os_strcmp(cmd, "FIRST") == 0) + bss = dl_list_first(&wpa_s->bss_id, struct wpa_bss, list_id); else if (os_strncmp(cmd, "ID-", 3) == 0) { i = atoi(cmd + 3); bss = wpa_bss_get_id(wpa_s, i); @@ -1502,13 +3212,20 @@ static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s, i = atoi(cmd + 5); bss = wpa_bss_get_id(wpa_s, i); if (bss) { - struct dl_list *next = bss->list_id.next; + next = bss->list_id.next; if (next == &wpa_s->bss_id) bss = NULL; else bss = dl_list_entry(next, struct wpa_bss, list_id); } +#ifdef CONFIG_P2P + } else if (os_strncmp(cmd, "p2p_dev_addr=", 13) == 0) { + if (hwaddr_aton(cmd + 13, bssid) == 0) + bss = wpa_bss_get_p2p_dev_addr(wpa_s, bssid); + else + bss = NULL; +#endif /* CONFIG_P2P */ } else if (hwaddr_aton(cmd, bssid) == 0) bss = wpa_bss_get_bssid(wpa_s, bssid); else { @@ -1524,118 +3241,90 @@ static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s, } } + if ((ctmp = os_strstr(cmd, "MASK=")) != NULL) { + mask = strtoul(ctmp + 5, NULL, 0x10); + if (mask == 0) + mask = WPA_BSS_MASK_ALL; + } + if (bss == NULL) return 0; - pos = buf; - end = buf + buflen; - ret = os_snprintf(pos, end - pos, - "id=%u\n" - "bssid=" MACSTR "\n" - "freq=%d\n" - "beacon_int=%d\n" - "capabilities=0x%04x\n" - "qual=%d\n" - "noise=%d\n" - "level=%d\n" - "tsf=%016llu\n" - "ie=", - bss->id, - MAC2STR(bss->bssid), bss->freq, bss->beacon_int, - bss->caps, bss->qual, bss->noise, bss->level, - (unsigned long long) bss->tsf); - if (ret < 0 || ret >= end - pos) - return pos - buf; - pos += ret; + if (bsslast == NULL) + bsslast = bss; + do { + len = print_bss_info(wpa_s, bss, mask, buf, buflen); + ret += len; + buf += len; + buflen -= len; + if (bss == bsslast) + break; + next = bss->list_id.next; + if (next == &wpa_s->bss_id) + break; + bss = dl_list_entry(next, struct wpa_bss, list_id); + } while (bss && len); - ie = (const u8 *) (bss + 1); - for (i = 0; i < bss->ie_len; i++) { - ret = os_snprintf(pos, end - pos, "%02x", *ie++); - if (ret < 0 || ret >= end - pos) - return pos - buf; - pos += ret; - } + return ret; +} - ret = os_snprintf(pos, end - pos, "\n"); - if (ret < 0 || ret >= end - pos) - return pos - buf; - pos += ret; - ret = os_snprintf(pos, end - pos, "flags="); - if (ret < 0 || ret >= end - pos) - return pos - buf; - pos += ret; +static int wpa_supplicant_ctrl_iface_ap_scan( + struct wpa_supplicant *wpa_s, char *cmd) +{ + int ap_scan = atoi(cmd); + return wpa_supplicant_set_ap_scan(wpa_s, ap_scan); +} - ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); - if (ie) - pos = wpa_supplicant_ie_txt(pos, end, "WPA", ie, 2 + ie[1]); - ie2 = wpa_bss_get_ie(bss, WLAN_EID_RSN); - if (ie2) - pos = wpa_supplicant_ie_txt(pos, end, "WPA2", ie2, 2 + ie2[1]); - pos = wpa_supplicant_wps_ie_txt(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; - pos += ret; - } - if (bss->caps & IEEE80211_CAP_IBSS) { - ret = os_snprintf(pos, end - pos, "[IBSS]"); - if (ret < 0 || ret >= end - pos) - return pos - buf; - pos += ret; - } - if (bss->caps & IEEE80211_CAP_ESS) { - ret = os_snprintf(pos, end - pos, "[ESS]"); - if (ret < 0 || ret >= end - pos) - return pos - buf; - pos += ret; - } - ret = os_snprintf(pos, end - pos, "\n"); - if (ret < 0 || ret >= end - pos) - return pos - buf; - pos += ret; +static int wpa_supplicant_ctrl_iface_scan_interval( + struct wpa_supplicant *wpa_s, char *cmd) +{ + int scan_int = atoi(cmd); + return wpa_supplicant_set_scan_interval(wpa_s, scan_int); +} - ret = os_snprintf(pos, end - pos, "ssid=%s\n", - wpa_ssid_txt(bss->ssid, bss->ssid_len)); - if (ret < 0 || ret >= end - pos) - return pos - buf; - pos += ret; -#ifdef CONFIG_WPS - ie = (const u8 *) (bss + 1); - ret = wpas_wps_scan_result_text(ie, bss->ie_len, pos, end); - if (ret < 0 || ret >= end - pos) - return pos - buf; - pos += ret; -#endif /* CONFIG_WPS */ +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); +} - return pos - buf; + +static int wpa_supplicant_ctrl_iface_bss_expire_count( + struct wpa_supplicant *wpa_s, char *cmd) +{ + int expire_count = atoi(cmd); + return wpa_supplicant_set_bss_expiration_count(wpa_s, expire_count); } -static int wpa_supplicant_ctrl_iface_ap_scan( +static int wpa_supplicant_ctrl_iface_bss_flush( struct wpa_supplicant *wpa_s, char *cmd) { - int ap_scan = atoi(cmd); - return wpa_supplicant_set_ap_scan(wpa_s, ap_scan); + int flush_age = atoi(cmd); + + if (flush_age == 0) + wpa_bss_flush(wpa_s); + else + wpa_bss_flush_by_age(wpa_s, flush_age); + return 0; } static void wpa_supplicant_ctrl_iface_drop_sa(struct wpa_supplicant *wpa_s) { - u8 *bcast = (u8 *) "\xff\xff\xff\xff\xff\xff"; - 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 +3340,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,6 +3377,1406 @@ 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 */ +} + + +#ifdef CONFIG_P2P +static int p2p_ctrl_find(struct wpa_supplicant *wpa_s, char *cmd) +{ + unsigned int timeout = atoi(cmd); + enum p2p_discovery_type type = P2P_FIND_START_WITH_FULL; + u8 dev_id[ETH_ALEN], *_dev_id = NULL; + char *pos; + unsigned int search_delay; + + if (os_strstr(cmd, "type=social")) + type = P2P_FIND_ONLY_SOCIAL; + else if (os_strstr(cmd, "type=progressive")) + type = P2P_FIND_PROGRESSIVE; + + pos = os_strstr(cmd, "dev_id="); + if (pos) { + pos += 7; + if (hwaddr_aton(pos, dev_id)) + return -1; + _dev_id = dev_id; + } + + pos = os_strstr(cmd, "delay="); + if (pos) { + pos += 6; + search_delay = atoi(pos); + } else + search_delay = wpas_p2p_search_delay(wpa_s); + + return wpas_p2p_find(wpa_s, timeout, type, 0, NULL, _dev_id, + search_delay); +} + + +static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd, + char *buf, size_t buflen) +{ + u8 addr[ETH_ALEN]; + char *pos, *pos2; + char *pin = NULL; + enum p2p_wps_method wps_method; + int new_pin; + int ret; + int persistent_group, persistent_id = -1; + int join; + int auth; + int automatic; + int go_intent = -1; + int freq = 0; + int pd; + int ht40; + + /* <addr> <"pbc" | "pin" | PIN> [label|display|keypad] + * [persistent|persistent=<network id>] + * [join] [auth] [go_intent=<0..15>] [freq=<in MHz>] [provdisc] + * [ht40] */ + + if (hwaddr_aton(cmd, addr)) + return -1; + + pos = cmd + 17; + if (*pos != ' ') + return -1; + pos++; + + persistent_group = os_strstr(pos, " persistent") != NULL; + pos2 = os_strstr(pos, " persistent="); + if (pos2) { + struct wpa_ssid *ssid; + persistent_id = atoi(pos2 + 12); + ssid = wpa_config_get_network(wpa_s->conf, persistent_id); + if (ssid == NULL || ssid->disabled != 2 || + ssid->mode != WPAS_MODE_P2P_GO) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find " + "SSID id=%d for persistent P2P group (GO)", + persistent_id); + return -1; + } + } + join = os_strstr(pos, " join") != NULL; + auth = os_strstr(pos, " auth") != NULL; + automatic = os_strstr(pos, " auto") != NULL; + pd = os_strstr(pos, " provdisc") != NULL; + ht40 = (os_strstr(cmd, " ht40") != NULL) || wpa_s->conf->p2p_go_ht40; + + pos2 = os_strstr(pos, " go_intent="); + if (pos2) { + pos2 += 11; + go_intent = atoi(pos2); + if (go_intent < 0 || go_intent > 15) + return -1; + } + + pos2 = os_strstr(pos, " freq="); + if (pos2) { + pos2 += 6; + freq = atoi(pos2); + if (freq <= 0) + return -1; + } + + if (os_strncmp(pos, "pin", 3) == 0) { + /* Request random PIN (to be displayed) and enable the PIN */ + wps_method = WPS_PIN_DISPLAY; + } else if (os_strncmp(pos, "pbc", 3) == 0) { + wps_method = WPS_PBC; + } else { + pin = pos; + pos = os_strchr(pin, ' '); + wps_method = WPS_PIN_KEYPAD; + if (pos) { + *pos++ = '\0'; + if (os_strncmp(pos, "display", 7) == 0) + wps_method = WPS_PIN_DISPLAY; + } + if (!wps_pin_str_valid(pin)) { + os_memcpy(buf, "FAIL-INVALID-PIN\n", 17); + return 17; + } + } + + new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method, + persistent_group, automatic, join, + auth, go_intent, freq, persistent_id, pd, + ht40); + 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(buf, "OK\n", 3); + return 3; +} + + +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; + enum wpas_p2p_prov_disc_use use = WPAS_P2P_PD_FOR_GO_NEG; + + /* <addr> <config method> [join|auto] */ + + if (hwaddr_aton(cmd, addr)) + return -1; + + pos = cmd + 17; + if (*pos != ' ') + return -1; + pos++; + + if (os_strstr(pos, " join") != NULL) + use = WPAS_P2P_PD_FOR_JOIN; + else if (os_strstr(pos, " auto") != NULL) + use = WPAS_P2P_PD_AUTO; + + return wpas_p2p_prov_disc(wpa_s, addr, pos, use); +} + + +static int p2p_get_passphrase(struct wpa_supplicant *wpa_s, char *buf, + size_t buflen) +{ + struct wpa_ssid *ssid = wpa_s->current_ssid; + + if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GO || + ssid->passphrase == NULL) + return -1; + + os_strlcpy(buf, ssid->passphrase, buflen); + return os_strlen(buf); +} + + +static int p2p_ctrl_serv_disc_req(struct wpa_supplicant *wpa_s, char *cmd, + char *buf, size_t buflen) +{ + u64 ref; + int res; + u8 dst_buf[ETH_ALEN], *dst; + struct wpabuf *tlvs; + char *pos; + size_t len; + + if (hwaddr_aton(cmd, dst_buf)) + return -1; + dst = dst_buf; + if (dst[0] == 0 && dst[1] == 0 && dst[2] == 0 && + dst[3] == 0 && dst[4] == 0 && dst[5] == 0) + dst = NULL; + pos = cmd + 17; + if (*pos != ' ') + return -1; + pos++; + + if (os_strncmp(pos, "upnp ", 5) == 0) { + u8 version; + pos += 5; + if (hexstr2bin(pos, &version, 1) < 0) + return -1; + pos += 2; + if (*pos != ' ') + return -1; + pos++; + ref = wpas_p2p_sd_request_upnp(wpa_s, dst, version, pos); +#ifdef CONFIG_WIFI_DISPLAY + } else if (os_strncmp(pos, "wifi-display ", 13) == 0) { + ref = wpas_p2p_sd_request_wifi_display(wpa_s, dst, pos + 13); +#endif /* CONFIG_WIFI_DISPLAY */ + } else { + len = os_strlen(pos); + if (len & 1) + return -1; + len /= 2; + tlvs = wpabuf_alloc(len); + if (tlvs == NULL) + return -1; + if (hexstr2bin(pos, wpabuf_put(tlvs, len), len) < 0) { + wpabuf_free(tlvs); + return -1; + } + + ref = wpas_p2p_sd_request(wpa_s, dst, tlvs); + wpabuf_free(tlvs); + } + if (ref == 0) + return -1; + res = os_snprintf(buf, buflen, "%llx", (long long unsigned) ref); + if (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, 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 = NULL, peer[ETH_ALEN]; + int freq = 0; + int ht40; + + id = atoi(cmd); + pos = os_strstr(cmd, " peer="); + if (pos) { + pos += 6; + if (hwaddr_aton(pos, peer)) + return -1; + _peer = peer; + } + ssid = wpa_config_get_network(wpa_s->conf, id); + if (ssid == NULL || ssid->disabled != 2) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d " + "for persistent P2P group", + id); + return -1; + } + + pos = os_strstr(cmd, " freq="); + if (pos) { + pos += 6; + freq = atoi(pos); + if (freq <= 0) + return -1; + } + + ht40 = (os_strstr(cmd, " ht40") != NULL) || wpa_s->conf->p2p_go_ht40; + + return wpas_p2p_invite(wpa_s, _peer, ssid, NULL, freq, ht40); +} + + +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 ht40) +{ + 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, ht40); +} + + +static int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd) +{ + int freq = 0, ht40; + char *pos; + + pos = os_strstr(cmd, "freq="); + if (pos) + freq = atoi(pos + 5); + + ht40 = (os_strstr(cmd, "ht40") != NULL) || wpa_s->conf->p2p_go_ht40; + + if (os_strncmp(cmd, "persistent=", 11) == 0) + return p2p_ctrl_group_add_persistent(wpa_s, cmd + 11, freq, + ht40); + if (os_strcmp(cmd, "persistent") == 0 || + os_strncmp(cmd, "persistent ", 11) == 0) + return wpas_p2p_group_add(wpa_s, 1, freq, ht40); + if (os_strncmp(cmd, "freq=", 5) == 0) + return wpas_p2p_group_add(wpa_s, 0, freq, ht40); + if (ht40) + return wpas_p2p_group_add(wpa_s, 0, freq, ht40); + + 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; + size_t i; + + if (!wpa_s->global->p2p) + return -1; + + if (os_strcmp(cmd, "FIRST") == 0) { + addr_ptr = NULL; + next = 0; + } else if (os_strncmp(cmd, "NEXT-", 5) == 0) { + if (hwaddr_aton(cmd + 5, addr) < 0) + return -1; + addr_ptr = addr; + next = 1; + } else { + if (hwaddr_aton(cmd, addr) < 0) + return -1; + addr_ptr = addr; + next = 0; + } + + info = p2p_get_peer_info(wpa_s->global->p2p, addr_ptr, next); + if (info == NULL) + return -1; + + pos = buf; + end = buf + buflen; + + res = os_snprintf(pos, end - pos, MACSTR "\n" + "pri_dev_type=%s\n" + "device_name=%s\n" + "manufacturer=%s\n" + "model_name=%s\n" + "model_number=%s\n" + "serial_number=%s\n" + "config_methods=0x%x\n" + "dev_capab=0x%x\n" + "group_capab=0x%x\n" + "level=%d\n", + MAC2STR(info->p2p_device_addr), + wps_dev_type_bin2str(info->pri_dev_type, + devtype, sizeof(devtype)), + info->device_name, + info->manufacturer, + info->model_name, + info->model_number, + info->serial_number, + info->config_methods, + info->dev_capab, + info->group_capab, + info->level); + if (res < 0 || res >= end - pos) + return pos - buf; + pos += res; + + for (i = 0; i < info->wps_sec_dev_type_list_len / WPS_DEV_TYPE_LEN; i++) + { + const u8 *t; + t = &info->wps_sec_dev_type_list[i * WPS_DEV_TYPE_LEN]; + res = os_snprintf(pos, end - pos, "sec_dev_type=%s\n", + wps_dev_type_bin2str(t, devtype, + sizeof(devtype))); + if (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_disallow_freq(struct wpa_supplicant *wpa_s, + const char *param) +{ + struct wpa_freq_range *freq = NULL, *n; + unsigned int count = 0, i; + const char *pos, *pos2, *pos3; + + if (wpa_s->global->p2p == NULL) + return -1; + + /* + * param includes comma separated frequency range. + * For example: 2412-2432,2462,5000-6000 + */ + pos = param; + while (pos && pos[0]) { + n = os_realloc_array(freq, count + 1, + sizeof(struct wpa_freq_range)); + if (n == NULL) { + os_free(freq); + return -1; + } + freq = n; + freq[count].min = atoi(pos); + pos2 = os_strchr(pos, '-'); + pos3 = os_strchr(pos, ','); + if (pos2 && (!pos3 || pos2 < pos3)) { + pos2++; + freq[count].max = atoi(pos2); + } else + freq[count].max = freq[count].min; + pos = pos3; + if (pos) + pos++; + count++; + } + + for (i = 0; i < count; i++) { + wpa_printf(MSG_DEBUG, "P2P: Disallowed frequency range %u-%u", + freq[i].min, freq[i].max); + } + + os_free(wpa_s->global->p2p_disallow_freq); + wpa_s->global->p2p_disallow_freq = freq; + wpa_s->global->num_p2p_disallow_freq = count; + wpas_p2p_update_channel_list(wpa_s); + return 0; +} + + +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, "conc_pref") == 0) { + if (os_strcmp(param, "sta") == 0) + wpa_s->global->conc_pref = WPA_CONC_PREF_STA; + else if (os_strcmp(param, "p2p") == 0) + wpa_s->global->conc_pref = WPA_CONC_PREF_P2P; + else { + wpa_printf(MSG_INFO, "Invalid conc_pref value"); + return -1; + } + wpa_printf(MSG_DEBUG, "Single channel concurrency preference: " + "%s", param); + return 0; + } + + if (os_strcmp(cmd, "force_long_sd") == 0) { + wpa_s->force_long_sd = atoi(param); + return 0; + } + + if (os_strcmp(cmd, "peer_filter") == 0) { + u8 addr[ETH_ALEN]; + if (hwaddr_aton(param, addr)) + return -1; + p2p_set_peer_filter(wpa_s->global->p2p, addr); + return 0; + } + + if (os_strcmp(cmd, "cross_connect") == 0) + return wpas_p2p_set_cross_connect(wpa_s, atoi(param)); + + if (os_strcmp(cmd, "go_apsd") == 0) { + if (os_strcmp(param, "disable") == 0) + wpa_s->set_ap_uapsd = 0; + else { + wpa_s->set_ap_uapsd = 1; + wpa_s->ap_uapsd = atoi(param); + } + return 0; + } + + if (os_strcmp(cmd, "client_apsd") == 0) { + if (os_strcmp(param, "disable") == 0) + wpa_s->set_sta_uapsd = 0; + else { + int be, bk, vi, vo; + char *pos; + /* format: BE,BK,VI,VO;max SP Length */ + be = atoi(param); + pos = os_strchr(param, ','); + if (pos == NULL) + return -1; + pos++; + bk = atoi(pos); + pos = os_strchr(pos, ','); + if (pos == NULL) + return -1; + pos++; + vi = atoi(pos); + pos = os_strchr(pos, ','); + if (pos == NULL) + return -1; + pos++; + vo = atoi(pos); + /* ignore max SP Length for now */ + + wpa_s->set_sta_uapsd = 1; + wpa_s->sta_uapsd = 0; + if (be) + wpa_s->sta_uapsd |= BIT(0); + if (bk) + wpa_s->sta_uapsd |= BIT(1); + if (vi) + wpa_s->sta_uapsd |= BIT(2); + if (vo) + wpa_s->sta_uapsd |= BIT(3); + } + return 0; + } + + if (os_strcmp(cmd, "disallow_freq") == 0) + return p2p_ctrl_disallow_freq(wpa_s, param); + + if (os_strcmp(cmd, "disc_int") == 0) { + int min_disc_int, max_disc_int, max_disc_tu; + char *pos; + + pos = param; + + min_disc_int = atoi(pos); + pos = os_strchr(pos, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + + max_disc_int = atoi(pos); + pos = os_strchr(pos, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + + max_disc_tu = atoi(pos); + + return p2p_set_disc_int(wpa_s->global->p2p, min_disc_int, + max_disc_int, max_disc_tu); + } + + 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); +} + + +static int gas_request(struct wpa_supplicant *wpa_s, char *cmd) +{ + u8 dst_addr[ETH_ALEN]; + struct wpabuf *advproto, *query = NULL; + int used, ret = -1; + char *pos, *end; + size_t len; + + used = hwaddr_aton2(cmd, dst_addr); + if (used < 0) + return -1; + + pos = cmd + used; + while (*pos == ' ') + pos++; + + /* Advertisement Protocol ID */ + end = os_strchr(pos, ' '); + if (end) + len = end - pos; + else + len = os_strlen(pos); + if (len & 0x01) + return -1; + len /= 2; + if (len == 0) + return -1; + advproto = wpabuf_alloc(len); + if (advproto == NULL) + return -1; + if (hexstr2bin(pos, wpabuf_put(advproto, len), len) < 0) + goto fail; + + if (end) { + /* Optional Query Request */ + pos = end + 1; + while (*pos == ' ') + pos++; + + len = os_strlen(pos); + if (len) { + if (len & 0x01) + goto fail; + len /= 2; + if (len == 0) + goto fail; + query = wpabuf_alloc(len); + if (query == NULL) + goto fail; + if (hexstr2bin(pos, wpabuf_put(query, len), len) < 0) + goto fail; + } + } + + ret = gas_send_request(wpa_s, dst_addr, advproto, query); + +fail: + wpabuf_free(advproto); + wpabuf_free(query); + + return ret; +} + + +static int gas_response_get(struct wpa_supplicant *wpa_s, char *cmd, char *buf, + size_t buflen) +{ + u8 addr[ETH_ALEN]; + int dialog_token; + int used; + char *pos; + size_t resp_len, start, requested_len; + + if (!wpa_s->last_gas_resp) + return -1; + + used = hwaddr_aton2(cmd, addr); + if (used < 0) + return -1; + + pos = cmd + used; + while (*pos == ' ') + pos++; + dialog_token = atoi(pos); + + if (os_memcmp(addr, wpa_s->last_gas_addr, ETH_ALEN) != 0 || + dialog_token != wpa_s->last_gas_dialog_token) + return -1; + + resp_len = wpabuf_len(wpa_s->last_gas_resp); + start = 0; + requested_len = resp_len; + + pos = os_strchr(pos, ' '); + if (pos) { + start = atoi(pos); + if (start > resp_len) + return os_snprintf(buf, buflen, "FAIL-Invalid range"); + pos = os_strchr(pos, ','); + if (pos == NULL) + return -1; + pos++; + requested_len = atoi(pos); + if (start + requested_len > resp_len) + return os_snprintf(buf, buflen, "FAIL-Invalid range"); + } + + if (requested_len * 2 + 1 > buflen) + return os_snprintf(buf, buflen, "FAIL-Too long response"); + + return wpa_snprintf_hex(buf, buflen, + wpabuf_head_u8(wpa_s->last_gas_resp) + start, + requested_len); +} +#endif /* CONFIG_INTERWORKING */ + + +#ifdef CONFIG_HS20 + +static int get_hs20_anqp(struct wpa_supplicant *wpa_s, char *dst) +{ + u8 dst_addr[ETH_ALEN]; + int used; + char *pos; + u32 subtypes = 0; + + used = hwaddr_aton2(dst, dst_addr); + if (used < 0) + return -1; + pos = dst + used; + for (;;) { + int num = atoi(pos); + if (num <= 0 || num > 31) + return -1; + subtypes |= BIT(num); + pos = os_strchr(pos + 1, ','); + if (pos == NULL) + break; + pos++; + } + + if (subtypes == 0) + return -1; + + return hs20_anqp_send_req(wpa_s, dst_addr, subtypes, NULL, 0); +} + + +static int hs20_nai_home_realm_list(struct wpa_supplicant *wpa_s, + const u8 *addr, const char *realm) +{ + u8 *buf; + size_t rlen, len; + int ret; + + rlen = os_strlen(realm); + len = 3 + rlen; + buf = os_malloc(len); + if (buf == NULL) + return -1; + buf[0] = 1; /* NAI Home Realm Count */ + buf[1] = 0; /* Formatted in accordance with RFC 4282 */ + buf[2] = rlen; + os_memcpy(buf + 3, realm, rlen); + + ret = hs20_anqp_send_req(wpa_s, addr, + BIT(HS20_STYPE_NAI_HOME_REALM_QUERY), + buf, len); + + os_free(buf); + + return ret; +} + + +static int hs20_get_nai_home_realm_list(struct wpa_supplicant *wpa_s, + char *dst) +{ + struct wpa_cred *cred = wpa_s->conf->cred; + u8 dst_addr[ETH_ALEN]; + int used; + u8 *buf; + size_t len; + int ret; + + used = hwaddr_aton2(dst, dst_addr); + if (used < 0) + return -1; + + while (dst[used] == ' ') + used++; + if (os_strncmp(dst + used, "realm=", 6) == 0) + return hs20_nai_home_realm_list(wpa_s, dst_addr, + dst + used + 6); + + len = os_strlen(dst + used); + + if (len == 0 && cred && cred->realm) + return hs20_nai_home_realm_list(wpa_s, dst_addr, cred->realm); + + if (len % 1) + return -1; + len /= 2; + buf = os_malloc(len); + if (buf == NULL) + return -1; + if (hexstr2bin(dst + used, buf, len) < 0) { + os_free(buf); + return -1; + } + + ret = hs20_anqp_send_req(wpa_s, dst_addr, + BIT(HS20_STYPE_NAI_HOME_REALM_QUERY), + buf, len); + os_free(buf); + + return ret; +} + +#endif /* CONFIG_HS20 */ + + +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; +} + + +#ifdef CONFIG_AUTOSCAN + +static int wpa_supplicant_ctrl_iface_autoscan(struct wpa_supplicant *wpa_s, + char *cmd) +{ + enum wpa_states state = wpa_s->wpa_state; + char *new_params = NULL; + + if (os_strlen(cmd) > 0) { + new_params = os_strdup(cmd); + if (new_params == NULL) + return -1; + } + + os_free(wpa_s->conf->autoscan); + wpa_s->conf->autoscan = new_params; + + if (wpa_s->conf->autoscan == NULL) + autoscan_deinit(wpa_s); + else if (state == WPA_DISCONNECTED || state == WPA_INACTIVE) + autoscan_init(wpa_s, 1); + else if (state == WPA_SCANNING) + wpa_supplicant_reinit_autoscan(wpa_s); + + return 0; +} + +#endif /* CONFIG_AUTOSCAN */ + + +#ifdef CONFIG_WNM + +static int wpas_ctrl_iface_wnm_sleep(struct wpa_supplicant *wpa_s, char *cmd) +{ + int enter; + int intval = 0; + char *pos; + int ret; + struct wpabuf *tfs_req = NULL; + + if (os_strncmp(cmd, "enter", 5) == 0) + enter = 1; + else if (os_strncmp(cmd, "exit", 4) == 0) + enter = 0; + else + return -1; + + pos = os_strstr(cmd, " interval="); + if (pos) + intval = atoi(pos + 10); + + pos = os_strstr(cmd, " tfs_req="); + if (pos) { + char *end; + size_t len; + pos += 9; + end = os_strchr(pos, ' '); + if (end) + len = end - pos; + else + len = os_strlen(pos); + if (len & 1) + return -1; + len /= 2; + tfs_req = wpabuf_alloc(len); + if (tfs_req == NULL) + return -1; + if (hexstr2bin(pos, wpabuf_put(tfs_req, len), len) < 0) { + wpabuf_free(tfs_req); + return -1; + } + } + + ret = ieee802_11_send_wnmsleep_req(wpa_s, enter ? WNM_SLEEP_MODE_ENTER : + WNM_SLEEP_MODE_EXIT, intval, + tfs_req); + wpabuf_free(tfs_req); + + return ret; +} + +#endif /* CONFIG_WNM */ + + +static int wpa_supplicant_signal_poll(struct wpa_supplicant *wpa_s, char *buf, + size_t buflen) +{ + struct wpa_signal_info si; + int ret; + + 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; +} + + +static int wpa_supplicant_pktcnt_poll(struct wpa_supplicant *wpa_s, char *buf, + size_t buflen) +{ + struct hostap_sta_driver_data sta; + int ret; + + ret = wpa_drv_pktcnt_poll(wpa_s, &sta); + if (ret) + return -1; + + ret = os_snprintf(buf, buflen, "TXGOOD=%lu\nTXBAD=%lu\nRXGOOD=%lu\n", + sta.tx_packets, sta.tx_retry_failed, sta.rx_packets); + if (ret < 0 || (size_t) ret > buflen) + return -1; + return ret; } @@ -1692,17 +4784,23 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, char *buf, size_t *resp_len) { char *reply; - const int reply_size = 2048; + const int reply_size = 4096; int ctrl_rsp = 0; int reply_len; if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0 || - os_strncmp(buf, "SET_NETWORK ", 12) == 0) { + os_strncmp(buf, "SET_NETWORK ", 12) == 0 || + os_strncmp(buf, "WPS_NFC_TAG_READ", 16) == 0 || + os_strncmp(buf, "NFC_RX_HANDOVER_SEL", 19) == 0) { wpa_hexdump_ascii_key(MSG_DEBUG, "RX ctrl_iface", (const u8 *) buf, os_strlen(buf)); } else { - wpa_hexdump_ascii(MSG_DEBUG, "RX ctrl_iface", + int level = MSG_DEBUG; + if (os_strcmp(buf, "PING") == 0) + level = MSG_EXCESSIVE; + wpa_hexdump_ascii(level, "RX ctrl_iface", (const u8 *) buf, os_strlen(buf)); + wpa_dbg(wpa_s, level, "Control interface command '%s'", buf); } reply = os_malloc(reply_size); @@ -1717,6 +4815,14 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, if (os_strcmp(buf, "PING") == 0) { os_memcpy(reply, "PONG\n", 5); reply_len = 5; + } else if (os_strcmp(buf, "IFNAME") == 0) { + reply_len = os_strlen(wpa_s->ifname); + os_memcpy(reply, wpa_s->ifname, reply_len); + } else if (os_strncmp(buf, "RELOG", 5) == 0) { + if (wpa_debug_reopen_file() < 0) + reply_len = -1; + } else if (os_strncmp(buf, "NOTE ", 5) == 0) { + wpa_printf(MSG_INFO, "NOTE: %s", buf + 5); } else if (os_strcmp(buf, "MIB") == 0) { reply_len = wpa_sm_get_mib(wpa_s->wpa, reply, reply_size); if (reply_len >= 0) { @@ -1737,20 +4843,23 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } 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 + wpas_request_connection(wpa_s); } else if (os_strcmp(buf, "RECONNECT") == 0) { - if (wpa_s->disconnected) { - 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 if (wpa_s->disconnected) + wpas_request_connection(wpa_s); #ifdef IEEE8021X_EAPOL } else if (os_strncmp(buf, "PREAUTH ", 8) == 0) { if (wpa_supplicant_ctrl_iface_preauth(wpa_s, buf + 8)) @@ -1768,26 +4877,70 @@ 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); -#ifdef CONFIG_WPS_OOB - } else if (os_strncmp(buf, "WPS_OOB ", 8) == 0) { - if (wpa_supplicant_ctrl_iface_wps_oob(wpa_s, buf + 8)) + } else if (os_strncmp(buf, "WPS_CHECK_PIN ", 14) == 0) { + reply_len = wpa_supplicant_ctrl_iface_wps_check_pin( + wpa_s, buf + 14, reply, reply_size); + } else if (os_strcmp(buf, "WPS_CANCEL") == 0) { + if (wpas_wps_cancel(wpa_s)) + reply_len = -1; +#ifdef CONFIG_WPS_NFC + } else if (os_strcmp(buf, "WPS_NFC") == 0) { + if (wpa_supplicant_ctrl_iface_wps_nfc(wpa_s, NULL)) + reply_len = -1; + } else if (os_strncmp(buf, "WPS_NFC ", 8) == 0) { + if (wpa_supplicant_ctrl_iface_wps_nfc(wpa_s, buf + 8)) reply_len = -1; -#endif /* CONFIG_WPS_OOB */ + } else if (os_strncmp(buf, "WPS_NFC_TOKEN ", 14) == 0) { + reply_len = wpa_supplicant_ctrl_iface_wps_nfc_token( + wpa_s, buf + 14, reply, reply_size); + } else if (os_strncmp(buf, "WPS_NFC_TAG_READ ", 17) == 0) { + if (wpa_supplicant_ctrl_iface_wps_nfc_tag_read(wpa_s, + buf + 17)) + reply_len = -1; + } else if (os_strncmp(buf, "NFC_GET_HANDOVER_REQ ", 21) == 0) { + reply_len = wpas_ctrl_nfc_get_handover_req( + wpa_s, buf + 21, reply, reply_size); + } else if (os_strncmp(buf, "NFC_GET_HANDOVER_SEL ", 21) == 0) { + reply_len = wpas_ctrl_nfc_get_handover_sel( + wpa_s, buf + 21, reply, reply_size); + } else if (os_strncmp(buf, "NFC_RX_HANDOVER_REQ ", 20) == 0) { + reply_len = wpas_ctrl_nfc_rx_handover_req( + wpa_s, buf + 20, reply, reply_size); + } else if (os_strncmp(buf, "NFC_RX_HANDOVER_SEL ", 20) == 0) { + if (wpas_ctrl_nfc_rx_handover_sel(wpa_s, buf + 20)) + reply_len = -1; +#endif /* CONFIG_WPS_NFC */ } else if (os_strncmp(buf, "WPS_REG ", 8) == 0) { if (wpa_supplicant_ctrl_iface_wps_reg(wpa_s, buf + 8)) reply_len = -1; +#ifdef CONFIG_AP + } else if (os_strncmp(buf, "WPS_AP_PIN ", 11) == 0) { + reply_len = wpa_supplicant_ctrl_iface_wps_ap_pin( + wpa_s, buf + 11, reply, reply_size); +#endif /* CONFIG_AP */ #ifdef CONFIG_WPS_ER } else if (os_strcmp(buf, "WPS_ER_START") == 0) { - if (wpas_wps_er_start(wpa_s)) + 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 +4949,33 @@ 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; +#ifdef CONFIG_WPS_NFC + } else if (os_strncmp(buf, "WPS_ER_NFC_CONFIG_TOKEN ", 24) == 0) { + reply_len = wpa_supplicant_ctrl_iface_wps_er_nfc_config_token( + wpa_s, buf + 24, reply, reply_size); +#endif /* CONFIG_WPS_NFC */ #endif /* CONFIG_WPS_ER */ #endif /* CONFIG_WPS */ #ifdef CONFIG_IBSS_RSN @@ -1808,6 +4983,135 @@ 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, 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_WIFI_DISPLAY + } else if (os_strncmp(buf, "WFD_SUBELEM_SET ", 16) == 0) { + if (wifi_display_subelem_set(wpa_s->global, buf + 16) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "WFD_SUBELEM_GET ", 16) == 0) { + reply_len = wifi_display_subelem_get(wpa_s->global, buf + 16, + reply, reply_size); +#endif /* CONFIG_WIFI_DISPLAY */ +#ifdef CONFIG_INTERWORKING + } else if (os_strcmp(buf, "FETCH_ANQP") == 0) { + if (interworking_fetch_anqp(wpa_s) < 0) + reply_len = -1; + } else if (os_strcmp(buf, "STOP_FETCH_ANQP") == 0) { + interworking_stop_fetch_anqp(wpa_s); + } else if (os_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; + } else if (os_strncmp(buf, "GAS_REQUEST ", 12) == 0) { + if (gas_request(wpa_s, buf + 12) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "GAS_RESPONSE_GET ", 17) == 0) { + reply_len = gas_response_get(wpa_s, buf + 17, reply, + reply_size); +#endif /* CONFIG_INTERWORKING */ +#ifdef CONFIG_HS20 + } else if (os_strncmp(buf, "HS20_ANQP_GET ", 14) == 0) { + if (get_hs20_anqp(wpa_s, buf + 14) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "HS20_GET_NAI_HOME_REALM_LIST ", 29) == 0) { + if (hs20_get_nai_home_realm_list(wpa_s, buf + 29) < 0) + reply_len = -1; +#endif /* CONFIG_HS20 */ } else if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0) { if (wpa_supplicant_ctrl_iface_ctrl_rsp( @@ -1823,17 +5127,49 @@ 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_cancel_sched_scan(wpa_s); + wpa_supplicant_cancel_scan(wpa_s); 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->sched_scanning && !wpa_s->scanning && + ((wpa_s->wpa_state <= WPA_SCANNING) || + (wpa_s->wpa_state == WPA_COMPLETED))) { + wpa_s->normal_scans = 0; + wpa_s->scan_req = MANUAL_SCAN_REQ; + wpa_supplicant_req_scan(wpa_s, 0, 0); + } else if (wpa_s->sched_scanning) { + wpa_printf(MSG_DEBUG, "Stop ongoing " + "sched_scan to allow requested " + "full scan to proceed"); + wpa_supplicant_cancel_sched_scan(wpa_s); + wpa_s->scan_req = MANUAL_SCAN_REQ; + wpa_supplicant_req_scan(wpa_s, 0, 0); + } else { + wpa_printf(MSG_DEBUG, "Ongoing scan action - " + "reject new request"); + reply_len = os_snprintf(reply, reply_size, + "FAIL-BUSY\n"); + } + } } else if (os_strcmp(buf, "SCAN_RESULTS") == 0) { reply_len = wpa_supplicant_ctrl_iface_scan_results( wpa_s, reply, reply_size); @@ -1858,6 +5194,18 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strncmp(buf, "GET_NETWORK ", 12) == 0) { reply_len = wpa_supplicant_ctrl_iface_get_network( wpa_s, buf + 12, reply, reply_size); + } else if (os_strcmp(buf, "LIST_CREDS") == 0) { + reply_len = wpa_supplicant_ctrl_iface_list_creds( + wpa_s, reply, reply_size); + } else if (os_strcmp(buf, "ADD_CRED") == 0) { + reply_len = wpa_supplicant_ctrl_iface_add_cred( + wpa_s, reply, reply_size); + } else if (os_strncmp(buf, "REMOVE_CRED ", 12) == 0) { + if (wpa_supplicant_ctrl_iface_remove_cred(wpa_s, buf + 12)) + reply_len = -1; + } else if (os_strncmp(buf, "SET_CRED ", 9) == 0) { + if (wpa_supplicant_ctrl_iface_set_cred(wpa_s, buf + 9)) + reply_len = -1; #ifndef CONFIG_NO_CONFIG_WRITE } else if (os_strcmp(buf, "SAVE_CONFIG") == 0) { if (wpa_supplicant_ctrl_iface_save_config(wpa_s)) @@ -1869,6 +5217,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); @@ -1887,6 +5238,12 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strncmp(buf, "STA-NEXT ", 9) == 0) { reply_len = ap_ctrl_iface_sta_next(wpa_s, buf + 9, reply, reply_size); + } else if (os_strncmp(buf, "DEAUTHENTICATE ", 15) == 0) { + if (ap_ctrl_iface_sta_deauthenticate(wpa_s, buf + 15)) + reply_len = -1; + } else if (os_strncmp(buf, "DISASSOCIATE ", 13) == 0) { + if (ap_ctrl_iface_sta_disassociate(wpa_s, buf + 13)) + reply_len = -1; #endif /* CONFIG_AP */ } else if (os_strcmp(buf, "SUSPEND") == 0) { wpas_notify_suspend(wpa_s->global); @@ -1897,6 +5254,49 @@ 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; + } else if (os_strncmp(buf, "BSS_FLUSH ", 10) == 0) { + if (wpa_supplicant_ctrl_iface_bss_flush(wpa_s, buf + 10)) + 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 if (os_strncmp(buf, "PKTCNT_POLL", 11) == 0) { + reply_len = wpa_supplicant_pktcnt_poll(wpa_s, reply, + reply_size); +#ifdef CONFIG_AUTOSCAN + } else if (os_strncmp(buf, "AUTOSCAN ", 9) == 0) { + if (wpa_supplicant_ctrl_iface_autoscan(wpa_s, buf + 9)) + reply_len = -1; +#endif /* CONFIG_AUTOSCAN */ + } else if (os_strcmp(buf, "REAUTHENTICATE") == 0) { + pmksa_cache_clear_current(wpa_s->wpa); + eapol_sm_request_reauth(wpa_s->eapol); +#ifdef CONFIG_WNM + } else if (os_strncmp(buf, "WNM_SLEEP ", 10) == 0) { + if (wpas_ctrl_iface_wnm_sleep(wpa_s, buf + 10)) + reply_len = -1; +#endif /* CONFIG_WNM */ } else { os_memcpy(reply, "UNKNOWN COMMAND\n", 16); reply_len = 16; @@ -2002,7 +5402,7 @@ static int wpa_supplicant_global_iface_remove(struct wpa_global *global, wpa_s = wpa_supplicant_get_iface(global, cmd); if (wpa_s == NULL) return -1; - return wpa_supplicant_remove_iface(global, wpa_s); + return wpa_supplicant_remove_iface(global, wpa_s, 0); } @@ -2093,8 +5493,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); diff --git a/contrib/wpa/wpa_supplicant/ctrl_iface.h b/contrib/wpa/wpa_supplicant/ctrl_iface.h index 051d99a..a329ef3 100644 --- a/contrib/wpa/wpa_supplicant/ctrl_iface.h +++ b/contrib/wpa/wpa_supplicant/ctrl_iface.h @@ -2,14 +2,8 @@ * WPA Supplicant / UNIX domain socket -based control interface * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef CTRL_IFACE_H diff --git a/contrib/wpa/wpa_supplicant/ctrl_iface_named_pipe.c b/contrib/wpa/wpa_supplicant/ctrl_iface_named_pipe.c index 5f7e24d..fd417ff 100644 --- a/contrib/wpa/wpa_supplicant/ctrl_iface_named_pipe.c +++ b/contrib/wpa/wpa_supplicant/ctrl_iface_named_pipe.c @@ -2,14 +2,8 @@ * WPA Supplicant / Windows Named Pipe -based control interface * 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. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/contrib/wpa/wpa_supplicant/ctrl_iface_udp.c b/contrib/wpa/wpa_supplicant/ctrl_iface_udp.c index 110ca4f..994f9b1 100644 --- a/contrib/wpa/wpa_supplicant/ctrl_iface_udp.c +++ b/contrib/wpa/wpa_supplicant/ctrl_iface_udp.c @@ -2,14 +2,8 @@ * WPA Supplicant / UDP socket -based control interface * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -169,6 +163,8 @@ static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx, perror("recvfrom(ctrl_iface)"); return; } + +#ifndef CONFIG_CTRL_IFACE_UDP_REMOTE if (from.sin_addr.s_addr != htonl((127 << 24) | 1)) { /* * The OS networking stack is expected to drop this kind of @@ -180,6 +176,8 @@ static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx, "source %s", inet_ntoa(from.sin_addr)); return; } +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ + buf[res] = '\0'; if (os_strcmp(buf, "GET_COOKIE") == 0) { @@ -272,6 +270,7 @@ wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) { struct ctrl_iface_priv *priv; struct sockaddr_in addr; + int port = WPA_CTRL_IFACE_PORT; priv = os_zalloc(sizeof(*priv)); if (priv == NULL) @@ -291,13 +290,25 @@ wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) os_memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE + addr.sin_addr.s_addr = INADDR_ANY; +#else /* CONFIG_CTRL_IFACE_UDP_REMOTE */ addr.sin_addr.s_addr = htonl((127 << 24) | 1); - addr.sin_port = htons(WPA_CTRL_IFACE_PORT); +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ +try_again: + addr.sin_port = htons(port); if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + port--; + if ((WPA_CTRL_IFACE_PORT - port) < WPA_CTRL_IFACE_PORT_LIMIT) + goto try_again; perror("bind(AF_INET)"); goto fail; } +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE + wpa_msg(wpa_s, MSG_DEBUG, "ctrl_iface_init UDP port: %d", port); +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ + eloop_register_read_sock(priv->sock, wpa_supplicant_ctrl_iface_receive, wpa_s, priv); wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb); @@ -448,6 +459,8 @@ static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx, perror("recvfrom(ctrl_iface)"); return; } + +#ifndef CONFIG_CTRL_IFACE_UDP_REMOTE if (from.sin_addr.s_addr != htonl((127 << 24) | 1)) { /* * The OS networking stack is expected to drop this kind of @@ -459,6 +472,8 @@ static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx, "source %s", inet_ntoa(from.sin_addr)); return; } +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ + buf[res] = '\0'; if (os_strcmp(buf, "GET_COOKIE") == 0) { @@ -508,6 +523,7 @@ wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global) { struct ctrl_iface_global_priv *priv; struct sockaddr_in addr; + int port = WPA_GLOBAL_CTRL_IFACE_PORT; priv = os_zalloc(sizeof(*priv)); if (priv == NULL) @@ -529,13 +545,26 @@ wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global) os_memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE + addr.sin_addr.s_addr = INADDR_ANY; +#else /* CONFIG_CTRL_IFACE_UDP_REMOTE */ addr.sin_addr.s_addr = htonl((127 << 24) | 1); - addr.sin_port = htons(WPA_GLOBAL_CTRL_IFACE_PORT); +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ +try_again: + addr.sin_port = htons(port); if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + port++; + if ((port - WPA_GLOBAL_CTRL_IFACE_PORT) < + WPA_GLOBAL_CTRL_IFACE_PORT_LIMIT) + goto try_again; perror("bind(AF_INET)"); goto fail; } +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE + wpa_printf(MSG_DEBUG, "global_ctrl_iface_init UDP port: %d", port); +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ + eloop_register_read_sock(priv->sock, wpa_supplicant_global_ctrl_iface_receive, global, priv); diff --git a/contrib/wpa/wpa_supplicant/ctrl_iface_unix.c b/contrib/wpa/wpa_supplicant/ctrl_iface_unix.c index 84ac760..f792863 100644 --- a/contrib/wpa/wpa_supplicant/ctrl_iface_unix.c +++ b/contrib/wpa/wpa_supplicant/ctrl_iface_unix.c @@ -2,14 +2,8 @@ * WPA Supplicant / UNIX domain socket -based control interface * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -17,6 +11,11 @@ #include <sys/stat.h> #include <grp.h> #include <stddef.h> +#include <unistd.h> +#include <fcntl.h> +#ifdef ANDROID +#include <cutils/sockets.h> +#endif /* ANDROID */ #include "utils/common.h" #include "utils/eloop.h" @@ -132,7 +131,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); @@ -262,6 +261,7 @@ wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) char *buf, *dir = NULL, *gid_str = NULL; struct group *grp; char *endp; + int flags; priv = os_zalloc(sizeof(*priv)); if (priv == NULL) @@ -276,6 +276,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="); @@ -298,6 +305,22 @@ wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) } } +#ifdef ANDROID + /* + * wpa_supplicant is started from /init.*.rc on Android and that seems + * to be using umask 0077 which would leave the control interface + * directory without group access. This breaks things since Wi-Fi + * framework assumes that this directory can be accessed by other + * applications in the wifi group. Fix this by adding group access even + * if umask value would prevent this. + */ + if (chmod(dir, S_IRWXU | S_IRWXG) < 0) { + wpa_printf(MSG_ERROR, "CTRL: Could not chmod directory: %s", + strerror(errno)); + /* Try to continue anyway */ + } +#endif /* ANDROID */ + if (gid_str) { grp = getgrnam(gid_str); if (grp) { @@ -371,7 +394,7 @@ wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) } if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("bind(PF_UNIX)"); + perror("supp-ctrl-iface-init: bind(PF_UNIX)"); goto fail; } wpa_printf(MSG_DEBUG, "Successfully replaced leftover " @@ -398,6 +421,23 @@ wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) } os_free(fname); +#ifdef ANDROID +havesock: +#endif /* ANDROID */ + + /* + * Make socket non-blocking so that we don't hang forever if + * target dies unexpectedly. + */ + flags = fcntl(priv->sock, F_GETFL); + if (flags >= 0) { + flags |= O_NONBLOCK; + if (fcntl(priv->sock, F_SETFL, flags) < 0) { + perror("fcntl(ctrl, O_NONBLOCK)"); + /* Not fatal, continue on.*/ + } + } + eloop_register_read_sock(priv->sock, wpa_supplicant_ctrl_iface_receive, wpa_s, priv); wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb); @@ -637,6 +677,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); @@ -654,7 +700,8 @@ wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global) os_strlcpy(addr.sun_path, global->params.ctrl_interface, sizeof(addr.sun_path)); if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("bind(PF_UNIX)"); + perror("supp-global-ctrl-iface-init (will try fixup): " + "bind(PF_UNIX)"); if (connect(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not" @@ -669,7 +716,7 @@ wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global) } if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("bind(PF_UNIX)"); + perror("supp-glb-iface-init: bind(PF_UNIX)"); goto fail; } wpa_printf(MSG_DEBUG, "Successfully replaced leftover " @@ -685,6 +732,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); diff --git a/contrib/wpa/wpa_supplicant/dbus/Makefile b/contrib/wpa/wpa_supplicant/dbus/Makefile index cfaf58d..d64c65c 100644 --- a/contrib/wpa/wpa_supplicant/dbus/Makefile +++ b/contrib/wpa/wpa_supplicant/dbus/Makefile @@ -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= \ diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_common.c b/contrib/wpa/wpa_supplicant/dbus/dbus_common.c index 5850636..5d0e31e 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_common.c +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_common.c @@ -4,14 +4,8 @@ * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com> * Copyright (c) 2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_common.h b/contrib/wpa/wpa_supplicant/dbus/dbus_common.h index 50da09b..aea7db7 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_common.h +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_common.h @@ -4,14 +4,8 @@ * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com> * Copyright (c) 2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef DBUS_COMMON_H diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_common_i.h b/contrib/wpa/wpa_supplicant/dbus/dbus_common_i.h index 9dab1ee..a551ccd 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_common_i.h +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_common_i.h @@ -4,14 +4,8 @@ * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com> * Copyright (c) 2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef DBUS_COMMON_I_H @@ -25,6 +19,10 @@ struct wpas_dbus_priv { struct wpa_global *global; u32 next_objid; int dbus_new_initialized; + +#if defined(CONFIG_CTRL_IFACE_DBUS_NEW) && defined(CONFIG_AP) + int dbus_noc_refcnt; +#endif /* CONFIG_CTRL_IFACE_DBUS_NEW && CONFIG_AP */ }; #endif /* DBUS_COMMON_I_H */ diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_dict_helpers.c b/contrib/wpa/wpa_supplicant/dbus/dbus_dict_helpers.c index b3aff40..61a9430 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_dict_helpers.c +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_dict_helpers.c @@ -2,20 +2,15 @@ * WPA Supplicant / dbus-based control interface * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc. * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include <dbus/dbus.h> #include "common.h" +#include "wpabuf.h" #include "dbus_dict_helpers.h" @@ -443,11 +438,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 +453,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 +477,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 +524,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 +643,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 +699,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,17 +729,16 @@ 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; - char *buffer, *nbuffer;; + char *buffer, *nbuffer; entry->bytearray_value = NULL; entry->array_type = DBUS_TYPE_BYTE; - buffer = os_zalloc(BYTE_ARRAY_ITEM_SIZE * BYTE_ARRAY_CHUNK_SIZE); + buffer = os_calloc(BYTE_ARRAY_CHUNK_SIZE, BYTE_ARRAY_ITEM_SIZE); if (!buffer) return FALSE; @@ -635,8 +748,9 @@ static dbus_bool_t _wpa_dbus_dict_entry_get_byte_array( char byte; if ((count % BYTE_ARRAY_CHUNK_SIZE) == 0 && count != 0) { - nbuffer = os_realloc(buffer, BYTE_ARRAY_ITEM_SIZE * - (count + BYTE_ARRAY_CHUNK_SIZE)); + nbuffer = os_realloc_array( + buffer, count + BYTE_ARRAY_CHUNK_SIZE, + BYTE_ARRAY_ITEM_SIZE); if (nbuffer == NULL) { os_free(buffer); wpa_printf(MSG_ERROR, "dbus: _wpa_dbus_dict_" @@ -682,7 +796,7 @@ static dbus_bool_t _wpa_dbus_dict_entry_get_string_array( entry->strarray_value = NULL; entry->array_type = DBUS_TYPE_STRING; - buffer = os_zalloc(STR_ARRAY_ITEM_SIZE * STR_ARRAY_CHUNK_SIZE); + buffer = os_calloc(STR_ARRAY_CHUNK_SIZE, STR_ARRAY_ITEM_SIZE); if (buffer == NULL) return FALSE; @@ -693,8 +807,9 @@ static dbus_bool_t _wpa_dbus_dict_entry_get_string_array( char *str; if ((count % STR_ARRAY_CHUNK_SIZE) == 0 && count != 0) { - nbuffer = os_realloc(buffer, STR_ARRAY_ITEM_SIZE * - (count + STR_ARRAY_CHUNK_SIZE)); + nbuffer = os_realloc_array( + buffer, count + STR_ARRAY_CHUNK_SIZE, + STR_ARRAY_ITEM_SIZE); if (nbuffer == NULL) { os_free(buffer); wpa_printf(MSG_ERROR, "dbus: _wpa_dbus_dict_" @@ -733,6 +848,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_array(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 +923,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 +930,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,9 +1091,14 @@ 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; } - memset(entry, 0, sizeof(struct wpa_dbus_dict_entry)); + os_memset(entry, 0, sizeof(struct wpa_dbus_dict_entry)); } diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_dict_helpers.h b/contrib/wpa/wpa_supplicant/dbus/dbus_dict_helpers.h index eb31575..9666349 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_dict_helpers.h +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_dict_helpers.h @@ -2,19 +2,15 @@ * WPA Supplicant / dbus-based control interface * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc. * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef DBUS_DICT_HELPERS_H #define DBUS_DICT_HELPERS_H +#include "wpabuf.h" + /* * Adding a dict to a DBusMessage */ @@ -74,7 +70,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 +86,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 +111,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 +143,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); diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new.c b/contrib/wpa/wpa_supplicant/dbus/dbus_new.c index bdfbbac..8bc6618 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new.c +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new.c @@ -4,29 +4,119 @@ * Copyright (c) 2009-2010, Witold Sowa <witold.sowa@gmail.com> * Copyright (c) 2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "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" + +#ifdef CONFIG_AP /* until needed by something else */ + +/* + * NameOwnerChanged handling + * + * Some services we provide allow an application to register for + * a signal that it needs. While it can also unregister, we must + * be prepared for the case where the application simply crashes + * and thus doesn't clean up properly. The way to handle this in + * DBus is to register for the NameOwnerChanged signal which will + * signal an owner change to NULL if the peer closes the socket + * for whatever reason. + * + * Handle this signal via a filter function whenever necessary. + * The code below also handles refcounting in case in the future + * there will be multiple instances of this subscription scheme. + */ +static const char wpas_dbus_noc_filter_str[] = + "interface=org.freedesktop.DBus,member=NameOwnerChanged"; + + +static DBusHandlerResult noc_filter(DBusConnection *conn, + DBusMessage *message, void *data) +{ + struct wpas_dbus_priv *priv = data; + + if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + if (dbus_message_is_signal(message, DBUS_INTERFACE_DBUS, + "NameOwnerChanged")) { + const char *name; + const char *prev_owner; + const char *new_owner; + DBusError derr; + struct wpa_supplicant *wpa_s; + + dbus_error_init(&derr); + + if (!dbus_message_get_args(message, &derr, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_STRING, &prev_owner, + DBUS_TYPE_STRING, &new_owner, + DBUS_TYPE_INVALID)) { + /* Ignore this error */ + dbus_error_free(&derr); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + for (wpa_s = priv->global->ifaces; wpa_s; wpa_s = wpa_s->next) + { + if (wpa_s->preq_notify_peer != NULL && + os_strcmp(name, wpa_s->preq_notify_peer) == 0 && + (new_owner == NULL || os_strlen(new_owner) == 0)) { + /* probe request owner disconnected */ + os_free(wpa_s->preq_notify_peer); + wpa_s->preq_notify_peer = NULL; + wpas_dbus_unsubscribe_noc(priv); + } + } + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + + +void wpas_dbus_subscribe_noc(struct wpas_dbus_priv *priv) +{ + priv->dbus_noc_refcnt++; + if (priv->dbus_noc_refcnt > 1) + return; + + if (!dbus_connection_add_filter(priv->con, noc_filter, priv, NULL)) { + wpa_printf(MSG_ERROR, "dbus: failed to add filter"); + return; + } + + dbus_bus_add_match(priv->con, wpas_dbus_noc_filter_str, NULL); +} + + +void wpas_dbus_unsubscribe_noc(struct wpas_dbus_priv *priv) +{ + priv->dbus_noc_refcnt--; + if (priv->dbus_noc_refcnt > 0) + return; + + dbus_bus_remove_match(priv->con, wpas_dbus_noc_filter_str, NULL); + dbus_connection_remove_filter(priv->con, noc_filter, priv); +} + +#endif /* CONFIG_AP */ /** @@ -42,7 +132,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 +151,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 +242,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 +262,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 +384,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 +410,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 +469,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,47 +786,974 @@ 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); +} + + +void wpas_dbus_signal_eap_status(struct wpa_supplicant *wpa_s, + const char *status, const char *parameter) +{ + struct wpas_dbus_priv *iface; + DBusMessage *msg; + DBusMessageIter iter; + + iface = wpa_s->global->dbus; + + /* Do nothing if the control interface is not turned on */ + if (iface == NULL) + return; + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_INTERFACE, + "EAP"); + if (msg == NULL) + return; + + dbus_message_iter_init_append(msg, &iter); + + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &status) + || + !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, + ¶meter)) + goto nomem; + + dbus_connection_send(iface->con, msg, NULL); + +nomem: + dbus_message_unref(msg); +} + + +#ifdef CONFIG_P2P + +/** + * wpas_dbus_signal_p2p_group_removed - Signals P2P group was removed + * @wpa_s: %wpa_supplicant network interface data + * @role: role of this device (client or GO) + * Sends signal with i/f name and role as string arguments + */ +void wpas_dbus_signal_p2p_group_removed(struct wpa_supplicant *wpa_s, + const char *role) +{ + + DBusMessage *msg; + DBusMessageIter iter; + struct wpas_dbus_priv *iface = wpa_s->global->dbus; + char *ifname = wpa_s->ifname; + + /* 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; + } + + 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_signal_p2p_provision_discovery - Signals various PD events + * + * @dev_addr - who sent the request or responded to our request. + * @request - Will be 1 if request, 0 for response. + * @status - valid only in case of response + * @config_methods - wps config methods + * @generated_pin - pin to be displayed in case of WPS_CONFIG_DISPLAY method + * + * Sends following provision discovery related events: + * ProvisionDiscoveryRequestDisplayPin + * ProvisionDiscoveryResponseDisplayPin + * ProvisionDiscoveryRequestEnterPin + * ProvisionDiscoveryResponseEnterPin + * ProvisionDiscoveryPBCRequest + * ProvisionDiscoveryPBCResponse + * + * TODO:: + * ProvisionDiscoveryFailure (timeout case) + */ +void wpas_dbus_signal_p2p_provision_discovery(struct wpa_supplicant *wpa_s, + const u8 *dev_addr, int request, + enum p2p_prov_disc_status status, + u16 config_methods, + unsigned int generated_pin) +{ + DBusMessage *msg; + DBusMessageIter iter; + struct wpas_dbus_priv *iface; + char *_signal; + int add_pin = 0; + char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path; + int error_ret = 1; + char pin[9], *p_pin = NULL; + + iface = wpa_s->global->dbus; + + /* Do nothing if the control interface is not turned on */ + if (iface == NULL) + return; + + if (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; + } + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_P2PDEVICE, _signal); + if (msg == NULL) + return; + + /* Check if this is a known peer */ + if (!p2p_peer_known(wpa_s->global->p2p, dev_addr)) + goto error; + + os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" + COMPACT_MACSTR, + wpa_s->dbus_new_path, MAC2STR(dev_addr)); + + path = peer_obj_path; + + dbus_message_iter_init_append(msg, &iter); + + if (!dbus_message_iter_append_basic(&iter, + DBUS_TYPE_OBJECT_PATH, + &path)) + goto error; + + if (!request && status) + /* Attach status to ProvisionDiscoveryFailure */ + error_ret = !dbus_message_iter_append_basic(&iter, + DBUS_TYPE_INT32, + &status); + else + error_ret = (add_pin && + !dbus_message_iter_append_basic(&iter, + DBUS_TYPE_STRING, + &p_pin)); + +error: + if (!error_ret) + dbus_connection_send(iface->con, msg, NULL); + else + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + + dbus_message_unref(msg); +} + + +void wpas_dbus_signal_p2p_go_neg_req(struct wpa_supplicant *wpa_s, + const u8 *src, u16 dev_passwd_id) +{ + 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); +} + + +static int wpas_dbus_get_group_obj_path(struct wpa_supplicant *wpa_s, + const struct wpa_ssid *ssid, + char *group_obj_path) +{ + char group_name[3]; + + if (os_memcmp(ssid->ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN)) + return -1; + + os_memcpy(group_name, ssid->ssid + P2P_WILDCARD_SSID_LEN, 2); + group_name[2] = '\0'; + + os_snprintf(group_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_P2P_GROUPS_PART "/%s", + wpa_s->dbus_new_path, group_name); + + return 0; +} + + +/** + * wpas_dbus_signal_p2p_group_started - Signals P2P group has + * started. Emitted when a group is successfully started + * irrespective of the role (client/GO) of the current device + * + * @wpa_s: %wpa_supplicant network interface data + * @ssid: SSID object + * @client: this device is P2P client + * @network_id: network id of the group started, use instead of ssid->id + * to account for persistent groups + */ +void wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s, + const struct wpa_ssid *ssid, + int client, int network_id) +{ + DBusMessage *msg; + DBusMessageIter iter, dict_iter; + struct wpas_dbus_priv *iface; + char group_obj_path[WPAS_DBUS_OBJECT_PATH_MAX]; + + iface = wpa_s->parent->global->dbus; + + /* Do nothing if the control interface is not turned on */ + if (iface == NULL) + return; + + if (wpas_dbus_get_group_obj_path(wpa_s, ssid, group_obj_path) < 0) + 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; + + if (!wpa_dbus_dict_append_object_path(&dict_iter, "group_object", + group_obj_path) || + !wpa_dbus_dict_close_write(&iter, &dict_iter)) + goto nomem; + + dbus_connection_send(iface->con, msg, NULL); + +nomem: + 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) +{ + DBusMessage *msg; + DBusMessageIter iter, dict_iter; + DBusMessageIter iter_dict_entry, iter_dict_val, iter_dict_array; + struct wpas_dbus_priv *iface; + char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path; + dbus_int32_t freqs[P2P_MAX_CHANNELS]; + dbus_int32_t *f_array = freqs; + + + iface = wpa_s->global->dbus; + + 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); +} + + +/** + * + * 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 ProertyChanged signals with path, interface and arguments + * 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) { - WPADBusPropertyAccessor getter; char *prop; + dbus_bool_t flush; if (wpa_s->dbus_new_path == NULL) return; /* Skip signal since D-Bus setup is not yet ready */ + flush = FALSE; switch (property) { case WPAS_DBUS_PROP_AP_SCAN: - 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; + case WPAS_DBUS_PROP_BSSS: + prop = "BSSs"; + break; + case WPAS_DBUS_PROP_CURRENT_AUTH_MODE: + prop = "CurrentAuthMode"; + break; + case WPAS_DBUS_PROP_DISCONNECT_REASON: + prop = "DisconnectReason"; + flush = TRUE; + break; default: wpa_printf(MSG_ERROR, "dbus: %s: Unknown Property value %d", __func__, property); @@ -700,6 +1763,10 @@ void wpas_dbus_signal_prop_changed(struct wpa_supplicant *wpa_s, wpa_dbus_mark_property_changed(wpa_s->global->dbus, wpa_s->dbus_new_path, WPAS_DBUS_NEW_IFACE_INTERFACE, prop); + if (flush) { + wpa_dbus_flush_object_changed_properties( + wpa_s->global->dbus->con, wpa_s->dbus_new_path); + } } @@ -763,7 +1830,7 @@ void wpas_dbus_bss_signal_prop_changed(struct wpa_supplicant *wpa_s, * wpas_dbus_signal_debug_level_changed - Signals change of debug param * @global: wpa_global structure * - * Sends ProertyChanged signals informing that debug level has changed. + * Sends PropertyChanged signals informing that debug level has changed. */ void wpas_dbus_signal_debug_level_changed(struct wpa_global *global) { @@ -777,7 +1844,7 @@ void wpas_dbus_signal_debug_level_changed(struct wpa_global *global) * wpas_dbus_signal_debug_timestamp_changed - Signals change of debug param * @global: wpa_global structure * - * Sends ProertyChanged signals informing that debug timestamp has changed. + * Sends PropertyChanged signals informing that debug timestamp has changed. */ void wpas_dbus_signal_debug_timestamp_changed(struct wpa_global *global) { @@ -791,7 +1858,7 @@ void wpas_dbus_signal_debug_timestamp_changed(struct wpa_global *global) * wpas_dbus_signal_debug_show_keys_changed - Signals change of debug param * @global: wpa_global structure * - * Sends ProertyChanged signals informing that debug show_keys has changed. + * Sends PropertyChanged signals informing that debug show_keys has changed. */ void wpas_dbus_signal_debug_show_keys_changed(struct wpa_global *global) { @@ -850,36 +1917,44 @@ static const struct wpa_dbus_method_desc wpas_dbus_global_methods[] = { END_ARGS } }, +#ifdef CONFIG_AUTOSCAN + { "AutoScan", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) &wpas_dbus_handler_autoscan, + { + { "arg", "s", ARG_IN }, + END_ARGS + } + }, +#endif /* CONFIG_AUTOSCAN */ { 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 + wpas_dbus_getter_debug_level, + wpas_dbus_setter_debug_level }, { "DebugTimestamp", WPAS_DBUS_NEW_INTERFACE, "b", - (WPADBusPropertyAccessor) wpas_dbus_getter_debug_timestamp, - (WPADBusPropertyAccessor) wpas_dbus_setter_debug_timestamp, - RW + wpas_dbus_getter_debug_timestamp, + wpas_dbus_setter_debug_timestamp }, { "DebugShowKeys", WPAS_DBUS_NEW_INTERFACE, "b", - (WPADBusPropertyAccessor) wpas_dbus_getter_debug_show_keys, - (WPADBusPropertyAccessor) wpas_dbus_setter_debug_show_keys, - RW + wpas_dbus_getter_debug_show_keys, + wpas_dbus_setter_debug_show_keys }, { "Interfaces", WPAS_DBUS_NEW_INTERFACE, "ao", - (WPADBusPropertyAccessor) &wpas_dbus_getter_interfaces, - NULL, - R + wpas_dbus_getter_interfaces, + NULL }, { "EapMethods", WPAS_DBUS_NEW_INTERFACE, "as", - (WPADBusPropertyAccessor) wpas_dbus_getter_eap_methods, - NULL, - R + wpas_dbus_getter_eap_methods, + NULL + }, + { "Capabilities", WPAS_DBUS_NEW_INTERFACE, "as", + wpas_dbus_getter_global_capabilities, + NULL }, - { NULL, NULL, NULL, NULL, NULL, 0 } + { NULL, NULL, NULL, NULL, NULL } }; static const struct wpa_dbus_signal_desc wpas_dbus_global_signals[] = { @@ -896,6 +1971,15 @@ static const struct wpa_dbus_signal_desc wpas_dbus_global_signals[] = { END_ARGS } }, + { "NetworkRequest", WPAS_DBUS_NEW_IFACE_INTERFACE, + { + { "path", "o", ARG_OUT }, + { "field", "s", ARG_OUT }, + { "text", "s", ARG_OUT }, + END_ARGS + } + }, + /* Deprecated: use org.freedesktop.DBus.Properties.PropertiesChanged */ { "PropertiesChanged", WPAS_DBUS_NEW_INTERFACE, { { "properties", "a{sv}", ARG_OUT }, @@ -972,20 +2056,19 @@ static void wpa_dbus_free(void *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 + wpas_dbus_getter_network_properties, + wpas_dbus_setter_network_properties }, { "Enabled", WPAS_DBUS_NEW_IFACE_NETWORK, "b", - (WPADBusPropertyAccessor) wpas_dbus_getter_enabled, - (WPADBusPropertyAccessor) wpas_dbus_setter_enabled, - RW + wpas_dbus_getter_enabled, + wpas_dbus_setter_enabled }, - { NULL, NULL, NULL, NULL, NULL, 0 } + { 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 }, @@ -1012,6 +2095,16 @@ int wpas_dbus_register_network(struct wpa_supplicant *wpa_s, 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; @@ -1074,10 +2167,18 @@ int wpas_dbus_unregister_network(struct wpa_supplicant *wpa_s, int nid) struct wpas_dbus_priv *ctrl_iface; char net_obj_path[WPAS_DBUS_OBJECT_PATH_MAX]; int ret; +#ifdef CONFIG_P2P + struct wpa_ssid *ssid; + + ssid = wpa_config_get_network(wpa_s->conf, nid); + + /* If it is a persistent group unregister it as such */ + if (ssid && network_is_persistent_group(ssid)) + return wpas_dbus_unregister_persistent_group(wpa_s, nid); +#endif /* CONFIG_P2P */ /* Do nothing if the control interface is not turned on */ - if (wpa_s == NULL || wpa_s->global == NULL || - wpa_s->dbus_new_path == NULL) + if (wpa_s->global == NULL || wpa_s->dbus_new_path == NULL) return 0; ctrl_iface = wpa_s->global->dbus; if (ctrl_iface == NULL) @@ -1100,60 +2201,55 @@ int wpas_dbus_unregister_network(struct wpa_supplicant *wpa_s, int nid) 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 + wpas_dbus_getter_bss_ssid, + NULL }, { "BSSID", WPAS_DBUS_NEW_IFACE_BSS, "ay", - (WPADBusPropertyAccessor) wpas_dbus_getter_bss_bssid, - NULL, - R + wpas_dbus_getter_bss_bssid, + NULL }, { "Privacy", WPAS_DBUS_NEW_IFACE_BSS, "b", - (WPADBusPropertyAccessor) wpas_dbus_getter_bss_privacy, - NULL, - R + wpas_dbus_getter_bss_privacy, + NULL }, { "Mode", WPAS_DBUS_NEW_IFACE_BSS, "s", - (WPADBusPropertyAccessor) wpas_dbus_getter_bss_mode, - NULL, - R + wpas_dbus_getter_bss_mode, + NULL }, { "Signal", WPAS_DBUS_NEW_IFACE_BSS, "n", - (WPADBusPropertyAccessor) wpas_dbus_getter_bss_signal, - NULL, - R + wpas_dbus_getter_bss_signal, + NULL }, { "Frequency", WPAS_DBUS_NEW_IFACE_BSS, "q", - (WPADBusPropertyAccessor) wpas_dbus_getter_bss_frequency, - NULL, - R + wpas_dbus_getter_bss_frequency, + NULL }, { "Rates", WPAS_DBUS_NEW_IFACE_BSS, "au", - (WPADBusPropertyAccessor) wpas_dbus_getter_bss_rates, - NULL, - R + wpas_dbus_getter_bss_rates, + NULL }, { "WPA", WPAS_DBUS_NEW_IFACE_BSS, "a{sv}", - (WPADBusPropertyAccessor) wpas_dbus_getter_bss_wpa, - NULL, - R + wpas_dbus_getter_bss_wpa, + NULL }, { "RSN", WPAS_DBUS_NEW_IFACE_BSS, "a{sv}", - (WPADBusPropertyAccessor) wpas_dbus_getter_bss_rsn, - NULL, - R + wpas_dbus_getter_bss_rsn, + NULL + }, + { "WPS", WPAS_DBUS_NEW_IFACE_BSS, "a{sv}", + wpas_dbus_getter_bss_wps, + NULL }, { "IEs", WPAS_DBUS_NEW_IFACE_BSS, "ay", - (WPADBusPropertyAccessor) wpas_dbus_getter_bss_ies, - NULL, - R + wpas_dbus_getter_bss_ies, + NULL }, - { NULL, NULL, NULL, NULL, NULL, 0 } + { 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 }, @@ -1199,6 +2295,7 @@ int wpas_dbus_unregister_bss(struct wpa_supplicant *wpa_s, } wpas_dbus_signal_bss_removed(wpa_s, bss_obj_path); + wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_BSSS); return 0; } @@ -1263,6 +2360,7 @@ int wpas_dbus_register_bss(struct wpa_supplicant *wpa_s, } wpas_dbus_signal_bss_added(wpa_s, bss_obj_path); + wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_BSSS); return 0; @@ -1294,6 +2392,12 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { END_ARGS } }, + { "Reassociate", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) &wpas_dbus_handler_reassociate, + { + END_ARGS + } + }, { "RemoveNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE, (WPADBusMethodHandler) &wpas_dbus_handler_remove_network, { @@ -1301,6 +2405,12 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { 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, { @@ -1308,6 +2418,15 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { 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, { @@ -1341,67 +2460,308 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { } }, #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 + } + }, +#ifdef CONFIG_AP + { "SubscribeProbeReq", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) wpas_dbus_handler_subscribe_preq, + { + END_ARGS + } + }, + { "UnsubscribeProbeReq", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) wpas_dbus_handler_unsubscribe_preq, + { + END_ARGS + } + }, +#endif /* CONFIG_AP */ { NULL, NULL, NULL, { END_ARGS } } }; 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 + wpas_dbus_getter_capabilities, + NULL }, { "State", WPAS_DBUS_NEW_IFACE_INTERFACE, "s", - (WPADBusPropertyAccessor) wpas_dbus_getter_state, - NULL, R + wpas_dbus_getter_state, + NULL }, { "Scanning", WPAS_DBUS_NEW_IFACE_INTERFACE, "b", - (WPADBusPropertyAccessor) wpas_dbus_getter_scanning, - NULL, R + wpas_dbus_getter_scanning, + NULL }, { "ApScan", WPAS_DBUS_NEW_IFACE_INTERFACE, "u", - (WPADBusPropertyAccessor) wpas_dbus_getter_ap_scan, - (WPADBusPropertyAccessor) wpas_dbus_setter_ap_scan, - RW + 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", - (WPADBusPropertyAccessor) wpas_dbus_getter_ifname, - NULL, R + wpas_dbus_getter_ifname, + NULL }, { "Driver", WPAS_DBUS_NEW_IFACE_INTERFACE, "s", - (WPADBusPropertyAccessor) wpas_dbus_getter_driver, - NULL, R + wpas_dbus_getter_driver, + NULL }, { "BridgeIfname", WPAS_DBUS_NEW_IFACE_INTERFACE, "s", - (WPADBusPropertyAccessor) wpas_dbus_getter_bridge_ifname, - NULL, R + wpas_dbus_getter_bridge_ifname, + NULL }, { "CurrentBSS", WPAS_DBUS_NEW_IFACE_INTERFACE, "o", - (WPADBusPropertyAccessor) wpas_dbus_getter_current_bss, - NULL, R + wpas_dbus_getter_current_bss, + NULL }, { "CurrentNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE, "o", - (WPADBusPropertyAccessor) wpas_dbus_getter_current_network, - NULL, R + 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}", - (WPADBusPropertyAccessor) wpas_dbus_getter_blobs, - NULL, R + wpas_dbus_getter_blobs, + NULL }, { "BSSs", WPAS_DBUS_NEW_IFACE_INTERFACE, "ao", - (WPADBusPropertyAccessor) wpas_dbus_getter_bsss, - NULL, R + wpas_dbus_getter_bsss, + NULL }, { "Networks", WPAS_DBUS_NEW_IFACE_INTERFACE, "ao", - (WPADBusPropertyAccessor) wpas_dbus_getter_networks, - NULL, R + wpas_dbus_getter_networks, + NULL + }, + { "FastReauth", WPAS_DBUS_NEW_IFACE_INTERFACE, "b", + wpas_dbus_getter_fast_reauth, + wpas_dbus_setter_fast_reauth + }, + { "ScanInterval", WPAS_DBUS_NEW_IFACE_INTERFACE, "i", + wpas_dbus_getter_scan_interval, + wpas_dbus_setter_scan_interval }, #ifdef CONFIG_WPS { "ProcessCredentials", WPAS_DBUS_NEW_IFACE_WPS, "b", - (WPADBusPropertyAccessor) wpas_dbus_getter_process_credentials, - (WPADBusPropertyAccessor) wpas_dbus_setter_process_credentials, - RW + wpas_dbus_getter_process_credentials, + wpas_dbus_setter_process_credentials }, #endif /* CONFIG_WPS */ - { NULL, NULL, NULL, NULL, NULL, 0 } +#ifdef CONFIG_P2P + { "P2PDeviceConfig", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "a{sv}", + wpas_dbus_getter_p2p_device_config, + wpas_dbus_setter_p2p_device_config + }, + { "Peers", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "ao", + wpas_dbus_getter_p2p_peers, + NULL + }, + { "Role", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "s", + wpas_dbus_getter_p2p_role, + NULL + }, + { "Group", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "o", + wpas_dbus_getter_p2p_group, + NULL + }, + { "PeerGO", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "o", + wpas_dbus_getter_p2p_peergo, + NULL + }, + { "PersistentGroups", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "ao", + wpas_dbus_getter_persistent_groups, + NULL + }, +#endif /* CONFIG_P2P */ + { "DisconnectReason", WPAS_DBUS_NEW_IFACE_INTERFACE, "i", + wpas_dbus_getter_disconnect_reason, + NULL + }, + { NULL, NULL, NULL, NULL, NULL } }; static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = { @@ -1455,6 +2815,7 @@ static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = { END_ARGS } }, + /* Deprecated: use org.freedesktop.DBus.Properties.PropertiesChanged */ { "PropertiesChanged", WPAS_DBUS_NEW_IFACE_INTERFACE, { { "properties", "a{sv}", ARG_OUT }, @@ -1475,6 +2836,7 @@ static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = { END_ARGS } }, + /* Deprecated: use org.freedesktop.DBus.Properties.PropertiesChanged */ { "PropertiesChanged", WPAS_DBUS_NEW_IFACE_WPS, { { "properties", "a{sv}", ARG_OUT }, @@ -1482,6 +2844,162 @@ static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = { } }, #endif /* CONFIG_WPS */ +#ifdef CONFIG_P2P + { "P2PStateChanged", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + { + { "states", "a{ss}", ARG_OUT }, + END_ARGS + } + }, + { "DeviceFound", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + { + { "path", "o", ARG_OUT }, + { "properties", "a{sv}", ARG_OUT }, + END_ARGS + } + }, + { "DeviceLost", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + { + { "path", "o", ARG_OUT }, + END_ARGS + } + }, + { "ProvisionDiscoveryRequestDisplayPin", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + { + { "peer_object", "o", ARG_OUT }, + { "pin", "s", ARG_OUT }, + END_ARGS + } + }, + { "ProvisionDiscoveryResponseDisplayPin", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + { + { "peer_object", "o", ARG_OUT }, + { "pin", "s", ARG_OUT }, + END_ARGS + } + }, + { "ProvisionDiscoveryRequestEnterPin", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + { + { "peer_object", "o", ARG_OUT }, + END_ARGS + } + }, + { "ProvisionDiscoveryResponseEnterPin", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + { + { "peer_object", "o", ARG_OUT }, + END_ARGS + } + }, + { "ProvisionDiscoveryPBCRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + { + { "peer_object", "o", ARG_OUT }, + END_ARGS + } + }, + { "ProvisionDiscoveryPBCResponse", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + { + { "peer_object", "o", ARG_OUT }, + END_ARGS + } + }, + { "ProvisionDiscoveryFailure", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + { + { "peer_object", "o", ARG_OUT }, + { "status", "i", ARG_OUT }, + END_ARGS + } + }, + { "GroupStarted", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + { + { "properties", "a{sv}", ARG_OUT }, + END_ARGS + } + }, + { "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 + } + }, + { "InvitationResult", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + { + { "invite_result", "a{sv}", ARG_OUT }, + END_ARGS + } + }, + { "GroupFinished", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + { + { "ifname", "s", ARG_OUT }, + { "role", "s", ARG_OUT }, + END_ARGS + } + }, + { "ServiceDiscoveryRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + { + { "sd_request", "a{sv}", ARG_OUT }, + END_ARGS + } + }, + { "ServiceDiscoveryResponse", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + { + { "sd_response", "a{sv}", ARG_OUT }, + END_ARGS + } + }, + { "PersistentGroupAdded", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + { + { "path", "o", ARG_OUT }, + { "properties", "a{sv}", ARG_OUT }, + END_ARGS + } + }, + { "PersistentGroupRemoved", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + { + { "path", "o", ARG_OUT }, + END_ARGS + } + }, + { "WpsFailed", WPAS_DBUS_NEW_IFACE_P2PDEVICE, + { + { "name", "s", ARG_OUT }, + { "args", "a{sv}", ARG_OUT }, + END_ARGS + } + }, +#endif /* CONFIG_P2P */ +#ifdef CONFIG_AP + { "ProbeRequest", WPAS_DBUS_NEW_IFACE_INTERFACE, + { + { "args", "a{sv}", ARG_OUT }, + END_ARGS + } + }, +#endif /* CONFIG_AP */ + { "Certification", WPAS_DBUS_NEW_IFACE_INTERFACE, + { + { "certification", "a{sv}", ARG_OUT }, + END_ARGS + } + }, + { "EAP", WPAS_DBUS_NEW_IFACE_INTERFACE, + { + { "status", "s", ARG_OUT }, + { "parameter", "s", ARG_OUT }, + END_ARGS + } + }, { NULL, NULL, { END_ARGS } } }; @@ -1549,6 +3067,15 @@ int wpas_dbus_unregister_interface(struct wpa_supplicant *wpa_s) wpa_printf(MSG_DEBUG, "dbus: Unregister interface object '%s'", wpa_s->dbus_new_path); + +#ifdef CONFIG_AP + if (wpa_s->preq_notify_peer) { + wpas_dbus_unsubscribe_noc(ctrl_iface); + os_free(wpa_s->preq_notify_peer); + wpa_s->preq_notify_peer = NULL; + } +#endif /* CONFIG_AP */ + if (wpa_dbus_unregister_object_per_iface(ctrl_iface, wpa_s->dbus_new_path)) return -1; @@ -1560,3 +3087,624 @@ int wpas_dbus_unregister_interface(struct wpa_supplicant *wpa_s) return 0; } + +#ifdef CONFIG_P2P + +static const struct wpa_dbus_property_desc wpas_dbus_p2p_peer_properties[] = { + { "DeviceName", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s", + wpas_dbus_getter_p2p_peer_device_name, + NULL + }, + { "PrimaryDeviceType", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ay", + wpas_dbus_getter_p2p_peer_primary_device_type, + NULL + }, + { "config_method", WPAS_DBUS_NEW_IFACE_P2P_PEER, "q", + wpas_dbus_getter_p2p_peer_config_method, + NULL + }, + { "level", WPAS_DBUS_NEW_IFACE_P2P_PEER, "i", + wpas_dbus_getter_p2p_peer_level, + NULL + }, + { "devicecapability", WPAS_DBUS_NEW_IFACE_P2P_PEER, "y", + wpas_dbus_getter_p2p_peer_device_capability, + NULL + }, + { "groupcapability", WPAS_DBUS_NEW_IFACE_P2P_PEER, "y", + wpas_dbus_getter_p2p_peer_group_capability, + NULL + }, + { "SecondaryDeviceTypes", WPAS_DBUS_NEW_IFACE_P2P_PEER, "aay", + wpas_dbus_getter_p2p_peer_secondary_device_types, + NULL + }, + { "VendorExtension", WPAS_DBUS_NEW_IFACE_P2P_PEER, "aay", + wpas_dbus_getter_p2p_peer_vendor_extension, + NULL + }, + { "IEs", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ay", + wpas_dbus_getter_p2p_peer_ies, + NULL + }, + { NULL, NULL, NULL, NULL, NULL } +}; + +static const struct wpa_dbus_signal_desc wpas_dbus_p2p_peer_signals[] = { + + { NULL, NULL, { END_ARGS } } +}; + +/** + * wpas_dbus_signal_peer - Send a peer related event signal + * @wpa_s: %wpa_supplicant network interface data + * @dev: peer device object + * @interface: name of the interface emitting this signal. + * In case of peer objects, it would be emitted by either + * the "interface object" or by "peer objects" + * @sig_name: signal name - DeviceFound + * + * Notify listeners about event related with newly found p2p peer device + */ +static void wpas_dbus_signal_peer(struct wpa_supplicant *wpa_s, + const u8 *dev_addr, const char *interface, + const char *sig_name) +{ + struct wpas_dbus_priv *iface; + DBusMessage *msg; + DBusMessageIter iter; + char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path; + + 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(dev_addr)); + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, interface, + sig_name); + if (msg == NULL) + return; + + dbus_message_iter_init_append(msg, &iter); + path = peer_obj_path; + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, + &path)) + 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_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 + * @ssid: network configuration data + * Returns: 0 on success, -1 on failure + * + * Registers network representing object with dbus + */ +int wpas_dbus_register_peer(struct wpa_supplicant *wpa_s, const u8 *dev_addr) +{ + struct wpas_dbus_priv *ctrl_iface; + struct wpa_dbus_object_desc *obj_desc; + struct peer_handler_args *arg; + char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX]; + + /* Do nothing if the control interface is not turned on */ + if (wpa_s == NULL || wpa_s->global == NULL) + return 0; + + ctrl_iface = wpa_s->global->dbus; + if (ctrl_iface == NULL) + return 0; + + os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR, + wpa_s->dbus_new_path, MAC2STR(dev_addr)); + + wpa_printf(MSG_INFO, "dbus: Register peer object '%s'", + peer_obj_path); + obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc)); + if (!obj_desc) { + wpa_printf(MSG_ERROR, "Not enough memory " + "to create object description"); + goto err; + } + + /* allocate memory for handlers arguments */ + arg = os_zalloc(sizeof(struct peer_handler_args)); + if (!arg) { + wpa_printf(MSG_ERROR, "Not enough memory " + "to create arguments for method"); + goto err; + } + + arg->wpa_s = wpa_s; + os_memcpy(arg->p2p_device_addr, dev_addr, ETH_ALEN); + + wpas_dbus_register(obj_desc, arg, wpa_dbus_free, + NULL, + wpas_dbus_p2p_peer_properties, + wpas_dbus_p2p_peer_signals); + + if (wpa_dbus_register_object_per_iface(ctrl_iface, peer_obj_path, + wpa_s->ifname, obj_desc)) + goto err; + + return 0; + +err: + free_dbus_object_desc(obj_desc); + return -1; +} + +/** + * wpas_dbus_unregister_peer - Unregister a peer object with dbus + * @wpa_s: wpa_supplicant interface structure + * @dev_addr: p2p device addr + * Returns: 0 on success, -1 on failure + * + * Registers network representing object with dbus + */ +int wpas_dbus_unregister_peer(struct wpa_supplicant *wpa_s, + const u8 *dev_addr) +{ + struct wpas_dbus_priv *ctrl_iface; + char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX]; + int ret; + + /* Do nothing if the control interface is not turned on */ + if (wpa_s == 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; + + os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR, + wpa_s->dbus_new_path, MAC2STR(dev_addr)); + + wpa_printf(MSG_INFO, "dbus: Unregister peer object '%s'", + peer_obj_path); + ret = wpa_dbus_unregister_object_per_iface(ctrl_iface, peer_obj_path); + + return ret; +} + + +static const struct wpa_dbus_property_desc wpas_dbus_p2p_group_properties[] = { + { "Members", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ao", + wpas_dbus_getter_p2p_group_members, + NULL + }, + { "Group", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "o", + wpas_dbus_getter_p2p_group, + NULL + }, + { "Role", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "s", + wpas_dbus_getter_p2p_role, + NULL + }, + { "SSID", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ay", + wpas_dbus_getter_p2p_group_ssid, + NULL + }, + { "BSSID", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ay", + wpas_dbus_getter_p2p_group_bssid, + NULL + }, + { "Frequency", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "q", + wpas_dbus_getter_p2p_group_frequency, + NULL + }, + { "Passphrase", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "s", + wpas_dbus_getter_p2p_group_passphrase, + NULL + }, + { "PSK", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ay", + wpas_dbus_getter_p2p_group_psk, + NULL + }, + { "WPSVendorExtensions", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "aay", + wpas_dbus_getter_p2p_group_vendor_ext, + wpas_dbus_setter_p2p_group_vendor_ext + }, + { NULL, NULL, NULL, NULL, NULL } +}; + +static const struct wpa_dbus_signal_desc wpas_dbus_p2p_group_signals[] = { + { "PeerJoined", WPAS_DBUS_NEW_IFACE_P2P_GROUP, + { + { "peer", "o", ARG_OUT }, + END_ARGS + } + }, + { "PeerDisconnected", WPAS_DBUS_NEW_IFACE_P2P_GROUP, + { + { "peer", "o", ARG_OUT }, + END_ARGS + } + }, + { NULL, NULL, { END_ARGS } } +}; + +/** + * wpas_dbus_register_p2p_group - Register a p2p group object with dbus + * @wpa_s: wpa_supplicant interface structure + * @ssid: SSID struct + * Returns: 0 on success, -1 on failure + * + * Registers p2p group representing object with dbus + */ +void wpas_dbus_register_p2p_group(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + struct wpas_dbus_priv *ctrl_iface; + struct wpa_dbus_object_desc *obj_desc; + char group_obj_path[WPAS_DBUS_OBJECT_PATH_MAX]; + + /* Do nothing if the control interface is not turned on */ + if (wpa_s == NULL || wpa_s->global == NULL) + return; + + ctrl_iface = wpa_s->global->dbus; + if (ctrl_iface == NULL) + return; + + if (wpa_s->dbus_groupobj_path) { + wpa_printf(MSG_INFO, "%s: Group object '%s' already exists", + __func__, wpa_s->dbus_groupobj_path); + return; + } + + if (wpas_dbus_get_group_obj_path(wpa_s, ssid, group_obj_path) < 0) + return; + + wpa_s->dbus_groupobj_path = os_strdup(group_obj_path); + if (wpa_s->dbus_groupobj_path == NULL) + return; + + wpa_printf(MSG_INFO, "dbus: Register group object '%s'", + group_obj_path); + obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc)); + if (!obj_desc) { + wpa_printf(MSG_ERROR, "Not enough memory " + "to create object description"); + goto err; + } + + wpas_dbus_register(obj_desc, wpa_s, NULL, NULL, + wpas_dbus_p2p_group_properties, + wpas_dbus_p2p_group_signals); + + if (wpa_dbus_register_object_per_iface(ctrl_iface, group_obj_path, + wpa_s->ifname, obj_desc)) + goto err; + + return; + +err: + if (wpa_s->dbus_groupobj_path) { + os_free(wpa_s->dbus_groupobj_path); + wpa_s->dbus_groupobj_path = NULL; + } + + free_dbus_object_desc(obj_desc); +} + +/** + * wpas_dbus_unregister_p2p_group - Unregister a p2p group object from dbus + * @wpa_s: wpa_supplicant interface structure + * @ssid: network name of the p2p group started + */ +void wpas_dbus_unregister_p2p_group(struct wpa_supplicant *wpa_s, + const struct wpa_ssid *ssid) +{ + struct wpas_dbus_priv *ctrl_iface; + + /* Do nothing if the control interface is not turned on */ + if (wpa_s == NULL || wpa_s->global == NULL) + return; + + 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 groupmember_handler_args *arg; + char groupmember_obj_path[WPAS_DBUS_OBJECT_PATH_MAX]; + + /* Do nothing if the control interface is not turned on */ + if (wpa_s == NULL || wpa_s->global == NULL) + return; + + ctrl_iface = wpa_s->global->dbus; + if (ctrl_iface == NULL) + return; + + if (!wpa_s->dbus_groupobj_path) + return; + + os_snprintf(groupmember_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_P2P_GROUPMEMBERS_PART "/" COMPACT_MACSTR, + wpa_s->dbus_groupobj_path, MAC2STR(p2p_if_addr)); + + obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc)); + if (!obj_desc) { + wpa_printf(MSG_ERROR, "Not enough memory " + "to create object description"); + goto err; + } + + /* allocate memory for handlers arguments */ + arg = os_zalloc(sizeof(struct groupmember_handler_args)); + if (!arg) { + wpa_printf(MSG_ERROR, "Not enough memory " + "to create arguments for method"); + goto err; + } + + arg->wpa_s = wpa_s; + os_memcpy(arg->member_addr, p2p_if_addr, ETH_ALEN); + + wpas_dbus_register(obj_desc, arg, wpa_dbus_free, NULL, + wpas_dbus_p2p_groupmember_properties, NULL); + + if (wpa_dbus_register_object_per_iface(ctrl_iface, groupmember_obj_path, + wpa_s->ifname, obj_desc)) + goto err; + + wpa_printf(MSG_INFO, + "dbus: Registered group member object '%s' successfully", + groupmember_obj_path); + return; + +err: + free_dbus_object_desc(obj_desc); +} + +/** + * wpas_dbus_unregister_p2p_groupmember - Unregister a p2p groupmember + * object with dbus + * @wpa_s: wpa_supplicant interface structure + * @p2p_if_addr: i/f addr of the device joining this group + * + * Unregisters p2p groupmember representing object with dbus + */ +void wpas_dbus_unregister_p2p_groupmember(struct wpa_supplicant *wpa_s, + const u8 *p2p_if_addr) +{ + struct wpas_dbus_priv *ctrl_iface; + char groupmember_obj_path[WPAS_DBUS_OBJECT_PATH_MAX]; + + /* Do nothing if the control interface is not turned on */ + if (wpa_s == NULL || wpa_s->global == NULL) + return; + + ctrl_iface = wpa_s->global->dbus; + if (ctrl_iface == NULL) + return; + + if (!wpa_s->dbus_groupobj_path) + return; + + os_snprintf(groupmember_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_P2P_GROUPMEMBERS_PART "/" COMPACT_MACSTR, + wpa_s->dbus_groupobj_path, MAC2STR(p2p_if_addr)); + + wpa_dbus_unregister_object_per_iface(ctrl_iface, groupmember_obj_path); +} + + +static const struct wpa_dbus_property_desc + wpas_dbus_persistent_group_properties[] = { + { "Properties", WPAS_DBUS_NEW_IFACE_PERSISTENT_GROUP, "a{sv}", + wpas_dbus_getter_persistent_group_properties, + 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: + free_dbus_object_desc(obj_desc); + return -1; +} + + +/** + * wpas_dbus_unregister_persistent_group - Unregister a persistent_group + * from dbus + * @wpa_s: wpa_supplicant interface structure + * @nid: network id + * Returns: 0 on success, -1 on failure + * + * Unregisters persistent group representing object from dbus + * + * NOTE: There is a slight issue with the semantics here. While the + * implementation simply means the persistent group is unloaded from memory, + * it should not get interpreted as the group is actually being erased/removed + * from persistent storage as well. + */ +int wpas_dbus_unregister_persistent_group(struct wpa_supplicant *wpa_s, + int nid) +{ + struct wpas_dbus_priv *ctrl_iface; + char pgrp_obj_path[WPAS_DBUS_OBJECT_PATH_MAX]; + int ret; + + /* Do nothing if the control interface is not turned on */ + if (wpa_s == 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; + + os_snprintf(pgrp_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/%u", + wpa_s->dbus_new_path, nid); + + wpa_printf(MSG_DEBUG, "dbus: Unregister persistent group object '%s'", + pgrp_obj_path); + ret = wpa_dbus_unregister_object_per_iface(ctrl_iface, pgrp_obj_path); + + if (!ret) + wpas_dbus_signal_persistent_group_removed(wpa_s, nid); + + return ret; +} + +#endif /* CONFIG_P2P */ diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new.h b/contrib/wpa/wpa_supplicant/dbus/dbus_new.h index 80ea98c..363a7e5 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new.h +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new.h @@ -3,26 +3,22 @@ * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc. * Copyright (c) 2009-2010, Witold Sowa <witold.sowa@gmail.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. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef CTRL_IFACE_DBUS_NEW_H #define CTRL_IFACE_DBUS_NEW_H +#include "common/defs.h" +#include "p2p/p2p.h" + struct wpa_global; struct wpa_supplicant; struct wpa_ssid; struct wps_event_m2d; struct wps_event_fail; struct wps_credential; -enum wpa_states; enum wpas_dbus_prop { WPAS_DBUS_PROP_AP_SCAN, @@ -30,6 +26,9 @@ 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, + WPAS_DBUS_PROP_DISCONNECT_REASON, }; enum wpas_dbus_bss_prop { @@ -40,6 +39,7 @@ enum wpas_dbus_bss_prop { WPAS_DBUS_BSS_PROP_RATES, WPAS_DBUS_BSS_PROP_WPA, WPAS_DBUS_BSS_PROP_RSN, + WPAS_DBUS_BSS_PROP_WPS, WPAS_DBUS_BSS_PROP_IES, }; @@ -59,6 +59,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,11 +99,29 @@ 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 \ WPAS_DBUS_NEW_INTERFACE ".BlobUnknown" +#define WPAS_DBUS_ERROR_SUBSCRIPTION_IN_USE \ + WPAS_DBUS_NEW_INTERFACE ".SubscriptionInUse" +#define WPAS_DBUS_ERROR_NO_SUBSCRIPTION \ + WPAS_DBUS_NEW_INTERFACE ".NoSubscription" +#define WPAS_DBUS_ERROR_SUBSCRIPTION_EPERM \ + WPAS_DBUS_NEW_INTERFACE ".SubscriptionNotYou" + + +void wpas_dbus_subscribe_noc(struct wpas_dbus_priv *priv); +void wpas_dbus_unsubscribe_noc(struct wpas_dbus_priv *priv); + #ifdef CONFIG_CTRL_IFACE_DBUS_NEW @@ -97,6 +138,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 +165,64 @@ 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); +void wpas_dbus_signal_preq(struct wpa_supplicant *wpa_s, + const u8 *addr, const u8 *dst, const u8 *bssid, + const u8 *ie, size_t ie_len, u32 ssi_signal); +void wpas_dbus_signal_eap_status(struct wpa_supplicant *wpa_s, + const char *status, const char *parameter); + #else /* CONFIG_CTRL_IFACE_DBUS_NEW */ static inline int wpas_dbus_register_interface(struct wpa_supplicant *wpa_s) @@ -155,6 +258,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 +338,161 @@ 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) +{ +} + +static inline void wpas_dbus_signal_preq(struct wpa_supplicant *wpa_s, + const u8 *addr, const u8 *dst, + const u8 *bssid, + const u8 *ie, size_t ie_len, + u32 ssi_signal) +{ +} + +static inline void wpas_dbus_signal_eap_status(struct wpa_supplicant *wpa_s, + const char *status, + const char *parameter) +{ +} + #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */ #endif /* CTRL_IFACE_DBUS_H_NEW */ diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.c b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.c index e2b5e50..5e06932 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.c +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.c @@ -4,14 +4,8 @@ * Copyright (c) 2009-2010, Witold Sowa <witold.sowa@gmail.com> * Copyright (c) 2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -25,88 +19,25 @@ #include "../wpa_supplicant_i.h" #include "../driver_i.h" #include "../notify.h" -#include "../wpas_glue.h" #include "../bss.h" #include "../scan.h" +#include "../autoscan.h" #include "dbus_new_helpers.h" #include "dbus_new.h" #include "dbus_new_handlers.h" #include "dbus_dict_helpers.h" +#include "dbus_common_i.h" extern int wpa_debug_level; 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 +48,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); } @@ -178,7 +123,7 @@ DBusMessage * wpas_dbus_error_invalid_args(DBusMessage *message, static const char *dont_quote[] = { "key_mgmt", "proto", "pairwise", "auth_alg", "group", "eap", "opensc_engine_path", "pkcs11_engine_path", "pkcs11_module_path", - "bssid", NULL + "bssid", "scan_freq", "freq_list", NULL }; static dbus_bool_t should_quote_opt(const char *key) @@ -213,36 +158,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) @@ -304,78 +248,66 @@ static DBusMessage * set_network_properties(DBusMessage *message, if ((os_strcmp(entry.key, "psk") == 0 && value[0] == '"' && ssid->ssid_len) || - (strcmp(entry.key, "ssid") == 0 && ssid->passphrase)) + (os_strcmp(entry.key, "ssid") == 0 && ssid->passphrase)) wpa_config_update_psk(ssid); else if (os_strcmp(entry.key, "priority") == 0) wpa_config_update_prio_list(wpa_s->conf); os_free(value); wpa_dbus_dict_entry_clear(&entry); - continue; - - error: - os_free(value); - reply = wpas_dbus_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 +316,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 +416,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 +426,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,28 +532,35 @@ 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)) goto error; - if (!strcmp(entry.key, "Driver") && + if (!os_strcmp(entry.key, "Driver") && (entry.type == DBUS_TYPE_STRING)) { driver = os_strdup(entry.str_value); wpa_dbus_dict_entry_clear(&entry); if (driver == NULL) goto error; - } else if (!strcmp(entry.key, "Ifname") && + } else if (!os_strcmp(entry.key, "Ifname") && (entry.type == DBUS_TYPE_STRING)) { ifname = os_strdup(entry.str_value); wpa_dbus_dict_entry_clear(&entry); if (ifname == NULL) goto error; - } else if (!strcmp(entry.key, "BridgeIfname") && + } else if (!os_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 (!os_strcmp(entry.key, "BridgeIfname") && (entry.type == DBUS_TYPE_STRING)) { bridge_ifname = os_strdup(entry.str_value); wpa_dbus_dict_entry_clear(&entry); @@ -604,6 +590,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))) { @@ -621,6 +608,7 @@ DBusMessage * wpas_dbus_handler_create_interface(DBusMessage *message, out: os_free(driver); os_free(ifname); + os_free(confname); os_free(bridge_ifname); return reply; @@ -654,7 +642,7 @@ DBusMessage * wpas_dbus_handler_remove_interface(DBusMessage *message, wpa_s = get_iface_by_dbus_path(global, path); if (wpa_s == NULL) reply = wpas_dbus_error_iface_unknown(message); - else if (wpa_supplicant_remove_iface(global, wpa_s)) { + else if (wpa_supplicant_remove_iface(global, wpa_s, 0)) { reply = wpas_dbus_error_unknown_error( message, "wpa_supplicant couldn't remove this " "interface."); @@ -706,79 +694,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 +784,180 @@ 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*)); + paths = os_calloc(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; +} + + +/** + * wpas_dbus_getter_global_capabilities - Request supported global capabilities + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Getter for "Capabilities" property. Handles requests by dbus clients to + * return a list of strings with supported capabilities like AP, RSN IBSS, + * and P2P that are determined at compile time. + */ +dbus_bool_t wpas_dbus_getter_global_capabilities(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + const char *capabilities[5] = { NULL, NULL, NULL, NULL, NULL }; + size_t num_items = 0; + +#ifdef CONFIG_AP + capabilities[num_items++] = "ap"; +#endif /* CONFIG_AP */ +#ifdef CONFIG_IBSS_RSN + capabilities[num_items++] = "ibss-rsn"; +#endif /* CONFIG_IBSS_RSN */ +#ifdef CONFIG_P2P + capabilities[num_items++] = "p2p"; +#endif /* CONFIG_P2P */ +#ifdef CONFIG_INTERWORKING + capabilities[num_items++] = "interworking"; +#endif /* CONFIG_INTERWORKING */ + + return wpas_dbus_simple_array_property_getter(iter, + DBUS_TYPE_STRING, + capabilities, + num_items, error); } @@ -987,21 +1024,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; @@ -1146,8 +1196,9 @@ static int wpas_dbus_get_scan_channels(DBusMessage *message, #define FREQS_ALLOC_CHUNK 32 if (freqs_num % FREQS_ALLOC_CHUNK == 0) { - nfreqs = os_realloc(freqs, sizeof(int) * - (freqs_num + FREQS_ALLOC_CHUNK)); + nfreqs = os_realloc_array( + freqs, freqs_num + FREQS_ALLOC_CHUNK, + sizeof(int)); if (nfreqs == NULL) os_free(freqs); freqs = nfreqs; @@ -1167,8 +1218,7 @@ static int wpas_dbus_get_scan_channels(DBusMessage *message, dbus_message_iter_next(&array_iter); } - nfreqs = os_realloc(freqs, - sizeof(int) * (freqs_num + 1)); + nfreqs = os_realloc_array(freqs, freqs_num + 1, sizeof(int)); if (nfreqs == NULL) os_free(freqs); freqs = nfreqs; @@ -1260,14 +1310,19 @@ 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, ¶ms); } else { - wpa_s->scan_req = 2; + wpa_s->scan_req = MANUAL_SCAN_REQ; wpa_supplicant_req_scan(wpa_s, 0, 0); } } else if (!os_strcmp(type, "active")) { + if (!params.num_ssids) { + /* Add wildcard ssid */ + params.num_ssids++; + } +#ifdef CONFIG_AUTOSCAN + autoscan_deinit(wpa_s); +#endif /* CONFIG_AUTOSCAN */ wpa_supplicant_trigger_scan(wpa_s, ¶ms); } else { wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: " @@ -1326,6 +1381,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 +1399,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; } @@ -1382,6 +1442,28 @@ err: /** + * wpas_dbus_handler_reassociate - Reassociate to current AP + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: NotConnected DBus error message if not connected + * or NULL otherwise. + * + * Handler function for "Reassociate" method call of network interface. + */ +DBusMessage * wpas_dbus_handler_reassociate(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + if (wpa_s->current_ssid != NULL) { + wpas_request_connection(wpa_s); + return NULL; + } + + return dbus_message_new_error(message, WPAS_DBUS_ERROR_NOT_CONNECTED, + "This interface is not connected"); +} + + +/** * wpas_dbus_handler_remove_network - Remove a configured network * @message: Pointer to incoming dbus message * @wpa_s: wpa_supplicant structure for a network interface @@ -1403,14 +1485,16 @@ 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); - if (iface == NULL || os_strcmp(iface, wpa_s->dbus_new_path) != 0) { + iface = wpas_dbus_new_decompose_object_path(op, 0, &net_id, NULL); + if (iface == NULL || net_id == NULL || + os_strcmp(iface, wpa_s->dbus_new_path) != 0) { reply = wpas_dbus_error_invalid_args(message, op); goto out; } + errno = 0; id = strtoul(net_id, NULL, 10); - if (errno == EINVAL) { + if (errno != 0) { reply = wpas_dbus_error_invalid_args(message, op); goto out; } @@ -1444,6 +1528,43 @@ 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_deauthenticate(wpa_s, + WLAN_REASON_DEAUTH_LEAVING); +} + + +/** + * wpas_dbus_handler_remove_all_networks - Remove all configured networks + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: NULL on success or dbus error on failure + * + * Handler function for "RemoveAllNetworks" method call of a network interface. + */ +DBusMessage * wpas_dbus_handler_remove_all_networks( + DBusMessage *message, struct wpa_supplicant *wpa_s) +{ + /* 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,14 +1587,16 @@ 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); - if (iface == NULL || os_strcmp(iface, wpa_s->dbus_new_path) != 0) { + iface = wpas_dbus_new_decompose_object_path(op, 0, &net_id, NULL); + if (iface == NULL || net_id == NULL || + os_strcmp(iface, wpa_s->dbus_new_path) != 0) { reply = wpas_dbus_error_invalid_args(message, op); goto out; } + errno = 0; id = strtoul(net_id, NULL, 10); - if (errno == EINVAL) { + if (errno != 0) { reply = wpas_dbus_error_invalid_args(message, op); goto out; } @@ -1495,6 +1618,72 @@ 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 || net_id == NULL || + os_strcmp(iface, wpa_s->dbus_new_path) != 0) { + reply = wpas_dbus_error_invalid_args(message, op); + goto out; + } + + errno = 0; + id = strtoul(net_id, NULL, 10); + if (errno != 0) { + reply = wpas_dbus_error_invalid_args(message, net_id); + goto out; + } + + ssid = wpa_config_get_network(wpa_s->conf, id); + if (ssid == NULL) { + reply = wpas_dbus_error_network_unknown(message); + goto out; + } + + if (wpa_supplicant_ctrl_iface_ctrl_rsp_handle(wpa_s, ssid, + field, value) < 0) + reply = wpas_dbus_error_invalid_args(message, field); + else { + /* Tell EAP to retry immediately */ + eapol_sm_notify_ctrl_response(wpa_s->eapol); + } + +out: + os_free(iface); + 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,37 +1847,99 @@ DBusMessage * wpas_dbus_handler_remove_blob(DBusMessage *message, } +/* + * wpas_dbus_handler_flush_bss - Flush the BSS cache + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: NULL + * + * Handler function for "FlushBSS" method call of network interface. + */ +DBusMessage * wpas_dbus_handler_flush_bss(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + dbus_uint32_t age; + + dbus_message_get_args(message, NULL, DBUS_TYPE_UINT32, &age, + DBUS_TYPE_INVALID); + + if (age == 0) + wpa_bss_flush(wpa_s); + else + wpa_bss_flush_by_age(wpa_s, age); + + return NULL; +} + +#ifdef CONFIG_AUTOSCAN /** - * wpas_dbus_getter_capabilities - Return interface capabilities + * wpas_dbus_handler_autoscan - Set autoscan parameters for the interface * @message: Pointer to incoming dbus message * @wpa_s: wpa_supplicant structure for a network interface - * Returns: A dbus message containing a dict of strings + * Returns: NULL * - * Getter for "Capabilities" property of an interface. + * Handler function for "AutoScan" method call of network interface. */ -DBusMessage * wpas_dbus_getter_capabilities(DBusMessage *message, - struct wpa_supplicant *wpa_s) +DBusMessage * wpas_dbus_handler_autoscan(DBusMessage *message, + struct wpa_supplicant *wpa_s) { DBusMessage *reply = NULL; + enum wpa_states state = wpa_s->wpa_state; + char *arg; + + dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &arg, + DBUS_TYPE_INVALID); + + if (arg != NULL && os_strlen(arg) > 0) { + char *tmp; + tmp = os_strdup(arg); + if (tmp == NULL) { + reply = dbus_message_new_error(message, + DBUS_ERROR_NO_MEMORY, + NULL); + } else { + os_free(wpa_s->conf->autoscan); + wpa_s->conf->autoscan = tmp; + if (state == WPA_DISCONNECTED || state == WPA_INACTIVE) + autoscan_init(wpa_s, 1); + else if (state == WPA_SCANNING) + wpa_supplicant_reinit_autoscan(wpa_s); + } + } else if (arg != NULL && os_strlen(arg) == 0) { + os_free(wpa_s->conf->autoscan); + wpa_s->conf->autoscan = NULL; + autoscan_deinit(wpa_s); + } else + reply = dbus_message_new_error(message, + DBUS_ERROR_INVALID_ARGS, + NULL); + + return reply; +} +#endif /* CONFIG_AUTOSCAN */ + + +/** + * wpas_dbus_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, iter_dict; - DBusMessageIter iter_dict_entry, iter_dict_val, iter_array, + DBusMessageIter iter_dict, 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 *); - - 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; @@ -1717,6 +1968,12 @@ DBusMessage * wpas_dbus_getter_capabilities(DBusMessage *message, goto nomem; } + if (capa.enc & WPA_DRIVER_CAPA_ENC_GCMP) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "gcmp")) + goto nomem; + } + if (capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) { if (!wpa_dbus_dict_string_array_add_element( &iter_array, "tkip")) @@ -1758,6 +2015,12 @@ DBusMessage * wpas_dbus_getter_capabilities(DBusMessage *message, goto nomem; } + if (capa.enc & WPA_DRIVER_CAPA_ENC_GCMP) { + if (!wpa_dbus_dict_string_array_add_element( + &iter_array, "gcmp")) + goto nomem; + } + if (capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) { if (!wpa_dbus_dict_string_array_add_element( &iter_array, "tkip")) @@ -1949,41 +2212,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 +2291,436 @@ 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_disconnect_reason - Get most recent reason for disconnect + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Getter for "DisconnectReason" property. The reason is negative if it is + * locally generated. + */ +dbus_bool_t wpas_dbus_getter_disconnect_reason(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + dbus_int32_t reason = wpa_s->disconnect_reason; + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_INT32, + &reason, error); +} + + +/** + * wpas_dbus_getter_bss_expire_age - Get BSS entry expiration age + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Getter function for "BSSExpireAge" property. + */ +dbus_bool_t wpas_dbus_getter_bss_expire_age(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + dbus_uint32_t expire_age = wpa_s->conf->bss_expiration_age; + + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT32, + &expire_age, error); +} + + +/** + * wpas_dbus_setter_bss_expire_age - Control BSS entry expiration age + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Setter function for "BSSExpireAge" property. + */ +dbus_bool_t wpas_dbus_setter_bss_expire_age(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + dbus_uint32_t expire_age; + + if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_UINT32, + &expire_age)) + return FALSE; + + if (wpa_supplicant_set_bss_expiration_age(wpa_s, expire_age)) { + dbus_set_error_const(error, DBUS_ERROR_FAILED, + "BSSExpireAge must be >= 10"); + return FALSE; + } + return TRUE; +} + + +/** + * wpas_dbus_getter_bss_expire_count - Get BSS entry expiration scan count + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Getter function for "BSSExpireCount" property. + */ +dbus_bool_t wpas_dbus_getter_bss_expire_count(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + dbus_uint32_t expire_count = wpa_s->conf->bss_expiration_scan_count; + + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT32, + &expire_count, error); +} + + +/** + * wpas_dbus_setter_bss_expire_count - Control BSS entry expiration scan count + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Setter function for "BSSExpireCount" property. + */ +dbus_bool_t wpas_dbus_setter_bss_expire_count(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + dbus_uint32_t expire_count; + + if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_UINT32, + &expire_count)) + return FALSE; + + if (wpa_supplicant_set_bss_expiration_count(wpa_s, expire_count)) { + dbus_set_error_const(error, DBUS_ERROR_FAILED, + "BSSExpireCount must be > 0"); + return FALSE; + } + return TRUE; +} + + +/** + * wpas_dbus_getter_country - Control country code + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Getter function for "Country" property. + */ +dbus_bool_t wpas_dbus_getter_country(DBusMessageIter *iter, DBusError *error, + void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + char country[3]; + char *str = country; + + country[0] = wpa_s->conf->country[0]; + country[1] = wpa_s->conf->country[1]; + country[2] = '\0'; + + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, + &str, error); +} + + +/** + * wpas_dbus_setter_country - Control country code + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Setter function for "Country" property. + */ +dbus_bool_t wpas_dbus_setter_country(DBusMessageIter *iter, DBusError *error, + void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + const char *country; + + if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_STRING, + &country)) + return FALSE; + + if (!country[0] || !country[1]) { + dbus_set_error_const(error, DBUS_ERROR_FAILED, + "invalid country code"); + return FALSE; + } + + if (wpa_s->drv_priv != NULL && wpa_drv_set_country(wpa_s, country)) { + wpa_printf(MSG_DEBUG, "Failed to set country"); + dbus_set_error_const(error, DBUS_ERROR_FAILED, + "failed to set country code"); + return FALSE; + } + + wpa_s->conf->country[0] = country[0]; + wpa_s->conf->country[1] = country[1]; + return TRUE; +} + + +/** + * wpas_dbus_getter_scan_interval - Get scan interval + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Getter function for "ScanInterval" property. + */ +dbus_bool_t wpas_dbus_getter_scan_interval(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + dbus_int32_t scan_interval = wpa_s->scan_interval; + + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_INT32, + &scan_interval, error); +} + + +/** + * wpas_dbus_setter_scan_interval - Control scan interval + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Setter function for "ScanInterval" property. + */ +dbus_bool_t wpas_dbus_setter_scan_interval(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + dbus_int32_t scan_interval; + + if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_INT32, + &scan_interval)) + return FALSE; + + if (wpa_supplicant_set_scan_interval(wpa_s, scan_interval)) { + dbus_set_error_const(error, DBUS_ERROR_FAILED, + "scan_interval must be >= 0"); + return FALSE; + } + return TRUE; } /** * wpas_dbus_getter_ifname - Get interface name - * @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 +2730,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 +2758,98 @@ 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 reply; + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_OBJECT_PATH, + &net_obj_path, 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 + * 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 "BridgeIfname" property. + * Getter for "CurrentAuthMode" property. */ -DBusMessage * wpas_dbus_getter_bridge_ifname(DBusMessage *message, - struct wpa_supplicant *wpa_s) +dbus_bool_t wpas_dbus_getter_current_auth_mode(DBusMessageIter *iter, + DBusError *error, + void *user_data) { - const char *bridge_ifname = NULL; + 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; - 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); + } else { + auth_mode = wpa_key_mgmt_txt(wpa_s->key_mgmt, + wpa_s->current_ssid->proto); } - return wpas_dbus_simple_property_getter(message, DBUS_TYPE_STRING, - &bridge_ifname); + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, + &auth_mode, error); +} + + +/** + * wpas_dbus_getter_bridge_ifname - Get interface name + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Getter for "BridgeIfname" property. + */ +dbus_bool_t wpas_dbus_getter_bridge_ifname(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + const char *bridge_ifname = wpa_s->bridge_ifname; + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, + &bridge_ifname, error); } /** * wpas_dbus_getter_bsss - Get array of BSSs objects - * @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 *)); + paths = os_calloc(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 +2858,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 *)); + paths = os_calloc(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 +2923,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 +2979,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 +3180,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 *pairwise[3]; /* max 3 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; @@ -2648,6 +3276,9 @@ static DBusMessage * wpas_dbus_get_bss_security_prop( case WPA_CIPHER_CCMP: group = "ccmp"; break; + case WPA_CIPHER_GCMP: + group = "gcmp"; + break; case WPA_CIPHER_WEP104: group = "wep104"; break; @@ -2665,6 +3296,8 @@ static DBusMessage * wpas_dbus_get_bss_security_prop( pairwise[n++] = "tkip"; if (ie_data->pairwise_cipher & WPA_CIPHER_CCMP) pairwise[n++] = "ccmp"; + if (ie_data->pairwise_cipher & WPA_CIPHER_GCMP) + pairwise[n++] = "gcmp"; if (!wpa_dbus_dict_append_string_array(&iter_dict, "Pairwise", pairwise, n)) @@ -2690,152 +3323,209 @@ 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(iter, &wpa_data, error); +} + + +/** + * wpas_dbus_getter_bss_wps - Return the WPS options of a BSS + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Getter for "WPS" property. + */ +dbus_bool_t wpas_dbus_getter_bss_wps(DBusMessageIter *iter, DBusError *error, + void *user_data) +{ + struct bss_handler_args *args = user_data; + struct wpa_bss *res; +#ifdef CONFIG_WPS + struct wpabuf *wps_ie; +#endif /* CONFIG_WPS */ + DBusMessageIter iter_dict, variant_iter; + const char *type = ""; + + res = get_bss_helper(args, error, __func__); + if (!res) + return FALSE; + + if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, + "a{sv}", &variant_iter)) + goto nomem; + + if (!wpa_dbus_dict_open_write(&variant_iter, &iter_dict)) + goto nomem; + +#ifdef CONFIG_WPS + wps_ie = wpa_bss_get_vendor_ie_multi(res, WPS_IE_VENDOR_TYPE); + if (wps_ie) { + if (wps_is_selected_pbc_registrar(wps_ie)) + type = "pbc"; + else if (wps_is_selected_pin_registrar(wps_ie)) + type = "pin"; } +#endif /* CONFIG_WPS */ + + if (!wpa_dbus_dict_append_string(&iter_dict, "Type", type)) + goto nomem; + + if (!wpa_dbus_dict_close_write(&variant_iter, &iter_dict)) + goto nomem; + if (!dbus_message_iter_close_container(iter, &variant_iter)) + goto nomem; + + return TRUE; - return wpas_dbus_get_bss_security_prop(message, &wpa_data); +nomem: + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); + return FALSE; } /** * wpas_dbus_getter_bss_ies - Return all IEs of a BSS - * @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 +3535,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 +3574,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 +3583,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 +3597,163 @@ 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_recurse(iter, &variant_iter); + return set_network_properties(net->wpa_s, ssid, &variant_iter, error); +} - dbus_message_iter_init(message, &iter); - dbus_message_iter_next(&iter); - dbus_message_iter_next(&iter); +#ifdef CONFIG_AP - dbus_message_iter_recurse(&iter, &variant_iter); +DBusMessage * wpas_dbus_handler_subscribe_preq( + DBusMessage *message, struct wpa_supplicant *wpa_s) +{ + struct wpas_dbus_priv *priv = wpa_s->global->dbus; + char *name; - 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"); + if (wpa_s->preq_notify_peer != NULL) { + if (os_strcmp(dbus_message_get_sender(message), + wpa_s->preq_notify_peer) == 0) + return NULL; - return reply; + return dbus_message_new_error(message, + WPAS_DBUS_ERROR_SUBSCRIPTION_IN_USE, + "Another application is already subscribed"); + } + + name = os_strdup(dbus_message_get_sender(message)); + if (!name) + return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, + "out of memory"); + + wpa_s->preq_notify_peer = name; + + /* Subscribe to clean up if application closes socket */ + wpas_dbus_subscribe_noc(priv); + + /* + * Double-check it's still alive to make sure that we didn't + * miss the NameOwnerChanged signal, e.g. while strdup'ing. + */ + if (!dbus_bus_name_has_owner(priv->con, name, NULL)) { + /* + * Application no longer exists, clean up. + * The return value is irrelevant now. + * + * Need to check if the NameOwnerChanged handling + * already cleaned up because we have processed + * DBus messages while checking if the name still + * has an owner. + */ + if (!wpa_s->preq_notify_peer) + return NULL; + os_free(wpa_s->preq_notify_peer); + wpa_s->preq_notify_peer = NULL; + wpas_dbus_unsubscribe_noc(priv); + } + + return NULL; +} + + +DBusMessage * wpas_dbus_handler_unsubscribe_preq( + DBusMessage *message, struct wpa_supplicant *wpa_s) +{ + struct wpas_dbus_priv *priv = wpa_s->global->dbus; + + if (!wpa_s->preq_notify_peer) + return dbus_message_new_error(message, + WPAS_DBUS_ERROR_NO_SUBSCRIPTION, + "Not subscribed"); + + if (os_strcmp(wpa_s->preq_notify_peer, + dbus_message_get_sender(message))) + return dbus_message_new_error(message, + WPAS_DBUS_ERROR_SUBSCRIPTION_EPERM, + "Can't unsubscribe others"); + + os_free(wpa_s->preq_notify_peer); + wpa_s->preq_notify_peer = NULL; + wpas_dbus_unsubscribe_noc(priv); + return NULL; } + + +void wpas_dbus_signal_preq(struct wpa_supplicant *wpa_s, + const u8 *addr, const u8 *dst, const u8 *bssid, + const u8 *ie, size_t ie_len, u32 ssi_signal) +{ + DBusMessage *msg; + DBusMessageIter iter, dict_iter; + struct wpas_dbus_priv *priv = wpa_s->global->dbus; + + /* Do nothing if the control interface is not turned on */ + if (priv == NULL) + return; + + if (wpa_s->preq_notify_peer == NULL) + return; + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_INTERFACE, + "ProbeRequest"); + if (msg == NULL) + return; + + dbus_message_set_destination(msg, wpa_s->preq_notify_peer); + + dbus_message_iter_init_append(msg, &iter); + + if (!wpa_dbus_dict_open_write(&iter, &dict_iter)) + goto fail; + if (addr && !wpa_dbus_dict_append_byte_array(&dict_iter, "addr", + (const char *) addr, + ETH_ALEN)) + goto fail; + if (dst && !wpa_dbus_dict_append_byte_array(&dict_iter, "dst", + (const char *) dst, + ETH_ALEN)) + goto fail; + if (bssid && !wpa_dbus_dict_append_byte_array(&dict_iter, "bssid", + (const char *) bssid, + ETH_ALEN)) + goto fail; + if (ie && ie_len && !wpa_dbus_dict_append_byte_array(&dict_iter, "ies", + (const char *) ie, + ie_len)) + goto fail; + if (ssi_signal && !wpa_dbus_dict_append_int32(&dict_iter, "signal", + ssi_signal)) + goto fail; + if (!wpa_dbus_dict_close_write(&iter, &dict_iter)) + goto fail; + + dbus_connection_send(priv->con, msg, NULL); + goto out; +fail: + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); +out: + dbus_message_unref(msg); +} + +#endif /* CONFIG_AP */ diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.h b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.h index 3cdf9cb..aa56550 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.h +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.h @@ -3,14 +3,8 @@ * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc. * Copyright (c) 2009-2010, Witold Sowa <witold.sowa@gmail.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. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef CTRL_IFACE_DBUS_NEW_HANDLERS_H @@ -26,17 +20,26 @@ struct bss_handler_args { unsigned int id; }; -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); + +dbus_bool_t wpas_dbus_simple_property_setter(DBusMessageIter *iter, + DBusError *error, + const int type, void *val); -DBusMessage * wpas_dbus_simple_property_setter(DBusMessage *message, - 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); -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_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 +50,39 @@ 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); + +dbus_bool_t wpas_dbus_getter_global_capabilities(DBusMessageIter *iter, + DBusError *error, + void *user_data); DBusMessage * wpas_dbus_handler_scan(DBusMessage *message, struct wpa_supplicant *wpa_s); @@ -77,15 +90,29 @@ 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_reassociate(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,102 +122,164 @@ 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); +DBusMessage * wpas_dbus_handler_autoscan(DBusMessage *message, + struct wpa_supplicant *wpa_s); -DBusMessage * wpas_dbus_getter_scanning(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_ap_scan(DBusMessage *message, - struct wpa_supplicant *wpa_s); +dbus_bool_t wpas_dbus_getter_state(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_scanning(DBusMessageIter *iter, DBusError *error, + void *user_data); -DBusMessage * wpas_dbus_getter_ifname(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_driver(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_bridge_ifname(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_current_bss(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_network(DBusMessage *message, - struct wpa_supplicant *wpa_s); +dbus_bool_t wpas_dbus_getter_disconnect_reason(DBusMessageIter *iter, + DBusError *error, + void *user_data); + +dbus_bool_t wpas_dbus_getter_bss_expire_age(DBusMessageIter *iter, + DBusError *error, void *user_data); + +dbus_bool_t wpas_dbus_setter_bss_expire_age(DBusMessageIter *iter, + DBusError *error, + void *user_data); + +dbus_bool_t wpas_dbus_getter_bss_expire_count(DBusMessageIter *iter, + DBusError *error, + void *user_data); -DBusMessage * wpas_dbus_getter_bsss(DBusMessage *message, - struct wpa_supplicant *wpa_s); +dbus_bool_t wpas_dbus_setter_bss_expire_count(DBusMessageIter *iter, + DBusError *error, + void *user_data); -DBusMessage * wpas_dbus_getter_networks(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_blobs(DBusMessage *message, - struct wpa_supplicant *bss); +dbus_bool_t wpas_dbus_setter_country(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_scan_interval(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_setter_scan_interval(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_ifname(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_driver(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_bridge_ifname(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_current_bss(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_current_network(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_current_auth_mode(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_bsss(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_networks(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_blobs(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_bssid(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_ssid(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_privacy(DBusMessageIter *iter, + DBusError *error, void *user_data); + +dbus_bool_t wpas_dbus_getter_bss_mode(DBusMessageIter *iter, DBusError *error, + void *user_data); + +dbus_bool_t wpas_dbus_getter_bss_signal(DBusMessageIter *iter, + DBusError *error, void *user_data); + +dbus_bool_t wpas_dbus_getter_bss_frequency(DBusMessageIter *iter, + DBusError *error, void *user_data); + +dbus_bool_t wpas_dbus_getter_bss_rates(DBusMessageIter *iter, + DBusError *error, void *user_data); + +dbus_bool_t wpas_dbus_getter_bss_wpa(DBusMessageIter *iter, DBusError *error, + void *user_data); + +dbus_bool_t wpas_dbus_getter_bss_rsn(DBusMessageIter *iter, DBusError *error, + void *user_data); + +dbus_bool_t wpas_dbus_getter_bss_wps(DBusMessageIter *iter, DBusError *error, + void *user_data); + +dbus_bool_t wpas_dbus_getter_bss_ies(DBusMessageIter *iter, DBusError *error, + void *user_data); + +dbus_bool_t wpas_dbus_getter_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); +dbus_bool_t wpas_dbus_getter_process_credentials(DBusMessageIter *iter, + DBusError *error, void *user_data); -DBusMessage * wpas_dbus_setter_process_credentials( - DBusMessage *message, struct wpa_supplicant *wpa_s); - -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); DBusMessage * wpas_dbus_error_unknown_error(DBusMessage *message, const char *arg); +DBusMessage * wpas_dbus_handler_subscribe_preq( + DBusMessage *message, struct wpa_supplicant *wpa_s); +DBusMessage * wpas_dbus_handler_unsubscribe_preq( + DBusMessage *message, struct wpa_supplicant *wpa_s); + #endif /* CTRL_IFACE_DBUS_HANDLERS_NEW_H */ diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.c b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.c new file mode 100644 index 0000000..30e0eb3 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.c @@ -0,0 +1,2438 @@ +/* + * WPA Supplicant / dbus-based control interface (P2P) + * Copyright (c) 2011-2012, Intel Corporation + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "utils/includes.h" +#include "common.h" +#include "../config.h" +#include "../wpa_supplicant_i.h" +#include "../wps_supplicant.h" +#include "../notify.h" +#include "dbus_new_helpers.h" +#include "dbus_new.h" +#include "dbus_new_handlers.h" +#include "dbus_new_handlers_p2p.h" +#include "dbus_dict_helpers.h" +#include "p2p/p2p.h" +#include "common/ieee802_11_defs.h" +#include "ap/hostapd.h" +#include "ap/ap_config.h" +#include "ap/wps_hostapd.h" + +#include "../p2p_supplicant.h" + +/** + * 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 = os_strrchr(peer_path, '/'); + if (!p) + return -1; + p++; + return hwaddr_compact_aton(p, addr); +} + + +/** + * wpas_dbus_error_persistent_group_unknown - Return a new PersistentGroupUnknown + * error message + * @message: Pointer to incoming dbus message this error refers to + * Returns: a dbus error message + * + * Convenience function to create and return an invalid persistent group error. + */ +static DBusMessage * wpas_dbus_error_persistent_group_unknown( + DBusMessage *message) +{ + return dbus_message_new_error(message, WPAS_DBUS_ERROR_NETWORK_UNKNOWN, + "There is no such persistent group in " + "this P2P device."); +} + + +DBusMessage * wpas_dbus_handler_p2p_find(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + struct wpa_dbus_dict_entry entry; + DBusMessage *reply = NULL; + DBusMessageIter iter; + DBusMessageIter iter_dict; + unsigned int timeout = 0; + enum p2p_discovery_type type = P2P_FIND_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, 0); + os_free(req_dev_types); + return reply; + +error_clear: + wpa_dbus_dict_entry_clear(&entry); +error: + os_free(req_dev_types); + reply = wpas_dbus_error_invalid_args(message, entry.key); + return reply; +} + + +DBusMessage * wpas_dbus_handler_p2p_stop_find(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + 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, 0)) { + reply = wpas_dbus_error_unknown_error( + message, + "Failed to reinvoke a persistent group"); + goto out; + } + } else if (wpas_p2p_group_add(wpa_s, persistent_group, freq, 0)) + 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, 0, join, authorize_only, + go_intent, freq, -1, 0, 0); + + if (new_pin >= 0) { + char npin[9]; + char *generated_pin; + os_snprintf(npin, sizeof(npin), "%08d", new_pin); + generated_pin = npin; + reply = dbus_message_new_method_return(message); + dbus_message_append_args(reply, DBUS_TYPE_STRING, + &generated_pin, DBUS_TYPE_INVALID); + } else { + switch (new_pin) { + case -2: + err_msg = "connect failed due to channel " + "unavailability."; + iface = WPAS_DBUS_ERROR_CONNECT_CHANNEL_UNAVAILABLE; + break; + + case -3: + err_msg = "connect failed due to unsupported channel."; + iface = WPAS_DBUS_ERROR_CONNECT_CHANNEL_UNSUPPORTED; + break; + + default: + err_msg = "connect failed due to unspecified error."; + iface = WPAS_DBUS_ERROR_CONNECT_UNSPECIFIED_ERROR; + break; + } + + /* + * TODO: + * Do we need specialized errors corresponding to above + * error conditions as against just returning a different + * error message? + */ + reply = dbus_message_new_error(message, iface, err_msg); + } + +out: + os_free(peer_object_path); + os_free(pin); + return reply; +inv_args_clear: + wpa_dbus_dict_entry_clear(&entry); +inv_args: + reply = wpas_dbus_error_invalid_args(message, NULL); + goto out; +} + + +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, 0) < 0) { + reply = wpas_dbus_error_unknown_error( + message, + "Failed to reinvoke a persistent group"); + goto out; + } + } else { + /* + * No group ID means propose to a peer to join my active group + */ + if (wpas_p2p_invite_group(wpa_s, wpa_s->ifname, + peer_addr, NULL)) { + reply = wpas_dbus_error_unknown_error( + message, "Failed to join to an active group"); + goto out; + } + } + +out: + os_free(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, + WPAS_P2P_PD_FOR_GO_NEG) < 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_calloc(num, sizeof(char *)); + + if (!peer_obj_paths) { + out_of_mem = 1; + goto error; + } + + dl_list_for_each_safe(node, tmp, &peer_objpath_list, + struct peer_objpath_node, list) + peer_obj_paths[i++] = node->path; + + success = wpas_dbus_simple_array_property_getter(iter, + DBUS_TYPE_OBJECT_PATH, + peer_obj_paths, num, + error); + +error: + if (peer_obj_paths) + os_free(peer_obj_paths); + + dl_list_for_each_safe(node, tmp, &peer_objpath_list, + struct peer_objpath_node, list) { + dl_list_del(&node->list); + os_free(node); + } + if (out_of_mem) + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); + + return success; +} + + +enum wpas_p2p_role { + WPAS_P2P_ROLE_DEVICE, + WPAS_P2P_ROLE_GO, + WPAS_P2P_ROLE_CLIENT, +}; + +static enum wpas_p2p_role wpas_get_p2p_role(struct wpa_supplicant *wpa_s) +{ + struct wpa_ssid *ssid = wpa_s->current_ssid; + + if (!ssid) + return WPAS_P2P_ROLE_DEVICE; + if (wpa_s->wpa_state != WPA_COMPLETED) + return WPAS_P2P_ROLE_DEVICE; + + switch (ssid->mode) { + case WPAS_MODE_P2P_GO: + case WPAS_MODE_P2P_GROUP_FORMATION: + return WPAS_P2P_ROLE_GO; + case WPAS_MODE_INFRA: + if (ssid->p2p_group) + return WPAS_P2P_ROLE_CLIENT; + return WPAS_P2P_ROLE_DEVICE; + default: + return WPAS_P2P_ROLE_DEVICE; + } +} + + +dbus_bool_t wpas_dbus_getter_p2p_role(DBusMessageIter *iter, DBusError *error, + void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + char *str; + + switch (wpas_get_p2p_role(wpa_s)) { + case WPAS_P2P_ROLE_GO: + str = "GO"; + break; + case WPAS_P2P_ROLE_CLIENT: + str = "client"; + break; + default: + str = "device"; + } + + 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_calloc(num, sizeof(char *)); + if (!paths) { + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); + return FALSE; + } + + /* Loop through configured networks and append object path of each */ + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { + if (!network_is_persistent_group(ssid)) + continue; + paths[i] = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX); + if (paths[i] == NULL) { + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, + "no memory"); + goto out; + } + /* Construct the object path for this network. */ + os_snprintf(paths[i++], WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/%d", + 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_calloc(num_members, sizeof(char *)); + if (!paths) + goto out_of_memory; + + i = 0; + while ((addr = p2p_iterate_group_members(wpa_s->p2p_group, &next))) { + paths[i] = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX); + if (!paths[i]) + goto out_of_memory; + os_snprintf(paths[i], WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_P2P_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; + + while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { + if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) + goto error; + + if (!os_strcmp(entry.key, "service_type") && + (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; + } 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, "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 (upnp == 1) { + if (version <= 0 || service == NULL) + goto error; + + if (wpas_p2p_service_add_upnp(wpa_s, version, service) != 0) + goto error; + + os_free(service); + service = NULL; + } else if (bonjour == 1) { + if (query == NULL || resp == NULL) + goto error; + + if (wpas_p2p_service_add_bonjour(wpa_s, query, resp) < 0) + goto error; + query = NULL; + resp = NULL; + } else + goto error; + + return reply; +error_clear: + wpa_dbus_dict_entry_clear(&entry); +error: + os_free(service); + wpabuf_free(query); + wpabuf_free(resp); + return wpas_dbus_error_invalid_args(message, NULL); +} + + +DBusMessage * wpas_dbus_handler_p2p_delete_service( + DBusMessage *message, struct wpa_supplicant *wpa_s) +{ + DBusMessageIter iter_dict; + DBusMessage *reply = NULL; + DBusMessageIter iter; + struct wpa_dbus_dict_entry entry; + int upnp = 0; + int bonjour = 0; + int ret = 0; + char *service = NULL; + struct wpabuf *query = NULL; + u8 version = 0; + + dbus_message_iter_init(message, &iter); + + if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) + goto error; + + if (wpa_dbus_dict_has_dict_entry(&iter_dict)) { + if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) + goto error; + + if (!os_strcmp(entry.key, "service_type") && + (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 = wpas_p2p_sd_request_upnp(wpa_s, addr, version, service); + } else { + if (tlv == NULL) + goto error; + ref = wpas_p2p_sd_request(wpa_s, addr, tlv); + wpabuf_free(tlv); + } + + if (ref != 0) { + reply = dbus_message_new_method_return(message); + dbus_message_append_args(reply, DBUS_TYPE_UINT64, + &ref, DBUS_TYPE_INVALID); + } else { + reply = wpas_dbus_error_unknown_error( + message, "Unable to send SD request"); + } +out: + os_free(service); + os_free(peer_object_path); + return reply; +error_clear: + wpa_dbus_dict_entry_clear(&entry); +error: + if (tlv) + wpabuf_free(tlv); + reply = wpas_dbus_error_invalid_args(message, NULL); + goto out; +} + + +DBusMessage * wpas_dbus_handler_p2p_service_sd_res( + DBusMessage *message, struct wpa_supplicant *wpa_s) +{ + DBusMessageIter iter_dict; + DBusMessage *reply = NULL; + DBusMessageIter iter; + struct wpa_dbus_dict_entry entry; + char *peer_object_path = NULL; + struct wpabuf *tlv = NULL; + int freq = 0; + int dlg_tok = 0; + u8 addr[ETH_ALEN]; + + dbus_message_iter_init(message, &iter); + + if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) + goto error; + + while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { + if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) + goto error; + + if (!os_strcmp(entry.key, "peer_object") && + 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, 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/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.h b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.h new file mode 100644 index 0000000..a11b3c8 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.h @@ -0,0 +1,211 @@ +/* + * WPA Supplicant / dbus-based control interface for p2p + * Copyright (c) 2011-2012, Intel Corporation + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef DBUS_NEW_HANDLERS_P2P_H +#define DBUS_NEW_HANDLERS_P2P_H + +struct peer_handler_args { + struct wpa_supplicant *wpa_s; + u8 p2p_device_addr[ETH_ALEN]; +}; + +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 */ diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_wps.c b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_wps.c index dc44a59..4ad5e7e 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_wps.c +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_wps.c @@ -3,14 +3,8 @@ * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc. * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.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. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -19,6 +13,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 +26,7 @@ struct wps_start_params { int type; /* 0 - not set, 1 - pin, 2 - pbc */ u8 *bssid; char *pin; + u8 *p2p_dev_addr; }; @@ -107,7 +104,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 +145,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, ¶ms->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 +197,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 +268,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), 0); + else +#endif /* CONFIG_AP */ + { + ret = wpas_wps_start_pin(wpa_s, params.bssid, + params.pin, 0, + DEV_PW_DEFAULT); + if (ret > 0) + os_snprintf(npin, sizeof(npin), "%08d", ret); + } + } else { +#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 +340,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 +387,5 @@ DBusMessage * wpas_dbus_setter_process_credentials( WPAS_DBUS_NEW_IFACE_WPS, "ProcessCredentials"); - return NULL; + return TRUE; } diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new_helpers.c b/contrib/wpa/wpa_supplicant/dbus/dbus_new_helpers.c index 06749db..cfa6a15 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new_helpers.c +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_helpers.c @@ -3,14 +3,8 @@ * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc. * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.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. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -21,112 +15,50 @@ #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) +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) { - - 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) -{ - 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 +74,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 +158,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"); + } + + reply = dbus_message_new_method_return(message); + dbus_message_iter_init_append(reply, &iter); - if (dsc->access != W && dsc->getter) - return dsc->getter(message, user_data); + 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 +192,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"); + } - if (dsc->access != R && dsc->setter) - return dsc->setter(message, user_data); + dbus_message_iter_init(message, &iter); + /* Skip the interface name and the property name */ + dbus_message_iter_next(&iter); + dbus_message_iter_next(&iter); + + /* Iter will now point to the property's new value */ + dbus_error_init(&error); + if (dsc->setter(&iter, &error, user_data) == TRUE) { + /* Success */ + reply = dbus_message_new_method_return(message); + } else { + reply = wpas_dbus_reply_new_from_error( + message, &error, DBUS_ERROR_FAILED, + "Failed to set property"); + dbus_error_free(&error); + } - return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, - "Property is read-only"); + return reply; } @@ -552,6 +532,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 +547,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 +602,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 +618,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 +722,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 +741,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 +915,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; + } + + 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'; } - fill_dict_with_properties(dict_iter, obj_desc->properties, - interface, obj_desc->user_data); + 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; } diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new_helpers.h b/contrib/wpa/wpa_supplicant/dbus/dbus_new_helpers.h index 8db7a37..6d31ad5 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new_helpers.h +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_helpers.h @@ -3,14 +3,8 @@ * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc. * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.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. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPA_DBUS_CTRL_H @@ -22,8 +16,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 +39,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 +60,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 +72,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 +89,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 +119,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 +137,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 */ diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new_introspect.c b/contrib/wpa/wpa_supplicant/dbus/dbus_new_introspect.c index c660c04..3b090c0 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new_introspect.c +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_introspect.c @@ -4,14 +4,8 @@ * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com> * 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. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -43,7 +37,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 +83,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 +158,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 +251,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; diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_old.c b/contrib/wpa/wpa_supplicant/dbus/dbus_old.c index 7f25bf0..5f298e7 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_old.c +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_old.c @@ -2,14 +2,8 @@ * WPA Supplicant / dbus-based control interface * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc. * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -23,7 +17,6 @@ #include "../bss.h" #include "dbus_old.h" #include "dbus_old_handlers.h" -#include "dbus_common.h" #include "dbus_common_i.h" @@ -287,6 +280,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 +542,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 diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_old.h b/contrib/wpa/wpa_supplicant/dbus/dbus_old.h index a9840c2..e668231 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_old.h +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_old.h @@ -2,14 +2,8 @@ * WPA Supplicant / dbus-based control interface * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc. * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef CTRL_IFACE_DBUS_H @@ -82,6 +76,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 +112,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) { diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_old_handlers.c b/contrib/wpa/wpa_supplicant/dbus/dbus_old_handlers.c index d914697..68e5515 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_old_handlers.c +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_old_handlers.c @@ -2,14 +2,8 @@ * WPA Supplicant / dbus-based control interface * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc. * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -116,7 +110,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)) @@ -229,7 +223,7 @@ DBusMessage * wpas_dbus_global_remove_interface(DBusMessage *message, goto out; } - if (!wpa_supplicant_remove_iface(global, wpa_s)) { + if (!wpa_supplicant_remove_iface(global, wpa_s, 0)) { reply = wpas_dbus_new_success_reply(message); } else { reply = dbus_message_new_error(message, @@ -337,7 +331,7 @@ DBusMessage * wpas_dbus_global_set_debugparams(DBusMessage *message, DBusMessage * wpas_dbus_iface_scan(DBusMessage *message, struct wpa_supplicant *wpa_s) { - wpa_s->scan_req = 2; + wpa_s->scan_req = MANUAL_SCAN_REQ; wpa_supplicant_req_scan(wpa_s, 0, 0); return wpas_dbus_new_success_reply(message); } @@ -922,7 +916,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 +1196,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 +1318,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 +1428,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); +} diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_old_handlers.h b/contrib/wpa/wpa_supplicant/dbus/dbus_old_handlers.h index 65e876f..825bc6d 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_old_handlers.h +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_old_handlers.h @@ -2,14 +2,8 @@ * WPA Supplicant / dbus-based control interface * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc. * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef CTRL_IFACE_DBUS_HANDLERS_H @@ -96,6 +90,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); diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_old_handlers_wps.c b/contrib/wpa/wpa_supplicant/dbus/dbus_old_handlers_wps.c index b5879f3..bb79382 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_old_handlers_wps.c +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_old_handlers_wps.c @@ -2,14 +2,8 @@ * WPA Supplicant / dbus-based control interface (WPS) * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc. * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -43,9 +37,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 +88,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/contrib/wpa/wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service b/contrib/wpa/wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service deleted file mode 100644 index a9ce1ec..0000000 --- a/contrib/wpa/wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service +++ /dev/null @@ -1,4 +0,0 @@ -[D-BUS Service] -Name=fi.epitest.hostap.WPASupplicant -Exec=/sbin/wpa_supplicant -u -User=root diff --git a/contrib/wpa/wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service.in b/contrib/wpa/wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service.in new file mode 100644 index 0000000..a75918f --- /dev/null +++ b/contrib/wpa/wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service.in @@ -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/contrib/wpa/wpa_supplicant/dbus/fi.w1.wpa_supplicant1.service b/contrib/wpa/wpa_supplicant/dbus/fi.w1.wpa_supplicant1.service deleted file mode 100644 index df78471..0000000 --- a/contrib/wpa/wpa_supplicant/dbus/fi.w1.wpa_supplicant1.service +++ /dev/null @@ -1,4 +0,0 @@ -[D-BUS Service] -Name=fi.w1.wpa_supplicant1 -Exec=/sbin/wpa_supplicant -u -User=root diff --git a/contrib/wpa/wpa_supplicant/dbus/fi.w1.wpa_supplicant1.service.in b/contrib/wpa/wpa_supplicant/dbus/fi.w1.wpa_supplicant1.service.in new file mode 100644 index 0000000..d97ff39 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/dbus/fi.w1.wpa_supplicant1.service.in @@ -0,0 +1,5 @@ +[D-BUS Service] +Name=fi.w1.wpa_supplicant1 +Exec=@BINDIR@/wpa_supplicant -u +User=root +SystemdService=wpa_supplicant.service diff --git a/contrib/wpa/wpa_supplicant/defconfig b/contrib/wpa/wpa_supplicant/defconfig index 8c32cb3..711b407 100644 --- a/contrib/wpa/wpa_supplicant/defconfig +++ b/contrib/wpa/wpa_supplicant/defconfig @@ -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,15 @@ CONFIG_EAP_LEAP=y # Wi-Fi Protected Setup (WPS) #CONFIG_WPS=y +# Enable WSC 2.0 support +#CONFIG_WPS2=y +# Enable WPS external registrar functionality +#CONFIG_WPS_ER=y +# Disable credentials for an open network by default when acting as a WPS +# registrar. +#CONFIG_WPS_REG_DISABLE_OPEN=y +# Enable WPS support with NFC config method +#CONFIG_WPS_NFC=y # EAP-IKEv2 #CONFIG_EAP_IKEV2=y @@ -206,6 +222,9 @@ CONFIG_SMARTCARD=y # Enable this if EAP-SIM or EAP-AKA is included #CONFIG_PCSC=y +# Support HT overrides (disable HT/HT40, mask MCS rates, etc.) +#CONFIG_HT_OVERRIDES=y + # Development testing #CONFIG_EAPOL_TEST=y @@ -213,6 +232,7 @@ CONFIG_SMARTCARD=y # unix = UNIX domain sockets (default for Linux/*BSD) # udp = UDP sockets using localhost (127.0.0.1) # named_pipe = Windows Named Pipe (default for Windows) +# udp-remote = UDP sockets with remote access (only for tests systems/purpose) # y = use default (backwards compatibility) # If this option is commented out, control interface is not included in the # build. @@ -224,6 +244,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% @@ -285,6 +309,9 @@ CONFIG_BACKEND=file # eloop_none = Empty template #CONFIG_ELOOP=eloop +# Should we use poll instead of select? Select is used by default. +#CONFIG_ELOOP_POLL=y + # Select layer 2 packet implementation # linux = Linux packet socket (default) # pcap = libpcap/libdnet/WinPcap @@ -297,26 +324,30 @@ CONFIG_BACKEND=file # PeerKey handshake for Station to Station Link (IEEE 802.11e DLS) CONFIG_PEERKEY=y -# IEEE 802.11w (management frame protection) -# This version is an experimental implementation based on IEEE 802.11w/D1.0 -# draft and is subject to change since the standard has not yet been finalized. +# IEEE 802.11w (management frame protection), also known as PMF # Driver support is also needed for IEEE 802.11w. #CONFIG_IEEE80211W=y # 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 + +# TLS-based EAP methods require at least TLS v1.0. Newer version of TLS (v1.2) +# can be enabled to enable use of stronger crypto algorithms. It should be +# noted that some existing TLS v1.0 -based implementation may not be compatible +# with TLS v1.2 message (ClientHello is sent prior to negotiating which version +# will be used) +#CONFIG_TLSV12=y # If CONFIG_TLS=internal is used, additional library and include paths are # needed for LibTomMath. Alternatively, an integrated, minimal version of @@ -378,6 +409,17 @@ 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 + +# Add support for sending all debug messages (regardless of debug verbosity) +# to the Linux kernel tracing facility. This helps debug the entire stack by +# making it easy to record everything happening from the driver up into the +# same file, e.g., using trace-cmd. +#CONFIG_DEBUG_LINUX_TRACING=y + # Enable privilege separation (see README 'Privilege separation' for details) #CONFIG_PRIVSEP=y @@ -389,7 +431,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 +440,84 @@ 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 + +# Wireless Network Management (IEEE Std 802.11v-2011) +# Note: This is experimental and not complete implementation. +#CONFIG_WNM=y + +# Interworking (IEEE 802.11u) +# This can be used to enable functionality to improve interworking with +# external networks (GAS/ANQP to learn more about the networks and network +# selection based on available credentials). +#CONFIG_INTERWORKING=y + +# Hotspot 2.0 +#CONFIG_HS20=y + +# AP mode operations with wpa_supplicant +# This can be used for controlling AP mode operations with wpa_supplicant. It +# should be noted that this is mainly aimed at simple cases like +# WPA2-Personal while more complex configurations like WPA2-Enterprise with an +# external RADIUS server can be supported with hostapd. +#CONFIG_AP=y + +# P2P (Wi-Fi Direct) +# This can be used to enable P2P support in wpa_supplicant. See README-P2P for +# more information on P2P operations. +#CONFIG_P2P=y + +# Autoscan +# This can be used to enable automatic scan support in wpa_supplicant. +# See wpa_supplicant.conf for more information on autoscan usage. +# +# Enabling directly a module will enable autoscan support. +# For exponential module: +#CONFIG_AUTOSCAN_EXPONENTIAL=y +# For periodic module: +#CONFIG_AUTOSCAN_PERIODIC=y + +# Password (and passphrase, etc.) backend for external storage +# These optional mechanisms can be used to add support for storing passwords +# and other secrets in external (to wpa_supplicant) location. This allows, for +# example, operating system specific key storage to be used +# +# External password backend for testing purposes (developer use) +#CONFIG_EXT_PASSWORD_TEST=y diff --git a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_background.8 b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_background.8 index 19162a3..ba838cd 100644 --- a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_background.8 +++ b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_background.8 @@ -3,7 +3,7 @@ .\" <http://shell.ipoline.com/~elmert/comp/docbook2X/> .\" Please send any bug reports, improvements, comments, patches, .\" etc. to Steve Cheng <steve@ggi-project.org>. -.TH "WPA_BACKGROUND" "8" "07 September 2010" "" "" +.TH "WPA_BACKGROUND" "8" "12 January 2013" "" "" .SH NAME wpa_background \- Background information on Wi-Fi Protected Access and IEEE 802.11i @@ -75,10 +75,10 @@ pre-authentication, and PMKSA caching). \fBwpa_supplicant\fR(8) .SH "LEGAL" .PP -wpa_supplicant is copyright (c) 2003-2007, +wpa_supplicant is copyright (c) 2003-2012, Jouni Malinen <j@w1.fi> and contributors. All Rights Reserved. .PP -This program is dual-licensed under both the GPL version 2 -and BSD license. Either license may be used at your option. +This program is licensed under the BSD license (the one with +advertisement clause removed). diff --git a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_background.sgml b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_background.sgml index f47235b..eb3a089 100644 --- a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_background.sgml +++ b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_background.sgml @@ -90,12 +90,12 @@ <refsect1> <title>Legal</title> - <para>wpa_supplicant is copyright (c) 2003-2007, + <para>wpa_supplicant is copyright (c) 2003-2012, Jouni Malinen <email>j@w1.fi</email> and contributors. All Rights Reserved.</para> - <para>This program is dual-licensed under both the GPL version 2 - and BSD license. Either license may be used at your option.</para> + <para>This program is licensed under the BSD license (the one with + advertisement clause removed).</para> </refsect1> </refentry> diff --git a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_cli.8 b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_cli.8 index e22fc92..886e9b0 100644 --- a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_cli.8 +++ b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_cli.8 @@ -3,7 +3,7 @@ .\" <http://shell.ipoline.com/~elmert/comp/docbook2X/> .\" Please send any bug reports, improvements, comments, patches, .\" etc. to Steve Cheng <steve@ggi-project.org>. -.TH "WPA_CLI" "8" "07 September 2010" "" "" +.TH "WPA_CLI" "8" "12 January 2013" "" "" .SH NAME wpa_cli \- WPA command line client @@ -201,10 +201,10 @@ exit wpa_cli \fBwpa_supplicant\fR(8) .SH "LEGAL" .PP -wpa_supplicant is copyright (c) 2003-2007, +wpa_supplicant is copyright (c) 2003-2012, Jouni Malinen <j@w1.fi> and contributors. All Rights Reserved. .PP -This program is dual-licensed under both the GPL version 2 -and BSD license. Either license may be used at your option. +This program is licensed under the BSD license (the one with +advertisement clause removed). diff --git a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_cli.sgml b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_cli.sgml index 1fe98f4..c080c07 100644 --- a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_cli.sgml +++ b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_cli.sgml @@ -328,12 +328,12 @@ CTRL-REQ-OTP-2:Challenge 1235663 needed for SSID foobar </refsect1> <refsect1> <title>Legal</title> - <para>wpa_supplicant is copyright (c) 2003-2007, + <para>wpa_supplicant is copyright (c) 2003-2012, Jouni Malinen <email>j@w1.fi</email> and contributors. All Rights Reserved.</para> - <para>This program is dual-licensed under both the GPL version 2 - and BSD license. Either license may be used at your option.</para> + <para>This program is licensed under the BSD license (the one with + advertisement clause removed).</para> </refsect1> </refentry> diff --git a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_gui.8 b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_gui.8 index f58a894..f94beb3 100644 --- a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_gui.8 +++ b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_gui.8 @@ -3,7 +3,7 @@ .\" <http://shell.ipoline.com/~elmert/comp/docbook2X/> .\" Please send any bug reports, improvements, comments, patches, .\" etc. to Steve Cheng <steve@ggi-project.org>. -.TH "WPA_GUI" "8" "07 September 2010" "" "" +.TH "WPA_GUI" "8" "12 January 2013" "" "" .SH NAME wpa_gui \- WPA Graphical User Interface @@ -42,10 +42,10 @@ shown. \fBwpa_supplicant\fR(8) .SH "LEGAL" .PP -wpa_supplicant is copyright (c) 2003-2007, +wpa_supplicant is copyright (c) 2003-2012, Jouni Malinen <j@w1.fi> and contributors. All Rights Reserved. .PP -This program is dual-licensed under both the GPL version 2 -and BSD license. Either license may be used at your option. +This program is licensed under the BSD license (the one with +advertisement clause removed). diff --git a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_gui.sgml b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_gui.sgml index 41b5849..0ab6419 100644 --- a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_gui.sgml +++ b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_gui.sgml @@ -74,12 +74,12 @@ </refsect1> <refsect1> <title>Legal</title> - <para>wpa_supplicant is copyright (c) 2003-2007, + <para>wpa_supplicant is copyright (c) 2003-2012, Jouni Malinen <email>j@w1.fi</email> and contributors. All Rights Reserved.</para> - <para>This program is dual-licensed under both the GPL version 2 - and BSD license. Either license may be used at your option.</para> + <para>This program is licensed under the BSD license (the one with + advertisement clause removed).</para> </refsect1> </refentry> diff --git a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_passphrase.8 b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_passphrase.8 index 945c1c0..d9c1e6c 100644 --- a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_passphrase.8 +++ b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_passphrase.8 @@ -3,7 +3,7 @@ .\" <http://shell.ipoline.com/~elmert/comp/docbook2X/> .\" Please send any bug reports, improvements, comments, patches, .\" etc. to Steve Cheng <steve@ggi-project.org>. -.TH "WPA_PASSPHRASE" "8" "07 September 2010" "" "" +.TH "WPA_PASSPHRASE" "8" "12 January 2013" "" "" .SH NAME wpa_passphrase \- Generate a WPA PSK from an ASCII passphrase for a SSID @@ -31,10 +31,10 @@ passphrase will be read from standard input. \fBwpa_supplicant\fR(8) .SH "LEGAL" .PP -wpa_supplicant is copyright (c) 2003-2007, +wpa_supplicant is copyright (c) 2003-2012, Jouni Malinen <j@w1.fi> and contributors. All Rights Reserved. .PP -This program is dual-licensed under both the GPL version 2 -and BSD license. Either license may be used at your option. +This program is licensed under the BSD license (the one with +advertisement clause removed). diff --git a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_passphrase.sgml b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_passphrase.sgml index 402ea09..336c03b 100644 --- a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_passphrase.sgml +++ b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_passphrase.sgml @@ -62,12 +62,12 @@ </refsect1> <refsect1> <title>Legal</title> - <para>wpa_supplicant is copyright (c) 2003-2007, + <para>wpa_supplicant is copyright (c) 2003-2012, Jouni Malinen <email>j@w1.fi</email> and contributors. All Rights Reserved.</para> - <para>This program is dual-licensed under both the GPL version 2 - and BSD license. Either license may be used at your option.</para> + <para>This program is licensed under the BSD license (the one with + advertisement clause removed).</para> </refsect1> </refentry> diff --git a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_priv.8 b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_priv.8 index 05ad983..108f7ee 100644 --- a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_priv.8 +++ b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_priv.8 @@ -3,7 +3,7 @@ .\" <http://shell.ipoline.com/~elmert/comp/docbook2X/> .\" Please send any bug reports, improvements, comments, patches, .\" etc. to Steve Cheng <steve@ggi-project.org>. -.TH "WPA_PRIV" "8" "07 September 2010" "" "" +.TH "WPA_PRIV" "8" "12 January 2013" "" "" .SH NAME wpa_priv \- wpa_supplicant privilege separation helper @@ -111,10 +111,10 @@ processes at the same time, if desired. \fBwpa_supplicant\fR(8) .SH "LEGAL" .PP -wpa_supplicant is copyright (c) 2003-2007, +wpa_supplicant is copyright (c) 2003-2012, Jouni Malinen <j@w1.fi> and contributors. All Rights Reserved. .PP -This program is dual-licensed under both the GPL version 2 -and BSD license. Either license may be used at your option. +This program is licensed under the BSD license (the one with +advertisement clause removed). diff --git a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_priv.sgml b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_priv.sgml index 89b8a92..eb907a8 100644 --- a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_priv.sgml +++ b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_priv.sgml @@ -137,12 +137,12 @@ wpa_supplicant -i ath0 -c wpa_supplicant.conf </refsect1> <refsect1> <title>Legal</title> - <para>wpa_supplicant is copyright (c) 2003-2007, + <para>wpa_supplicant is copyright (c) 2003-2012, Jouni Malinen <email>j@w1.fi</email> and contributors. All Rights Reserved.</para> - <para>This program is dual-licensed under both the GPL version 2 - and BSD license. Either license may be used at your option.</para> + <para>This program is licensed under the BSD license (the one with + advertisement clause removed).</para> </refsect1> </refentry> diff --git a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_supplicant.8 b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_supplicant.8 index 3334d0c..7941dcf 100644 --- a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_supplicant.8 +++ b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_supplicant.8 @@ -3,7 +3,7 @@ .\" <http://shell.ipoline.com/~elmert/comp/docbook2X/> .\" Please send any bug reports, improvements, comments, patches, .\" etc. to Steve Cheng <steve@ggi-project.org>. -.TH "WPA_SUPPLICANT" "8" "07 September 2010" "" "" +.TH "WPA_SUPPLICANT" "8" "12 January 2013" "" "" .SH NAME wpa_supplicant \- Wi-Fi Protected Access client and IEEE 802.1X supplicant @@ -217,31 +217,9 @@ list of supported driver backends that may be used with the -D option on your system, refer to the help output of wpa_supplicant (\fBwpa_supplicant -h\fR). .TP -\fBhostap\fR -(default) Host AP driver (Intersil Prism2/2.5/3). -(this can also be used with Linuxant DriverLoader). -.TP -\fBhermes\fR -Agere Systems Inc. driver (Hermes-I/Hermes-II). -.TP -\fBmadwifi\fR -MADWIFI 802.11 support (Atheros, etc.). -.TP -\fBatmel\fR -ATMEL AT76C5XXx (USB, PCMCIA). -.TP \fBwext\fR Linux wireless extensions (generic). .TP -\fBndiswrapper\fR -Linux ndiswrapper. -.TP -\fBbroadcom\fR -Broadcom wl.o driver. -.TP -\fBipw\fR -Intel ipw2100/2200 driver. -.TP \fBwired\fR wpa_supplicant wired Ethernet driver .TP @@ -303,7 +281,7 @@ Include timestamp in debug messages. Help. Show a usage message. .TP \fB-L\fR -Show license (GPL and BSD). +Show license (BSD). .TP \fB-p\fR Driver parameters. (Per interface) @@ -375,8 +353,8 @@ wpa_supplicant for two interfaces: .nf wpa_supplicant \\ - -c wpa1.conf -i wlan0 -D hostap -N \\ - -c wpa2.conf -i ath0 -D madwifi + -c wpa1.conf -i wlan0 -D nl80211 -N \\ + -c wpa2.conf -i ath0 -D wext .fi .RE .SH "OS REQUIREMENTS" @@ -395,56 +373,6 @@ Microsoft Windows with WinPcap (at least WinXP, may work with other versions) .SH "SUPPORTED DRIVERS" .TP -\fBHost AP driver for Prism2/2.5/3 (development snapshot/v0.2.x)\fR -(http://hostap.epitest.fi/) Driver needs to be set in -Managed mode (\fBiwconfig wlan0 mode managed\fR). -Please note that station firmware version needs to be 1.7.0 or -newer to work in WPA mode. -.TP -\fBLinuxant DriverLoader\fR -(http://www.linuxant.com/driverloader/) -with Windows NDIS driver for your wlan card supporting WPA. -.TP -\fBAgere Systems Inc. Linux Driver\fR -(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. -.TP -\fBmadwifi driver for cards based on Atheros chip set (ar521x)\fR -(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). -.TP -\fBATMEL AT76C5XXx driver for USB and PCMCIA cards\fR -(http://atmelwlandriver.sourceforge.net/). -.TP -\fBLinux ndiswrapper\fR -(http://ndiswrapper.sourceforge.net/) with Windows -NDIS driver. -.TP -\fBBroadcom wl.o driver\fR -This is a generic Linux driver for Broadcom IEEE -802.11a/g cards. However, it is proprietary driver that is -not publicly available except for couple of exceptions, mainly -Broadcom-based APs/wireless routers that use Linux. The driver -binary can be downloaded, e.g., from Linksys support site -(http://www.linksys.com/support/gpl.asp) for Linksys -WRT54G. The GPL tarball includes cross-compiler and the needed -header file, wlioctl.h, for compiling wpa_supplicant. This -driver support in wpa_supplicant is expected to work also with -other devices based on Broadcom driver (assuming the driver -includes client mode support). -.TP -\fB Intel ipw2100 driver\fR -(http://sourceforge.net/projects/ipw2100/) -.TP -\fBIntel ipw2200 driver\fR -(http://sourceforge.net/projects/ipw2200/) -.TP \fBLinux wireless extensions\fR In theory, any driver that supports Linux wireless extensions can be used with IEEE 802.1X (i.e., not WPA) when @@ -574,10 +502,10 @@ in. \fBwpa_passphrase\fR(8) .SH "LEGAL" .PP -wpa_supplicant is copyright (c) 2003-2007, +wpa_supplicant is copyright (c) 2003-2012, Jouni Malinen <j@w1.fi> and contributors. All Rights Reserved. .PP -This program is dual-licensed under both the GPL version 2 -and BSD license. Either license may be used at your option. +This program is licensed under the BSD license (the one with +advertisement clause removed). diff --git a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_supplicant.conf.5 b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_supplicant.conf.5 index 6371965..6f57aa0 100644 --- a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_supplicant.conf.5 +++ b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_supplicant.conf.5 @@ -3,7 +3,7 @@ .\" <http://shell.ipoline.com/~elmert/comp/docbook2X/> .\" Please send any bug reports, improvements, comments, patches, .\" etc. to Steve Cheng <steve@ggi-project.org>. -.TH "WPA_SUPPLICANT.CONF" "5" "07 September 2010" "" "" +.TH "WPA_SUPPLICANT.CONF" "5" "12 January 2013" "" "" .SH NAME wpa_supplicant.conf \- configuration file for wpa_supplicant diff --git a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_supplicant.sgml b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_supplicant.sgml index 3aae51b..aa20e57 100644 --- a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_supplicant.sgml +++ b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_supplicant.sgml @@ -246,35 +246,6 @@ <variablelist> <varlistentry> - <term>hostap</term> - <listitem> - <para>(default) Host AP driver (Intersil Prism2/2.5/3). - (this can also be used with Linuxant DriverLoader).</para> - </listitem> - </varlistentry> - - <varlistentry> - <term>hermes</term> - <listitem> - <para>Agere Systems Inc. driver (Hermes-I/Hermes-II).</para> - </listitem> - </varlistentry> - - <varlistentry> - <term>madwifi</term> - <listitem> - <para>MADWIFI 802.11 support (Atheros, etc.).</para> - </listitem> - </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> @@ -282,27 +253,6 @@ </varlistentry> <varlistentry> - <term>ndiswrapper</term> - <listitem> - <para>Linux ndiswrapper.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term>broadcom</term> - <listitem> - <para>Broadcom wl.o driver.</para> - </listitem> - </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> @@ -432,7 +382,7 @@ <varlistentry> <term>-L</term> <listitem> - <para>Show license (GPL and BSD).</para> + <para>Show license (BSD).</para> </listitem> </varlistentry> @@ -527,8 +477,8 @@ wpa_supplicant -Dnl80211,wext -c/etc/wpa_supplicant.conf -iwlan0 <blockquote><programlisting> wpa_supplicant \ - -c wpa1.conf -i wlan0 -D hostap -N \ - -c wpa2.conf -i ath0 -D madwifi + -c wpa1.conf -i wlan0 -D nl80211 -N \ + -c wpa2.conf -i ath0 -D wext </programlisting></blockquote> </refsect1> @@ -558,93 +508,6 @@ wpa_supplicant \ <title>Supported Drivers</title> <variablelist> <varlistentry> - <term>Host AP driver for Prism2/2.5/3 (development - snapshot/v0.2.x)</term> - <listitem> - <para> (http://hostap.epitest.fi/) Driver needs to be set in - Managed mode (<emphasis>iwconfig wlan0 mode managed</emphasis>). - Please note that station firmware version needs to be 1.7.0 or - newer to work in WPA mode.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term>Linuxant DriverLoader</term> - <listitem> - <para>(http://www.linuxant.com/driverloader/) - with Windows NDIS driver for your wlan card supporting WPA.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term>Agere Systems Inc. Linux Driver</term> - <listitem> - <para> (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.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term>madwifi driver for cards based on Atheros chip set (ar521x)</term> - <listitem> - <para> (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).</para> - </listitem> - </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 - NDIS driver.</para> - </listitem> - </varlistentry> - - <varlistentry> - <term>Broadcom wl.o driver</term> - <listitem> - <para> This is a generic Linux driver for Broadcom IEEE - 802.11a/g cards. However, it is proprietary driver that is - not publicly available except for couple of exceptions, mainly - Broadcom-based APs/wireless routers that use Linux. The driver - binary can be downloaded, e.g., from Linksys support site - (http://www.linksys.com/support/gpl.asp) for Linksys - WRT54G. The GPL tarball includes cross-compiler and the needed - header file, wlioctl.h, for compiling wpa_supplicant. This - driver support in wpa_supplicant is expected to work also with - other devices based on Broadcom driver (assuming the driver - includes client mode support).</para> - </listitem> - </varlistentry> - - <varlistentry> - <term> Intel ipw2100 driver</term> - <listitem> - <para> (http://sourceforge.net/projects/ipw2100/)</para> - </listitem> - </varlistentry> - - <varlistentry> - <term>Intel ipw2200 driver</term> - <listitem> - <para> (http://sourceforge.net/projects/ipw2200/)</para> - </listitem> - </varlistentry> - - <varlistentry> <term>Linux wireless extensions</term> <listitem> <para>In theory, any driver that supports Linux wireless @@ -816,12 +679,12 @@ fi </refsect1> <refsect1> <title>Legal</title> - <para>wpa_supplicant is copyright (c) 2003-2007, + <para>wpa_supplicant is copyright (c) 2003-2012, Jouni Malinen <email>j@w1.fi</email> and contributors. All Rights Reserved.</para> - <para>This program is dual-licensed under both the GPL version 2 - and BSD license. Either license may be used at your option.</para> + <para>This program is licensed under the BSD license (the one with + advertisement clause removed).</para> </refsect1> </refentry> diff --git a/contrib/wpa/wpa_supplicant/driver_i.h b/contrib/wpa/wpa_supplicant/driver_i.h index a70aa6a..847600d 100644 --- a/contrib/wpa/wpa_supplicant/driver_i.h +++ b/contrib/wpa/wpa_supplicant/driver_i.h @@ -2,14 +2,8 @@ * wpa_supplicant - Internal driver interface wrappers * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef DRIVER_I_H @@ -79,6 +73,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) { @@ -128,16 +139,6 @@ static inline int wpa_drv_deauthenticate(struct wpa_supplicant *wpa_s, return -1; } -static inline int wpa_drv_disassociate(struct wpa_supplicant *wpa_s, - const u8 *addr, int reason_code) -{ - if (wpa_s->driver->disassociate) { - return wpa_s->driver->disassociate(wpa_s->drv_priv, addr, - reason_code); - } - return -1; -} - static inline int wpa_drv_add_pmkid(struct wpa_supplicant *wpa_s, const u8 *bssid, const u8 *pmkid) { @@ -236,35 +237,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) { @@ -274,29 +246,11 @@ static inline int wpa_drv_set_country(struct wpa_supplicant *wpa_s, } static inline int wpa_drv_send_mlme(struct wpa_supplicant *wpa_s, - const u8 *data, size_t data_len) + const u8 *data, size_t data_len, int noack) { if (wpa_s->driver->send_mlme) return wpa_s->driver->send_mlme(wpa_s->drv_priv, - data, data_len); - 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); + data, data_len, noack); return -1; } @@ -320,15 +274,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 +301,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 +333,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 +364,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 +410,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 +417,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 +445,248 @@ 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_pktcnt_poll(struct wpa_supplicant *wpa_s, + struct hostap_sta_driver_data *sta) +{ + if (wpa_s->driver->read_sta_data) + return wpa_s->driver->read_sta_data(wpa_s->drv_priv, sta, + wpa_s->bssid); + return -1; +} + static inline int wpa_drv_set_ap_wps_ie(struct wpa_supplicant *wpa_s, const struct wpabuf *beacon, - const struct wpabuf *proberesp) + const struct wpabuf *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); +} + +static inline int wpa_drv_radio_disable(struct wpa_supplicant *wpa_s, + int disabled) +{ + if (!wpa_s->driver->radio_disable) + return -1; + return wpa_s->driver->radio_disable(wpa_s->drv_priv, disabled); +} + +static inline int wpa_drv_switch_channel(struct wpa_supplicant *wpa_s, + unsigned int freq) +{ + if (!wpa_s->driver->switch_channel) + return -1; + return wpa_s->driver->switch_channel(wpa_s->drv_priv, freq); +} + +static inline int wpa_drv_wnm_oper(struct wpa_supplicant *wpa_s, + enum wnm_oper oper, const u8 *peer, + u8 *buf, u16 *buf_len) +{ + if (!wpa_s->driver->wnm_oper) + return -1; + return wpa_s->driver->wnm_oper(wpa_s->drv_priv, oper, peer, buf, + buf_len); } #endif /* DRIVER_I_H */ diff --git a/contrib/wpa/wpa_supplicant/eap_register.c b/contrib/wpa/wpa_supplicant/eap_register.c index f668874..d1eb4ff 100644 --- a/contrib/wpa/wpa_supplicant/eap_register.c +++ b/contrib/wpa/wpa_supplicant/eap_register.c @@ -2,14 +2,8 @@ * EAP method registration * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -17,6 +11,7 @@ #include "common.h" #include "eap_peer/eap_methods.h" #include "eap_server/eap_methods.h" +#include "wpa_supplicant_i.h" /** @@ -40,6 +35,11 @@ int eap_register_methods(void) ret = eap_peer_tls_register(); #endif /* EAP_TLS */ +#ifdef EAP_UNAUTH_TLS + if (ret == 0) + ret = eap_peer_unauth_tls_register(); +#endif /* EAP_UNAUTH_TLS */ + #ifdef EAP_MSCHAPv2 if (ret == 0) ret = eap_peer_mschapv2_register(); @@ -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) @@ -146,6 +150,11 @@ int eap_register_methods(void) ret = eap_server_tls_register(); #endif /* EAP_SERVER_TLS */ +#ifdef EAP_SERVER_UNAUTH_TLS + if (ret == 0) + ret = eap_server_unauth_tls_register(); +#endif /* EAP_SERVER_UNAUTH_TLS */ + #ifdef EAP_SERVER_MSCHAPV2 if (ret == 0) ret = eap_server_mschapv2_register(); @@ -231,5 +240,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; } diff --git a/contrib/wpa/wpa_supplicant/eapol_test.c b/contrib/wpa/wpa_supplicant/eapol_test.c index 4eed854..80fe2c6 100644 --- a/contrib/wpa/wpa_supplicant/eapol_test.c +++ b/contrib/wpa/wpa_supplicant/eapol_test.c @@ -1,15 +1,9 @@ /* * WPA Supplicant - test code - * Copyright (c) 2003-2007, 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 - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * IEEE 802.1X Supplicant test code (to be used in place of wpa_supplicant.c. * Not used in production version. @@ -19,15 +13,19 @@ #include <assert.h> #include "common.h" +#include "utils/ext_password.h" #include "config.h" #include "eapol_supp/eapol_supp_sm.h" #include "eap_peer/eap.h" +#include "eap_server/eap_methods.h" #include "eloop.h" +#include "utils/base64.h" #include "rsn_supp/wpa.h" #include "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" @@ -58,9 +56,8 @@ struct eapol_test_data { struct radius_client_data *radius; struct hostapd_radius_servers *radius_conf; - u8 *last_eap_radius; /* last received EAP Response from Authentication - * Server */ - size_t last_eap_radius_len; + /* last received EAP Response from Authentication Server */ + struct wpabuf *last_eap_radius; u8 authenticator_pmk[PMK_LEN]; size_t authenticator_pmk_len; @@ -74,6 +71,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; @@ -99,7 +98,7 @@ static int add_extra_attr(struct radius_msg *msg, size_t len; char *pos; u32 val; - char buf[128]; + char buf[RADIUS_MAX_ATTR_LEN + 1]; switch (attr->syntax) { case 's': @@ -115,7 +114,7 @@ static int add_extra_attr(struct radius_msg *msg, if (pos[0] == '0' && pos[1] == 'x') pos += 2; len = os_strlen(pos); - if ((len & 1) || (len / 2) > sizeof(buf)) { + if ((len & 1) || (len / 2) > RADIUS_MAX_ATTR_LEN) { printf("Invalid extra attribute hexstring\n"); return -1; } @@ -172,7 +171,7 @@ static void ieee802_1x_encapsulate_radius(struct eapol_test_data *e, const u8 *eap, size_t len) { struct radius_msg *msg; - char buf[128]; + char buf[RADIUS_MAX_ATTR_LEN + 1]; const struct eap_hdr *hdr; const u8 *pos; @@ -279,7 +278,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 +291,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 +304,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 +382,84 @@ 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 void eapol_test_set_anon_id(void *ctx, const u8 *id, size_t len) +{ + struct eapol_test_data *e = ctx; + struct wpa_supplicant *wpa_s = e->wpa_s; + char *str; + int res; + + wpa_hexdump_ascii(MSG_DEBUG, "EAP method updated anonymous_identity", + id, len); + + if (wpa_s->current_ssid == NULL) + return; + + if (id == NULL) { + if (wpa_config_set(wpa_s->current_ssid, "anonymous_identity", + "NULL", 0) < 0) + return; + } else { + str = os_malloc(len * 2 + 1); + if (str == NULL) + return; + wpa_snprintf_hex(str, len * 2 + 1, id, len); + res = wpa_config_set(wpa_s->current_ssid, "anonymous_identity", + str, 0); + os_free(str); + if (res < 0) + return; + } +} + + static int test_eapol(struct eapol_test_data *e, struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { @@ -393,7 +471,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 +485,9 @@ 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; + ctx->set_anon_id = eapol_test_set_anon_id; wpa_s->eapol = eapol_sm_init(ctx); if (wpa_s->eapol == NULL) { @@ -439,7 +520,7 @@ static void test_eapol_clean(struct eapol_test_data *e, struct extra_radius_attr *p, *prev; radius_client_deinit(e->radius); - os_free(e->last_eap_radius); + wpabuf_free(e->last_eap_radius); radius_msg_free(e->last_recv_radius); e->last_recv_radius = NULL; os_free(e->eap_identity); @@ -457,6 +538,10 @@ static void test_eapol_clean(struct eapol_test_data *e, wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface); wpa_s->ctrl_iface = NULL; } + + ext_password_deinit(wpa_s->ext_pw); + wpa_s->ext_pw = NULL; + wpa_config_free(wpa_s->conf); p = e->extra_attrs; @@ -525,9 +610,8 @@ static char *eap_type_text(u8 type) static void ieee802_1x_decapsulate_radius(struct eapol_test_data *e) { - u8 *eap; - size_t len; - struct eap_hdr *hdr; + struct wpabuf *eap; + const struct eap_hdr *hdr; int eap_type = -1; char buf[64]; struct radius_msg *msg; @@ -537,30 +621,29 @@ static void ieee802_1x_decapsulate_radius(struct eapol_test_data *e) msg = e->last_recv_radius; - eap = radius_msg_get_eap(msg, &len); + eap = radius_msg_get_eap(msg); if (eap == NULL) { /* draft-aboba-radius-rfc2869bis-20.txt, Chap. 2.6.3: * RADIUS server SHOULD NOT send Access-Reject/no EAP-Message * attribute */ wpa_printf(MSG_DEBUG, "could not extract " "EAP-Message from RADIUS message"); - os_free(e->last_eap_radius); + wpabuf_free(e->last_eap_radius); e->last_eap_radius = NULL; - e->last_eap_radius_len = 0; return; } - if (len < sizeof(*hdr)) { + if (wpabuf_len(eap) < sizeof(*hdr)) { wpa_printf(MSG_DEBUG, "too short EAP packet " "received from authentication server"); - os_free(eap); + wpabuf_free(eap); return; } - if (len > sizeof(*hdr)) - eap_type = eap[sizeof(*hdr)]; + if (wpabuf_len(eap) > sizeof(*hdr)) + eap_type = (wpabuf_head_u8(eap))[sizeof(*hdr)]; - hdr = (struct eap_hdr *) eap; + hdr = wpabuf_head(eap); switch (hdr->code) { case EAP_CODE_REQUEST: os_snprintf(buf, sizeof(buf), "EAP-Request-%s (%d)", @@ -583,7 +666,7 @@ static void ieee802_1x_decapsulate_radius(struct eapol_test_data *e) break; default: os_strlcpy(buf, "unknown EAP code", sizeof(buf)); - wpa_hexdump(MSG_DEBUG, "Decapsulated EAP packet", eap, len); + wpa_hexdump_buf(MSG_DEBUG, "Decapsulated EAP packet", eap); break; } wpa_printf(MSG_DEBUG, "decapsulated EAP packet (code=%d " @@ -592,20 +675,21 @@ static void ieee802_1x_decapsulate_radius(struct eapol_test_data *e) /* sta->eapol_sm->be_auth.idFromServer = hdr->identifier; */ - os_free(e->last_eap_radius); + wpabuf_free(e->last_eap_radius); e->last_eap_radius = eap; - e->last_eap_radius_len = len; { struct ieee802_1x_hdr *dot1x; - dot1x = os_malloc(sizeof(*dot1x) + len); + dot1x = os_malloc(sizeof(*dot1x) + wpabuf_len(eap)); assert(dot1x != NULL); dot1x->version = EAPOL_VERSION; dot1x->type = IEEE802_1X_TYPE_EAP_PACKET; - dot1x->length = htons(len); - os_memcpy((u8 *) (dot1x + 1), eap, len); + dot1x->length = htons(wpabuf_len(eap)); + os_memcpy((u8 *) (dot1x + 1), wpabuf_head(eap), + wpabuf_len(eap)); eapol_sm_rx_eapol(e->wpa_s->eapol, e->wpa_s->bssid, - (u8 *) dot1x, sizeof(*dot1x) + len); + (u8 *) dot1x, + sizeof(*dot1x) + wpabuf_len(eap)); os_free(dot1x); } } @@ -809,7 +893,7 @@ static int scard_test(void) unsigned char aka_ik[IK_LEN]; unsigned char aka_ck[CK_LEN]; - scard = scard_init(SCARD_TRY_BOTH); + scard = scard_init(SCARD_TRY_BOTH, NULL); if (scard == NULL) return -1; if (scard_set_pin(scard, "1234")) { @@ -824,6 +908,9 @@ static int scard_test(void) wpa_hexdump_ascii(MSG_DEBUG, "SCARD: IMSI", (u8 *) imsi, len); /* NOTE: Permanent Username: 1 | IMSI */ + wpa_printf(MSG_DEBUG, "SCARD: MNC length %d", + scard_get_mnc_len(scard)); + os_memset(_rand, 0, sizeof(_rand)); if (scard_gsm_auth(scard, _rand, sres, kc)) goto failed; @@ -906,7 +993,7 @@ static int scard_get_triplets(int argc, char *argv[]) wpa_debug_level = 99; } - scard = scard_init(SCARD_GSM_SIM_ONLY); + scard = scard_init(SCARD_GSM_SIM_ONLY, NULL); if (scard == NULL) { printf("Failed to open smartcard connection\n"); return -1; @@ -963,7 +1050,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 +1076,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 +1119,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 +1144,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; @@ -1074,7 +1173,7 @@ int main(int argc, char *argv[]) wait_for_monitor++; break; case 'N': - p1 = os_zalloc(sizeof(p1)); + p1 = os_zalloc(sizeof(*p1)); if (p1 == NULL) break; if (!p) @@ -1164,6 +1263,9 @@ int main(int argc, char *argv[]) if (test_eapol(&eapol_test, &wpa_s, wpa_s.conf->ssid)) return -1; + if (wpas_init_ext_pw(&wpa_s) < 0) + return -1; + if (wait_for_monitor) wpa_supplicant_ctrl_iface_wait(wpa_s.ctrl_iface); @@ -1191,9 +1293,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) diff --git a/contrib/wpa/wpa_supplicant/events.c b/contrib/wpa/wpa_supplicant/events.c index 85dcfb2..baca363 100644 --- a/contrib/wpa/wpa_supplicant/events.c +++ b/contrib/wpa/wpa_supplicant/events.c @@ -1,15 +1,9 @@ /* * WPA Supplicant - Driver event processing - * 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 - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -28,48 +22,84 @@ #include "common/wpa_ctrl.h" #include "eap_peer/eap.h" #include "ap/hostapd.h" +#include "p2p/p2p.h" +#include "wnm_sta.h" #include "notify.h" #include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "crypto/random.h" #include "blacklist.h" #include "wpas_glue.h" #include "wps_supplicant.h" #include "ibss_rsn.h" #include "sme.h" +#include "gas_query.h" +#include "p2p_supplicant.h" #include "bgscan.h" +#include "autoscan.h" #include "ap.h" #include "bss.h" -#include "mlme.h" #include "scan.h" +#include "offchannel.h" +#include "interworking.h" + + +static int wpas_temp_disabled(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + struct os_time now; + + if (ssid == NULL || ssid->disabled_until.sec == 0) + return 0; + + os_get_time(&now); + if (ssid->disabled_until.sec > now.sec) + return ssid->disabled_until.sec - now.sec; + + wpas_clear_temp_disabled(wpa_s, ssid, 0); + + return 0; +} static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s) { struct wpa_ssid *ssid, *old_ssid; + int res; if (wpa_s->conf->ap_scan == 1 && wpa_s->current_ssid) 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 (wpas_network_disabled(wpa_s, ssid)) { + wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is disabled"); + return -1; + } + + if (disallowed_bssid(wpa_s, wpa_s->bssid) || + disallowed_ssid(wpa_s, ssid->ssid, ssid->ssid_len)) { + wpa_dbg(wpa_s, MSG_DEBUG, "Selected BSS is disallowed"); return -1; } - if (ssid->disabled) { - wpa_printf(MSG_DEBUG, "Selected network is disabled"); + res = wpas_temp_disabled(wpa_s, ssid); + if (res > 0) { + wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is temporarily " + "disabled for %d second(s)", res); return -1; } - wpa_printf(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 | - WPA_KEY_MGMT_PSK_SHA256 | - WPA_KEY_MGMT_IEEE8021X_SHA256)) { + wpa_dbg(wpa_s, MSG_DEBUG, "Network configuration found for the " + "current AP"); + if (wpa_key_mgmt_wpa_any(ssid->key_mgmt)) { u8 wpa_ie[80]; size_t wpa_ie_len = sizeof(wpa_ie); wpa_supplicant_set_suites(wpa_s, NULL, ssid, @@ -91,8 +121,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 +138,39 @@ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s) { int bssid_changed; + wnm_bss_keep_alive_deinit(wpa_s); + +#ifdef CONFIG_IBSS_RSN + ibss_rsn_deinit(wpa_s->ibss_rsn); + wpa_s->ibss_rsn = NULL; +#endif /* CONFIG_IBSS_RSN */ + +#ifdef CONFIG_AP + wpa_supplicant_ap_deinit(wpa_s); +#endif /* CONFIG_AP */ + + if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) + return; + wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); bssid_changed = !is_zero_ether_addr(wpa_s->bssid); os_memset(wpa_s->bssid, 0, ETH_ALEN); os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); +#ifdef CONFIG_SME + wpa_s->sme.prev_bssid_set = 0; +#endif /* CONFIG_SME */ +#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); @@ -122,6 +179,8 @@ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s) if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) eapol_sm_notify_eap_success(wpa_s->eapol, FALSE); wpa_s->ap_ies_from_associnfo = 0; + wpa_s->current_ssid = NULL; + wpa_s->key_mgmt = 0; } @@ -145,8 +204,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 +213,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 +264,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) @@ -219,7 +280,8 @@ int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s, if (eap->vendor == EAP_VENDOR_IETF) { if (eap->method == EAP_TYPE_SIM) sim = 1; - else if (eap->method == EAP_TYPE_AKA) + else if (eap->method == EAP_TYPE_AKA || + eap->method == EAP_TYPE_AKA_PRIME) aka = 1; } eap++; @@ -228,17 +290,20 @@ int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s, if (eap_peer_get_eap_method(EAP_VENDOR_IETF, EAP_TYPE_SIM) == NULL) sim = 0; - if (eap_peer_get_eap_method(EAP_VENDOR_IETF, EAP_TYPE_AKA) == NULL) + if (eap_peer_get_eap_method(EAP_VENDOR_IETF, EAP_TYPE_AKA) == NULL && + eap_peer_get_eap_method(EAP_VENDOR_IETF, EAP_TYPE_AKA_PRIME) == + NULL) aka = 0; if (!sim && !aka) { - wpa_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) @@ -246,14 +311,15 @@ int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s, else type = SCARD_GSM_SIM_ONLY; - wpa_s->scard = scard_init(type); + wpa_s->scard = scard_init(type, NULL); 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; @@ -261,7 +327,7 @@ int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s, #ifndef CONFIG_NO_SCAN_PROCESSING -static int wpa_supplicant_match_privacy(struct wpa_scan_res *bss, +static int wpa_supplicant_match_privacy(struct wpa_bss *bss, struct wpa_ssid *ssid) { int i, privacy = 0; @@ -287,6 +353,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; @@ -295,100 +364,146 @@ static int wpa_supplicant_match_privacy(struct wpa_scan_res *bss, static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, - struct wpa_scan_res *bss) + struct wpa_bss *bss) { struct wpa_ie_data ie; int proto_match = 0; const u8 *rsn_ie, *wpa_ie; int ret; + int wep_ok; ret = wpas_wps_ssid_bss_match(wpa_s, ssid, bss); if (ret >= 0) return ret; - rsn_ie = wpa_scan_get_ie(bss, WLAN_EID_RSN); + /* Allow TSN if local configuration accepts WEP use without WPA/WPA2 */ + wep_ok = !wpa_key_mgmt_wpa(ssid->key_mgmt) && + (((ssid->key_mgmt & WPA_KEY_MGMT_NONE) && + ssid->wep_key_len[ssid->wep_tx_keyidx] > 0) || + (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)); + + rsn_ie = wpa_bss_get_ie(bss, WLAN_EID_RSN); while ((ssid->proto & WPA_PROTO_RSN) && rsn_ie) { proto_match++; if (wpa_parse_wpa_ie(rsn_ie, 2 + rsn_ie[1], &ie)) { - wpa_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"); + (ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT ? + wpa_s->conf->pmf : ssid->ieee80211w) == + MGMT_FRAME_PROTECTION_REQUIRED) { + 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; } - wpa_ie = wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); + wpa_ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); while ((ssid->proto & WPA_PROTO_WPA) && wpa_ie) { proto_match++; if (wpa_parse_wpa_ie(wpa_ie, 2 + wpa_ie[1], &ie)) { - wpa_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 ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && !wpa_ie && + !rsn_ie) { + wpa_dbg(wpa_s, MSG_DEBUG, " allow for non-WPA IEEE 802.1X"); + return 1; + } + + if ((ssid->proto & (WPA_PROTO_WPA | WPA_PROTO_RSN)) && + wpa_key_mgmt_wpa(ssid->key_mgmt) && proto_match == 0) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - no WPA/RSN proto match"); + return 0; + } + + if (!wpa_key_mgmt_wpa(ssid->key_mgmt)) { + wpa_dbg(wpa_s, MSG_DEBUG, " allow in non-WPA/WPA2"); return 1; } - if (proto_match == 0) - wpa_printf(MSG_DEBUG, " skip - no WPA/RSN proto match"); + wpa_dbg(wpa_s, MSG_DEBUG, " reject due to mismatch with " + "WPA/WPA2"); return 0; } @@ -408,285 +523,333 @@ 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_bss *bss) +{ + const struct hostapd_hw_modes *mode = NULL, *modes; + const u8 scan_ie[2] = { WLAN_EID_SUPP_RATES, WLAN_EID_EXT_SUPP_RATES }; + const u8 *rate_ie; + int i, j, k; - 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_bss_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_bss *bss, + struct wpa_ssid *group) { - struct wpa_ssid *ssid; - struct wpa_scan_res *bss; - size_t i; + u8 wpa_ie_len, rsn_ie_len; + int wpa; struct wpa_blacklist *e; const u8 *ie; + struct wpa_ssid *ssid; + + ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); + wpa_ie_len = ie ? ie[1] : 0; + + ie = wpa_bss_get_ie(bss, WLAN_EID_RSN); + rsn_ie_len = ie ? ie[1] : 0; + + 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(bss->ssid, bss->ssid_len), + wpa_ie_len, rsn_ie_len, bss->caps, bss->level, + wpa_bss_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE) ? " wps" : ""); + + e = wpa_blacklist_get(wpa_s, bss->bssid); + if (e) { + int limit = 1; + if (wpa_supplicant_enabled_networks(wpa_s) == 1) { + /* + * When only a single network is enabled, we can + * trigger blacklisting on the first failure. This + * should not be done with multiple enabled networks to + * avoid getting forced to move into a worse ESS on + * single error if there are no other BSSes of the + * current ESS. + */ + limit = 0; + } + if (e->count > limit) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - blacklisted " + "(count=%d limit=%d)", e->count, limit); + return NULL; + } + } - 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]; + if (bss->ssid_len == 0) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - SSID not known"); + return NULL; + } - ie = wpa_scan_get_ie(bss, WLAN_EID_SSID); - ssid_ = ie ? ie + 2 : (u8 *) ""; - ssid_len = ie ? ie[1] : 0; + if (disallowed_bssid(wpa_s, bss->bssid)) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - BSSID disallowed"); + return NULL; + } - ie = wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); - wpa_ie_len = ie ? ie[1] : 0; + if (disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len)) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - SSID disallowed"); + return NULL; + } - ie = wpa_scan_get_ie(bss, WLAN_EID_RSN); - rsn_ie_len = ie ? ie[1] : 0; + wpa = wpa_ie_len > 0 || rsn_ie_len > 0; - 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); + for (ssid = group; ssid; ssid = ssid->pnext) { + int check_ssid = wpa ? 1 : (ssid->ssid_len != 0); + int res; - e = wpa_blacklist_get(wpa_s, bss->bssid); - if (e && e->count > 1) { - wpa_printf(MSG_DEBUG, " skip - blacklisted"); + if (wpas_network_disabled(wpa_s, ssid)) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - disabled"); continue; } - if (ssid_len == 0) { - wpa_printf(MSG_DEBUG, " skip - SSID not known"); + res = wpas_temp_disabled(wpa_s, ssid); + if (res > 0) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - disabled " + "temporarily for %d second(s)", res); continue; } - for (ssid = group; ssid; ssid = ssid->pnext) { - int check_ssid = ssid->ssid_len != 0; +#ifdef CONFIG_WPS + if ((ssid->key_mgmt & WPA_KEY_MGMT_WPS) && e && e->count > 0) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - blacklisted " + "(WPS)"); + continue; + } - if (ssid->disabled) { - wpa_printf(MSG_DEBUG, " skip - disabled"); - continue; - } + if (wpa && ssid->ssid_len == 0 && + wpas_wps_ssid_wildcard_ok(wpa_s, ssid, bss)) + check_ssid = 0; -#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 && + (bss->ssid_len != ssid->ssid_len || + os_memcmp(bss->ssid, ssid->ssid, bss->ssid_len) != 0)) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - SSID mismatch"); + continue; + } - if (!(ssid->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; + } + + if (!freq_allowed(ssid->freq_list, bss->freq)) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - frequency not " + "allowed"); + 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 (!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; } static struct wpa_bss * wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s, - struct wpa_scan_results *scan_res, struct wpa_ssid *group, struct wpa_ssid **selected_ssid) { - struct wpa_bss *selected; + unsigned int 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 < wpa_s->last_scan_res_used; i++) { + struct wpa_bss *bss = wpa_s->last_scan_res[i]; + *selected_ssid = wpa_scan_res_match(wpa_s, i, bss, group); + if (!*selected_ssid) + continue; + wpa_dbg(wpa_s, MSG_DEBUG, " selected BSS " MACSTR + " ssid='%s'", + MAC2STR(bss->bssid), + wpa_ssid_txt(bss->ssid, bss->ssid_len)); + return bss; + } - /* 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); + return NULL; } static struct wpa_bss * wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s, - struct wpa_scan_results *scan_res, struct wpa_ssid **selected_ssid) { struct wpa_bss *selected = NULL; int prio; + if (wpa_s->last_scan_res == NULL || + wpa_s->last_scan_res_used == 0) + return NULL; /* no scan results from last update */ + while (selected == NULL) { for (prio = 0; prio < wpa_s->conf->num_prio; prio++) { selected = wpa_supplicant_select_bss( - wpa_s, scan_res, wpa_s->conf->pssid[prio], + wpa_s, wpa_s->conf->pssid[prio], selected_ssid); if (selected) break; } - if (selected == NULL && wpa_s->blacklist) { - wpa_printf(MSG_DEBUG, "No APs found - clear blacklist " - "and try again"); + if (selected == NULL && wpa_s->blacklist && + !wpa_s->countermeasures) { + wpa_dbg(wpa_s, MSG_DEBUG, "No APs found - clear " + "blacklist and try again"); wpa_blacklist_clear(wpa_s); wpa_s->blacklist_cleared++; } else if (selected == NULL) @@ -700,28 +863,42 @@ wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s, static void wpa_supplicant_req_new_scan(struct wpa_supplicant *wpa_s, int timeout_sec, int timeout_usec) { - if (!wpa_supplicant_enabled_networks(wpa_s->conf)) { + if (!wpa_supplicant_enabled_networks(wpa_s)) { /* * No networks are enabled; short-circuit request so * we don't wait timeout seconds before transitioning * to INACTIVE state. */ + wpa_dbg(wpa_s, MSG_DEBUG, "Short-circuit new scan request " + "since there are no enabled networks"); wpa_supplicant_set_state(wpa_s, WPA_INACTIVE); +#ifdef CONFIG_P2P + wpa_s->sta_scan_pending = 0; +#endif /* CONFIG_P2P */ return; } + + wpa_s->scan_for_connection = 1; wpa_supplicant_req_scan(wpa_s, timeout_sec, timeout_usec); } -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 +908,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; } @@ -755,7 +941,7 @@ wpa_supplicant_pick_new_network(struct wpa_supplicant *wpa_s) for (prio = 0; prio < wpa_s->conf->num_prio; prio++) { for (ssid = wpa_s->conf->pssid[prio]; ssid; ssid = ssid->pnext) { - if (ssid->disabled) + if (wpas_network_disabled(wpa_s, ssid)) continue; if (ssid->mode == IEEE80211_MODE_IBSS || ssid->mode == IEEE80211_MODE_AP) @@ -769,28 +955,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); } } @@ -798,11 +981,9 @@ static void wpa_supplicant_rsn_preauth_scan_results( static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s, struct wpa_bss *selected, - struct wpa_ssid *ssid, - struct wpa_scan_results *scan_res) + struct wpa_ssid *ssid) { - size_t i; - struct wpa_scan_res *current_bss = NULL; + struct wpa_bss *current_bss = NULL; int min_diff; if (wpa_s->reassociate) @@ -814,39 +995,46 @@ static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s, if (wpa_s->current_ssid != ssid) return 1; /* different network block */ - for (i = 0; i < scan_res->num; i++) { - struct wpa_scan_res *res = scan_res->res[i]; - const u8 *ie; - if (os_memcmp(res->bssid, wpa_s->bssid, ETH_ALEN) != 0) - continue; + if (wpas_driver_bss_selection(wpa_s)) + return 0; /* Driver-based roaming */ - ie = wpa_scan_get_ie(res, WLAN_EID_SSID); - if (ie == NULL) - continue; - if (ie[1] != wpa_s->current_ssid->ssid_len || - os_memcmp(ie + 2, wpa_s->current_ssid->ssid, ie[1]) != 0) - continue; - current_bss = res; - break; - } + if (wpa_s->current_ssid->ssid) + current_bss = wpa_bss_get(wpa_s, wpa_s->bssid, + wpa_s->current_ssid->ssid, + wpa_s->current_ssid->ssid_len); + if (!current_bss) + current_bss = wpa_bss_get_bssid(wpa_s, wpa_s->bssid); if (!current_bss) return 1; /* current BSS not seen in scan results */ - 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); + if (current_bss == selected) + return 0; + + if (selected->last_update_idx > current_bss->last_update_idx) + return 1; /* current BSS not seen in the last scan */ + +#ifndef CONFIG_NO_ROAMING + wpa_dbg(wpa_s, MSG_DEBUG, "Considering within-ESS reassociation"); + wpa_dbg(wpa_s, MSG_DEBUG, "Current BSS: " MACSTR " level=%d", + 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; } + if (current_bss->level < 0 && current_bss->level > selected->level) { + wpa_dbg(wpa_s, MSG_DEBUG, "Skip roam - Current BSS has better " + "signal level"); + return 0; + } + min_diff = 2; if (current_bss->level < 0) { if (current_bss->level < -85) @@ -861,22 +1049,28 @@ 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; +#else /* CONFIG_NO_ROAMING */ + return 0; +#endif /* CONFIG_NO_ROAMING */ } -static void wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, +/* Return != 0 if no scan results could be fetched or if scan results should not + * be shared with other virtual interfaces. */ +static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { - struct wpa_bss *selected; - struct wpa_ssid *ssid = NULL; struct wpa_scan_results *scan_res; int ap = 0; +#ifndef CONFIG_NO_RANDOM_POOL + size_t i, num; +#endif /* CONFIG_NO_RANDOM_POOL */ #ifdef CONFIG_AP if (wpa_s->ap_iface) @@ -885,102 +1079,343 @@ 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->global->p2p_cb_on_scan_complete && + !wpa_s->global->p2p_disabled && + wpa_s->global->p2p != NULL && !wpa_s->sta_scan_pending && + !wpa_s->scan_res_handler) { + wpa_s->global->p2p_cb_on_scan_complete = 0; + if (p2p_other_scan_completed(wpa_s->global->p2p) == 1) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Pending P2P operation " + "stopped scan processing"); + wpa_s->sta_scan_pending = 1; + wpa_supplicant_req_scan(wpa_s, 5, 0); + return -1; + } + } + wpa_s->sta_scan_pending = 0; +#endif /* CONFIG_P2P */ + scan_res = wpa_supplicant_get_scan_results(wpa_s, data ? &data->scan_info : NULL, 1); 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 + 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 -2; } 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); wpas_notify_scan_done(wpa_s, 1); + if (sme_proc_obss_scan(wpa_s) > 0) { + wpa_scan_results_free(scan_res); + return 0; + } + if ((wpa_s->conf->ap_scan == 2 && !wpas_wps_searching(wpa_s))) { wpa_scan_results_free(scan_res); - return; + return 0; + } + + if (autoscan_notify_scan(wpa_s, scan_res)) { + wpa_scan_results_free(scan_res); + 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); + wpas_wps_update_ap_info(wpa_s, scan_res); - selected = wpa_supplicant_pick_network(wpa_s, scan_res, &ssid); + wpa_scan_results_free(scan_res); + + return wpas_select_network_from_last_scan(wpa_s); +} + + +int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s) +{ + struct wpa_bss *selected; + struct wpa_ssid *ssid = NULL; + + selected = wpa_supplicant_pick_network(wpa_s, &ssid); if (selected) { int skip; - 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); + skip = !wpa_supplicant_need_to_roam(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); + /* + * Do not notify other virtual radios of scan results since we do not + * want them to start other associations at the same time. + */ + return 1; } else { - 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 (wpas_p2p_scan_no_go_seen(wpa_s) == 1) + return 0; + + 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 */ +#ifdef CONFIG_INTERWORKING + if (wpa_s->conf->auto_interworking && + wpa_s->conf->interworking && + wpa_s->conf->cred) { + wpa_dbg(wpa_s, MSG_DEBUG, "Interworking: " + "start ANQP fetch since no matching " + "networks found"); + wpa_s->network_select = 1; + wpa_s->auto_network_select = 1; + interworking_start_fetch_anqp(wpa_s); + return 1; + } +#endif /* CONFIG_INTERWORKING */ + 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. Similarly, if scan results started a new operation on this + * interface, do not notify other interfaces to avoid concurrent + * operations during a connection attempt. + */ + return; + } + + /* + * 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 */ +#ifdef CONFIG_WNM + +static void wnm_bss_keep_alive(void *eloop_ctx, void *sock_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + + if (wpa_s->wpa_state < WPA_ASSOCIATED) + return; + + if (!wpa_s->no_keep_alive) { + wpa_printf(MSG_DEBUG, "WNM: Send keep-alive to AP " MACSTR, + MAC2STR(wpa_s->bssid)); + /* TODO: could skip this if normal data traffic has been sent */ + /* TODO: Consider using some more appropriate data frame for + * this */ + if (wpa_s->l2) + l2_packet_send(wpa_s->l2, wpa_s->bssid, 0x0800, + (u8 *) "", 0); + } + +#ifdef CONFIG_SME + if (wpa_s->sme.bss_max_idle_period) { + unsigned int msec; + msec = wpa_s->sme.bss_max_idle_period * 1024; /* times 1000 */ + if (msec > 100) + msec -= 100; + eloop_register_timeout(msec / 1000, msec % 1000 * 1000, + wnm_bss_keep_alive, wpa_s, NULL); + } +#endif /* CONFIG_SME */ +} + + +static void wnm_process_assoc_resp(struct wpa_supplicant *wpa_s, + const u8 *ies, size_t ies_len) +{ + struct ieee802_11_elems elems; + + if (ies == NULL) + return; + + if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) + return; + +#ifdef CONFIG_SME + if (elems.bss_max_idle_period) { + unsigned int msec; + wpa_s->sme.bss_max_idle_period = + WPA_GET_LE16(elems.bss_max_idle_period); + wpa_printf(MSG_DEBUG, "WNM: BSS Max Idle Period: %u (* 1000 " + "TU)%s", wpa_s->sme.bss_max_idle_period, + (elems.bss_max_idle_period[2] & 0x01) ? + " (protected keep-live required)" : ""); + if (wpa_s->sme.bss_max_idle_period == 0) + wpa_s->sme.bss_max_idle_period = 1; + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) { + eloop_cancel_timeout(wnm_bss_keep_alive, wpa_s, NULL); + /* msec times 1000 */ + msec = wpa_s->sme.bss_max_idle_period * 1024; + if (msec > 100) + msec -= 100; + eloop_register_timeout(msec / 1000, msec % 1000 * 1000, + wnm_bss_keep_alive, wpa_s, + NULL); + } + } +#endif /* CONFIG_SME */ +} + +#endif /* CONFIG_WNM */ + + +void wnm_bss_keep_alive_deinit(struct wpa_supplicant *wpa_s) +{ +#ifdef CONFIG_WNM + eloop_cancel_timeout(wnm_bss_keep_alive, wpa_s, NULL); +#endif /* CONFIG_WNM */ +} + + static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { int l, len, found = 0, wpa_found, rsn_found; const u8 *p; - 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 */ +#ifdef CONFIG_WNM + wnm_process_assoc_resp(wpa_s, data->assoc_info.resp_ies, + data->assoc_info.resp_ies_len); +#endif /* CONFIG_WNM */ + } 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 +1452,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 +1463,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,18 +1546,64 @@ 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; } +static struct wpa_bss * wpa_supplicant_get_new_bss( + struct wpa_supplicant *wpa_s, const u8 *bssid) +{ + struct wpa_bss *bss = NULL; + struct wpa_ssid *ssid = wpa_s->current_ssid; + + if (ssid->ssid_len > 0) + bss = wpa_bss_get(wpa_s, bssid, ssid->ssid, ssid->ssid_len); + if (!bss) + bss = wpa_bss_get_bssid(wpa_s, bssid); + + return bss; +} + + +static int wpa_supplicant_assoc_update_ie(struct wpa_supplicant *wpa_s) +{ + const u8 *bss_wpa = NULL, *bss_rsn = NULL; + + if (!wpa_s->current_bss || !wpa_s->current_ssid) + return -1; + + if (!wpa_key_mgmt_wpa_any(wpa_s->current_ssid->key_mgmt)) + return 0; + + bss_wpa = wpa_bss_get_vendor_ie(wpa_s->current_bss, + WPA_IE_VENDOR_TYPE); + bss_rsn = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_RSN); + + if (wpa_sm_set_ap_wpa_ie(wpa_s->wpa, bss_wpa, + bss_wpa ? 2 + bss_wpa[1] : 0) || + wpa_sm_set_ap_rsn_ie(wpa_s->wpa, bss_rsn, + bss_rsn ? 2 + bss_rsn[1] : 0)) + return -1; + + return 0; +} + + static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { u8 bssid[ETH_ALEN]; int ft_completed; - int bssid_changed; struct wpa_driver_capa capa; #ifdef CONFIG_AP @@ -1109,7 +1611,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 */ @@ -1118,39 +1621,51 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, if (data && wpa_supplicant_event_associnfo(wpa_s, data) < 0) return; + if (wpa_drv_get_bssid(wpa_s, bssid) < 0) { + wpa_dbg(wpa_s, MSG_ERROR, "Failed to get BSSID"); + wpa_supplicant_deauthenticate( + wpa_s, WLAN_REASON_DEAUTH_LEAVING); + return; + } + wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATED); - if (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 (os_memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0) { + wpa_dbg(wpa_s, MSG_DEBUG, "Associated to a new BSS: BSSID=" MACSTR, MAC2STR(bssid)); - bssid_changed = os_memcmp(wpa_s->bssid, bssid, ETH_ALEN); + random_add_randomness(bssid, ETH_ALEN); os_memcpy(wpa_s->bssid, bssid, ETH_ALEN); os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); - if (bssid_changed) - wpas_notify_bssid_changed(wpa_s); + wpas_notify_bssid_changed(wpa_s); if (wpa_supplicant_dynamic_keys(wpa_s) && !ft_completed) { wpa_clear_keys(wpa_s, bssid); } if (wpa_supplicant_select_config(wpa_s) < 0) { - wpa_supplicant_disassociate( + wpa_supplicant_deauthenticate( wpa_s, WLAN_REASON_DEAUTH_LEAVING); return; } if (wpa_s->current_ssid) { struct wpa_bss *bss = NULL; - struct wpa_ssid *ssid = wpa_s->current_ssid; - if (ssid->ssid_len > 0) - bss = wpa_bss_get(wpa_s, bssid, - ssid->ssid, ssid->ssid_len); - if (!bss) - bss = wpa_bss_get_bssid(wpa_s, bssid); + + bss = wpa_supplicant_get_new_bss(wpa_s, bssid); + if (!bss) { + wpa_supplicant_update_scan_results(wpa_s); + + /* Get the BSS from the new scan results */ + bss = wpa_supplicant_get_new_bss(wpa_s, bssid); + } + if (bss) wpa_s->current_bss = bss; } + + if (wpa_s->conf->ap_scan == 1 && + wpa_s->drv_flags & WPA_DRIVER_FLAGS_BSS_SELECTION) { + if (wpa_supplicant_assoc_update_ie(wpa_s) < 0) + wpa_msg(wpa_s, MSG_WARNING, + "WPA/RSN IEs not updated"); + } } #ifdef CONFIG_SME @@ -1209,8 +1724,27 @@ 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); } + wpa_s->last_eapol_matches_bssid = 0; + if (wpa_s->pending_eapol_rx) { struct os_time now, age; os_get_time(&now); @@ -1218,9 +1752,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 +1764,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,20 +1771,101 @@ 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 */ + + wpas_wps_notify_assoc(wpa_s, bssid); +} + + +static int disconnect_reason_recoverable(u16 reason_code) +{ + return reason_code == WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY || + reason_code == WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA || + reason_code == WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA; } static void wpa_supplicant_event_disassoc(struct wpa_supplicant *wpa_s, - u16 reason_code) + u16 reason_code, + int locally_generated) +{ + const u8 *bssid; + + if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) { + /* + * At least Host AP driver and a Prism3 card seemed to be + * generating streams of disconnected events when configuring + * IBSS for WPA-None. Ignore them for now. + */ + return; + } + + bssid = wpa_s->bssid; + if (is_zero_ether_addr(bssid)) + bssid = wpa_s->pending_bssid; + + if (!is_zero_ether_addr(bssid) || + wpa_s->wpa_state >= WPA_AUTHENTICATING) { + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid=" MACSTR + " reason=%d%s", + MAC2STR(bssid), reason_code, + locally_generated ? " locally_generated=1" : ""); + } +} + + +static int could_be_psk_mismatch(struct wpa_supplicant *wpa_s, u16 reason_code, + int locally_generated) +{ + if (wpa_s->wpa_state != WPA_4WAY_HANDSHAKE || + !wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) + return 0; /* Not in 4-way handshake with PSK */ + + /* + * It looks like connection was lost while trying to go through PSK + * 4-way handshake. Filter out known disconnection cases that are caused + * by something else than PSK mismatch to avoid confusing reports. + */ + + if (locally_generated) { + if (reason_code == WLAN_REASON_IE_IN_4WAY_DIFFERS) + return 0; + } + + return 1; +} + + +static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s, + u16 reason_code, + int locally_generated) { const u8 *bssid; -#ifdef CONFIG_SME int authenticating; u8 prev_pending_bssid[ETH_ALEN]; + struct wpa_bss *fast_reconnect = NULL; + struct wpa_ssid *fast_reconnect_ssid = NULL; + struct wpa_ssid *last_ssid; authenticating = wpa_s->wpa_state == WPA_AUTHENTICATING; os_memcpy(prev_pending_bssid, wpa_s->pending_bssid, ETH_ALEN); -#endif /* CONFIG_SME */ if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) { /* @@ -1277,61 +1873,93 @@ 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; } - if (wpa_s->wpa_state == WPA_4WAY_HANDSHAKE && - wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) { + if (could_be_psk_mismatch(wpa_s, reason_code, locally_generated)) { wpa_msg(wpa_s, MSG_INFO, "WPA: 4-Way Handshake failed - " "pre-shared key may be incorrect"); + wpas_auth_failed(wpa_s); + } + if (!wpa_s->auto_reconnect_disabled || + wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) { + wpa_dbg(wpa_s, MSG_DEBUG, "Auto connect enabled: try to " + "reconnect (wps=%d wpa_state=%d)", + wpa_s->key_mgmt == WPA_KEY_MGMT_WPS, + wpa_s->wpa_state); + if (wpa_s->wpa_state == WPA_COMPLETED && + wpa_s->current_ssid && + wpa_s->current_ssid->mode == WPAS_MODE_INFRA && + !locally_generated && + disconnect_reason_recoverable(reason_code)) { + /* + * It looks like the AP has dropped association with + * us, but could allow us to get back in. Try to + * reconnect to the same BSS without full scan to save + * time for some common cases. + */ + fast_reconnect = wpa_s->current_bss; + fast_reconnect_ssid = wpa_s->current_ssid; + } else if (wpa_s->wpa_state >= WPA_ASSOCIATING) + wpa_supplicant_req_scan(wpa_s, 0, 100000); + else + wpa_dbg(wpa_s, MSG_DEBUG, "Do not request new " + "immediate scan"); + } else { + wpa_dbg(wpa_s, MSG_DEBUG, "Auto connect disabled: do not " + "try to re-connect"); + wpa_s->reassociate = 0; + wpa_s->disconnected = 1; + wpa_supplicant_cancel_sched_scan(wpa_s); } - if (wpa_s->wpa_state >= WPA_ASSOCIATED) - wpa_supplicant_req_scan(wpa_s, 0, 100000); bssid = wpa_s->bssid; if (is_zero_ether_addr(bssid)) bssid = wpa_s->pending_bssid; - wpa_blacklist_add(wpa_s, bssid); + if (wpa_s->wpa_state >= WPA_AUTHENTICATING) + 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 (locally_generated) + wpa_s->disconnect_reason = -reason_code; + else + wpa_s->disconnect_reason = reason_code; + wpas_notify_disconnect_reason(wpa_s); if (wpa_supplicant_dynamic_keys(wpa_s)) { - wpa_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); } + last_ssid = wpa_s->current_ssid; 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); + + if (authenticating && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)) { + sme_disassoc_while_authenticating(wpa_s, prev_pending_bssid); + wpa_s->current_ssid = last_ssid; + } + + if (fast_reconnect) { +#ifndef CONFIG_NO_SCAN_PROCESSING + wpa_dbg(wpa_s, MSG_DEBUG, "Try to reconnect to the same BSS"); + if (wpa_supplicant_connect(wpa_s, fast_reconnect, + fast_reconnect_ssid) < 0) { + /* Recover through full scan */ + wpa_supplicant_req_scan(wpa_s, 0, 100000); + } +#endif /* CONFIG_NO_SCAN_PROCESSING */ } -#endif /* CONFIG_SME */ } #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; } @@ -1368,6 +1996,9 @@ wpa_supplicant_event_michael_mic_failure(struct wpa_supplicant *wpa_s, /* initialize countermeasures */ wpa_s->countermeasures = 1; + + wpa_blacklist_add(wpa_s, wpa_s->bssid); + wpa_msg(wpa_s, MSG_WARNING, "TKIP countermeasures started"); /* @@ -1406,8 +2037,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 +2085,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 +2125,44 @@ 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_WNM +static void wpa_supplicant_event_wnm(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + if (data == NULL) + return; + switch (data->wnm.oper) { + case WNM_OPER_SLEEP: + wpa_printf(MSG_DEBUG, "Start sending WNM-Sleep Request " + "(action=%d, intval=%d)", + data->wnm.sleep_action, data->wnm.sleep_intval); + ieee802_11_send_wnmsleep_req(wpa_s, data->wnm.sleep_action, + data->wnm.sleep_intval, NULL); + break; + } +} +#endif /* CONFIG_WNM */ + + #ifdef CONFIG_IEEE80211R static void wpa_supplicant_event_ft_response(struct wpa_supplicant *wpa_s, @@ -1512,8 +2187,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 +2220,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,11 +2257,67 @@ 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 */ +} + + void wpa_supplicant_event(void *ctx, enum wpa_event_type event, union wpa_event_data *data) { struct wpa_supplicant *wpa_s = ctx; u16 reason_code = 0; + int locally_generated = 0; + + if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED && + event != EVENT_INTERFACE_ENABLED && + event != EVENT_INTERFACE_STATUS && + event != EVENT_SCHED_SCAN_STOPPED) { + wpa_dbg(wpa_s, MSG_DEBUG, + "Ignore event %s (%d) while interface is disabled", + event_to_string(event), event); + return; + } + +#ifndef CONFIG_NO_STDOUT_DEBUG +{ + int level = MSG_DEBUG; + + if (event == EVENT_RX_MGMT && data->rx_mgmt.frame_len >= 24) { + const struct ieee80211_hdr *hdr; + u16 fc; + hdr = (const struct ieee80211_hdr *) data->rx_mgmt.frame; + fc = le_to_host16(hdr->frame_control); + if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && + WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON) + level = MSG_EXCESSIVE; + } + + wpa_dbg(wpa_s, level, "Event %s (%d) received", + event_to_string(event), event); +} +#endif /* CONFIG_NO_STDOUT_DEBUG */ switch (event) { case EVENT_AUTH: @@ -1587,24 +2327,69 @@ 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%s", + data->disassoc_info.reason_code, + data->disassoc_info.locally_generated ? + " (locally generated)" : ""); + if (data->disassoc_info.addr) + wpa_dbg(wpa_s, MSG_DEBUG, " * address " MACSTR, + MAC2STR(data->disassoc_info.addr)); + } #ifdef CONFIG_AP if (wpa_s->ap_iface && data && data->disassoc_info.addr) { hostapd_notif_disassoc(wpa_s->ap_iface->bss[0], data->disassoc_info.addr); break; } + if (wpa_s->ap_iface) { + wpa_dbg(wpa_s, MSG_DEBUG, "Ignore disassoc event in " + "AP mode"); + break; + } #endif /* CONFIG_AP */ - if (data) - reason_code = data->deauth_info.reason_code; + if (data) { + reason_code = data->disassoc_info.reason_code; + locally_generated = + data->disassoc_info.locally_generated; + wpa_hexdump(MSG_DEBUG, "Disassociation frame IE(s)", + data->disassoc_info.ie, + data->disassoc_info.ie_len); +#ifdef CONFIG_P2P + wpas_p2p_disassoc_notif( + wpa_s, data->disassoc_info.addr, reason_code, + data->disassoc_info.ie, + data->disassoc_info.ie_len, + locally_generated); +#endif /* CONFIG_P2P */ + } if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) sme_event_disassoc(wpa_s, data); /* fall through */ 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; + locally_generated = + data->deauth_info.locally_generated; + wpa_dbg(wpa_s, MSG_DEBUG, " * reason %u%s", + data->deauth_info.reason_code, + data->deauth_info.locally_generated ? + " (locally generated)" : ""); + if (data->deauth_info.addr) { + wpa_dbg(wpa_s, MSG_DEBUG, " * address " + MACSTR, + MAC2STR(data->deauth_info. + addr)); + } + wpa_hexdump(MSG_DEBUG, + "Deauthentication frame IE(s)", + data->deauth_info.ie, + data->deauth_info.ie_len); + } } #ifdef CONFIG_AP if (wpa_s->ap_iface && data && data->deauth_info.addr) { @@ -1612,8 +2397,38 @@ 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); + wpa_supplicant_event_disassoc(wpa_s, reason_code, + locally_generated); + if (reason_code == WLAN_REASON_IEEE_802_1X_AUTH_FAILED || + ((wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) || + (wpa_s->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) && + eapol_sm_failed(wpa_s->eapol))) + wpas_auth_failed(wpa_s); +#ifdef CONFIG_P2P + if (event == EVENT_DEAUTH && data) { + if (wpas_p2p_deauth_notif(wpa_s, + data->deauth_info.addr, + reason_code, + data->deauth_info.ie, + data->deauth_info.ie_len, + locally_generated) > 0) { + /* + * The interface was removed, so cannot + * continue processing any additional + * operations after this. + */ + break; + } + } +#endif /* CONFIG_P2P */ + wpa_supplicant_event_disassoc_finish(wpa_s, reason_code, + locally_generated); break; case EVENT_MICHAEL_MIC_FAILURE: wpa_supplicant_event_michael_mic_failure(wpa_s, data); @@ -1621,6 +2436,18 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, #ifndef CONFIG_NO_SCAN_PROCESSING case EVENT_SCAN_RESULTS: wpa_supplicant_event_scan_results(wpa_s, data); +#ifdef CONFIG_P2P + if (wpa_s->global->p2p_cb_on_scan_complete && !wpa_s->global->p2p_disabled && + wpa_s->global->p2p != NULL && + wpa_s->wpa_state != WPA_AUTHENTICATING && + wpa_s->wpa_state != WPA_ASSOCIATING) { + wpa_s->global->p2p_cb_on_scan_complete = 0; + if (p2p_other_scan_completed(wpa_s->global->p2p) == 1) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Pending P2P operation " + "continued after scan result processing"); + } + } +#endif /* CONFIG_P2P */ break; #endif /* CONFIG_NO_SCAN_PROCESSING */ case EVENT_ASSOCINFO: @@ -1637,6 +2464,16 @@ 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_WNM + case EVENT_WNM: + wpa_supplicant_event_wnm(wpa_s, data); + break; +#endif /* CONFIG_WNM */ #ifdef CONFIG_IEEE80211R case EVENT_FT_RESPONSE: wpa_supplicant_event_ft_response(wpa_s, data); @@ -1648,18 +2485,76 @@ 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); + else { + const u8 *bssid = data->assoc_reject.bssid; + if (bssid == NULL || is_zero_ether_addr(bssid)) + bssid = wpa_s->pending_bssid; + wpas_connection_failed(wpa_s, bssid); + wpa_supplicant_mark_disassoc(wpa_s); + } break; case EVENT_AUTH_TIMED_OUT: - 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 +2569,91 @@ 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_EAPOL_TX_STATUS: + ap_eapol_tx_status(wpa_s, data->eapol_tx_status.dst, + data->eapol_tx_status.data, + data->eapol_tx_status.data_len, + data->eapol_tx_status.ack); + break; + case EVENT_DRIVER_CLIENT_POLL_OK: + ap_client_poll_ok(wpa_s, data->client_poll.addr); break; case EVENT_RX_FROM_UNKNOWN: if (wpa_s->ap_iface == NULL) break; - ap_rx_from_unknown_sta(wpa_s, data->rx_from_unknown.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) + case EVENT_CH_SWITCH: + if (!data) + break; + if (!wpa_s->ap_iface) { + wpa_dbg(wpa_s, MSG_DEBUG, "AP: Ignore channel switch " + "event in non-AP mode"); + break; + } + +#ifdef CONFIG_AP + wpas_ap_ch_switch(wpa_s, data->ch_switch.freq, + data->ch_switch.ht_enabled, + data->ch_switch.ch_offset); +#endif /* CONFIG_AP */ + break; + case EVENT_RX_MGMT: { + 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 (wpa_s->ap_iface == NULL) { +#ifdef CONFIG_P2P + if (stype == WLAN_FC_STYPE_PROBE_REQ && + data->rx_mgmt.frame_len > 24) { + const u8 *src = mgmt->sa; + const u8 *ie = mgmt->u.probe_req.variable; + size_t ie_len = data->rx_mgmt.frame_len - + (mgmt->u.probe_req.variable - + data->rx_mgmt.frame); + wpas_p2p_probe_req_rx( + wpa_s, src, mgmt->da, + mgmt->bssid, ie, ie_len, + data->rx_mgmt.ssi_signal); + break; + } +#endif /* CONFIG_P2P */ + wpa_dbg(wpa_s, MSG_DEBUG, "AP: ignore received " + "management frame in non-AP mode"); break; + } + + if (stype == WLAN_FC_STYPE_PROBE_REQ && + data->rx_mgmt.frame_len > 24) { + const u8 *ie = mgmt->u.probe_req.variable; + size_t ie_len = data->rx_mgmt.frame_len - + (mgmt->u.probe_req.variable - + data->rx_mgmt.frame); + + wpas_notify_preq(wpa_s, mgmt->sa, mgmt->da, + mgmt->bssid, ie, ie_len, + data->rx_mgmt.ssi_signal); + } + ap_mgmt_rx(wpa_s, &data->rx_mgmt); 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 +2661,161 @@ 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_WNM + if (data->rx_action.category == WLAN_ACTION_WNM) { + ieee802_11_rx_wnm_action(wpa_s, &data->rx_action); + break; + } +#endif /* CONFIG_WNM */ +#ifdef CONFIG_GAS + if (data->rx_action.category == WLAN_ACTION_PUBLIC && + gas_query_rx(wpa_s->gas, data->rx_action.da, + data->rx_action.sa, data->rx_action.bssid, + data->rx_action.data, data->rx_action.len, + data->rx_action.freq) == 0) + break; +#endif /* CONFIG_GAS */ +#ifdef CONFIG_TDLS + if (data->rx_action.category == WLAN_ACTION_PUBLIC && + data->rx_action.len >= 4 && + data->rx_action.data[0] == WLAN_TDLS_DISCOVERY_RESPONSE) { + wpa_dbg(wpa_s, MSG_DEBUG, "TDLS: Received Discovery " + "Response from " MACSTR, + MAC2STR(data->rx_action.sa)); + break; + } +#endif /* CONFIG_TDLS */ +#ifdef CONFIG_P2P + wpas_p2p_rx_action(wpa_s, data->rx_action.da, + data->rx_action.sa, + data->rx_action.bssid, + data->rx_action.category, + data->rx_action.data, + data->rx_action.len, data->rx_action.freq); +#endif /* CONFIG_P2P */ break; -#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, + data->rx_probe_req.ssi_signal); + break; + } +#endif /* CONFIG_AP */ +#ifdef CONFIG_P2P + wpas_p2p_probe_req_rx(wpa_s, data->rx_probe_req.sa, + data->rx_probe_req.da, + data->rx_probe_req.bssid, + data->rx_probe_req.ie, + data->rx_probe_req.ie_len, + data->rx_probe_req.ssi_signal); +#endif /* CONFIG_P2P */ break; - } -#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 +2823,119 @@ 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 (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) + break; + + /* + * 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/contrib/wpa/wpa_supplicant/examples/dbus-listen-preq.py b/contrib/wpa/wpa_supplicant/examples/dbus-listen-preq.py new file mode 100755 index 0000000..5ac9859 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/examples/dbus-listen-preq.py @@ -0,0 +1,62 @@ +#!/usr/bin/python + +import dbus +import sys +import time +import gobject +from dbus.mainloop.glib import DBusGMainLoop + +WPAS_DBUS_SERVICE = "fi.w1.wpa_supplicant1" +WPAS_DBUS_INTERFACE = "fi.w1.wpa_supplicant1" +WPAS_DBUS_OPATH = "/fi/w1/wpa_supplicant1" +WPAS_DBUS_INTERFACES_INTERFACE = "fi.w1.wpa_supplicant1.Interface" + +def usage(): + print "Usage: %s <ifname>" % sys.argv[0] + print "Press Ctrl-C to stop" + +def ProbeRequest(args): + if 'addr' in args: + print '%.2x:%.2x:%.2x:%.2x:%.2x:%.2x' % tuple(args['addr']), + if 'dst' in args: + print '-> %.2x:%.2x:%.2x:%.2x:%.2x:%.2x' % tuple(args['dst']), + if 'bssid' in args: + print '(bssid %.2x:%.2x:%.2x:%.2x:%.2x:%.2x)' % tuple(args['dst']), + if 'signal' in args: + print 'signal:%d' % args['signal'], + if 'ies' in args: + print 'have IEs (%d bytes)' % len(args['ies']), + print '' + +if __name__ == "__main__": + global bus + global wpas_obj + global if_obj + global p2p_iface + + dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) + + bus = dbus.SystemBus() + wpas_obj = bus.get_object(WPAS_DBUS_SERVICE, WPAS_DBUS_OPATH) + + # Print list of i/f if no one is specified + if (len(sys.argv) < 2) : + usage() + sys.exit(0) + + wpas = dbus.Interface(wpas_obj, WPAS_DBUS_INTERFACE) + + ifname = sys.argv[1] + + path = wpas.GetInterface(ifname) + + if_obj = bus.get_object(WPAS_DBUS_SERVICE, path) + iface = dbus.Interface(if_obj, WPAS_DBUS_INTERFACES_INTERFACE) + + bus.add_signal_receiver(ProbeRequest, + dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE, + signal_name="ProbeRequest") + + iface.SubscribeProbeReq() + + gobject.MainLoop().run() diff --git a/contrib/wpa/wpa_supplicant/examples/p2p-action-udhcp.sh b/contrib/wpa/wpa_supplicant/examples/p2p-action-udhcp.sh new file mode 100755 index 0000000..d7d0e79 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/examples/p2p-action-udhcp.sh @@ -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/contrib/wpa/wpa_supplicant/examples/p2p-action.sh b/contrib/wpa/wpa_supplicant/examples/p2p-action.sh new file mode 100755 index 0000000..8759f54 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/examples/p2p-action.sh @@ -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/contrib/wpa/wpa_supplicant/examples/p2p/p2p_connect.py b/contrib/wpa/wpa_supplicant/examples/p2p/p2p_connect.py new file mode 100644 index 0000000..59b0a9d --- /dev/null +++ b/contrib/wpa/wpa_supplicant/examples/p2p/p2p_connect.py @@ -0,0 +1,299 @@ +#!/usr/bin/python +# Tests p2p_connect +# Will try to connect to another peer +# and form a group +######### MAY NEED TO RUN AS SUDO ############# + +import dbus +import sys, os +import time +import gobject +import getopt +from dbus.mainloop.glib import DBusGMainLoop + + +def usage(): + print "Usage:" + print " %s -i <interface_name> -m <wps_method> \ " \ + % sys.argv[0] + print " -a <addr> [-p <pin>] [-g <go_intent>] \ " + print " [-w <wpas_dbus_interface>]" + print "Options:" + print " -i = interface name" + print " -m = wps method" + print " -a = peer address" + print " -p = pin number (8 digits)" + print " -g = group owner intent" + print " -w = wpas dbus interface = fi.w1.wpa_supplicant1" + print "Example:" + print " %s -i wlan0 -a 0015008352c0 -m display -p 12345670" % sys.argv[0] + + +# Required Signals +def GONegotiationSuccess(status): + print "Go Negotiation Success" + +def GONegotiationFailure(status): + print 'Go Negotiation Failed. Status:' + print format(status) + os._exit(0) + +def GroupStarted(properties): + if properties.has_key("group_object"): + print 'Group Formation Complete %s' \ + % properties["group_object"] + os._exit(0) + +def WpsFailure(status, etc): + print "WPS Authentication Failure".format(status) + print etc + os._exit(0) + +class P2P_Connect(): + # Needed Variables + global bus + global wpas_object + global interface_object + global p2p_interface + global ifname + global wpas + global wpas_dbus_interface + global timeout + global path + global wps_method + global go_intent + global addr + global pin + + # Dbus Paths + global wpas_dbus_opath + global wpas_dbus_interfaces_opath + global wpas_dbus_interfaces_interface + global wpas_dbus_interfaces_p2pdevice + + # Dictionary of Arguements + global p2p_connect_arguements + + # Constructor + def __init__(self,ifname,wpas_dbus_interface,addr, + pin,wps_method,go_intent): + # Initializes variables and threads + self.ifname = ifname + self.wpas_dbus_interface = wpas_dbus_interface + self.wps_method = wps_method + self.go_intent = go_intent + self.addr = addr + self.pin = pin + + # Generating interface/object paths + self.wpas_dbus_opath = \ + "/" + self.wpas_dbus_interface.replace(".","/") + self.wpas_wpas_dbus_interfaces_opath = \ + self.wpas_dbus_opath + "/Interfaces" + self.wpas_dbus_interfaces_interface = \ + self.wpas_dbus_interface + ".Interface" + self.wpas_dbus_interfaces_p2pdevice = \ + self.wpas_dbus_interfaces_interface + ".P2PDevice" + + # Getting interfaces and objects + DBusGMainLoop(set_as_default=True) + self.bus = dbus.SystemBus() + self.wpas_object = self.bus.get_object( + self.wpas_dbus_interface, + self.wpas_dbus_opath) + self.wpas = dbus.Interface( + self.wpas_object, self.wpas_dbus_interface) + + # See if wpa_supplicant already knows about this interface + self.path = None + try: + self.path = self.wpas.GetInterface(ifname) + except: + if not str(exc).startswith( + self.wpas_dbus_interface + \ + ".InterfaceUnknown:"): + raise exc + try: + path = self.wpas.CreateInterface( + {'Ifname': ifname, 'Driver': 'test'}) + time.sleep(1) + + except dbus.DBusException, exc: + if not str(exc).startswith( + self.wpas_dbus_interface + \ + ".InterfaceExists:"): + raise exc + + # Get Interface and objects + self.interface_object = self.bus.get_object( + self.wpas_dbus_interface,self.path) + self.p2p_interface = dbus.Interface( + self.interface_object, + self.wpas_dbus_interfaces_p2pdevice) + + # Add signals + self.bus.add_signal_receiver(GONegotiationSuccess, + dbus_interface=self.wpas_dbus_interfaces_p2pdevice, + signal_name="GONegotiationSuccess") + self.bus.add_signal_receiver(GONegotiationFailure, + dbus_interface=self.wpas_dbus_interfaces_p2pdevice, + signal_name="GONegotiationFailure") + self.bus.add_signal_receiver(GroupStarted, + dbus_interface=self.wpas_dbus_interfaces_p2pdevice, + signal_name="GroupStarted") + self.bus.add_signal_receiver(WpsFailure, + dbus_interface=self.wpas_dbus_interfaces_p2pdevice, + signal_name="WpsFailed") + + + #Constructing all the arguements needed to connect + def constructArguements(self): + # Adding required arguements + self.p2p_connect_arguements = {'wps_method':self.wps_method, + 'peer':dbus.ObjectPath(self.path+'/Peers/'+self.addr)} + + # Display requires a pin, and a go intent of 15 + if (self.wps_method == 'display'): + if (self.pin != None): + self.p2p_connect_arguements.update({'pin':self.pin}) + else: + print "Error:\n Pin required for wps_method=display" + usage() + quit() + + if (self.go_intent != None and int(self.go_intent) != 15): + print "go_intent overwritten to 15" + + self.go_intent = '15' + + # Keypad requires a pin, and a go intent of less than 15 + elif (self.wps_method == 'keypad'): + if (self.pin != None): + self.p2p_connect_arguements.update({'pin':self.pin}) + else: + print "Error:\n Pin required for wps_method=keypad" + usage() + quit() + + if (self.go_intent != None and int(self.go_intent) == 15): + error = "Error :\n Group Owner intent cannot be" + \ + " 15 for wps_method=keypad" + print error + usage() + quit() + + # Doesn't require pin + # for ./wpa_cli, p2p_connect [mac] [pin#], wps_method=keypad + elif (self.wps_method == 'pin'): + if (self.pin != None): + print "pin ignored" + + # No pin is required for pbc so it is ignored + elif (self.wps_method == 'pbc'): + if (self.pin != None): + print "pin ignored" + + else: + print "Error:\n wps_method not supported or does not exist" + usage() + quit() + + # Go_intent is optional for all arguements + if (self.go_intent != None): + self.p2p_connect_arguements.update( + {'go_intent':dbus.Int32(self.go_intent)}) + + # Running p2p_connect + def run(self): + try: + result_pin = self.p2p_interface.Connect( + self.p2p_connect_arguements) + + except dbus.DBusException, exc: + raise exc + + if (self.wps_method == 'pin' and \ + not self.p2p_connect_arguements.has_key('pin') ): + print "Connect return with pin value of %d " % int(result_pin) + gobject.MainLoop().run() + +if __name__ == "__main__": + + # Required + interface_name = None + wps_method = None + addr = None + + # Conditionally optional + pin = None + + # Optional + wpas_dbus_interface = 'fi.w1.wpa_supplicant1' + go_intent = None + + # Using getopts to handle options + try: + options, args = getopt.getopt(sys.argv[1:],"hi:m:a:p:g:w:") + + except getopt.GetoptError: + usage() + quit() + + # If theres a switch, override default option + for key, value in options: + # Help + if (key == "-h"): + usage() + quit() + # Interface Name + elif (key == "-i"): + interface_name = value + # WPS Method + elif (key == "-m"): + wps_method = value + # Address + elif (key == "-a"): + addr = value + # Pin + elif (key == "-p"): + pin = value + # Group Owner Intent + elif (key == "-g"): + go_intent = value + # Dbus interface + elif (key == "-w"): + wpas_dbus_interface = value + else: + assert False, "unhandled option" + + # Required Arguements check + if (interface_name == None or wps_method == None or addr == None): + print "Error:\n Required arguements not specified" + usage() + quit() + + # Group Owner Intent Check + if (go_intent != None and (int(go_intent) > 15 or int(go_intent) < 0) ): + print "Error:\n Group Owner Intent must be between 0 and 15 inclusive" + usage() + quit() + + # Pin Check + if (pin != None and len(pin) != 8): + print "Error:\n Pin is not 8 digits" + usage() + quit() + + try: + p2p_connect_test = P2P_Connect(interface_name,wpas_dbus_interface, + addr,pin,wps_method,go_intent) + + except: + print "Error:\n Invalid Arguements" + usage() + quit() + + p2p_connect_test.constructArguements() + p2p_connect_test.run() + + os._exit(0) diff --git a/contrib/wpa/wpa_supplicant/examples/p2p/p2p_disconnect.py b/contrib/wpa/wpa_supplicant/examples/p2p/p2p_disconnect.py new file mode 100644 index 0000000..c3e39b3 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/examples/p2p/p2p_disconnect.py @@ -0,0 +1,169 @@ +#!/usr/bin/python +# Tests P2P_Disconnect +# Will perform disconnect on interface_name +######### MAY NEED TO RUN AS SUDO ############# + +import dbus +import sys, os +import time +import gobject +import threading +import getopt +from dbus.mainloop.glib import DBusGMainLoop + +def usage(): + print "Usage:" + print " %s -i <interface_name> \ " \ + % sys.argv[0] + print " [-w <wpas_dbus_interface>]" + print "Options:" + print " -i = interface name" + print " -w = wpas dbus interface = fi.w1.wpa_supplicant1" + print "Example:" + print " %s -i p2p-wlan0-0" % sys.argv[0] + +# Required Signals +def GroupFinished(status, etc): + print "Disconnected" + os._exit(0) + +class P2P_Disconnect (threading.Thread): + # Needed Variables + global bus + global wpas_object + global interface_object + global p2p_interface + global interface_name + global wpas + global wpas_dbus_interface + global path + global timeout + + # Dbus Paths + global wpas_dbus_opath + global wpas_dbus_interfaces_opath + global wpas_dbus_interfaces_interface + global wpas_dbus_interfaces_p2pdevice + + # Constructor + def __init__(self,interface_name,wpas_dbus_interface,timeout): + # Initializes variables and threads + self.interface_name = interface_name + self.wpas_dbus_interface = wpas_dbus_interface + self.timeout = timeout + + # Initializes thread and daemon allows for ctrl-c kill + threading.Thread.__init__(self) + self.daemon = True + + # Generating interface/object paths + self.wpas_dbus_opath = "/" + \ + self.wpas_dbus_interface.replace(".","/") + self.wpas_wpas_dbus_interfaces_opath = self.wpas_dbus_opath + \ + "/Interfaces" + self.wpas_dbus_interfaces_interface = \ + self.wpas_dbus_interface + ".Interface" + self.wpas_dbus_interfaces_p2pdevice = \ + self.wpas_dbus_interfaces_interface \ + + ".P2PDevice" + + # Getting interfaces and objects + DBusGMainLoop(set_as_default=True) + self.bus = dbus.SystemBus() + self.wpas_object = self.bus.get_object( + self.wpas_dbus_interface, + self.wpas_dbus_opath) + self.wpas = dbus.Interface(self.wpas_object, + self.wpas_dbus_interface) + + # Try to see if supplicant knows about interface + # If not, throw an exception + try: + self.path = self.wpas.GetInterface( + self.interface_name) + except dbus.DBusException, exc: + error = 'Error:\n Interface ' + self.interface_name \ + + ' was not found' + print error + usage() + os._exit(0) + + self.interface_object = self.bus.get_object( + self.wpas_dbus_interface, self.path) + self.p2p_interface = dbus.Interface(self.interface_object, + self.wpas_dbus_interfaces_p2pdevice) + + # Signals + self.bus.add_signal_receiver(GroupFinished, + dbus_interface=self.wpas_dbus_interfaces_p2pdevice, + signal_name="GroupFinished") + + # Runs p2p_disconnect + def run(self): + # Allows other threads to keep working while MainLoop runs + # Required for timeout implementation + gobject.MainLoop().get_context().iteration(True) + gobject.threads_init() + self.p2p_interface.Disconnect() + gobject.MainLoop().run() + + +if __name__ == "__main__": + + timeout = 5 + # Defaults for optional inputs + wpas_dbus_interface = 'fi.w1.wpa_supplicant1' + + # interface_name is required + interface_name = None + + # Using getopts to handle options + try: + options, args = getopt.getopt(sys.argv[1:],"hi:w:") + + except getopt.GetoptError: + usage() + quit() + + # If theres a switch, override default option + for key, value in options: + # Help + if (key == "-h"): + usage() + quit() + # Interface Name + elif (key == "-i"): + interface_name = value + # Dbus interface + elif (key == "-w"): + wpas_dbus_interface = value + else: + assert False, "unhandled option" + + # Interface name is required and was not given + if (interface_name == None): + print "Error:\n interface_name is required" + usage() + quit() + + # Constructor + try: + p2p_disconnect_test = P2P_Disconnect(interface_name, + wpas_dbus_interface,timeout) + + except: + print "Error:\n Invalid wpas_dbus_interface" + usage() + quit() + + # Start P2P_Disconnect + p2p_disconnect_test.start() + + try: + time.sleep(int(p2p_disconnect_test.timeout)) + + except: + pass + + print "Disconnect timed out" + quit() diff --git a/contrib/wpa/wpa_supplicant/examples/p2p/p2p_find.py b/contrib/wpa/wpa_supplicant/examples/p2p/p2p_find.py new file mode 100644 index 0000000..973d46a --- /dev/null +++ b/contrib/wpa/wpa_supplicant/examples/p2p/p2p_find.py @@ -0,0 +1,192 @@ +#!/usr/bin/python +# Tests p2p_find +# Will list all devices found/lost within a time frame (timeout) +# Then Program will exit +######### MAY NEED TO RUN AS SUDO ############# + +import dbus +import sys, os +import time +import gobject +import threading +import getopt +from dbus.mainloop.glib import DBusGMainLoop + +def usage(): + print "Usage:" + print " %s -i <interface_name> [-t <timeout>] \ " \ + % sys.argv[0] + print " [-w <wpas_dbus_interface>]" + print "Options:" + print " -i = interface name" + print " -t = timeout = 0s (infinite)" + print " -w = wpas dbus interface = fi.w1.wpa_supplicant1" + print "Example:" + print " %s -i wlan0 -t 10" % sys.argv[0] + +# Required Signals +def deviceFound(devicepath): + print "Device found: %s" % (devicepath) + +def deviceLost(devicepath): + print "Device lost: %s" % (devicepath) + +class P2P_Find (threading.Thread): + # Needed Variables + global bus + global wpas_object + global interface_object + global p2p_interface + global interface_name + global wpas + global wpas_dbus_interface + global timeout + global path + + # Dbus Paths + global wpas_dbus_opath + global wpas_dbus_interfaces_opath + global wpas_dbus_interfaces_interface + global wpas_dbus_interfaces_p2pdevice + + # Constructor + def __init__(self,interface_name,wpas_dbus_interface,timeout): + # Initializes variables and threads + self.timeout = int(timeout) + self.interface_name = interface_name + self.wpas_dbus_interface = wpas_dbus_interface + + # Initializes thread and daemon allows for ctrl-c kill + threading.Thread.__init__(self) + self.daemon = True + + # Generating interface/object paths + self.wpas_dbus_opath = "/" + \ + self.wpas_dbus_interface.replace(".","/") + self.wpas_wpas_dbus_interfaces_opath = self.wpas_dbus_opath + \ + "/Interfaces" + self.wpas_dbus_interfaces_interface = \ + self.wpas_dbus_interface + ".Interface" + self.wpas_dbus_interfaces_p2pdevice = \ + self.wpas_dbus_interfaces_interface \ + + ".P2PDevice" + + # Getting interfaces and objects + DBusGMainLoop(set_as_default=True) + self.bus = dbus.SystemBus() + self.wpas_object = self.bus.get_object( + self.wpas_dbus_interface, + self.wpas_dbus_opath) + self.wpas = dbus.Interface(self.wpas_object, + self.wpas_dbus_interface) + + # Try to see if supplicant knows about interface + # If not, throw an exception + try: + self.path = self.wpas.GetInterface( + self.interface_name) + except dbus.DBusException, exc: + error = 'Error:\n Interface ' + self.interface_name \ + + ' was not found' + print error + usage() + os._exit(0) + + self.interface_object = self.bus.get_object( + self.wpas_dbus_interface, self.path) + self.p2p_interface = dbus.Interface(self.interface_object, + self.wpas_dbus_interfaces_p2pdevice) + + #Adds listeners for find and lost + self.bus.add_signal_receiver(deviceFound, + dbus_interface=self.wpas_dbus_interfaces_p2pdevice, + signal_name="DeviceFound") + self.bus.add_signal_receiver(deviceLost, + dbus_interface=self.wpas_dbus_interfaces_p2pdevice, + signal_name="DeviceLost") + + + # Sets up p2p_find + P2PFindDict = dbus.Dictionary( + {'Timeout':int(self.timeout)}) + self.p2p_interface.Find(P2PFindDict) + + # Run p2p_find + def run(self): + # Allows other threads to keep working while MainLoop runs + # Required for timeout implementation + gobject.MainLoop().get_context().iteration(True) + gobject.threads_init() + gobject.MainLoop().run() + +if __name__ == "__main__": + + # Defaults for optional inputs + timeout = 0 + wpas_dbus_interface = 'fi.w1.wpa_supplicant1' + + # interface_name is required + interface_name = None + + # Using getopts to handle options + try: + options, args = getopt.getopt(sys.argv[1:],"hi:t:w:") + + except getopt.GetoptError: + usage() + quit() + + # If theres a switch, override default option + for key, value in options: + # Help + if (key == "-h"): + usage() + quit() + # Interface Name + elif (key == "-i"): + interface_name = value + # Timeout + elif (key == "-t"): + if ( int(value) >= 0): + timeout = value + else: + print "Error:\n Timeout cannot be negative" + usage() + quit() + # Dbus interface + elif (key == "-w"): + wpas_dbus_interface = value + else: + assert False, "unhandled option" + + # Interface name is required and was not given + if (interface_name == None): + print "Error:\n interface_name is required" + usage() + quit() + + # Constructor + try: + p2p_find_test = P2P_Find(interface_name, wpas_dbus_interface, timeout) + + except: + print "Error:\n Invalid wpas_dbus_interface" + usage() + quit() + + # Start P2P_Find + p2p_find_test.start() + + try: + # If timeout is 0, then run forever + if (timeout == 0): + while(True): + pass + # Else sleep for (timeout) + else: + time.sleep(p2p_find_test.timeout) + + except: + pass + + quit() diff --git a/contrib/wpa/wpa_supplicant/examples/p2p/p2p_flush.py b/contrib/wpa/wpa_supplicant/examples/p2p/p2p_flush.py new file mode 100644 index 0000000..ff8509d --- /dev/null +++ b/contrib/wpa/wpa_supplicant/examples/p2p/p2p_flush.py @@ -0,0 +1,168 @@ +#!/usr/bin/python +# Tests P2P_Flush +# Will flush the p2p interface +# Then Program will exit +######### MAY NEED TO RUN AS SUDO ############# + +import dbus +import sys, os +import time +import gobject +import threading +import getopt +from dbus.mainloop.glib import DBusGMainLoop + +def usage(): + print "Usage:" + print " %s -i <interface_name> \ " \ + % sys.argv[0] + print " [-w <wpas_dbus_interface>]" + print "Options:" + print " -i = interface name" + print " -w = wpas dbus interface = fi.w1.wpa_supplicant1" + print "Example:" + print " %s -i wlan0" % sys.argv[0] + +# Required Signals\ +def deviceLost(devicepath): + print "Device lost: %s" % (devicepath) + +class P2P_Flush (threading.Thread): + # Needed Variables + global bus + global wpas_object + global interface_object + global p2p_interface + global interface_name + global wpas + global wpas_dbus_interface + global path + global timeout + + # Dbus Paths + global wpas_dbus_opath + global wpas_dbus_interfaces_opath + global wpas_dbus_interfaces_interface + global wpas_dbus_interfaces_p2pdevice + + # Constructor + def __init__(self,interface_name,wpas_dbus_interface,timeout): + # Initializes variables and threads + self.interface_name = interface_name + self.wpas_dbus_interface = wpas_dbus_interface + self.timeout = timeout + + # Initializes thread and daemon allows for ctrl-c kill + threading.Thread.__init__(self) + self.daemon = True + + # Generating interface/object paths + self.wpas_dbus_opath = "/" + \ + self.wpas_dbus_interface.replace(".","/") + self.wpas_wpas_dbus_interfaces_opath = self.wpas_dbus_opath + \ + "/Interfaces" + self.wpas_dbus_interfaces_interface = \ + self.wpas_dbus_interface + ".Interface" + self.wpas_dbus_interfaces_p2pdevice = \ + self.wpas_dbus_interfaces_interface \ + + ".P2PDevice" + + # Getting interfaces and objects + DBusGMainLoop(set_as_default=True) + self.bus = dbus.SystemBus() + self.wpas_object = self.bus.get_object( + self.wpas_dbus_interface, + self.wpas_dbus_opath) + self.wpas = dbus.Interface(self.wpas_object, + self.wpas_dbus_interface) + + # Try to see if supplicant knows about interface + # If not, throw an exception + try: + self.path = self.wpas.GetInterface( + self.interface_name) + except dbus.DBusException, exc: + error = 'Error:\n Interface ' + self.interface_name \ + + ' was not found' + print error + usage() + os._exit(0) + + self.interface_object = self.bus.get_object( + self.wpas_dbus_interface, self.path) + self.p2p_interface = dbus.Interface(self.interface_object, + self.wpas_dbus_interfaces_p2pdevice) + + # Signals + self.bus.add_signal_receiver(deviceLost, + dbus_interface=self.wpas_dbus_interfaces_p2pdevice, + signal_name="DeviceLost") + + # Runs p2p_flush + def run(self): + # Allows other threads to keep working while MainLoop runs + # Required for timeout implementation + gobject.MainLoop().get_context().iteration(True) + gobject.threads_init() + self.p2p_interface.Flush() + gobject.MainLoop().run() + + +if __name__ == "__main__": + # Needed to show which devices were lost + timeout = 5 + # Defaults for optional inputs + wpas_dbus_interface = 'fi.w1.wpa_supplicant1' + + # interface_name is required + interface_name = None + + # Using getopts to handle options + try: + options, args = getopt.getopt(sys.argv[1:],"hi:w:") + + except getopt.GetoptError: + usage() + quit() + + # If theres a switch, override default option + for key, value in options: + # Help + if (key == "-h"): + usage() + quit() + # Interface Name + elif (key == "-i"): + interface_name = value + # Dbus interface + elif (key == "-w"): + wpas_dbus_interface = value + else: + assert False, "unhandled option" + + # Interface name is required and was not given + if (interface_name == None): + print "Error:\n interface_name is required" + usage() + quit() + + # Constructor + try: + p2p_flush_test = P2P_Flush(interface_name, wpas_dbus_interface,timeout) + + except: + print "Error:\n Invalid wpas_dbus_interface" + usage() + quit() + + # Start P2P_Find + p2p_flush_test.start() + + try: + time.sleep(int(p2p_flush_test.timeout)) + + except: + pass + + print "p2p_flush complete" + quit() diff --git a/contrib/wpa/wpa_supplicant/examples/p2p/p2p_group_add.py b/contrib/wpa/wpa_supplicant/examples/p2p/p2p_group_add.py new file mode 100644 index 0000000..5c8fdaf --- /dev/null +++ b/contrib/wpa/wpa_supplicant/examples/p2p/p2p_group_add.py @@ -0,0 +1,222 @@ +#!/usr/bin/python +# Tests p2p_group_add +######### MAY NEED TO RUN AS SUDO ############# + +import dbus +import sys, os +import time +import gobject +import getopt +import threading +from dbus.mainloop.glib import DBusGMainLoop + +def usage(): + print "Usage:" + print " %s -i <interface_name> [-p <persistent>] \ " \ + % sys.argv[0] + print " [-f <frequency>] [-o <group_object_path>] \ " + print " [-w <wpas_dbus_interface>]" + print "Options:" + print " -i = interface name" + print " -p = persistant group = 0 (0=false, 1=true)" + print " -f = frequency" + print " -o = persistent group object path" + print " -w = wpas dbus interface = fi.w1.wpa_supplicant1" + print "Example:" + print " %s -i wlan0" % sys.argv[0] + +# Required Signals +def GroupStarted(properties): + if properties.has_key("group_object"): + print 'Group Formation Complete %s' \ + % properties["group_object"] + os._exit(0) + +def WpsFailure(status, etc): + print "WPS Authentication Failure".format(status) + print etc + os._exit(0) + +class P2P_Group_Add (threading.Thread): + # Needed Variables + global bus + global wpas_object + global interface_object + global p2p_interface + global interface_name + global wpas + global wpas_dbus_interface + global path + global persistent + global frequency + global persistent_group_object + + # Dbus Paths + global wpas_dbus_opath + global wpas_dbus_interfaces_opath + global wpas_dbus_interfaces_interface + global wpas_dbus_interfaces_p2pdevice + + # Arguements + global P2PDictionary + + # Constructor + def __init__(self,interface_name,wpas_dbus_interface,persistent,frequency, + persistent_group_object): + # Initializes variables and threads + self.interface_name = interface_name + self.wpas_dbus_interface = wpas_dbus_interface + self.persistent = persistent + self.frequency = frequency + self.persistent_group_object = persistent_group_object + + # Initializes thread and daemon allows for ctrl-c kill + threading.Thread.__init__(self) + self.daemon = True + + # Generating interface/object paths + self.wpas_dbus_opath = "/" + \ + self.wpas_dbus_interface.replace(".","/") + self.wpas_wpas_dbus_interfaces_opath = self.wpas_dbus_opath + \ + "/Interfaces" + self.wpas_dbus_interfaces_interface = \ + self.wpas_dbus_interface + ".Interface" + self.wpas_dbus_interfaces_p2pdevice = \ + self.wpas_dbus_interfaces_interface \ + + ".P2PDevice" + + # Getting interfaces and objects + DBusGMainLoop(set_as_default=True) + self.bus = dbus.SystemBus() + self.wpas_object = self.bus.get_object( + self.wpas_dbus_interface, + self.wpas_dbus_opath) + self.wpas = dbus.Interface(self.wpas_object, + self.wpas_dbus_interface) + + # Try to see if supplicant knows about interface + # If not, throw an exception + try: + self.path = self.wpas.GetInterface( + self.interface_name) + except dbus.DBusException, exc: + error = 'Error:\n Interface ' + self.interface_name \ + + ' was not found' + print error + usage() + os._exit(0) + + self.interface_object = self.bus.get_object( + self.wpas_dbus_interface, self.path) + self.p2p_interface = dbus.Interface(self.interface_object, + self.wpas_dbus_interfaces_p2pdevice) + + #Adds listeners + self.bus.add_signal_receiver(GroupStarted, + dbus_interface=self.wpas_dbus_interfaces_p2pdevice, + signal_name="GroupStarted") + self.bus.add_signal_receiver(WpsFailure, + dbus_interface=self.wpas_dbus_interfaces_p2pdevice, + signal_name="WpsFailed") + + # Sets up p2p_group_add dictionary + def constructArguements(self): + self.P2PDictionary = {'persistent':self.persistent} + + if (self.frequency != None): + if (int(self.frequency) > 0): + self.P2PDictionary.update({'frequency':int(self.frequency)}) + else: + print "Error:\n Frequency must be greater than 0" + usage() + os._exit(0) + + if (self.persistent_group_object != None): + self.P2PDictionary.update({'persistent_group_object': + self.persistent_group_object}) + + # Run p2p_group_remove + def run(self): + try: + self.p2p_interface.GroupAdd(self.P2PDictionary) + + except: + print "Error:\n Could not preform group add" + usage() + os._exit(0) + + # Allows other threads to keep working while MainLoop runs + # Required for timeout implementation + gobject.MainLoop().get_context().iteration(True) + gobject.threads_init() + gobject.MainLoop().run() + + +if __name__ == "__main__": + + # Defaults for optional inputs + # 0 = false, 1 = true + persistent = False + frequency = None + persistent_group_object = None + wpas_dbus_interface = 'fi.w1.wpa_supplicant1' + + # interface_name is required + interface_name = None + + # Using getopts to handle options + try: + options, args = getopt.getopt(sys.argv[1:],"hi:p:f:o:w:") + + except getopt.GetoptError: + usage() + quit() + + # If theres a switch, override default option + for key, value in options: + # Help + if (key == "-h"): + usage() + quit() + # Interface Name + elif (key == "-i"): + interface_name = value + # Timeout + elif (key == "-p"): + if (value == '0'): + persistent = False + elif (value == '1'): + persistent = True + else: + print "Error:\n Persistent can only be 1 or 0" + usage() + os._exit(0) + # Frequency + elif (key == "-f"): + frequency = value + # Persistent group object path + elif (key == "-o"): + persistent_group_object = value + # Dbus interface + elif (key == "-w"): + wpas_dbus_interface = value + else: + assert False, "unhandled option" + + # Interface name is required and was not given + if (interface_name == None): + print "Error:\n interface_name is required" + usage() + quit() + + try: + p2p_group_add_test = P2P_Group_Add(interface_name,wpas_dbus_interface, + persistent,frequency,persistent_group_object) + except: + print "Error:\n Invalid Arguements" + + p2p_group_add_test.constructArguements() + p2p_group_add_test.start() + time.sleep(5) + print "Error:\n Group formation timed out" + os._exit(0) diff --git a/contrib/wpa/wpa_supplicant/examples/p2p/p2p_invite.py b/contrib/wpa/wpa_supplicant/examples/p2p/p2p_invite.py new file mode 100644 index 0000000..6deb397 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/examples/p2p/p2p_invite.py @@ -0,0 +1,201 @@ +#!/usr/bin/python +# Tests p2p_invite +######### MAY NEED TO RUN AS SUDO ############# + +import dbus +import sys, os +import time +import gobject +import getopt +import threading +from dbus.mainloop.glib import DBusGMainLoop + +def usage(): + print "Usage:" + print " %s -i <interface_name> -a <addr> \ " \ + % sys.argv[0] + print " [-o <persistent_group_object>] [-w <wpas_dbus_interface>]" + print "Options:" + print " -i = interface name" + print " -a = address of peer" + print " -o = persistent group object path" + print " -w = wpas dbus interface = fi.w1.wpa_supplicant1" + print "Example:" + print " %s -i p2p-wlan0-0 -a 00150083523c" % sys.argv[0] + +# Required Signals +def InvitationResult(invite_result): + print "Inviation Result signal :" + status = invite_result['status'] + print "status = ", status + if invite_result.has_key('BSSID'): + bssid = invite_result['BSSID'] + print "BSSID = ", hex(bssid[0]) , ":" , \ + hex(bssid[1]) , ":" , hex(bssid[2]) , ":", \ + hex(bssid[3]) , ":" , hex(bssid[4]) , ":" , \ + hex(bssid[5]) + os._exit(0) + +class P2P_Invite (threading.Thread): + # Needed Variables + global bus + global wpas_object + global interface_object + global p2p_interface + global interface_name + global wpas + global wpas_dbus_interface + global path + global addr + global persistent_group_object + + # Dbus Paths + global wpas_dbus_opath + global wpas_dbus_interfaces_opath + global wpas_dbus_interfaces_interface + global wpas_dbus_interfaces_p2pdevice + + # Arguements + global P2PDictionary + + # Constructor + def __init__(self,interface_name,wpas_dbus_interface,addr, + persistent_group_object): + # Initializes variables and threads + self.interface_name = interface_name + self.wpas_dbus_interface = wpas_dbus_interface + self.addr = addr + self.persistent_group_object = persistent_group_object + + # Initializes thread and daemon allows for ctrl-c kill + threading.Thread.__init__(self) + self.daemon = True + + # Generating interface/object paths + self.wpas_dbus_opath = "/" + \ + self.wpas_dbus_interface.replace(".","/") + self.wpas_wpas_dbus_interfaces_opath = self.wpas_dbus_opath + \ + "/Interfaces" + self.wpas_dbus_interfaces_interface = \ + self.wpas_dbus_interface + ".Interface" + self.wpas_dbus_interfaces_p2pdevice = \ + self.wpas_dbus_interfaces_interface \ + + ".P2PDevice" + + # Getting interfaces and objects + DBusGMainLoop(set_as_default=True) + self.bus = dbus.SystemBus() + self.wpas_object = self.bus.get_object( + self.wpas_dbus_interface, + self.wpas_dbus_opath) + self.wpas = dbus.Interface(self.wpas_object, + self.wpas_dbus_interface) + + # Try to see if supplicant knows about interface + # If not, throw an exception + try: + self.path = self.wpas.GetInterface( + self.interface_name) + except dbus.DBusException, exc: + error = 'Error:\n Interface ' + self.interface_name \ + + ' was not found' + print error + usage() + os._exit(0) + + self.interface_object = self.bus.get_object( + self.wpas_dbus_interface, self.path) + self.p2p_interface = dbus.Interface(self.interface_object, + self.wpas_dbus_interfaces_p2pdevice) + + #Adds listeners + self.bus.add_signal_receiver(InvitationResult, + dbus_interface=self.wpas_dbus_interfaces_p2pdevice, + signal_name="InvitationResult") + + # Sets up p2p_invite dictionary + def constructArguements(self): + self.P2PDictionary = \ + {'peer':dbus.ObjectPath(self.path+'/Peers/'+self.addr)} + if (self.persistent_group_object != None): + self.P2PDictionary.update({"persistent_group_object": + self.persistent_group_object}) + + # Run p2p_invite + def run(self): + try: + self.p2p_interface.Invite(self.P2PDictionary) + + except: + print "Error:\n Invalid Arguements" + usage() + os._exit(0) + + # Allows other threads to keep working while MainLoop runs + # Required for timeout implementation + gobject.MainLoop().get_context().iteration(True) + gobject.threads_init() + gobject.MainLoop().run() + +if __name__ == "__main__": + # Defaults for optional inputs + addr = None + persistent_group_object = None + wpas_dbus_interface = 'fi.w1.wpa_supplicant1' + + # interface_name is required + interface_name = None + + # Using getopts to handle options + try: + options, args = getopt.getopt(sys.argv[1:],"hi:o:w:a:") + + except getopt.GetoptError: + usage() + quit() + + # If theres a switch, override default option + for key, value in options: + # Help + if (key == "-h"): + usage() + quit() + # Interface Name + elif (key == "-i"): + interface_name = value + elif (key == "-a"): + addr = value + # Persistent group object path + elif (key == "-o"): + persistent_group_object = value + # Dbus interface + elif (key == "-w"): + wpas_dbus_interface = value + else: + assert False, "unhandled option" + + # Interface name is required and was not given + if (interface_name == None): + print "Error:\n interface_name is required" + usage() + quit() + + if (addr == None): + print "Error:\n peer address is required" + usage() + quit() + + try: + p2p_invite_test = \ + P2P_Invite(interface_name,wpas_dbus_interface, + addr,persistent_group_object) + except: + print "Error:\n Invalid Arguements" + usage() + os._exit(1) + + p2p_invite_test.constructArguements() + p2p_invite_test.start() + time.sleep(10) + print "Error:\n p2p_invite timed out" + os._exit(0) diff --git a/contrib/wpa/wpa_supplicant/examples/p2p/p2p_listen.py b/contrib/wpa/wpa_supplicant/examples/p2p/p2p_listen.py new file mode 100644 index 0000000..bb3c1e4 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/examples/p2p/p2p_listen.py @@ -0,0 +1,182 @@ +#!/usr/bin/python +# Tests P2P_Find +# Will listen +# Then Program will exit +######### MAY NEED TO RUN AS SUDO ############# + +import dbus +import sys, os +import time +import gobject +import threading +import getopt +from dbus.mainloop.glib import DBusGMainLoop + +def usage(): + print "Usage:" + print " %s -i <interface_name> [-t <timeout>] \ " \ + % sys.argv[0] + print " [-w <wpas_dbus_interface>]" + print "Options:" + print " -i = interface name" + print " -t = timeout = 0s (infinite)" + print " -w = wpas dbus interface = fi.w1.wpa_supplicant1" + print "Example:" + print " %s -i wlan0 -t 5" % sys.argv[0] + +# Required Signals +def p2pStateChange(status): + print status + +class P2P_Listen(threading.Thread): + # Needed Variables + global bus + global wpas_object + global interface_object + global p2p_interface + global interface_name + global wpas + global wpas_dbus_interface + global path + global timeout + + # Dbus Paths + global wpas_dbus_opath + global wpas_dbus_interfaces_opath + global wpas_dbus_interfaces_interface + global wpas_dbus_interfaces_p2pdevice + + # Constructor + def __init__(self,interface_name,wpas_dbus_interface,timeout): + # Initializes variables and threads + self.timeout = int(timeout) + self.interface_name = interface_name + self.wpas_dbus_interface = wpas_dbus_interface + + # Initializes thread and daemon allows for ctrl-c kill + threading.Thread.__init__(self) + self.daemon = True + + # Generating interface/object paths + self.wpas_dbus_opath = "/" + \ + self.wpas_dbus_interface.replace(".","/") + self.wpas_wpas_dbus_interfaces_opath = self.wpas_dbus_opath + \ + "/Interfaces" + self.wpas_dbus_interfaces_interface = \ + self.wpas_dbus_interface + ".Interface" + self.wpas_dbus_interfaces_p2pdevice = \ + self.wpas_dbus_interfaces_interface \ + + ".P2PDevice" + + # Getting interfaces and objects + DBusGMainLoop(set_as_default=True) + self.bus = dbus.SystemBus() + self.wpas_object = self.bus.get_object( + self.wpas_dbus_interface, + self.wpas_dbus_opath) + self.wpas = dbus.Interface(self.wpas_object, + self.wpas_dbus_interface) + + # Try to see if supplicant knows about interface + # If not, throw an exception + try: + self.path = self.wpas.GetInterface( + self.interface_name) + except dbus.DBusException, exc: + error = 'Error:\n Interface ' + self.interface_name \ + + ' was not found' + print error + usage() + os._exit(0) + + self.interface_object = self.bus.get_object( + self.wpas_dbus_interface, self.path) + self.p2p_interface = dbus.Interface(self.interface_object, + self.wpas_dbus_interfaces_p2pdevice) + + self.bus.add_signal_receiver(p2pStateChange, + dbus_interface=self.wpas_dbus_interfaces_p2pdevice, + signal_name="P2PStateChanged") + + # Run p2p_find + def run(self): + # Sets up p2p_listen + self.p2p_interface.Listen(int(self.timeout)) + + # Allows other threads to keep working while MainLoop runs + # Required for timeout implementation + gobject.MainLoop().get_context().iteration(True) + gobject.threads_init() + gobject.MainLoop().run() + +if __name__ == "__main__": + + # Defaults for optional inputs + timeout = 0 + wpas_dbus_interface = 'fi.w1.wpa_supplicant1' + + # interface_name is required + interface_name = None + + # Using getopts to handle options + try: + options, args = getopt.getopt(sys.argv[1:],"hi:t:w:") + + except getopt.GetoptError: + usage() + quit() + + # If theres a switch, override default option + for key, value in options: + # Help + if (key == "-h"): + usage() + quit() + # Interface Name + elif (key == "-i"): + interface_name = value + # Timeout + elif (key == "-t"): + if ( int(value) >= 0): + timeout = value + else: + print "Error:\n Timeout cannot be negative" + usage() + quit() + # Dbus interface + elif (key == "-w"): + wpas_dbus_interface = value + else: + assert False, "unhandled option" + + # Interface name is required and was not given + if (interface_name == None): + print "Error:\n interface_name is required" + usage() + quit() + + # Constructor + try: + p2p_listen_test = P2P_Listen(interface_name, wpas_dbus_interface, timeout) + + except: + print "Error:\n Invalid wpas_dbus_interface" + usage() + quit() + + # Start P2P_Find + p2p_listen_test.start() + + try: + # If timeout is 0, then run forever + if (int(p2p_listen_test.timeout) == 0): + while(True): + pass + # Else sleep for (timeout) + else: + time.sleep(int(p2p_listen_test.timeout)) + + except: + pass + + quit() diff --git a/contrib/wpa/wpa_supplicant/examples/p2p/p2p_stop_find.py b/contrib/wpa/wpa_supplicant/examples/p2p/p2p_stop_find.py new file mode 100644 index 0000000..f6c03b0 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/examples/p2p/p2p_stop_find.py @@ -0,0 +1,174 @@ +#!/usr/bin/python +# Tests p2p_stop_find +######### MAY NEED TO RUN AS SUDO ############# + +import dbus +import sys, os +import time +import gobject +import threading +import getopt +from dbus.mainloop.glib import DBusGMainLoop + +def usage(): + print "Usage:" + print " %s -i <interface_name> \ " \ + % sys.argv[0] + print " [-w <wpas_dbus_interface>]" + print "Options:" + print " -i = interface name" + print " -w = wpas dbus interface = fi.w1.wpa_supplicant1" + print "Example:" + print " %s -i wlan0" % sys.argv[0] + +# Required Signals +def deviceLost(devicepath): + print "Device lost: %s" % (devicepath) + +def p2pStateChange(status): + print status + os._exit(0) + +class P2P_Stop_Find (threading.Thread): + # Needed Variables + global bus + global wpas_object + global interface_object + global p2p_interface + global interface_name + global wpas + global wpas_dbus_interface + global path + global timeout + + # Dbus Paths + global wpas_dbus_opath + global wpas_dbus_interfaces_opath + global wpas_dbus_interfaces_interface + global wpas_dbus_interfaces_p2pdevice + + # Constructor + def __init__(self,interface_name,wpas_dbus_interface,timeout): + # Initializes variables and threads + self.interface_name = interface_name + self.wpas_dbus_interface = wpas_dbus_interface + self.timeout = timeout + + # Initializes thread and daemon allows for ctrl-c kill + threading.Thread.__init__(self) + self.daemon = True + + # Generating interface/object paths + self.wpas_dbus_opath = "/" + \ + self.wpas_dbus_interface.replace(".","/") + self.wpas_wpas_dbus_interfaces_opath = self.wpas_dbus_opath + \ + "/Interfaces" + self.wpas_dbus_interfaces_interface = \ + self.wpas_dbus_interface + ".Interface" + self.wpas_dbus_interfaces_p2pdevice = \ + self.wpas_dbus_interfaces_interface \ + + ".P2PDevice" + + # Getting interfaces and objects + DBusGMainLoop(set_as_default=True) + self.bus = dbus.SystemBus() + self.wpas_object = self.bus.get_object( + self.wpas_dbus_interface, + self.wpas_dbus_opath) + self.wpas = dbus.Interface(self.wpas_object, + self.wpas_dbus_interface) + + # Try to see if supplicant knows about interface + # If not, throw an exception + try: + self.path = self.wpas.GetInterface( + self.interface_name) + except dbus.DBusException, exc: + error = 'Error:\n Interface ' + self.interface_name \ + + ' was not found' + print error + usage() + os._exit(0) + + self.interface_object = self.bus.get_object( + self.wpas_dbus_interface, self.path) + self.p2p_interface = dbus.Interface(self.interface_object, + self.wpas_dbus_interfaces_p2pdevice) + + # Signals + self.bus.add_signal_receiver(deviceLost, + dbus_interface=self.wpas_dbus_interfaces_p2pdevice, + signal_name="DeviceLost") + self.bus.add_signal_receiver(p2pStateChange, + dbus_interface=self.wpas_dbus_interfaces_p2pdevice, + signal_name="P2PStateChanged") + + # Runs p2p_stop_find + def run(self): + # Allows other threads to keep working while MainLoop runs + # Required for timeout implementation + gobject.MainLoop().get_context().iteration(True) + gobject.threads_init() + self.p2p_interface.StopFind() + gobject.MainLoop().run() + + +if __name__ == "__main__": + # Needed because P2PStateChanged signal is not caught + timeout = 5 + # Defaults for optional inputs + wpas_dbus_interface = 'fi.w1.wpa_supplicant1' + + # interface_name is required + interface_name = None + + # Using getopts to handle options + try: + options, args = getopt.getopt(sys.argv[1:],"ht:i:w:") + + except getopt.GetoptError: + usage() + quit() + + # If theres a switch, override default option + for key, value in options: + # Help + if (key == "-h"): + usage() + quit() + # Interface Name + elif (key == "-i"): + interface_name = value + # Dbus interface + elif (key == "-w"): + wpas_dbus_interface = value + else: + assert False, "unhandled option" + + # Interface name is required and was not given + if (interface_name == None): + print "Error:\n interface_name is required" + usage() + quit() + + # Constructor + try: + p2p_stop_find_test = P2P_Stop_Find(interface_name, + wpas_dbus_interface,timeout) + + except: + print "Error:\n Invalid wpas_dbus_interface" + usage() + quit() + + # Start P2P_Find + p2p_stop_find_test.start() + + try: + time.sleep(int(p2p_stop_find_test.timeout)) + + except: + pass + + print "p2p find stopped" + quit() diff --git a/contrib/wpa/wpa_supplicant/examples/udhcpd-p2p.conf b/contrib/wpa/wpa_supplicant/examples/udhcpd-p2p.conf new file mode 100644 index 0000000..df59094 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/examples/udhcpd-p2p.conf @@ -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 + + diff --git a/contrib/wpa/wpa_supplicant/examples/wpas-dbus-new-signals.py b/contrib/wpa/wpa_supplicant/examples/wpas-dbus-new-signals.py index b040e0a..d90ef18 100755 --- a/contrib/wpa/wpa_supplicant/examples/wpas-dbus-new-signals.py +++ b/contrib/wpa/wpa_supplicant/examples/wpas-dbus-new-signals.py @@ -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/contrib/wpa/wpa_supplicant/examples/wps-ap-cli b/contrib/wpa/wpa_supplicant/examples/wps-ap-cli new file mode 100755 index 0000000..7c6b0aa --- /dev/null +++ b/contrib/wpa/wpa_supplicant/examples/wps-ap-cli @@ -0,0 +1,78 @@ +#!/bin/sh + +CLI=wpa_cli + +pbc() +{ + echo "Starting PBC mode" + echo "Push button on the station within two minutes" + if ! $CLI wps_pbc | grep -q OK; then + echo "Failed to enable PBC mode" + fi +} + +enter_pin() +{ + echo "Enter a PIN from a station to be enrolled to the network." + read -p "Enrollee PIN: " pin + cpin=`$CLI wps_check_pin "$pin" | tail -1` + if [ "$cpin" = "FAIL-CHECKSUM" ]; then + echo "Checksum digit is not valid" + read -p "Do you want to use this PIN (y/n)? " resp + case "$resp" in + y*) + cpin=`echo "$pin" | sed "s/[^1234567890]//g"` + ;; + *) + return 1 + ;; + esac + fi + if [ "$cpin" = "FAIL" ]; then + echo "Invalid PIN: $pin" + return 1 + fi + echo "Enabling Enrollee PIN: $cpin" + $CLI wps_pin any "$cpin" +} + +show_config() +{ + $CLI status wps +} + +main_menu() +{ + echo "WPS AP" + echo "------" + echo "1: Push button (activate PBC)" + echo "2: Enter Enrollee PIN" + echo "3: Show current configuration" + echo "0: Exit wps-ap-cli" + + read -p "Command: " cmd + + case "$cmd" in + 1) + pbc + ;; + 2) + enter_pin + ;; + 3) + show_config + ;; + 0) + exit 0 + ;; + *) + echo "Unknown command: $cmd" + ;; + esac + + echo + main_menu +} + + +main_menu diff --git a/contrib/wpa/wpa_supplicant/examples/wps-nfc.py b/contrib/wpa/wpa_supplicant/examples/wps-nfc.py new file mode 100755 index 0000000..0cfc1f6 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/examples/wps-nfc.py @@ -0,0 +1,162 @@ +#!/usr/bin/python +# +# Example nfcpy to wpa_supplicant wrapper for WPS NFC operations +# Copyright (c) 2012, Jouni Malinen <j@w1.fi> +# +# This software may be distributed under the terms of the BSD license. +# See README for more details. + +import os +import sys +import time + +import nfc +import nfc.ndef +import nfc.llcp +import nfc.handover + +import wpactrl + +wpas_ctrl = '/var/run/wpa_supplicant' + +def wpas_connect(): + ifaces = [] + if os.path.isdir(wpas_ctrl): + try: + ifaces = [os.path.join(wpas_ctrl, i) for i in os.listdir(wpas_ctrl)] + except OSError, error: + print "Could not find wpa_supplicant: ", error + return None + + if len(ifaces) < 1: + print "No wpa_supplicant control interface found" + return None + + for ctrl in ifaces: + try: + wpas = wpactrl.WPACtrl(ctrl) + return wpas + except wpactrl.error, error: + print "Error: ", error + pass + return None + + +def wpas_tag_read(message): + wpas = wpas_connect() + if (wpas == None): + return + print wpas.request("WPS_NFC_TAG_READ " + message.encode("hex")) + + +def wpas_get_handover_req(): + wpas = wpas_connect() + if (wpas == None): + return None + return wpas.request("NFC_GET_HANDOVER_REQ NDEF WPS").rstrip().decode("hex") + + +def wpas_put_handover_sel(message): + wpas = wpas_connect() + if (wpas == None): + return + print wpas.request("NFC_RX_HANDOVER_SEL " + str(message).encode("hex")) + + +def wps_handover_init(peer): + print "Trying to initiate WPS handover" + + data = wpas_get_handover_req() + if (data == None): + print "Could not get handover request message from wpa_supplicant" + return + print "Handover request from wpa_supplicant: " + data.encode("hex") + message = nfc.ndef.Message(data) + print "Parsed handover request: " + message.pretty() + + nfc.llcp.activate(peer); + time.sleep(0.5) + + client = nfc.handover.HandoverClient() + try: + print "Trying handover"; + client.connect() + print "Connected for handover" + except nfc.llcp.ConnectRefused: + print "Handover connection refused" + nfc.llcp.shutdown() + client.close() + return + + print "Sending handover request" + + if not client.send(message): + print "Failed to send handover request" + + print "Receiving handover response" + message = client._recv() + print "Handover select received" + print message.pretty() + wpas_put_handover_sel(message) + + print "Remove peer" + nfc.llcp.shutdown() + client.close() + print "Done with handover" + + +def wps_tag_read(tag): + if len(tag.ndef.message): + message = nfc.ndef.Message(tag.ndef.message) + print "message type " + message.type + + for record in message: + print "record type " + record.type + if record.type == "application/vnd.wfa.wsc": + print "WPS tag - send to wpa_supplicant" + wpas_tag_read(tag.ndef.message) + break + else: + print "Empty tag" + + print "Remove tag" + while tag.is_present: + time.sleep(0.1) + + +def main(): + clf = nfc.ContactlessFrontend() + + try: + while True: + print "Waiting for a tag or peer to be touched" + + while True: + general_bytes = nfc.llcp.startup({}) + tag = clf.poll(general_bytes) + if tag == None: + continue + + if isinstance(tag, nfc.DEP): + wps_handover_init(tag) + break + + if tag.ndef: + wps_tag_read(tag) + break + + if tag: + print "Not an NDEF tag - remove tag" + while tag.is_present: + time.sleep(0.1) + break + + except KeyboardInterrupt: + raise SystemExit + finally: + clf.close() + + raise SystemExit + +if __name__ == '__main__': + main() diff --git a/contrib/wpa/wpa_supplicant/gas_query.c b/contrib/wpa/wpa_supplicant/gas_query.c new file mode 100644 index 0000000..27bcc7a --- /dev/null +++ b/contrib/wpa/wpa_supplicant/gas_query.c @@ -0,0 +1,519 @@ +/* + * Generic advertisement service (GAS) query + * Copyright (c) 2009, Atheros Communications + * Copyright (c) 2011, Qualcomm Atheros + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "utils/eloop.h" +#include "common/ieee802_11_defs.h" +#include "common/gas.h" +#include "wpa_supplicant_i.h" +#include "driver_i.h" +#include "offchannel.h" +#include "gas_query.h" + + +/** GAS query timeout in seconds */ +#define GAS_QUERY_TIMEOUT_PERIOD 5 + + +/** + * struct gas_query_pending - Pending GAS query + */ +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 - Internal GAS query data + */ +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); + + +/** + * gas_query_init - Initialize GAS query component + * @wpa_s: Pointer to wpa_supplicant data + * Returns: Pointer to GAS query data or %NULL on failure + */ +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); +} + + +/** + * gas_query_deinit - Deinitialize GAS query component + * @gas: GAS query data from gas_query_init() + */ +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); +} + + +/** + * gas_query_rx - Indicate reception of a Public Action frame + * @gas: GAS query data from gas_query_init() + * @da: Destination MAC address of the Action frame + * @sa: Source MAC address of the Action frame + * @bssid: BSSID of the Action frame + * @data: Payload of the Action frame + * @len: Length of @data + * @freq: Frequency (in MHz) on which the frame was received + * Returns: 0 if the Public Action frame was a GAS frame or -1 if not + */ +int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa, + const u8 *bssid, const u8 *data, size_t len, int freq) +{ + 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; +} + + +/** + * gas_query_req - Request a GAS query + * @gas: GAS query data from gas_query_init() + * @dst: Destination MAC address for the query + * @freq: Frequency (in MHz) for the channel on which to send the query + * @req: GAS query payload + * @cb: Callback function for reporting GAS query result and response + * @ctx: Context pointer to use with the @cb call + * Returns: dialog token (>= 0) on success or -1 on failure + */ +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)); + dl_list_del(&query->list); + os_free(query); + return -1; + } + + eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0, gas_query_timeout, + gas, query); + + return dialog_token; +} + + +/** + * gas_query_cancel - Cancel a pending GAS query + * @gas: GAS query data from gas_query_init() + * @dst: Destination MAC address for the query + * @dialog_token: Dialog token from gas_query_req() + */ +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/contrib/wpa/wpa_supplicant/gas_query.h b/contrib/wpa/wpa_supplicant/gas_query.h new file mode 100644 index 0000000..5c3d161 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/gas_query.h @@ -0,0 +1,58 @@ +/* + * Generic advertisement service (GAS) query + * Copyright (c) 2009, Atheros Communications + * Copyright (c) 2011, Qualcomm Atheros + * + * This software may be distributed under the terms of the BSD license. + * See README 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 result + */ +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 */ diff --git a/contrib/wpa/wpa_supplicant/hs20_supplicant.c b/contrib/wpa/wpa_supplicant/hs20_supplicant.c new file mode 100644 index 0000000..1404241 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/hs20_supplicant.c @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2009, Atheros Communications, Inc. + * Copyright (c) 2011-2012, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "common/ieee802_11_common.h" +#include "common/ieee802_11_defs.h" +#include "common/gas.h" +#include "common/wpa_ctrl.h" +#include "wpa_supplicant_i.h" +#include "driver_i.h" +#include "config.h" +#include "bss.h" +#include "gas_query.h" +#include "interworking.h" +#include "hs20_supplicant.h" + + +void wpas_hs20_add_indication(struct wpabuf *buf) +{ + wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); + wpabuf_put_u8(buf, 5); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, HS20_INDICATION_OUI_TYPE); + wpabuf_put_u8(buf, 0x00); /* Hotspot Configuration */ +} + + +struct wpabuf * hs20_build_anqp_req(u32 stypes, const u8 *payload, + size_t payload_len) +{ + struct wpabuf *buf; + u8 *len_pos; + + buf = gas_anqp_build_initial_req(0, 100 + payload_len); + if (buf == NULL) + return NULL; + + len_pos = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); + if (stypes == BIT(HS20_STYPE_NAI_HOME_REALM_QUERY)) { + wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY); + wpabuf_put_u8(buf, 0); /* Reserved */ + if (payload) + wpabuf_put_data(buf, payload, payload_len); + } else { + u8 i; + wpabuf_put_u8(buf, HS20_STYPE_QUERY_LIST); + wpabuf_put_u8(buf, 0); /* Reserved */ + for (i = 0; i < 32; i++) { + if (stypes & BIT(i)) + wpabuf_put_u8(buf, i); + } + } + gas_anqp_set_element_len(buf, len_pos); + + gas_anqp_set_len(buf); + + return buf; +} + + +int hs20_anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, u32 stypes, + const u8 *payload, size_t payload_len) +{ + struct wpabuf *buf; + int ret = 0; + int freq; + struct wpa_bss *bss; + int res; + + freq = wpa_s->assoc_freq; + bss = wpa_bss_get_bssid(wpa_s, dst); + if (bss) { + wpa_bss_anqp_unshare_alloc(bss); + freq = bss->freq; + } + if (freq <= 0) + return -1; + + wpa_printf(MSG_DEBUG, "HS20: ANQP Query Request to " MACSTR " for " + "subtypes 0x%x", MAC2STR(dst), stypes); + + buf = hs20_build_anqp_req(stypes, payload, payload_len); + if (buf == NULL) + return -1; + + res = gas_query_req(wpa_s->gas, dst, freq, buf, anqp_resp_cb, wpa_s); + if (res < 0) { + wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request"); + ret = -1; + } else + wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token " + "%u", res); + + wpabuf_free(buf); + return ret; +} + + +void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s, + const u8 *sa, const u8 *data, size_t slen) +{ + const u8 *pos = data; + u8 subtype; + struct wpa_bss *bss = wpa_bss_get_bssid(wpa_s, sa); + struct wpa_bss_anqp *anqp = NULL; + + if (slen < 2) + return; + + if (bss) + anqp = bss->anqp; + + subtype = *pos++; + slen--; + + pos++; /* Reserved */ + slen--; + + switch (subtype) { + case HS20_STYPE_CAPABILITY_LIST: + wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR + " HS Capability List", MAC2STR(sa)); + wpa_hexdump_ascii(MSG_DEBUG, "HS Capability List", pos, slen); + break; + case HS20_STYPE_OPERATOR_FRIENDLY_NAME: + wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR + " Operator Friendly Name", MAC2STR(sa)); + wpa_hexdump_ascii(MSG_DEBUG, "oper friendly name", pos, slen); + if (anqp) { + wpabuf_free(anqp->hs20_operator_friendly_name); + anqp->hs20_operator_friendly_name = + wpabuf_alloc_copy(pos, slen); + } + break; + case HS20_STYPE_WAN_METRICS: + wpa_hexdump(MSG_DEBUG, "WAN Metrics", pos, slen); + if (slen < 13) { + wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short WAN " + "Metrics value from " MACSTR, MAC2STR(sa)); + break; + } + wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR + " WAN Metrics %02x:%u:%u:%u:%u:%u", MAC2STR(sa), + pos[0], WPA_GET_LE32(pos + 1), WPA_GET_LE32(pos + 5), + pos[9], pos[10], WPA_GET_LE16(pos + 11)); + if (anqp) { + wpabuf_free(anqp->hs20_wan_metrics); + anqp->hs20_wan_metrics = wpabuf_alloc_copy(pos, slen); + } + break; + case HS20_STYPE_CONNECTION_CAPABILITY: + wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR + " Connection Capability", MAC2STR(sa)); + wpa_hexdump_ascii(MSG_DEBUG, "conn capability", pos, slen); + if (anqp) { + wpabuf_free(anqp->hs20_connection_capability); + anqp->hs20_connection_capability = + wpabuf_alloc_copy(pos, slen); + } + break; + case HS20_STYPE_OPERATING_CLASS: + wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR + " Operating Class", MAC2STR(sa)); + wpa_hexdump_ascii(MSG_DEBUG, "Operating Class", pos, slen); + if (anqp) { + wpabuf_free(anqp->hs20_operating_class); + anqp->hs20_operating_class = + wpabuf_alloc_copy(pos, slen); + } + break; + default: + wpa_printf(MSG_DEBUG, "HS20: Unsupported subtype %u", subtype); + break; + } +} diff --git a/contrib/wpa/wpa_supplicant/hs20_supplicant.h b/contrib/wpa/wpa_supplicant/hs20_supplicant.h new file mode 100644 index 0000000..6eb3926 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/hs20_supplicant.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2011-2012, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef HS20_SUPPLICANT_H +#define HS20_SUPPLICANT_H + +void wpas_hs20_add_indication(struct wpabuf *buf); + +int hs20_anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, u32 stypes, + const u8 *payload, size_t payload_len); +struct wpabuf * hs20_build_anqp_req(u32 stypes, const u8 *payload, + size_t payload_len); +void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s, + const u8 *sa, const u8 *data, size_t slen); + +#endif /* HS20_SUPPLICANT_H */ diff --git a/contrib/wpa/wpa_supplicant/ibss_rsn.c b/contrib/wpa/wpa_supplicant/ibss_rsn.c index 0e33253..046f181 100644 --- a/contrib/wpa/wpa_supplicant/ibss_rsn.c +++ b/contrib/wpa/wpa_supplicant/ibss_rsn.c @@ -2,14 +2,8 @@ * wpa_supplicant - IBSS RSN * Copyright (c) 2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -24,6 +18,18 @@ #include "ibss_rsn.h" +static struct ibss_rsn_peer * ibss_rsn_get_peer(struct ibss_rsn *ibss_rsn, + const u8 *addr) +{ + struct ibss_rsn_peer *peer; + + for (peer = ibss_rsn->peers; peer; peer = peer->next) + if (os_memcmp(addr, peer->addr, ETH_ALEN) == 0) + break; + return peer; +} + + static void ibss_rsn_free(struct ibss_rsn_peer *peer) { wpa_auth_sta_deinit(peer->auth); @@ -39,6 +45,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 +138,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,8 +168,14 @@ static void supp_cancel_auth_timeout(void *ctx) } -int ibss_rsn_supp_init(struct ibss_rsn_peer *peer, const u8 *own_addr, - const u8 *psk) +static void supp_deauthenticate(void * ctx, int reason_code) +{ + wpa_printf(MSG_DEBUG, "SUPP: %s (TODO)", __func__); +} + + +static int ibss_rsn_supp_init(struct ibss_rsn_peer *peer, const u8 *own_addr, + const u8 *psk) { struct wpa_sm_ctx *ctx = os_zalloc(sizeof(*ctx)); if (ctx == NULL) @@ -163,6 +184,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 +192,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 +296,71 @@ 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 void ibss_set_sta_authorized(struct ibss_rsn *ibss_rsn, + struct ibss_rsn_peer *peer, int authorized) +{ + int res; + + if (authorized) { + res = wpa_drv_sta_set_flags(ibss_rsn->wpa_s, peer->addr, + WPA_STA_AUTHORIZED, + WPA_STA_AUTHORIZED, ~0); + wpa_printf(MSG_DEBUG, "AUTH: " MACSTR " authorizing port", + MAC2STR(peer->addr)); + } else { + res = wpa_drv_sta_set_flags(ibss_rsn->wpa_s, peer->addr, + 0, 0, ~WPA_STA_AUTHORIZED); + wpa_printf(MSG_DEBUG, "AUTH: " MACSTR " unauthorizing port", + MAC2STR(peer->addr)); + } + + if (res && errno != ENOENT) { + wpa_printf(MSG_DEBUG, "Could not set station " MACSTR " flags " + "for kernel driver (errno=%d)", + MAC2STR(peer->addr), errno); + } +} + + +static void auth_set_eapol(void *ctx, const u8 *addr, + wpa_eapol_variable var, int value) +{ + struct ibss_rsn *ibss_rsn = ctx; + struct ibss_rsn_peer *peer = ibss_rsn_get_peer(ibss_rsn, addr); + + if (peer == NULL) + return; + + switch (var) { + case WPA_EAPOL_authorized: + ibss_set_sta_authorized(ibss_rsn, peer, value); + break; + default: + /* do not handle any other event */ + wpa_printf(MSG_DEBUG, "AUTH: eapol event not handled %d", var); + break; + } +} + + static int ibss_rsn_auth_init_group(struct ibss_rsn *ibss_rsn, const u8 *own_addr) { @@ -288,13 +376,16 @@ 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; cb.logger = auth_logger; + cb.set_eapol = auth_set_eapol; cb.send_eapol = auth_send_eapol; cb.get_psk = auth_get_psk; cb.set_key = auth_set_key; + cb.for_each_sta = auth_for_each_sta; ibss_rsn->auth_group = wpa_init(own_addr, &conf, &cb); if (ibss_rsn->auth_group == NULL) { @@ -302,6 +393,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 +434,16 @@ int ibss_rsn_start(struct ibss_rsn *ibss_rsn, const u8 *addr) { struct ibss_rsn_peer *peer; + if (ibss_rsn == NULL) + return -1; + + if (ibss_rsn_get_peer(ibss_rsn, addr)) { + wpa_printf(MSG_DEBUG, "RSN: IBSS Authenticator and Supplicant " + "for peer " MACSTR " already running", + MAC2STR(addr)); + return 0; + } + wpa_printf(MSG_DEBUG, "RSN: Starting IBSS Authenticator and " "Supplicant for peer " MACSTR, MAC2STR(addr)); @@ -369,6 +472,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,11 +626,12 @@ int ibss_rsn_rx_eapol(struct ibss_rsn *ibss_rsn, const u8 *src_addr, { struct ibss_rsn_peer *peer; - 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, - buf, len); - } + if (ibss_rsn == NULL) + return -1; + + peer = ibss_rsn_get_peer(ibss_rsn, src_addr); + if (peer) + return ibss_rsn_process_rx_eapol(ibss_rsn, peer, buf, len); if (ibss_rsn_eapol_dst_supp(buf, len) > 0) { /* @@ -506,5 +650,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); } diff --git a/contrib/wpa/wpa_supplicant/ibss_rsn.h b/contrib/wpa/wpa_supplicant/ibss_rsn.h index 11e63ad..1da94ab 100644 --- a/contrib/wpa/wpa_supplicant/ibss_rsn.h +++ b/contrib/wpa/wpa_supplicant/ibss_rsn.h @@ -2,14 +2,8 @@ * wpa_supplicant - IBSS RSN * Copyright (c) 2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef IBSS_RSN_H @@ -42,6 +36,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/contrib/wpa/wpa_supplicant/interworking.c b/contrib/wpa/wpa_supplicant/interworking.c new file mode 100644 index 0000000..b8a8bb2 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/interworking.c @@ -0,0 +1,2107 @@ +/* + * Interworking (IEEE 802.11u) + * Copyright (c) 2011-2012, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "common/ieee802_11_defs.h" +#include "common/gas.h" +#include "common/wpa_ctrl.h" +#include "utils/pcsc_funcs.h" +#include "utils/eloop.h" +#include "drivers/driver.h" +#include "eap_common/eap_defs.h" +#include "eap_peer/eap.h" +#include "eap_peer/eap_methods.h" +#include "wpa_supplicant_i.h" +#include "config.h" +#include "config_ssid.h" +#include "bss.h" +#include "scan.h" +#include "notify.h" +#include "gas_query.h" +#include "hs20_supplicant.h" +#include "interworking.h" + + +#if defined(EAP_SIM) | defined(EAP_SIM_DYNAMIC) +#define INTERWORKING_3GPP +#else +#if defined(EAP_AKA) | defined(EAP_AKA_DYNAMIC) +#define INTERWORKING_3GPP +#else +#if defined(EAP_AKA_PRIME) | defined(EAP_AKA_PRIME_DYNAMIC) +#define INTERWORKING_3GPP +#endif +#endif +#endif + +static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s); + + +static void interworking_reconnect(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->wpa_state >= WPA_AUTHENTICATING) { + wpa_supplicant_cancel_sched_scan(wpa_s); + wpa_supplicant_deauthenticate(wpa_s, + WLAN_REASON_DEAUTH_LEAVING); + } + wpa_s->disconnected = 0; + wpa_s->reassociate = 1; + + if (wpa_s->last_scan_res_used > 0) { + struct os_time now; + os_get_time(&now); + if (now.sec - wpa_s->last_scan.sec <= 5) { + wpa_printf(MSG_DEBUG, "Interworking: Old scan results " + "are fresh - connect without new scan"); + if (wpas_select_network_from_last_scan(wpa_s) >= 0) + return; + } + } + + wpa_supplicant_req_scan(wpa_s, 0, 0); +} + + +static struct wpabuf * anqp_build_req(u16 info_ids[], size_t num_ids, + struct wpabuf *extra) +{ + struct wpabuf *buf; + size_t i; + u8 *len_pos; + + buf = gas_anqp_build_initial_req(0, 4 + num_ids * 2 + + (extra ? wpabuf_len(extra) : 0)); + if (buf == NULL) + return NULL; + + len_pos = gas_anqp_add_element(buf, ANQP_QUERY_LIST); + for (i = 0; i < num_ids; i++) + wpabuf_put_le16(buf, info_ids[i]); + gas_anqp_set_element_len(buf, len_pos); + if (extra) + wpabuf_put_buf(buf, extra); + + gas_anqp_set_len(buf); + + return buf; +} + + +static void interworking_anqp_resp_cb(void *ctx, const u8 *dst, + u8 dialog_token, + enum gas_query_result result, + const struct wpabuf *adv_proto, + const struct wpabuf *resp, + u16 status_code) +{ + struct wpa_supplicant *wpa_s = ctx; + + anqp_resp_cb(wpa_s, dst, dialog_token, result, adv_proto, resp, + status_code); + interworking_next_anqp_fetch(wpa_s); +} + + +static int cred_with_roaming_consortium(struct wpa_supplicant *wpa_s) +{ + struct wpa_cred *cred; + + for (cred = wpa_s->conf->cred; cred; cred = cred->next) { + if (cred->roaming_consortium_len) + return 1; + } + return 0; +} + + +static int cred_with_3gpp(struct wpa_supplicant *wpa_s) +{ + struct wpa_cred *cred; + + for (cred = wpa_s->conf->cred; cred; cred = cred->next) { + if (cred->pcsc || cred->imsi) + return 1; + } + return 0; +} + + +static int cred_with_nai_realm(struct wpa_supplicant *wpa_s) +{ + struct wpa_cred *cred; + + for (cred = wpa_s->conf->cred; cred; cred = cred->next) { + if (cred->pcsc || cred->imsi) + continue; + if (!cred->eap_method) + return 1; + if (cred->realm && cred->roaming_consortium_len == 0) + return 1; + } + return 0; +} + + +static int cred_with_domain(struct wpa_supplicant *wpa_s) +{ + struct wpa_cred *cred; + + for (cred = wpa_s->conf->cred; cred; cred = cred->next) { + if (cred->domain || cred->pcsc || cred->imsi) + return 1; + } + return 0; +} + + +static int additional_roaming_consortiums(struct wpa_bss *bss) +{ + const u8 *ie; + ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM); + if (ie == NULL || ie[1] == 0) + return 0; + return ie[2]; /* Number of ANQP OIs */ +} + + +static void interworking_continue_anqp(void *eloop_ctx, void *sock_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + interworking_next_anqp_fetch(wpa_s); +} + + +static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss) +{ + struct wpabuf *buf; + int ret = 0; + int res; + u16 info_ids[8]; + size_t num_info_ids = 0; + struct wpabuf *extra = NULL; + int all = wpa_s->fetch_all_anqp; + + wpa_printf(MSG_DEBUG, "Interworking: ANQP Query Request to " MACSTR, + MAC2STR(bss->bssid)); + + info_ids[num_info_ids++] = ANQP_CAPABILITY_LIST; + if (all) { + info_ids[num_info_ids++] = ANQP_VENUE_NAME; + info_ids[num_info_ids++] = ANQP_NETWORK_AUTH_TYPE; + } + if (all || (cred_with_roaming_consortium(wpa_s) && + additional_roaming_consortiums(bss))) + info_ids[num_info_ids++] = ANQP_ROAMING_CONSORTIUM; + if (all) + info_ids[num_info_ids++] = ANQP_IP_ADDR_TYPE_AVAILABILITY; + if (all || cred_with_nai_realm(wpa_s)) + info_ids[num_info_ids++] = ANQP_NAI_REALM; + if (all || cred_with_3gpp(wpa_s)) + info_ids[num_info_ids++] = ANQP_3GPP_CELLULAR_NETWORK; + if (all || cred_with_domain(wpa_s)) + info_ids[num_info_ids++] = ANQP_DOMAIN_NAME; + wpa_hexdump(MSG_DEBUG, "Interworking: ANQP Query info", + (u8 *) info_ids, num_info_ids * 2); + +#ifdef CONFIG_HS20 + if (wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE)) { + u8 *len_pos; + + extra = wpabuf_alloc(100); + if (!extra) + return -1; + + len_pos = gas_anqp_add_element(extra, ANQP_VENDOR_SPECIFIC); + wpabuf_put_be24(extra, OUI_WFA); + wpabuf_put_u8(extra, HS20_ANQP_OUI_TYPE); + wpabuf_put_u8(extra, HS20_STYPE_QUERY_LIST); + wpabuf_put_u8(extra, 0); /* Reserved */ + wpabuf_put_u8(extra, HS20_STYPE_CAPABILITY_LIST); + if (all) { + wpabuf_put_u8(extra, + HS20_STYPE_OPERATOR_FRIENDLY_NAME); + wpabuf_put_u8(extra, HS20_STYPE_WAN_METRICS); + wpabuf_put_u8(extra, HS20_STYPE_CONNECTION_CAPABILITY); + wpabuf_put_u8(extra, HS20_STYPE_OPERATING_CLASS); + } + gas_anqp_set_element_len(extra, len_pos); + } +#endif /* CONFIG_HS20 */ + + buf = anqp_build_req(info_ids, num_info_ids, extra); + wpabuf_free(extra); + if (buf == NULL) + return -1; + + res = gas_query_req(wpa_s->gas, bss->bssid, bss->freq, buf, + interworking_anqp_resp_cb, wpa_s); + if (res < 0) { + wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request"); + ret = -1; + eloop_register_timeout(0, 0, interworking_continue_anqp, wpa_s, + NULL); + } else + wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token " + "%u", res); + + 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_calloc(r->eap_count, sizeof(struct nai_realm_eap)); + if (r->eap == NULL) + return NULL; + + for (e = 0; e < r->eap_count; e++) { + pos = nai_realm_parse_eap(&r->eap[e], pos, f_end); + if (pos == NULL) + return NULL; + } + + return f_end; +} + + +static struct nai_realm * nai_realm_parse(struct wpabuf *anqp, u16 *count) +{ + struct nai_realm *realm; + const u8 *pos, *end; + u16 i, num; + + 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_calloc(num, sizeof(struct nai_realm)); + if (realm == NULL) + return NULL; + + for (i = 0; i < num; i++) { + pos = nai_realm_parse_realm(&realm[i], pos, end); + if (pos == NULL) { + nai_realm_free(realm, num); + return NULL; + } + } + + *count = num; + return realm; +} + + +static int nai_realm_match(struct nai_realm *realm, const char *home_realm) +{ + char *tmp, *pos, *end; + int match = 0; + + if (realm->realm == NULL || home_realm == NULL) + return 0; + + if (os_strchr(realm->realm, ';') == NULL) + return os_strcasecmp(realm->realm, home_realm) == 0; + + tmp = os_strdup(realm->realm); + if (tmp == NULL) + return 0; + + pos = tmp; + while (*pos) { + end = os_strchr(pos, ';'); + if (end) + *end = '\0'; + if (os_strcasecmp(pos, home_realm) == 0) { + match = 1; + break; + } + if (end == NULL) + break; + pos = end + 1; + } + + os_free(tmp); + + return match; +} + + +static int nai_realm_cred_username(struct 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) { + if (eap->inner_method && + eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL) + return 0; + if (!eap->inner_method && + eap_get_name(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2) == NULL) + return 0; + } + + if (eap->method == EAP_TYPE_TTLS) { + if (eap->inner_method == 0 && eap->inner_non_eap == 0) + return 1; /* Assume TTLS/MSCHAPv2 is used */ + if (eap->inner_method && + eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL) + 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; +} + + +static int nai_realm_cred_cert(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_TLS) { + /* Only EAP-TLS supported for credential authentication */ + return 0; + } + + return 1; +} + + +static struct nai_realm_eap * nai_realm_find_eap(struct wpa_cred *cred, + struct nai_realm *realm) +{ + u8 e; + + if (cred == NULL || + cred->username == NULL || + cred->username[0] == '\0' || + ((cred->password == NULL || + cred->password[0] == '\0') && + (cred->private_key == NULL || + cred->private_key[0] == '\0'))) + return NULL; + + for (e = 0; e < realm->eap_count; e++) { + struct nai_realm_eap *eap = &realm->eap[e]; + if (cred->password && cred->password[0] && + nai_realm_cred_username(eap)) + return eap; + if (cred->private_key && cred->private_key[0] && + nai_realm_cred_cert(eap)) + return eap; + } + + return NULL; +} + + +#ifdef INTERWORKING_3GPP + +static int plmn_id_match(struct wpabuf *anqp, const char *imsi, int mnc_len) +{ + u8 plmn[3]; + const u8 *pos, *end; + u8 udhl; + + /* 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'; + /* default to MNC length 3 if unknown */ + if (mnc_len != 2) + 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 += 3; + } + } + + pos = l_end; + } + + return 0; +} + + +static int build_root_nai(char *nai, size_t nai_len, const char *imsi, + size_t mnc_len, char prefix) +{ + const char *sep, *msin; + char *end, *pos; + size_t msin_len, plmn_len; + + /* + * TS 23.003, Clause 14 (3GPP to WLAN Interworking) + * Root NAI: + * <aka:0|sim:1><IMSI>@wlan.mnc<MNC>.mcc<MCC>.3gppnetwork.org + * <MNC> is zero-padded to three digits in case two-digit MNC is used + */ + + if (imsi == NULL || os_strlen(imsi) > 16) { + wpa_printf(MSG_DEBUG, "No valid IMSI available"); + return -1; + } + sep = os_strchr(imsi, '-'); + if (sep) { + plmn_len = sep - imsi; + msin = sep + 1; + } else if (mnc_len && os_strlen(imsi) >= 3 + mnc_len) { + plmn_len = 3 + mnc_len; + msin = imsi + plmn_len; + } else + return -1; + if (plmn_len != 5 && plmn_len != 6) + return -1; + msin_len = os_strlen(msin); + + pos = nai; + end = nai + nai_len; + if (prefix) + *pos++ = prefix; + os_memcpy(pos, imsi, plmn_len); + pos += plmn_len; + os_memcpy(pos, msin, msin_len); + pos += msin_len; + pos += os_snprintf(pos, end - pos, "@wlan.mnc"); + if (plmn_len == 5) { + *pos++ = '0'; + *pos++ = imsi[3]; + *pos++ = imsi[4]; + } else { + *pos++ = imsi[3]; + *pos++ = imsi[4]; + *pos++ = imsi[5]; + } + pos += os_snprintf(pos, end - pos, ".mcc%c%c%c.3gppnetwork.org", + imsi[0], imsi[1], imsi[2]); + + return 0; +} + + +static int set_root_nai(struct wpa_ssid *ssid, const char *imsi, char prefix) +{ + char nai[100]; + if (build_root_nai(nai, sizeof(nai), imsi, 0, prefix) < 0) + return -1; + return wpa_config_set_quoted(ssid, "identity", nai); +} + +#endif /* INTERWORKING_3GPP */ + + +static int interworking_set_hs20_params(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + if (wpa_config_set(ssid, "key_mgmt", + wpa_s->conf->pmf != NO_MGMT_FRAME_PROTECTION ? + "WPA-EAP WPA-EAP-SHA256" : "WPA-EAP", 0) < 0) + return -1; + if (wpa_config_set(ssid, "proto", "RSN", 0) < 0) + return -1; + if (wpa_config_set(ssid, "pairwise", "CCMP", 0) < 0) + return -1; + return 0; +} + + +static int interworking_connect_3gpp(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss) +{ +#ifdef INTERWORKING_3GPP + struct wpa_cred *cred; + struct wpa_ssid *ssid; + const u8 *ie; + int eap_type; + int res; + char prefix; + + if (bss->anqp == NULL || bss->anqp->anqp_3gpp == NULL) + return -1; + + for (cred = wpa_s->conf->cred; cred; cred = cred->next) { + char *sep; + const char *imsi; + int mnc_len; + +#ifdef PCSC_FUNCS + if (cred->pcsc && wpa_s->conf->pcsc_reader && wpa_s->scard && + wpa_s->imsi[0]) { + imsi = wpa_s->imsi; + mnc_len = wpa_s->mnc_len; + goto compare; + } +#endif /* PCSC_FUNCS */ + + if (cred->imsi == NULL || !cred->imsi[0] || + cred->milenage == NULL || !cred->milenage[0]) + continue; + + sep = os_strchr(cred->imsi, '-'); + if (sep == NULL || + (sep - cred->imsi != 5 && sep - cred->imsi != 6)) + continue; + mnc_len = sep - cred->imsi - 3; + imsi = cred->imsi; + +#ifdef PCSC_FUNCS + compare: +#endif /* PCSC_FUNCS */ + if (plmn_id_match(bss->anqp->anqp_3gpp, imsi, mnc_len)) + break; + } + if (cred == NULL) + return -1; + + ie = wpa_bss_get_ie(bss, WLAN_EID_SSID); + if (ie == NULL) + return -1; + wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR " (3GPP)", + MAC2STR(bss->bssid)); + + ssid = wpa_config_add_network(wpa_s->conf); + if (ssid == NULL) + return -1; + ssid->parent_cred = cred; + + wpas_notify_network_added(wpa_s, ssid); + wpa_config_set_network_defaults(ssid); + ssid->priority = cred->priority; + ssid->temporary = 1; + ssid->ssid = os_zalloc(ie[1] + 1); + if (ssid->ssid == NULL) + goto fail; + os_memcpy(ssid->ssid, ie + 2, ie[1]); + ssid->ssid_len = ie[1]; + + if (interworking_set_hs20_params(wpa_s, ssid) < 0) + goto fail; + + eap_type = EAP_TYPE_SIM; + if (cred->pcsc && wpa_s->scard && scard_supports_umts(wpa_s->scard)) + eap_type = EAP_TYPE_AKA; + if (cred->eap_method && cred->eap_method[0].vendor == EAP_VENDOR_IETF) { + if (cred->eap_method[0].method == EAP_TYPE_SIM || + cred->eap_method[0].method == EAP_TYPE_AKA || + cred->eap_method[0].method == EAP_TYPE_AKA_PRIME) + eap_type = cred->eap_method[0].method; + } + + switch (eap_type) { + case EAP_TYPE_SIM: + prefix = '1'; + res = wpa_config_set(ssid, "eap", "SIM", 0); + break; + case EAP_TYPE_AKA: + prefix = '0'; + res = wpa_config_set(ssid, "eap", "AKA", 0); + break; + case EAP_TYPE_AKA_PRIME: + prefix = '6'; + res = wpa_config_set(ssid, "eap", "AKA'", 0); + break; + default: + res = -1; + break; + } + if (res < 0) { + wpa_printf(MSG_DEBUG, "Selected EAP method (%d) not supported", + eap_type); + goto fail; + } + + if (!cred->pcsc && set_root_nai(ssid, cred->imsi, prefix) < 0) { + wpa_printf(MSG_DEBUG, "Failed to set Root NAI"); + goto fail; + } + + if (cred->milenage && cred->milenage[0]) { + if (wpa_config_set_quoted(ssid, "password", + cred->milenage) < 0) + goto fail; + } else if (cred->pcsc) { + if (wpa_config_set_quoted(ssid, "pcsc", "") < 0) + goto fail; + if (wpa_s->conf->pcsc_pin && + wpa_config_set_quoted(ssid, "pin", wpa_s->conf->pcsc_pin) + < 0) + goto fail; + } + + if (cred->password && cred->password[0] && + wpa_config_set_quoted(ssid, "password", cred->password) < 0) + goto fail; + + wpa_config_update_prio_list(wpa_s->conf); + interworking_reconnect(wpa_s); + + return 0; + +fail: + wpas_notify_network_removed(wpa_s, ssid); + wpa_config_remove_network(wpa_s->conf, ssid->id); +#endif /* INTERWORKING_3GPP */ + return -1; +} + + +static int roaming_consortium_element_match(const u8 *ie, const u8 *rc_id, + size_t rc_len) +{ + const u8 *pos, *end; + u8 lens; + + if (ie == NULL) + return 0; + + pos = ie + 2; + end = ie + 2 + ie[1]; + + /* Roaming Consortium element: + * Number of ANQP OIs + * OI #1 and #2 lengths + * OI #1, [OI #2], [OI #3] + */ + + if (pos + 2 > end) + return 0; + + pos++; /* skip Number of ANQP OIs */ + lens = *pos++; + if (pos + (lens & 0x0f) + (lens >> 4) > end) + return 0; + + if ((lens & 0x0f) == rc_len && os_memcmp(pos, rc_id, rc_len) == 0) + return 1; + pos += lens & 0x0f; + + if ((lens >> 4) == rc_len && os_memcmp(pos, rc_id, rc_len) == 0) + return 1; + pos += lens >> 4; + + if (pos < end && (size_t) (end - pos) == rc_len && + os_memcmp(pos, rc_id, rc_len) == 0) + return 1; + + return 0; +} + + +static int roaming_consortium_anqp_match(const struct wpabuf *anqp, + const u8 *rc_id, size_t rc_len) +{ + const u8 *pos, *end; + u8 len; + + if (anqp == NULL) + return 0; + + pos = wpabuf_head(anqp); + end = pos + wpabuf_len(anqp); + + /* Set of <OI Length, OI> duples */ + while (pos < end) { + len = *pos++; + if (pos + len > end) + break; + if (len == rc_len && os_memcmp(pos, rc_id, rc_len) == 0) + return 1; + pos += len; + } + + return 0; +} + + +static int roaming_consortium_match(const u8 *ie, const struct wpabuf *anqp, + const u8 *rc_id, size_t rc_len) +{ + return roaming_consortium_element_match(ie, rc_id, rc_len) || + roaming_consortium_anqp_match(anqp, rc_id, rc_len); +} + + +static int cred_excluded_ssid(struct wpa_cred *cred, struct wpa_bss *bss) +{ + size_t i; + + if (!cred->excluded_ssid) + return 0; + + for (i = 0; i < cred->num_excluded_ssid; i++) { + struct excluded_ssid *e = &cred->excluded_ssid[i]; + if (bss->ssid_len == e->ssid_len && + os_memcmp(bss->ssid, e->ssid, e->ssid_len) == 0) + return 1; + } + + return 0; +} + + +static struct wpa_cred * interworking_credentials_available_roaming_consortium( + struct wpa_supplicant *wpa_s, struct wpa_bss *bss) +{ + struct wpa_cred *cred, *selected = NULL; + const u8 *ie; + + ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM); + + if (ie == NULL && + (bss->anqp == NULL || bss->anqp->roaming_consortium == NULL)) + return NULL; + + if (wpa_s->conf->cred == NULL) + return NULL; + + for (cred = wpa_s->conf->cred; cred; cred = cred->next) { + if (cred->roaming_consortium_len == 0) + continue; + + if (!roaming_consortium_match(ie, + bss->anqp ? + bss->anqp->roaming_consortium : + NULL, + cred->roaming_consortium, + cred->roaming_consortium_len)) + continue; + + if (cred_excluded_ssid(cred, bss)) + continue; + + if (selected == NULL || + selected->priority < cred->priority) + selected = cred; + } + + return selected; +} + + +static int interworking_set_eap_params(struct wpa_ssid *ssid, + struct wpa_cred *cred, int ttls) +{ + if (cred->eap_method) { + ttls = cred->eap_method->vendor == EAP_VENDOR_IETF && + cred->eap_method->method == EAP_TYPE_TTLS; + + os_free(ssid->eap.eap_methods); + ssid->eap.eap_methods = + os_malloc(sizeof(struct eap_method_type) * 2); + if (ssid->eap.eap_methods == NULL) + return -1; + os_memcpy(ssid->eap.eap_methods, cred->eap_method, + sizeof(*cred->eap_method)); + ssid->eap.eap_methods[1].vendor = EAP_VENDOR_IETF; + ssid->eap.eap_methods[1].method = EAP_TYPE_NONE; + } + + if (ttls && cred->username && cred->username[0]) { + const char *pos; + char *anon; + /* Use anonymous NAI in Phase 1 */ + pos = os_strchr(cred->username, '@'); + if (pos) { + size_t buflen = 9 + os_strlen(pos) + 1; + anon = os_malloc(buflen); + if (anon == NULL) + return -1; + os_snprintf(anon, buflen, "anonymous%s", pos); + } else if (cred->realm) { + size_t buflen = 10 + os_strlen(cred->realm) + 1; + anon = os_malloc(buflen); + if (anon == NULL) + return -1; + os_snprintf(anon, buflen, "anonymous@%s", cred->realm); + } else { + anon = os_strdup("anonymous"); + if (anon == NULL) + return -1; + } + if (wpa_config_set_quoted(ssid, "anonymous_identity", anon) < + 0) { + os_free(anon); + return -1; + } + os_free(anon); + } + + if (cred->username && cred->username[0] && + wpa_config_set_quoted(ssid, "identity", cred->username) < 0) + return -1; + + if (cred->password && cred->password[0]) { + if (cred->ext_password && + wpa_config_set(ssid, "password", cred->password, 0) < 0) + return -1; + if (!cred->ext_password && + wpa_config_set_quoted(ssid, "password", cred->password) < + 0) + return -1; + } + + if (cred->client_cert && cred->client_cert[0] && + wpa_config_set_quoted(ssid, "client_cert", cred->client_cert) < 0) + return -1; + +#ifdef ANDROID + if (cred->private_key && + os_strncmp(cred->private_key, "keystore://", 11) == 0) { + /* Use OpenSSL engine configuration for Android keystore */ + if (wpa_config_set_quoted(ssid, "engine_id", "keystore") < 0 || + wpa_config_set_quoted(ssid, "key_id", + cred->private_key + 11) < 0 || + wpa_config_set(ssid, "engine", "1", 0) < 0) + return -1; + } else +#endif /* ANDROID */ + if (cred->private_key && cred->private_key[0] && + wpa_config_set_quoted(ssid, "private_key", cred->private_key) < 0) + return -1; + + if (cred->private_key_passwd && cred->private_key_passwd[0] && + wpa_config_set_quoted(ssid, "private_key_passwd", + cred->private_key_passwd) < 0) + return -1; + + if (cred->phase1) { + os_free(ssid->eap.phase1); + ssid->eap.phase1 = os_strdup(cred->phase1); + } + if (cred->phase2) { + os_free(ssid->eap.phase2); + ssid->eap.phase2 = os_strdup(cred->phase2); + } + + if (cred->ca_cert && cred->ca_cert[0] && + wpa_config_set_quoted(ssid, "ca_cert", cred->ca_cert) < 0) + return -1; + + return 0; +} + + +static int interworking_connect_roaming_consortium( + struct wpa_supplicant *wpa_s, struct wpa_cred *cred, + struct wpa_bss *bss, const u8 *ssid_ie) +{ + struct wpa_ssid *ssid; + + wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR " based on " + "roaming consortium match", MAC2STR(bss->bssid)); + + ssid = wpa_config_add_network(wpa_s->conf); + if (ssid == NULL) + return -1; + ssid->parent_cred = cred; + wpas_notify_network_added(wpa_s, ssid); + wpa_config_set_network_defaults(ssid); + ssid->priority = cred->priority; + ssid->temporary = 1; + ssid->ssid = os_zalloc(ssid_ie[1] + 1); + if (ssid->ssid == NULL) + goto fail; + os_memcpy(ssid->ssid, ssid_ie + 2, ssid_ie[1]); + ssid->ssid_len = ssid_ie[1]; + + if (interworking_set_hs20_params(wpa_s, ssid) < 0) + goto fail; + + if (cred->eap_method == NULL) { + wpa_printf(MSG_DEBUG, "Interworking: No EAP method set for " + "credential using roaming consortium"); + goto fail; + } + + if (interworking_set_eap_params( + ssid, cred, + cred->eap_method->vendor == EAP_VENDOR_IETF && + cred->eap_method->method == EAP_TYPE_TTLS) < 0) + goto fail; + + wpa_config_update_prio_list(wpa_s->conf); + interworking_reconnect(wpa_s); + + return 0; + +fail: + wpas_notify_network_removed(wpa_s, ssid); + wpa_config_remove_network(wpa_s->conf, ssid->id); + return -1; +} + + +int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) +{ + struct wpa_cred *cred; + struct wpa_ssid *ssid; + struct nai_realm *realm; + struct nai_realm_eap *eap = NULL; + u16 count, i; + char buf[100]; + const u8 *ie; + + if (wpa_s->conf->cred == NULL || bss == NULL) + return -1; + ie = wpa_bss_get_ie(bss, WLAN_EID_SSID); + if (ie == NULL || ie[1] == 0) { + wpa_printf(MSG_DEBUG, "Interworking: No SSID known for " + MACSTR, MAC2STR(bss->bssid)); + return -1; + } + + if (!wpa_bss_get_ie(bss, WLAN_EID_RSN)) { + /* + * We currently support only HS 2.0 networks and those are + * required to use WPA2-Enterprise. + */ + wpa_printf(MSG_DEBUG, "Interworking: Network does not use " + "RSN"); + return -1; + } + + cred = interworking_credentials_available_roaming_consortium(wpa_s, + bss); + if (cred) + return interworking_connect_roaming_consortium(wpa_s, cred, + bss, ie); + + realm = nai_realm_parse(bss->anqp ? bss->anqp->nai_realm : NULL, + &count); + if (realm == NULL) { + wpa_printf(MSG_DEBUG, "Interworking: Could not parse NAI " + "Realm list from " MACSTR, MAC2STR(bss->bssid)); + count = 0; + } + + for (cred = wpa_s->conf->cred; cred; cred = cred->next) { + for (i = 0; i < count; i++) { + if (!nai_realm_match(&realm[i], cred->realm)) + continue; + eap = nai_realm_find_eap(cred, &realm[i]); + if (eap) + break; + } + 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; + } + ssid->parent_cred = cred; + wpas_notify_network_added(wpa_s, ssid); + wpa_config_set_network_defaults(ssid); + ssid->priority = cred->priority; + ssid->temporary = 1; + ssid->ssid = os_zalloc(ie[1] + 1); + if (ssid->ssid == NULL) + goto fail; + os_memcpy(ssid->ssid, ie + 2, ie[1]); + ssid->ssid_len = ie[1]; + + if (interworking_set_hs20_params(wpa_s, ssid) < 0) + goto fail; + + if (wpa_config_set(ssid, "eap", eap_get_name(EAP_VENDOR_IETF, + eap->method), 0) < 0) + goto fail; + + switch (eap->method) { + case EAP_TYPE_TTLS: + if (eap->inner_method) { + os_snprintf(buf, sizeof(buf), "\"autheap=%s\"", + eap_get_name(EAP_VENDOR_IETF, + eap->inner_method)); + if (wpa_config_set(ssid, "phase2", buf, 0) < 0) + goto fail; + break; + } + switch (eap->inner_non_eap) { + case NAI_REALM_INNER_NON_EAP_PAP: + if (wpa_config_set(ssid, "phase2", "\"auth=PAP\"", 0) < + 0) + goto fail; + break; + case NAI_REALM_INNER_NON_EAP_CHAP: + if (wpa_config_set(ssid, "phase2", "\"auth=CHAP\"", 0) + < 0) + goto fail; + break; + case NAI_REALM_INNER_NON_EAP_MSCHAP: + if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAP\"", + 0) < 0) + goto fail; + break; + case NAI_REALM_INNER_NON_EAP_MSCHAPV2: + if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAPV2\"", + 0) < 0) + goto fail; + break; + default: + /* EAP params were not set - assume TTLS/MSCHAPv2 */ + if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAPV2\"", + 0) < 0) + goto fail; + break; + } + break; + case EAP_TYPE_PEAP: + os_snprintf(buf, sizeof(buf), "\"auth=%s\"", + eap_get_name(EAP_VENDOR_IETF, + eap->inner_method ? + eap->inner_method : + EAP_TYPE_MSCHAPV2)); + if (wpa_config_set(ssid, "phase2", buf, 0) < 0) + goto fail; + break; + case EAP_TYPE_TLS: + break; + } + + if (interworking_set_eap_params(ssid, cred, + eap->method == EAP_TYPE_TTLS) < 0) + goto fail; + + nai_realm_free(realm, count); + + wpa_config_update_prio_list(wpa_s->conf); + interworking_reconnect(wpa_s); + + 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 struct wpa_cred * interworking_credentials_available_3gpp( + struct wpa_supplicant *wpa_s, struct wpa_bss *bss) +{ + struct wpa_cred *cred, *selected = NULL; + int ret; + +#ifdef INTERWORKING_3GPP + if (bss->anqp == NULL || bss->anqp->anqp_3gpp == NULL) + return NULL; + + for (cred = wpa_s->conf->cred; cred; cred = cred->next) { + char *sep; + const char *imsi; + int mnc_len; + +#ifdef PCSC_FUNCS + if (cred->pcsc && wpa_s->conf->pcsc_reader && wpa_s->scard && + wpa_s->imsi[0]) { + imsi = wpa_s->imsi; + mnc_len = wpa_s->mnc_len; + goto compare; + } +#endif /* PCSC_FUNCS */ + + if (cred->imsi == NULL || !cred->imsi[0] || + cred->milenage == NULL || !cred->milenage[0]) + continue; + + sep = os_strchr(cred->imsi, '-'); + if (sep == NULL || + (sep - cred->imsi != 5 && sep - cred->imsi != 6)) + continue; + mnc_len = sep - cred->imsi - 3; + imsi = cred->imsi; + +#ifdef PCSC_FUNCS + compare: +#endif /* PCSC_FUNCS */ + wpa_printf(MSG_DEBUG, "Interworking: Parsing 3GPP info from " + MACSTR, MAC2STR(bss->bssid)); + ret = plmn_id_match(bss->anqp->anqp_3gpp, imsi, mnc_len); + wpa_printf(MSG_DEBUG, "PLMN match %sfound", ret ? "" : "not "); + if (ret) { + if (cred_excluded_ssid(cred, bss)) + continue; + if (selected == NULL || + selected->priority < cred->priority) + selected = cred; + } + } +#endif /* INTERWORKING_3GPP */ + return selected; +} + + +static struct wpa_cred * interworking_credentials_available_realm( + struct wpa_supplicant *wpa_s, struct wpa_bss *bss) +{ + struct wpa_cred *cred, *selected = NULL; + struct nai_realm *realm; + u16 count, i; + + if (bss->anqp == NULL || bss->anqp->nai_realm == NULL) + return NULL; + + if (wpa_s->conf->cred == NULL) + return NULL; + + 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 NULL; + } + + for (cred = wpa_s->conf->cred; cred; cred = cred->next) { + if (cred->realm == NULL) + continue; + + for (i = 0; i < count; i++) { + if (!nai_realm_match(&realm[i], cred->realm)) + continue; + if (nai_realm_find_eap(cred, &realm[i])) { + if (cred_excluded_ssid(cred, bss)) + continue; + if (selected == NULL || + selected->priority < cred->priority) + selected = cred; + break; + } + } + } + + nai_realm_free(realm, count); + + return selected; +} + + +static struct wpa_cred * interworking_credentials_available( + struct wpa_supplicant *wpa_s, struct wpa_bss *bss) +{ + struct wpa_cred *cred, *cred2; + + cred = interworking_credentials_available_realm(wpa_s, bss); + cred2 = interworking_credentials_available_3gpp(wpa_s, bss); + if (cred && cred2 && cred2->priority >= cred->priority) + cred = cred2; + if (!cred) + cred = cred2; + + cred2 = interworking_credentials_available_roaming_consortium(wpa_s, + bss); + if (cred && cred2 && cred2->priority >= cred->priority) + cred = cred2; + if (!cred) + cred = cred2; + + return cred; +} + + +static int domain_name_list_contains(struct wpabuf *domain_names, + const char *domain) +{ + const u8 *pos, *end; + size_t len; + + len = os_strlen(domain); + pos = wpabuf_head(domain_names); + end = pos + wpabuf_len(domain_names); + + while (pos + 1 < end) { + if (pos + 1 + pos[0] > end) + break; + + wpa_hexdump_ascii(MSG_DEBUG, "Interworking: AP domain name", + pos + 1, pos[0]); + if (pos[0] == len && + os_strncasecmp(domain, (const char *) (pos + 1), len) == 0) + return 1; + + pos += 1 + pos[0]; + } + + return 0; +} + + +int interworking_home_sp_cred(struct wpa_supplicant *wpa_s, + struct wpa_cred *cred, + struct wpabuf *domain_names) +{ +#ifdef INTERWORKING_3GPP + char nai[100], *realm; + + char *imsi = NULL; + int mnc_len = 0; + if (cred->imsi) + imsi = cred->imsi; +#ifdef CONFIG_PCSC + else if (cred->pcsc && wpa_s->conf->pcsc_reader && + wpa_s->scard && wpa_s->imsi[0]) { + imsi = wpa_s->imsi; + mnc_len = wpa_s->mnc_len; + } +#endif /* CONFIG_PCSC */ + if (domain_names && + imsi && build_root_nai(nai, sizeof(nai), imsi, mnc_len, 0) == 0) { + realm = os_strchr(nai, '@'); + if (realm) + realm++; + wpa_printf(MSG_DEBUG, "Interworking: Search for match " + "with SIM/USIM domain %s", realm); + if (realm && + domain_name_list_contains(domain_names, realm)) + return 1; + } +#endif /* INTERWORKING_3GPP */ + + if (domain_names == NULL || cred->domain == NULL) + return 0; + + wpa_printf(MSG_DEBUG, "Interworking: Search for match with " + "home SP FQDN %s", cred->domain); + if (domain_name_list_contains(domain_names, cred->domain)) + return 1; + + return 0; +} + + +static int interworking_home_sp(struct wpa_supplicant *wpa_s, + struct wpabuf *domain_names) +{ + struct wpa_cred *cred; + + if (domain_names == NULL || wpa_s->conf->cred == NULL) + return -1; + + for (cred = wpa_s->conf->cred; cred; cred = cred->next) { + int res = interworking_home_sp_cred(wpa_s, cred, domain_names); + if (res) + return res; + } + + return 0; +} + + +static int interworking_find_network_match(struct wpa_supplicant *wpa_s) +{ + struct wpa_bss *bss; + struct wpa_ssid *ssid; + + dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { + if (wpas_network_disabled(wpa_s, ssid) || + ssid->mode != WPAS_MODE_INFRA) + continue; + if (ssid->ssid_len != bss->ssid_len || + os_memcmp(ssid->ssid, bss->ssid, ssid->ssid_len) != + 0) + continue; + /* + * TODO: Consider more accurate matching of security + * configuration similarly to what is done in events.c + */ + return 1; + } + } + + return 0; +} + + +static void interworking_select_network(struct wpa_supplicant *wpa_s) +{ + struct wpa_bss *bss, *selected = NULL, *selected_home = NULL; + int selected_prio = -999999, selected_home_prio = -999999; + unsigned int count = 0; + const char *type; + int res; + struct wpa_cred *cred; + + wpa_s->network_select = 0; + + dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { + cred = interworking_credentials_available(wpa_s, bss); + if (!cred) + continue; + if (!wpa_bss_get_ie(bss, WLAN_EID_RSN)) { + /* + * We currently support only HS 2.0 networks and those + * are required to use WPA2-Enterprise. + */ + wpa_printf(MSG_DEBUG, "Interworking: Credential match " + "with " MACSTR " but network does not use " + "RSN", MAC2STR(bss->bssid)); + continue; + } + count++; + res = interworking_home_sp(wpa_s, bss->anqp ? + bss->anqp->domain_name : NULL); + if (res > 0) + type = "home"; + else if (res == 0) + type = "roaming"; + else + type = "unknown"; + wpa_msg(wpa_s, MSG_INFO, INTERWORKING_AP MACSTR " type=%s", + MAC2STR(bss->bssid), type); + if (wpa_s->auto_select || + (wpa_s->conf->auto_interworking && + wpa_s->auto_network_select)) { + if (selected == NULL || + cred->priority > selected_prio) { + selected = bss; + selected_prio = cred->priority; + } + if (res > 0 && + (selected_home == NULL || + cred->priority > selected_home_prio)) { + selected_home = bss; + selected_home_prio = cred->priority; + } + } + } + + if (selected_home && selected_home != selected && + selected_home_prio >= selected_prio) { + /* Prefer network operated by the Home SP */ + selected = selected_home; + } + + if (count == 0) { + /* + * No matching network was found based on configured + * credentials. Check whether any of the enabled network blocks + * have matching APs. + */ + if (interworking_find_network_match(wpa_s)) { + wpa_printf(MSG_DEBUG, "Interworking: Possible BSS " + "match for enabled network configurations"); + if (wpa_s->auto_select) + interworking_reconnect(wpa_s); + return; + } + + if (wpa_s->auto_network_select) { + wpa_printf(MSG_DEBUG, "Interworking: Continue " + "scanning after ANQP fetch"); + wpa_supplicant_req_scan(wpa_s, wpa_s->scan_interval, + 0); + return; + } + + wpa_msg(wpa_s, MSG_INFO, INTERWORKING_NO_MATCH "No network " + "with matching credentials found"); + } + + if (selected) + interworking_connect(wpa_s, selected); +} + + +static struct wpa_bss_anqp * +interworking_match_anqp_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) +{ + struct wpa_bss *other; + + if (is_zero_ether_addr(bss->hessid)) + return NULL; /* Cannot be in the same homegenous ESS */ + + dl_list_for_each(other, &wpa_s->bss, struct wpa_bss, list) { + if (other == bss) + continue; + if (other->anqp == NULL) + continue; + if (other->anqp->roaming_consortium == NULL && + other->anqp->nai_realm == NULL && + other->anqp->anqp_3gpp == NULL && + other->anqp->domain_name == NULL) + continue; + if (!(other->flags & WPA_BSS_ANQP_FETCH_TRIED)) + continue; + if (os_memcmp(bss->hessid, other->hessid, ETH_ALEN) != 0) + continue; + if (bss->ssid_len != other->ssid_len || + os_memcmp(bss->ssid, other->ssid, bss->ssid_len) != 0) + continue; + + wpa_printf(MSG_DEBUG, "Interworking: Share ANQP data with " + "already fetched BSSID " MACSTR " and " MACSTR, + MAC2STR(other->bssid), MAC2STR(bss->bssid)); + other->anqp->users++; + return other->anqp; + } + + return NULL; +} + + +static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s) +{ + struct wpa_bss *bss; + int found = 0; + const u8 *ie; + + if (eloop_terminated() || !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)) { + if (bss->anqp == NULL) { + bss->anqp = interworking_match_anqp_info(wpa_s, + bss); + if (bss->anqp) { + /* Shared data already fetched */ + continue; + } + bss->anqp = wpa_bss_anqp_alloc(); + if (bss->anqp == NULL) + break; + } + found++; + bss->flags |= WPA_BSS_ANQP_FETCH_TRIED; + wpa_msg(wpa_s, MSG_INFO, "Starting ANQP fetch for " + MACSTR, MAC2STR(bss->bssid)); + interworking_anqp_send_req(wpa_s, bss); + break; + } + } + + if (found == 0) { + wpa_msg(wpa_s, MSG_INFO, "ANQP fetch completed"); + wpa_s->fetch_anqp_in_progress = 0; + if (wpa_s->network_select) + interworking_select_network(wpa_s); + } +} + + +void interworking_start_fetch_anqp(struct wpa_supplicant *wpa_s) +{ + struct wpa_bss *bss; + + dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) + bss->flags &= ~WPA_BSS_ANQP_FETCH_TRIED; + + wpa_s->fetch_anqp_in_progress = 1; + 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; + wpa_s->fetch_all_anqp = 1; + + 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) { + wpa_bss_anqp_unshare_alloc(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); + struct wpa_bss_anqp *anqp = NULL; +#ifdef CONFIG_HS20 + u8 type; +#endif /* CONFIG_HS20 */ + + if (bss) + anqp = bss->anqp; + + switch (info_id) { + case ANQP_CAPABILITY_LIST: + wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR + " ANQP Capability list", MAC2STR(sa)); + break; + case ANQP_VENUE_NAME: + wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR + " Venue Name", MAC2STR(sa)); + wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Venue Name", pos, slen); + if (anqp) { + wpabuf_free(anqp->venue_name); + anqp->venue_name = wpabuf_alloc_copy(pos, slen); + } + break; + case ANQP_NETWORK_AUTH_TYPE: + wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR + " Network Authentication Type information", + MAC2STR(sa)); + wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Network Authentication " + "Type", pos, slen); + if (anqp) { + wpabuf_free(anqp->network_auth_type); + anqp->network_auth_type = wpabuf_alloc_copy(pos, slen); + } + break; + case ANQP_ROAMING_CONSORTIUM: + wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR + " Roaming Consortium list", MAC2STR(sa)); + wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Roaming Consortium", + pos, slen); + if (anqp) { + wpabuf_free(anqp->roaming_consortium); + anqp->roaming_consortium = wpabuf_alloc_copy(pos, slen); + } + break; + case ANQP_IP_ADDR_TYPE_AVAILABILITY: + wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR + " IP Address Type Availability information", + MAC2STR(sa)); + wpa_hexdump(MSG_MSGDUMP, "ANQP: IP Address Availability", + pos, slen); + if (anqp) { + wpabuf_free(anqp->ip_addr_type_availability); + anqp->ip_addr_type_availability = + wpabuf_alloc_copy(pos, slen); + } + break; + case ANQP_NAI_REALM: + wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR + " NAI Realm list", MAC2STR(sa)); + wpa_hexdump_ascii(MSG_DEBUG, "ANQP: NAI Realm", pos, slen); + if (anqp) { + wpabuf_free(anqp->nai_realm); + anqp->nai_realm = wpabuf_alloc_copy(pos, slen); + } + break; + case ANQP_3GPP_CELLULAR_NETWORK: + wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR + " 3GPP Cellular Network information", MAC2STR(sa)); + wpa_hexdump_ascii(MSG_DEBUG, "ANQP: 3GPP Cellular Network", + pos, slen); + if (anqp) { + wpabuf_free(anqp->anqp_3gpp); + anqp->anqp_3gpp = wpabuf_alloc_copy(pos, slen); + } + break; + case ANQP_DOMAIN_NAME: + wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR + " Domain Name list", MAC2STR(sa)); + wpa_hexdump_ascii(MSG_MSGDUMP, "ANQP: Domain Name", pos, slen); + if (anqp) { + wpabuf_free(anqp->domain_name); + anqp->domain_name = wpabuf_alloc_copy(pos, slen); + } + break; + case ANQP_VENDOR_SPECIFIC: + if (slen < 3) + return; + + switch (WPA_GET_BE24(pos)) { +#ifdef CONFIG_HS20 + case OUI_WFA: + pos += 3; + slen -= 3; + + if (slen < 1) + return; + type = *pos++; + slen--; + + switch (type) { + case HS20_ANQP_OUI_TYPE: + hs20_parse_rx_hs20_anqp_resp(wpa_s, sa, pos, + slen); + break; + default: + wpa_printf(MSG_DEBUG, "HS20: Unsupported ANQP " + "vendor type %u", type); + break; + } + break; +#endif /* CONFIG_HS20 */ + default: + wpa_printf(MSG_DEBUG, "Interworking: Unsupported " + "vendor-specific ANQP OUI %06x", + WPA_GET_BE24(pos)); + 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_network_select = 0; + wpa_s->auto_select = !!auto_select; + wpa_s->fetch_all_anqp = 0; + wpa_printf(MSG_DEBUG, "Interworking: Start scan for network " + "selection"); + wpa_s->scan_res_handler = interworking_scan_res_handler; + wpa_s->scan_req = MANUAL_SCAN_REQ; + wpa_supplicant_req_scan(wpa_s, 0, 0); + + return 0; +} + + +static void gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token, + enum gas_query_result result, + const struct wpabuf *adv_proto, + const struct wpabuf *resp, u16 status_code) +{ + struct wpa_supplicant *wpa_s = ctx; + + wpa_msg(wpa_s, MSG_INFO, GAS_RESPONSE_INFO "addr=" MACSTR + " dialog_token=%d status_code=%d resp_len=%d", + MAC2STR(addr), dialog_token, status_code, + resp ? (int) wpabuf_len(resp) : -1); + if (!resp) + return; + + wpabuf_free(wpa_s->last_gas_resp); + wpa_s->last_gas_resp = wpabuf_dup(resp); + if (wpa_s->last_gas_resp == NULL) + return; + os_memcpy(wpa_s->last_gas_addr, addr, ETH_ALEN); + wpa_s->last_gas_dialog_token = dialog_token; +} + + +int gas_send_request(struct wpa_supplicant *wpa_s, const u8 *dst, + const struct wpabuf *adv_proto, + const struct wpabuf *query) +{ + struct wpabuf *buf; + int ret = 0; + int freq; + struct wpa_bss *bss; + int res; + size_t len; + u8 query_resp_len_limit = 0, pame_bi = 0; + + freq = wpa_s->assoc_freq; + bss = wpa_bss_get_bssid(wpa_s, dst); + if (bss) + freq = bss->freq; + if (freq <= 0) + return -1; + + wpa_printf(MSG_DEBUG, "GAS request to " MACSTR " (freq %d MHz)", + MAC2STR(dst), freq); + wpa_hexdump_buf(MSG_DEBUG, "Advertisement Protocol ID", adv_proto); + wpa_hexdump_buf(MSG_DEBUG, "GAS Query", query); + + len = 3 + wpabuf_len(adv_proto) + 2; + if (query) + len += wpabuf_len(query); + buf = gas_build_initial_req(0, len); + if (buf == NULL) + return -1; + + /* Advertisement Protocol IE */ + wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO); + wpabuf_put_u8(buf, 1 + wpabuf_len(adv_proto)); /* Length */ + wpabuf_put_u8(buf, (query_resp_len_limit & 0x7f) | + (pame_bi ? 0x80 : 0)); + wpabuf_put_buf(buf, adv_proto); + + /* GAS Query */ + if (query) { + wpabuf_put_le16(buf, wpabuf_len(query)); + wpabuf_put_buf(buf, query); + } else + wpabuf_put_le16(buf, 0); + + res = gas_query_req(wpa_s->gas, dst, freq, buf, gas_resp_cb, wpa_s); + if (res < 0) { + wpa_printf(MSG_DEBUG, "GAS: Failed to send Query Request"); + ret = -1; + } else + wpa_printf(MSG_DEBUG, "GAS: Query started with dialog token " + "%u", res); + + wpabuf_free(buf); + return ret; +} diff --git a/contrib/wpa/wpa_supplicant/interworking.h b/contrib/wpa/wpa_supplicant/interworking.h new file mode 100644 index 0000000..4a4af82 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/interworking.h @@ -0,0 +1,32 @@ +/* + * Interworking (IEEE 802.11u) + * Copyright (c) 2011-2012, Qualcomm Atheros + * + * This software may be distributed under the terms of the BSD license. + * See README 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 gas_send_request(struct wpa_supplicant *wpa_s, const u8 *dst, + const struct wpabuf *adv_proto, + const struct wpabuf *query); +int interworking_fetch_anqp(struct wpa_supplicant *wpa_s); +void interworking_stop_fetch_anqp(struct wpa_supplicant *wpa_s); +int interworking_select(struct wpa_supplicant *wpa_s, int auto_select); +int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss); +void interworking_start_fetch_anqp(struct wpa_supplicant *wpa_s); +int interworking_home_sp_cred(struct wpa_supplicant *wpa_s, + struct wpa_cred *cred, + struct wpabuf *domain_names); + +#endif /* INTERWORKING_H */ diff --git a/contrib/wpa/wpa_supplicant/main.c b/contrib/wpa/wpa_supplicant/main.c index c0aa59c..19f7ce6 100644 --- a/contrib/wpa/wpa_supplicant/main.c +++ b/contrib/wpa/wpa_supplicant/main.c @@ -2,14 +2,8 @@ * WPA Supplicant / main() function for UNIX like OSes and MinGW * Copyright (c) 2003-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. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -33,7 +27,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 +51,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 */ @@ -65,9 +61,13 @@ static void usage(void) #ifdef CONFIG_DEBUG_SYSLOG printf(" -s = log output to syslog instead of stdout\n"); #endif /* CONFIG_DEBUG_SYSLOG */ +#ifdef CONFIG_DEBUG_LINUX_TRACING + printf(" -T = record to Linux tracing in addition to logging\n"); + printf(" (records all messages regardless of debug verbosity)\n"); +#endif /* CONFIG_DEBUG_LINUX_TRACING */ printf(" -t = include timestamp in debug messages\n" " -h = show this help text\n" - " -L = show license (GPL and BSD)\n" + " -L = show license (BSD)\n" " -o = override driver parameter for new interfaces\n" " -O = override ctrl_interface parameter for new interfaces\n" " -p = driver parameters\n" @@ -101,20 +101,31 @@ static void license(void) } -static void wpa_supplicant_fd_workaround(void) +static void wpa_supplicant_fd_workaround(int start) { #ifdef __linux__ - int s, i; + static int fd[3] = { -1, -1, -1 }; + int i; /* When started from pcmcia-cs scripts, wpa_supplicant might start with * fd 0, 1, and 2 closed. This will cause some issues because many * places in wpa_supplicant are still printing out to stdout. As a * workaround, make sure that fd's 0, 1, and 2 are not used for other * sockets. */ - for (i = 0; i < 3; i++) { - s = open("/dev/null", O_RDWR); - if (s > 2) { - close(s); - break; + if (start) { + for (i = 0; i < 3; i++) { + fd[i] = open("/dev/null", O_RDWR); + if (fd[i] > 2) { + close(fd[i]); + fd[i] = -1; + break; + } + } + } else { + for (i = 0; i < 3; i++) { + if (fd[i] >= 0) { + close(fd[i]); + fd[i] = -1; + } } } #endif /* __linux__ */ @@ -140,10 +151,11 @@ int main(int argc, char *argv[]) return -1; iface_count = 1; - wpa_supplicant_fd_workaround(); + wpa_supplicant_fd_workaround(1); 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:qsTtuvW"); if (c < 0) break; switch (c) { @@ -172,6 +184,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; @@ -215,6 +230,11 @@ int main(int argc, char *argv[]) params.wpa_debug_syslog++; break; #endif /* CONFIG_DEBUG_SYSLOG */ +#ifdef CONFIG_DEBUG_LINUX_TRACING + case 'T': + params.wpa_debug_tracing++; + break; +#endif /* CONFIG_DEBUG_LINUX_TRACING */ case 't': params.wpa_debug_timestamp++; break; @@ -232,8 +252,8 @@ int main(int argc, char *argv[]) break; case 'N': iface_count++; - iface = os_realloc(ifaces, iface_count * - sizeof(struct wpa_interface)); + iface = os_realloc_array(ifaces, iface_count, + sizeof(struct wpa_interface)); if (iface == NULL) goto out; ifaces = iface; @@ -253,6 +273,9 @@ int main(int argc, char *argv[]) wpa_printf(MSG_ERROR, "Failed to initialize wpa_supplicant"); exitcode = -1; goto out; + } else { + wpa_printf(MSG_INFO, "Successfully initialized " + "wpa_supplicant"); } for (i = 0; exitcode == 0 && i < iface_count; i++) { @@ -276,6 +299,7 @@ int main(int argc, char *argv[]) wpa_supplicant_deinit(global); out: + wpa_supplicant_fd_workaround(0); os_free(ifaces); os_free(params.pid_file); diff --git a/contrib/wpa/wpa_supplicant/main_none.c b/contrib/wpa/wpa_supplicant/main_none.c index 993338a..010c30a3 100644 --- a/contrib/wpa/wpa_supplicant/main_none.c +++ b/contrib/wpa/wpa_supplicant/main_none.c @@ -2,14 +2,8 @@ * WPA Supplicant / Example program entrypoint * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/contrib/wpa/wpa_supplicant/mlme.c b/contrib/wpa/wpa_supplicant/mlme.c deleted file mode 100644 index eb60ac5..0000000 --- a/contrib/wpa/wpa_supplicant/mlme.c +++ /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(¶ms, 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, ¶ms); -} - - -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/contrib/wpa/wpa_supplicant/mlme.h b/contrib/wpa/wpa_supplicant/mlme.h deleted file mode 100644 index 5db3665..0000000 --- a/contrib/wpa/wpa_supplicant/mlme.h +++ /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 */ diff --git a/contrib/wpa/wpa_supplicant/nfc_pw_token.c b/contrib/wpa/wpa_supplicant/nfc_pw_token.c new file mode 100644 index 0000000..11afb5b --- /dev/null +++ b/contrib/wpa/wpa_supplicant/nfc_pw_token.c @@ -0,0 +1,83 @@ +/* + * nfc_pw_token - Tool for building NFC password tokens for WPS + * Copyright (c) 2012, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "utils/common.h" +#include "crypto/random.h" +#include "wpa_supplicant_i.h" +#include "config.h" +#include "wps_supplicant.h" + + +static void print_bin(const char *title, const struct wpabuf *buf) +{ + size_t i, len; + const u8 *pos; + + if (buf == NULL) + return; + + printf("%s=", title); + + pos = wpabuf_head(buf); + len = wpabuf_len(buf); + for (i = 0; i < len; i++) + printf("%02X", *pos++); + + printf("\n"); +} + + +int main(int argc, char *argv[]) +{ + struct wpa_supplicant wpa_s; + int ret = -1; + struct wpabuf *buf = NULL, *ndef = NULL; + char txt[1000]; + + if (os_program_init()) + return -1; + random_init(NULL); + + os_memset(&wpa_s, 0, sizeof(wpa_s)); + wpa_s.conf = os_zalloc(sizeof(*wpa_s.conf)); + if (wpa_s.conf == NULL) + goto fail; + + buf = wpas_wps_nfc_token(&wpa_s, 0); + if (buf == NULL) + goto fail; + + ndef = ndef_build_wifi(buf); + if (ndef == NULL) + goto fail; + + wpa_snprintf_hex_uppercase(txt, sizeof(txt), wpabuf_head(buf), + wpabuf_len(buf)); + printf("#WPS=%s\n", txt); + + wpa_snprintf_hex_uppercase(txt, sizeof(txt), wpabuf_head(ndef), + wpabuf_len(ndef)); + printf("#NDEF=%s\n", txt); + + printf("wps_nfc_dev_pw_id=%d\n", wpa_s.conf->wps_nfc_dev_pw_id); + print_bin("wps_nfc_dh_pubkey", wpa_s.conf->wps_nfc_dh_pubkey); + print_bin("wps_nfc_dh_privkey", wpa_s.conf->wps_nfc_dh_privkey); + print_bin("wps_nfc_dev_pw", wpa_s.conf->wps_nfc_dev_pw); + + ret = 0; +fail: + wpabuf_free(ndef); + wpabuf_free(buf); + wpa_config_free(wpa_s.conf); + random_deinit(); + os_program_deinit(); + + return ret; +} diff --git a/contrib/wpa/wpa_supplicant/notify.c b/contrib/wpa/wpa_supplicant/notify.c index ac65b4f..9251f62 100644 --- a/contrib/wpa/wpa_supplicant/notify.c +++ b/contrib/wpa/wpa_supplicant/notify.c @@ -2,14 +2,8 @@ * wpa_supplicant - Event notifications * 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. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -22,8 +16,11 @@ #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 +78,28 @@ 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 */ +} + + +void wpas_notify_disconnect_reason(struct wpa_supplicant *wpa_s) +{ + wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_DISCONNECT_REASON); } @@ -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 */ } @@ -258,6 +323,9 @@ void wpas_notify_bss_rsnie_changed(struct wpa_supplicant *wpa_s, void wpas_notify_bss_wps_changed(struct wpa_supplicant *wpa_s, unsigned int id) { +#ifdef CONFIG_WPS + wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_WPS, id); +#endif /* CONFIG_WPS */ } @@ -337,3 +405,226 @@ 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); +} + + +void wpas_notify_preq(struct wpa_supplicant *wpa_s, + const u8 *addr, const u8 *dst, const u8 *bssid, + const u8 *ie, size_t ie_len, u32 ssi_signal) +{ +#ifdef CONFIG_AP + wpas_dbus_signal_preq(wpa_s, addr, dst, bssid, ie, ie_len, ssi_signal); +#endif /* CONFIG_AP */ +} + + +void wpas_notify_eap_status(struct wpa_supplicant *wpa_s, const char *status, + const char *parameter) +{ + wpas_dbus_signal_eap_status(wpa_s, status, parameter); +} diff --git a/contrib/wpa/wpa_supplicant/notify.h b/contrib/wpa/wpa_supplicant/notify.h index 2e70bdb..58675ac 100644 --- a/contrib/wpa/wpa_supplicant/notify.h +++ b/contrib/wpa/wpa_supplicant/notify.h @@ -2,19 +2,15 @@ * wpa_supplicant - Event notifications * 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. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef NOTIFY_H #define NOTIFY_H +#include "p2p/p2p.h" + struct wps_credential; struct wps_event_m2d; struct wps_event_fail; @@ -26,13 +22,19 @@ void wpas_notify_iface_removed(struct wpa_supplicant *wpa_s); void wpas_notify_state_changed(struct wpa_supplicant *wpa_s, enum wpa_states new_state, enum wpa_states old_state); +void wpas_notify_disconnect_reason(struct wpa_supplicant *wpa_s); void wpas_notify_network_changed(struct wpa_supplicant *wpa_s); void wpas_notify_ap_scan_changed(struct wpa_supplicant *wpa_s); void wpas_notify_bssid_changed(struct wpa_supplicant *wpa_s); +void wpas_notify_auth_changed(struct wpa_supplicant *wpa_s); void wpas_notify_network_enabled_changed(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); void wpas_notify_network_selected(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); +void wpas_notify_network_request(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + enum wpa_ctrl_req_type rtype, + const char *default_txt); void wpas_notify_scanning(struct wpa_supplicant *wpa_s); void wpas_notify_scan_done(struct wpa_supplicant *wpa_s, int success); void wpas_notify_scan_results(struct wpa_supplicant *wpa_s); @@ -78,4 +80,52 @@ 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); +void wpas_notify_preq(struct wpa_supplicant *wpa_s, + const u8 *addr, const u8 *dst, const u8 *bssid, + const u8 *ie, size_t ie_len, u32 ssi_signal); +void wpas_notify_eap_status(struct wpa_supplicant *wpa_s, const char *status, + const char *parameter); + #endif /* NOTIFY_H */ diff --git a/contrib/wpa/wpa_supplicant/offchannel.c b/contrib/wpa/wpa_supplicant/offchannel.c new file mode 100644 index 0000000..856eca7 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/offchannel.c @@ -0,0 +1,397 @@ +/* + * wpa_supplicant - Off-channel Action frame TX/RX + * Copyright (c) 2009-2010, Atheros Communications + * Copyright (c) 2011, Qualcomm Atheros + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "utils/eloop.h" +#include "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); + } +} + + +/** + * offchannel_send_action_tx_status - TX status callback + * @wpa_s: Pointer to wpa_supplicant data + * @dst: Destination MAC address of the transmitted Action frame + * @data: Transmitted frame payload + * @data_len: Length of @data in bytes + * @result: TX status + * + * This function is called whenever the driver indicates a TX status event for + * a frame sent by offchannel_send_action() using wpa_drv_send_action(). + */ +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); + } +} + + +/** + * offchannel_send_action - Request off-channel Action frame TX + * @wpa_s: Pointer to wpa_supplicant data + * @freq: The frequency in MHz indicating the channel on which the frame is to + * transmitted or 0 for the current channel (only if associated) + * @dst: Action frame destination MAC address + * @src: Action frame source MAC address + * @bssid: Action frame BSSID + * @buf: Frame to transmit starting from the Category field + * @len: Length of @buf in bytes + * @wait_time: Wait time for response in milliseconds + * @tx_cb: Callback function for indicating TX status or %NULL for now callback + * @no_cck: Whether CCK rates are to be disallowed for TX rate selection + * Returns: 0 on success or -1 on failure + * + * This function is used to request an Action frame to be transmitted on the + * current operating channel or on another channel (off-channel). The actual + * frame transmission will be delayed until the driver is ready on the specified + * channel. The @wait_time parameter can be used to request the driver to remain + * awake on the channel to wait for a response. + */ +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; +} + + +/** + * offchannel_send_send_action_done - Notify completion of Action frame sequence + * @wpa_s: Pointer to wpa_supplicant data + * + * This function can be used to cancel a wait for additional response frames on + * the channel that was used with offchannel_send_action(). + */ +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; + } +} + + +/** + * offchannel_remain_on_channel_cb - Remain-on-channel callback function + * @wpa_s: Pointer to wpa_supplicant data + * @freq: Frequency (in MHz) of the selected channel + * @duration: Duration of the remain-on-channel operation in milliseconds + * + * This function is called whenever the driver notifies beginning of a + * remain-on-channel operation. + */ +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); +} + + +/** + * offchannel_cancel_remain_on_channel_cb - Remain-on-channel stopped callback + * @wpa_s: Pointer to wpa_supplicant data + * @freq: Frequency (in MHz) of the selected channel + * + * This function is called whenever the driver notifies termination of a + * remain-on-channel operation. + */ +void offchannel_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s, + unsigned int freq) +{ + wpa_s->off_channel_freq = 0; +} + + +/** + * offchannel_pending_action_tx - Check whether there is a pending Action TX + * @wpa_s: Pointer to wpa_supplicant data + * Returns: Pointer to pending frame or %NULL if no pending operation + * + * This function can be used to check whether there is a pending Action frame TX + * operation. The returned pointer should be used only for checking whether it + * is %NULL (no pending frame) or to print the pointer value in debug + * information (i.e., the pointer should not be dereferenced). + */ +const void * offchannel_pending_action_tx(struct wpa_supplicant *wpa_s) +{ + return wpa_s->pending_action_tx; +} + + +/** + * offchannel_clear_pending_action_tx - Clear pending Action frame TX + * @wpa_s: Pointer to wpa_supplicant data + */ +void offchannel_clear_pending_action_tx(struct wpa_supplicant *wpa_s) +{ + wpabuf_free(wpa_s->pending_action_tx); + wpa_s->pending_action_tx = NULL; +} + + +/** + * offchannel_deinit - Deinit off-channel operations + * @wpa_s: Pointer to wpa_supplicant data + * + * This function is used to free up any allocated resources for off-channel + * operations. + */ +void offchannel_deinit(struct wpa_supplicant *wpa_s) +{ + offchannel_clear_pending_action_tx(wpa_s); + eloop_cancel_timeout(wpas_send_action_cb, wpa_s, NULL); +} diff --git a/contrib/wpa/wpa_supplicant/offchannel.h b/contrib/wpa/wpa_supplicant/offchannel.h new file mode 100644 index 0000000..0ad7e18 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/offchannel.h @@ -0,0 +1,35 @@ +/* + * wpa_supplicant - Off-channel Action frame TX/RX + * Copyright (c) 2009-2010, Atheros Communications + * Copyright (c) 2011, Qualcomm Atheros + * + * This software may be distributed under the terms of the BSD license. + * See README 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); +const void * offchannel_pending_action_tx(struct wpa_supplicant *wpa_s); +void offchannel_clear_pending_action_tx(struct wpa_supplicant *wpa_s); + +#endif /* OFFCHANNEL_H */ diff --git a/contrib/wpa/wpa_supplicant/p2p_supplicant.c b/contrib/wpa/wpa_supplicant/p2p_supplicant.c new file mode 100644 index 0000000..0a09b00 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/p2p_supplicant.c @@ -0,0 +1,5484 @@ +/* + * wpa_supplicant - P2P + * Copyright (c) 2009-2010, Atheros Communications + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#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 + +#define P2P_AUTO_PD_SCAN_ATTEMPTS 5 + +#ifndef P2P_MAX_CLIENT_IDLE +/* + * How many seconds to try to reconnect to the GO when connection in P2P client + * role has been lost. + */ +#define P2P_MAX_CLIENT_IDLE 10 +#endif /* P2P_MAX_CLIENT_IDLE */ + +#ifndef P2P_MAX_INITIAL_CONN_WAIT +/* + * How many seconds to wait for initial 4-way handshake to get completed after + * WPS provisioning step. + */ +#define P2P_MAX_INITIAL_CONN_WAIT 10 +#endif /* P2P_MAX_INITIAL_CONN_WAIT */ + +#ifndef P2P_CONCURRENT_SEARCH_DELAY +#define P2P_CONCURRENT_SEARCH_DELAY 500 +#endif /* P2P_CONCURRENT_SEARCH_DELAY */ + +enum p2p_group_removal_reason { + P2P_GROUP_REMOVAL_UNKNOWN, + P2P_GROUP_REMOVAL_SILENT, + P2P_GROUP_REMOVAL_FORMATION_FAILED, + P2P_GROUP_REMOVAL_REQUESTED, + P2P_GROUP_REMOVAL_IDLE_TIMEOUT, + P2P_GROUP_REMOVAL_UNAVAILABLE, + P2P_GROUP_REMOVAL_GO_ENDING_SESSION +}; + + +static void wpas_p2p_long_listen_timeout(void *eloop_ctx, void *timeout_ctx); +static struct wpa_supplicant * +wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated, + int go); +static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s); +static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq); +static void wpas_p2p_join_scan(void *eloop_ctx, void *timeout_ctx); +static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr, + const u8 *dev_addr, enum p2p_wps_method wps_method, + int auto_join); +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_fallback_to_go_neg(struct wpa_supplicant *wpa_s, + int group_added); +static int wpas_p2p_stop_find_oper(struct wpa_supplicant *wpa_s); + + +static void wpas_p2p_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->age, 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, u16 pw_id) +{ + struct wpa_supplicant *wpa_s = ctx; + struct wpa_supplicant *ifs; + struct wpa_driver_scan_params params; + int ret; + struct wpabuf *wps_ie, *ies; + int social_channels[] = { 2412, 2437, 2462, 0, 0 }; + size_t ielen; + + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return -1; + + for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) { + if (ifs->sta_scan_pending && + wpas_p2p_in_progress(wpa_s) == 2) { + wpa_printf(MSG_DEBUG, "Delaying P2P scan to allow " + "pending station mode scan to be " + "completed on interface %s", ifs->ifname); + wpa_s->global->p2p_cb_on_scan_complete = 1; + wpa_supplicant_req_scan(ifs, 0, 0); + return 1; + } + } + + os_memset(¶ms, 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(pw_id, &wpa_s->wps->dev, + wpa_s->wps->uuid, WPS_REQ_ENROLLEE, + num_req_dev_types, req_dev_types); + if (wps_ie == NULL) + return -1; + + 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_SOCIAL_PLUS_ONE: + social_channels[3] = freq; + params.freqs = social_channels; + break; + } + + ret = wpa_drv_scan(wpa_s, ¶ms); + + wpabuf_free(ies); + + if (ret) { + for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) { + if (ifs->scanning || + ifs->scan_res_handler == wpas_p2p_scan_res_handler) { + wpa_s->global->p2p_cb_on_scan_complete = 1; + ret = 1; + break; + } + } + } else + wpa_s->scan_res_handler = wpas_p2p_scan_res_handler; + + 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 int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s, + enum p2p_group_removal_reason removal_reason) +{ + struct wpa_ssid *ssid; + char *gtype; + const char *reason; + + ssid = wpa_s->current_ssid; + if (ssid == NULL) { + /* + * The current SSID was not known, but there may still be a + * pending P2P group interface waiting for provisioning or a + * P2P group that is trying to reconnect. + */ + ssid = wpa_s->conf->ssid; + while (ssid) { + if (ssid->p2p_group && ssid->disabled != 2) + break; + ssid = ssid->next; + } + if (ssid == NULL && + wpa_s->p2p_group_interface == NOT_P2P_GROUP_INTERFACE) + { + wpa_printf(MSG_ERROR, "P2P: P2P group interface " + "not found"); + return -1; + } + } + if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_GO) + gtype = "GO"; + else if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT || + (ssid && ssid->mode == WPAS_MODE_INFRA)) { + wpa_s->reassociate = 0; + wpa_s->disconnected = 1; + 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 (removal_reason) { + case P2P_GROUP_REMOVAL_REQUESTED: + reason = " reason=REQUESTED"; + break; + case P2P_GROUP_REMOVAL_FORMATION_FAILED: + reason = " reason=FORMATION_FAILED"; + break; + case P2P_GROUP_REMOVAL_IDLE_TIMEOUT: + reason = " reason=IDLE"; + break; + case P2P_GROUP_REMOVAL_UNAVAILABLE: + reason = " reason=UNAVAILABLE"; + break; + case P2P_GROUP_REMOVAL_GO_ENDING_SESSION: + reason = " reason=GO_ENDING_SESSION"; + break; + default: + reason = ""; + break; + } + if (removal_reason != P2P_GROUP_REMOVAL_SILENT) { + wpa_msg(wpa_s->parent, MSG_INFO, + P2P_EVENT_GROUP_REMOVED "%s %s%s", + wpa_s->ifname, gtype, reason); + } + + if (eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL) > 0) + wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group idle timeout"); + + if (removal_reason != P2P_GROUP_REMOVAL_SILENT && 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, 0); + wpa_s = global->ifaces; + if (wpa_s && ifname) + wpa_drv_if_remove(wpa_s, type, ifname); + os_free(ifname); + return 1; + } + + 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); + wpa_s->sta_scan_pending = 0; + } 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); + + return 0; +} + + +static int wpas_p2p_persistent_group(struct wpa_supplicant *wpa_s, + u8 *go_dev_addr, + const u8 *ssid, size_t ssid_len) +{ + struct wpa_bss *bss; + const u8 *bssid; + struct wpabuf *p2p; + u8 group_capab; + const u8 *addr; + + if (wpa_s->go_params) + bssid = wpa_s->go_params->peer_interface_addr; + else + bssid = wpa_s->bssid; + + bss = wpa_bss_get(wpa_s, bssid, ssid, ssid_len); + if (bss == NULL) { + 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; + int found = 0; + + 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) + continue; + + if (i == s->num_p2p_clients - 1) + return; /* already the most recent entry */ + + /* move the entry to mark it most recent */ + os_memmove(s->p2p_client_list + i * ETH_ALEN, + s->p2p_client_list + (i + 1) * ETH_ALEN, + (s->num_p2p_clients - i - 1) * ETH_ALEN); + os_memcpy(s->p2p_client_list + + (s->num_p2p_clients - 1) * ETH_ALEN, addr, ETH_ALEN); + found = 1; + break; + } + + if (!found && s->num_p2p_clients < P2P_MAX_STORED_CLIENTS) { + n = os_realloc_array(s->p2p_client_list, + s->num_p2p_clients + 1, ETH_ALEN); + 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++; + } else if (!found) { + /* Not enough room for an additional entry - drop the oldest + * entry */ + os_memmove(s->p2p_client_list, + s->p2p_client_list + ETH_ALEN, + (s->num_p2p_clients - 1) * ETH_ALEN); + os_memcpy(s->p2p_client_list + + (s->num_p2p_clients - 1) * ETH_ALEN, + addr, ETH_ALEN); + } + +#ifndef CONFIG_NO_CONFIG_WRITE + if (wpa_s->parent->conf->update_config && + wpa_config_write(wpa_s->parent->confname, wpa_s->parent->conf)) + wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration"); +#endif /* CONFIG_NO_CONFIG_WRITE */ +} + + +static void wpas_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, + P2P_GROUP_REMOVAL_FORMATION_FAILED); + 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->p2p_fallback_to_go_neg) { + wpa_s->pending_pd_before_join = 0; + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: No ACK for PD Req " + "during p2p_connect-auto"); + wpas_p2p_fallback_to_go_neg(wpa_s, 0); + return; + } +} + + +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; + if (os_strlen(params->passphrase) > 0) { + 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, + MAC2STR(wpa_s->global->p2p_dev_addr), + params->persistent_group ? " [PERSISTENT]" : + ""); + } else { + char psk[65]; + wpa_snprintf_hex(psk, sizeof(psk), params->psk, + sizeof(params->psk)); + wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED + "%s GO ssid=\"%s\" freq=%d psk=%s " + "go_dev_addr=" MACSTR "%s", wpa_s->ifname, + wpa_ssid_txt(ssid->ssid, ssid->ssid_len), + ssid->frequency, psk, + MAC2STR(wpa_s->global->p2p_dev_addr), + params->persistent_group ? " [PERSISTENT]" : + ""); + } + + if (params->persistent_group) + 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, 0); + os_free(wpa_s->go_params); + wpa_s->go_params = NULL; +} + + +static void wpas_start_wps_go(struct wpa_supplicant *wpa_s, + struct p2p_go_neg_results *params, + int group_formation) +{ + struct wpa_ssid *ssid; + + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Starting GO"); + if (wpas_copy_go_neg_results(wpa_s, params) < 0) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Could not copy GO Negotiation " + "results"); + return; + } + + ssid = wpa_config_add_network(wpa_s->conf); + if (ssid == NULL) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Could not add network for GO"); + return; + } + + wpa_s->show_group_started = 0; + + wpa_config_set_network_defaults(ssid); + ssid->temporary = 1; + ssid->p2p_group = 1; + ssid->p2p_persistent_group = params->persistent_group; + ssid->mode = group_formation ? WPAS_MODE_P2P_GROUP_FORMATION : + WPAS_MODE_P2P_GO; + ssid->frequency = params->freq; + ssid->ht40 = params->ht40; + ssid->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; + if (os_strlen(params->passphrase) > 0) { + ssid->passphrase = os_strdup(params->passphrase); + if (ssid->passphrase == NULL) { + wpa_msg(wpa_s, MSG_ERROR, "P2P: Failed to copy " + "passphrase for GO"); + wpa_config_remove_network(wpa_s->conf, ssid->id); + return; + } + } else + ssid->passphrase = NULL; + ssid->psk_set = params->psk_set; + if (ssid->psk_set) + os_memcpy(ssid->psk, params->psk, sizeof(ssid->psk)); + else if (ssid->passphrase) + wpa_config_update_psk(ssid); + ssid->ap_max_inactivity = wpa_s->parent->conf->p2p_go_max_inactivity; + + wpa_s->ap_configured_cb = p2p_go_configured; + wpa_s->ap_configured_cb_ctx = wpa_s; + wpa_s->ap_configured_cb_data = wpa_s->go_params; + wpa_s->connect_without_scan = ssid; + wpa_s->reassociate = 1; + wpa_s->disconnected = 0; + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Request scan (that will be skipped) to " + "start GO)"); + wpa_supplicant_req_scan(wpa_s, 0, 0); +} + + +static void wpas_p2p_clone_config(struct wpa_supplicant *dst, + const struct wpa_supplicant *src) +{ + struct wpa_config *d; + const struct wpa_config *s; + + d = dst->conf; + s = src->conf; + +#define C(n) if (s->n) d->n = os_strdup(s->n) + C(device_name); + C(manufacturer); + C(model_name); + C(model_number); + C(serial_number); + C(config_methods); +#undef C + + os_memcpy(d->device_type, s->device_type, WPS_DEV_TYPE_LEN); + os_memcpy(d->sec_device_type, s->sec_device_type, + sizeof(d->sec_device_type)); + d->num_sec_device_types = s->num_sec_device_types; + + d->p2p_group_idle = s->p2p_group_idle; + d->p2p_intra_bss = s->p2p_intra_bss; + d->persistent_reconnect = s->persistent_reconnect; + d->max_num_sta = s->max_num_sta; + d->pbc_in_m1 = s->pbc_in_m1; +} + + +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; + } + + if (wpa_s->p2p_go_ht40) + res->ht40 = 1; + + wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_SUCCESS); + wpas_notify_p2p_go_neg_completed(wpa_s, res); + + if (res->role_go && wpa_s->p2p_persistent_id >= 0) { + struct wpa_ssid *ssid; + ssid = wpa_config_get_network(wpa_s->conf, + wpa_s->p2p_persistent_id); + if (ssid && ssid->disabled == 2 && + ssid->mode == WPAS_MODE_P2P_GO && ssid->passphrase) { + size_t len = os_strlen(ssid->passphrase); + wpa_printf(MSG_DEBUG, "P2P: Override passphrase based " + "on requested persistent group"); + os_memcpy(res->passphrase, ssid->passphrase, len); + res->passphrase[len] = '\0'; + } + } + + if (wpa_s->create_p2p_iface) { + struct wpa_supplicant *group_wpa_s = + wpas_p2p_init_group_interface(wpa_s, res->role_go); + if (group_wpa_s == NULL) { + wpas_p2p_remove_pending_group_interface(wpa_s); + 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) +{ +#ifndef CONFIG_NO_STDOUT_DEBUG + 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); +#endif /* CONFIG_NO_STDOUT_DEBUG */ + + wpas_notify_p2p_device_found(ctx, info->p2p_device_addr, new_device); +} + + +static void wpas_dev_lost(void *ctx, const u8 *dev_addr) +{ + struct wpa_supplicant *wpa_s = ctx; + + wpa_msg(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), 1); +} + + +/* + * DNS Header section is used only to calculate compression pointers, so the + * contents of this data does not matter, but the length needs to be reserved + * in the virtual packet. + */ +#define DNS_HEADER_LEN 12 + +/* + * 27-octet in-memory packet from P2P specification containing two implied + * queries for _tcp.lcoal. PTR IN and _udp.local. PTR IN + */ +#define P2P_SD_IN_MEMORY_LEN 27 + +static int p2p_sd_dns_uncompress_label(char **upos, char *uend, u8 *start, + u8 **spos, const u8 *end) +{ + while (*spos < end) { + u8 val = ((*spos)[0] & 0xc0) >> 6; + int len; + + if (val == 1 || val == 2) { + /* These are reserved values in RFC 1035 */ + wpa_printf(MSG_DEBUG, "P2P: Invalid domain name " + "sequence starting with 0x%x", val); + return -1; + } + + if (val == 3) { + u16 offset; + u8 *spos_tmp; + + /* Offset */ + if (*spos + 2 > end) { + wpa_printf(MSG_DEBUG, "P2P: No room for full " + "DNS offset field"); + return -1; + } + + offset = (((*spos)[0] & 0x3f) << 8) | (*spos)[1]; + if (offset >= *spos - start) { + wpa_printf(MSG_DEBUG, "P2P: Invalid DNS " + "pointer offset %u", offset); + return -1; + } + + (*spos) += 2; + spos_tmp = start + offset; + return p2p_sd_dns_uncompress_label(upos, uend, start, + &spos_tmp, + *spos - 2); + } + + /* Label */ + len = (*spos)[0] & 0x3f; + if (len == 0) + return 0; + + (*spos)++; + if (*spos + len > end) { + wpa_printf(MSG_DEBUG, "P2P: Invalid domain name " + "sequence - no room for label with length " + "%u", len); + return -1; + } + + if (*upos + len + 2 > uend) + return -2; + + os_memcpy(*upos, *spos, len); + *spos += len; + *upos += len; + (*upos)[0] = '.'; + (*upos)++; + (*upos)[0] = '\0'; + } + + return 0; +} + + +/* Uncompress domain names per RFC 1035 using the P2P SD in-memory packet. + * Returns -1 on parsing error (invalid input sequence), -2 if output buffer is + * not large enough */ +static int p2p_sd_dns_uncompress(char *buf, size_t buf_len, const u8 *msg, + size_t msg_len, size_t offset) +{ + /* 27-octet in-memory packet from P2P specification */ + const char *prefix = "\x04_tcp\x05local\x00\x00\x0C\x00\x01" + "\x04_udp\xC0\x11\x00\x0C\x00\x01"; + u8 *tmp, *end, *spos; + char *upos, *uend; + int ret = 0; + + if (buf_len < 2) + return -1; + if (offset > msg_len) + return -1; + + tmp = os_malloc(DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN + msg_len); + if (tmp == NULL) + return -1; + spos = tmp + DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN; + end = spos + msg_len; + spos += offset; + + os_memset(tmp, 0, DNS_HEADER_LEN); + os_memcpy(tmp + DNS_HEADER_LEN, prefix, P2P_SD_IN_MEMORY_LEN); + os_memcpy(tmp + DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN, msg, msg_len); + + upos = buf; + uend = buf + buf_len; + + ret = p2p_sd_dns_uncompress_label(&upos, uend, tmp, &spos, end); + if (ret) { + os_free(tmp); + return ret; + } + + if (upos == buf) { + upos[0] = '.'; + upos[1] = '\0'; + } else if (upos[-1] == '.') + upos[-1] = '\0'; + + os_free(tmp); + return 0; +} + + +static struct p2p_srv_bonjour * +wpas_p2p_service_get_bonjour(struct wpa_supplicant *wpa_s, + const struct wpabuf *query) +{ + struct p2p_srv_bonjour *bsrv; + size_t len; + + len = wpabuf_len(query); + dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour, + struct p2p_srv_bonjour, list) { + if (len == wpabuf_len(bsrv->query) && + os_memcmp(wpabuf_head(query), wpabuf_head(bsrv->query), + len) == 0) + return bsrv; + } + return NULL; +} + + +static struct p2p_srv_upnp * +wpas_p2p_service_get_upnp(struct wpa_supplicant *wpa_s, u8 version, + const char *service) +{ + struct p2p_srv_upnp *usrv; + + dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp, + struct p2p_srv_upnp, list) { + if (version == usrv->version && + os_strcmp(service, usrv->service) == 0) + return usrv; + } + return NULL; +} + + +static void wpas_sd_add_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 int match_bonjour_query(struct p2p_srv_bonjour *bsrv, const u8 *query, + size_t query_len) +{ + char str_rx[256], str_srv[256]; + + if (query_len < 3 || wpabuf_len(bsrv->query) < 3) + return 0; /* Too short to include DNS Type and Version */ + if (os_memcmp(query + query_len - 3, + wpabuf_head_u8(bsrv->query) + wpabuf_len(bsrv->query) - 3, + 3) != 0) + return 0; /* Mismatch in DNS Type or Version */ + if (query_len == wpabuf_len(bsrv->query) && + os_memcmp(query, wpabuf_head(bsrv->query), query_len - 3) == 0) + return 1; /* Binary match */ + + if (p2p_sd_dns_uncompress(str_rx, sizeof(str_rx), query, query_len - 3, + 0)) + return 0; /* Failed to uncompress query */ + if (p2p_sd_dns_uncompress(str_srv, sizeof(str_srv), + wpabuf_head(bsrv->query), + wpabuf_len(bsrv->query) - 3, 0)) + return 0; /* Failed to uncompress service */ + + return os_strcmp(str_rx, str_srv) == 0; +} + + +static void wpas_sd_req_bonjour(struct wpa_supplicant *wpa_s, + struct wpabuf *resp, u8 srv_trans_id, + const u8 *query, size_t query_len) +{ + struct p2p_srv_bonjour *bsrv; + u8 *len_pos; + int matches = 0; + + wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for Bonjour", + query, query_len); + if (dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) { + wpa_printf(MSG_DEBUG, "P2P: Bonjour protocol not available"); + wpas_sd_add_proto_not_avail(resp, P2P_SERV_BONJOUR, + srv_trans_id); + return; + } + + if (query_len == 0) { + wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id); + return; + } + + dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour, + struct p2p_srv_bonjour, list) { + if (!match_bonjour_query(bsrv, query, query_len)) + continue; + + if (wpabuf_tailroom(resp) < + 5 + query_len + wpabuf_len(bsrv->resp)) + return; + + matches++; + + /* Length (to be filled) */ + len_pos = wpabuf_put(resp, 2); + wpabuf_put_u8(resp, P2P_SERV_BONJOUR); + wpabuf_put_u8(resp, srv_trans_id); + + /* Status Code */ + wpabuf_put_u8(resp, P2P_SD_SUCCESS); + wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service", + wpabuf_head(bsrv->resp), + wpabuf_len(bsrv->resp)); + + /* Response Data */ + wpabuf_put_data(resp, query, query_len); /* Key */ + wpabuf_put_buf(resp, bsrv->resp); /* Value */ + + WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2); + } + + if (matches == 0) { + wpa_printf(MSG_DEBUG, "P2P: Requested Bonjour service not " + "available"); + if (wpabuf_tailroom(resp) < 5) + return; + + /* Length (to be filled) */ + len_pos = wpabuf_put(resp, 2); + wpabuf_put_u8(resp, P2P_SERV_BONJOUR); + wpabuf_put_u8(resp, srv_trans_id); + + /* Status Code */ + wpabuf_put_u8(resp, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE); + /* Response Data: empty */ + WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - + 2); + } +} + + +static void wpas_sd_all_upnp(struct wpa_supplicant *wpa_s, + struct wpabuf *resp, u8 srv_trans_id) +{ + struct p2p_srv_upnp *usrv; + u8 *len_pos; + + wpa_printf(MSG_DEBUG, "P2P: SD Request for all UPnP services"); + + if (dl_list_empty(&wpa_s->global->p2p_srv_upnp)) { + wpa_printf(MSG_DEBUG, "P2P: UPnP protocol not available"); + return; + } + + dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp, + struct p2p_srv_upnp, list) { + if (wpabuf_tailroom(resp) < 5 + 1 + os_strlen(usrv->service)) + return; + + /* Length (to be filled) */ + len_pos = wpabuf_put(resp, 2); + wpabuf_put_u8(resp, P2P_SERV_UPNP); + wpabuf_put_u8(resp, srv_trans_id); + + /* Status Code */ + wpabuf_put_u8(resp, P2P_SD_SUCCESS); + /* Response Data */ + wpabuf_put_u8(resp, usrv->version); + wpa_printf(MSG_DEBUG, "P2P: Matching UPnP Service: %s", + usrv->service); + wpabuf_put_str(resp, usrv->service); + WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - + 2); + } +} + + +static void wpas_sd_req_upnp(struct wpa_supplicant *wpa_s, + struct wpabuf *resp, u8 srv_trans_id, + const u8 *query, size_t query_len) +{ + struct p2p_srv_upnp *usrv; + u8 *len_pos; + u8 version; + char *str; + int count = 0; + + wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for UPnP", + query, query_len); + + if (dl_list_empty(&wpa_s->global->p2p_srv_upnp)) { + wpa_printf(MSG_DEBUG, "P2P: UPnP protocol not available"); + wpas_sd_add_proto_not_avail(resp, P2P_SERV_UPNP, + srv_trans_id); + return; + } + + if (query_len == 0) { + wpas_sd_all_upnp(wpa_s, resp, srv_trans_id); + return; + } + + if (wpabuf_tailroom(resp) < 5) + return; + + /* Length (to be filled) */ + len_pos = wpabuf_put(resp, 2); + wpabuf_put_u8(resp, P2P_SERV_UPNP); + wpabuf_put_u8(resp, srv_trans_id); + + version = query[0]; + str = os_malloc(query_len); + if (str == NULL) + return; + os_memcpy(str, query + 1, query_len - 1); + str[query_len - 1] = '\0'; + + dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp, + struct p2p_srv_upnp, list) { + if (version != usrv->version) + continue; + + if (os_strcmp(str, "ssdp:all") != 0 && + os_strstr(usrv->service, str) == NULL) + continue; + + if (wpabuf_tailroom(resp) < 2) + break; + if (count == 0) { + /* Status Code */ + wpabuf_put_u8(resp, P2P_SD_SUCCESS); + /* Response Data */ + wpabuf_put_u8(resp, version); + } else + wpabuf_put_u8(resp, ','); + + count++; + + wpa_printf(MSG_DEBUG, "P2P: Matching UPnP Service: %s", + usrv->service); + if (wpabuf_tailroom(resp) < os_strlen(usrv->service)) + break; + wpabuf_put_str(resp, usrv->service); + } + os_free(str); + + if (count == 0) { + wpa_printf(MSG_DEBUG, "P2P: Requested UPnP service not " + "available"); + /* Status Code */ + wpabuf_put_u8(resp, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE); + /* Response Data: empty */ + } + + WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2); +} + + +#ifdef CONFIG_WIFI_DISPLAY +static void wpas_sd_req_wfd(struct wpa_supplicant *wpa_s, + struct wpabuf *resp, u8 srv_trans_id, + const u8 *query, size_t query_len) +{ + const u8 *pos; + u8 role; + u8 *len_pos; + + wpa_hexdump(MSG_DEBUG, "P2P: SD Request for WFD", query, query_len); + + if (!wpa_s->global->wifi_display) { + wpa_printf(MSG_DEBUG, "P2P: WFD protocol not available"); + wpas_sd_add_proto_not_avail(resp, P2P_SERV_WIFI_DISPLAY, + srv_trans_id); + return; + } + + if (query_len < 1) { + wpa_printf(MSG_DEBUG, "P2P: Missing WFD Requested Device " + "Role"); + return; + } + + if (wpabuf_tailroom(resp) < 5) + return; + + pos = query; + role = *pos++; + wpa_printf(MSG_DEBUG, "P2P: WSD for device role 0x%x", role); + + /* TODO: role specific handling */ + + /* Length (to be filled) */ + len_pos = wpabuf_put(resp, 2); + wpabuf_put_u8(resp, P2P_SERV_WIFI_DISPLAY); + wpabuf_put_u8(resp, srv_trans_id); + wpabuf_put_u8(resp, P2P_SD_SUCCESS); /* Status Code */ + + while (pos < query + query_len) { + if (*pos < MAX_WFD_SUBELEMS && + wpa_s->global->wfd_subelem[*pos] && + wpabuf_tailroom(resp) >= + wpabuf_len(wpa_s->global->wfd_subelem[*pos])) { + wpa_printf(MSG_DEBUG, "P2P: Add WSD response " + "subelement %u", *pos); + wpabuf_put_buf(resp, wpa_s->global->wfd_subelem[*pos]); + } + pos++; + } + + WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2); +} +#endif /* CONFIG_WIFI_DISPLAY */ + + +void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token, + u16 update_indic, const u8 *tlvs, size_t tlvs_len) +{ + struct wpa_supplicant *wpa_s = ctx; + const u8 *pos = tlvs; + const u8 *end = tlvs + tlvs_len; + const u8 *tlv_end; + u16 slen; + struct wpabuf *resp; + u8 srv_proto, srv_trans_id; + size_t buf_len; + char *buf; + + wpa_hexdump(MSG_MSGDUMP, "P2P: Service Discovery Request TLVs", + tlvs, tlvs_len); + buf_len = 2 * tlvs_len + 1; + buf = os_malloc(buf_len); + if (buf) { + wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len); + wpa_msg_ctrl(wpa_s, MSG_INFO, P2P_EVENT_SERV_DISC_REQ "%d " + MACSTR " %u %u %s", + freq, MAC2STR(sa), dialog_token, update_indic, + buf); + os_free(buf); + } + + if (wpa_s->p2p_sd_over_ctrl_iface) { + wpas_notify_p2p_sd_request(wpa_s, freq, sa, dialog_token, + update_indic, tlvs, tlvs_len); + return; /* to be processed by an external program */ + } + + resp = wpabuf_alloc(10000); + if (resp == NULL) + return; + + while (pos + 1 < end) { + wpa_printf(MSG_DEBUG, "P2P: Service Request TLV"); + slen = WPA_GET_LE16(pos); + pos += 2; + if (pos + slen > end || slen < 2) { + wpa_printf(MSG_DEBUG, "P2P: Unexpected Query Data " + "length"); + wpabuf_free(resp); + return; + } + tlv_end = pos + slen; + + srv_proto = *pos++; + wpa_printf(MSG_DEBUG, "P2P: Service Protocol Type %u", + srv_proto); + srv_trans_id = *pos++; + wpa_printf(MSG_DEBUG, "P2P: Service Transaction ID %u", + srv_trans_id); + + wpa_hexdump(MSG_MSGDUMP, "P2P: Query Data", + pos, tlv_end - pos); + + + if (wpa_s->force_long_sd) { + wpa_printf(MSG_DEBUG, "P2P: SD test - force long " + "response"); + wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id); + wpas_sd_all_upnp(wpa_s, resp, srv_trans_id); + goto done; + } + + switch (srv_proto) { + case P2P_SERV_ALL_SERVICES: + wpa_printf(MSG_DEBUG, "P2P: Service Discovery Request " + "for all services"); + if (dl_list_empty(&wpa_s->global->p2p_srv_upnp) && + dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) { + wpa_printf(MSG_DEBUG, "P2P: No service " + "discovery protocols available"); + wpas_sd_add_proto_not_avail( + resp, P2P_SERV_ALL_SERVICES, + srv_trans_id); + break; + } + wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id); + wpas_sd_all_upnp(wpa_s, resp, srv_trans_id); + break; + case P2P_SERV_BONJOUR: + wpas_sd_req_bonjour(wpa_s, resp, srv_trans_id, + pos, tlv_end - pos); + break; + case P2P_SERV_UPNP: + wpas_sd_req_upnp(wpa_s, resp, srv_trans_id, + pos, tlv_end - pos); + break; +#ifdef CONFIG_WIFI_DISPLAY + case P2P_SERV_WIFI_DISPLAY: + wpas_sd_req_wfd(wpa_s, resp, srv_trans_id, + pos, tlv_end - pos); + break; +#endif /* CONFIG_WIFI_DISPLAY */ + 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); +} + + +u64 wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst, + const struct wpabuf *tlvs) +{ + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) + return wpa_drv_p2p_sd_request(wpa_s, dst, tlvs); + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return 0; + return (uintptr_t) p2p_sd_request(wpa_s->global->p2p, dst, tlvs); +} + + +u64 wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst, + u8 version, const char *query) +{ + struct wpabuf *tlvs; + u64 ret; + + tlvs = wpabuf_alloc(2 + 1 + 1 + 1 + os_strlen(query)); + if (tlvs == NULL) + return 0; + wpabuf_put_le16(tlvs, 1 + 1 + 1 + os_strlen(query)); + wpabuf_put_u8(tlvs, P2P_SERV_UPNP); /* Service Protocol Type */ + wpabuf_put_u8(tlvs, 1); /* Service Transaction ID */ + wpabuf_put_u8(tlvs, version); + wpabuf_put_str(tlvs, query); + ret = wpas_p2p_sd_request(wpa_s, dst, tlvs); + wpabuf_free(tlvs); + return ret; +} + + +#ifdef CONFIG_WIFI_DISPLAY + +static u64 wpas_p2p_sd_request_wfd(struct wpa_supplicant *wpa_s, const u8 *dst, + const struct wpabuf *tlvs) +{ + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) + return 0; + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return 0; + return (uintptr_t) p2p_sd_request_wfd(wpa_s->global->p2p, dst, tlvs); +} + + +#define MAX_WFD_SD_SUBELEMS 20 + +static void wfd_add_sd_req_role(struct wpabuf *tlvs, u8 id, u8 role, + const char *subelems) +{ + u8 *len; + const char *pos; + int val; + int count = 0; + + len = wpabuf_put(tlvs, 2); + wpabuf_put_u8(tlvs, P2P_SERV_WIFI_DISPLAY); /* Service Protocol Type */ + wpabuf_put_u8(tlvs, id); /* Service Transaction ID */ + + wpabuf_put_u8(tlvs, role); + + pos = subelems; + while (*pos) { + val = atoi(pos); + if (val >= 0 && val < 256) { + wpabuf_put_u8(tlvs, val); + count++; + if (count == MAX_WFD_SD_SUBELEMS) + break; + } + pos = os_strchr(pos + 1, ','); + if (pos == NULL) + break; + pos++; + } + + WPA_PUT_LE16(len, (u8 *) wpabuf_put(tlvs, 0) - len - 2); +} + + +u64 wpas_p2p_sd_request_wifi_display(struct wpa_supplicant *wpa_s, + const u8 *dst, const char *role) +{ + struct wpabuf *tlvs; + u64 ret; + const char *subelems; + u8 id = 1; + + subelems = os_strchr(role, ' '); + if (subelems == NULL) + return 0; + subelems++; + + tlvs = wpabuf_alloc(4 * (2 + 1 + 1 + 1 + MAX_WFD_SD_SUBELEMS)); + if (tlvs == NULL) + return 0; + + if (os_strstr(role, "[source]")) + wfd_add_sd_req_role(tlvs, id++, 0x00, subelems); + if (os_strstr(role, "[pri-sink]")) + wfd_add_sd_req_role(tlvs, id++, 0x01, subelems); + if (os_strstr(role, "[sec-sink]")) + wfd_add_sd_req_role(tlvs, id++, 0x02, subelems); + if (os_strstr(role, "[source+sink]")) + wfd_add_sd_req_role(tlvs, id++, 0x03, subelems); + + ret = wpas_p2p_sd_request_wfd(wpa_s, dst, tlvs); + wpabuf_free(tlvs); + return ret; +} + +#endif /* CONFIG_WIFI_DISPLAY */ + + +int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, u64 req) +{ + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) + return wpa_drv_p2p_sd_cancel_request(wpa_s, req); + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return -1; + return p2p_sd_cancel_request(wpa_s->global->p2p, + (void *) (uintptr_t) req); +} + + +void wpas_p2p_sd_response(struct wpa_supplicant *wpa_s, int freq, + const u8 *dst, u8 dialog_token, + const struct wpabuf *resp_tlvs) +{ + if (wpa_s->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 = 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; + char params[20]; + + if (wpa_s->pending_pd_before_join && + (os_memcmp(peer, wpa_s->pending_join_dev_addr, ETH_ALEN) == 0 || + os_memcmp(peer, wpa_s->pending_join_iface_addr, ETH_ALEN) == 0)) { + wpa_s->pending_pd_before_join = 0; + wpa_printf(MSG_DEBUG, "P2P: Starting pending " + "join-existing-group operation"); + wpas_p2p_join_start(wpa_s); + return; + } + + if (wpa_s->pending_pd_use == AUTO_PD_JOIN || + wpa_s->pending_pd_use == AUTO_PD_GO_NEG) + os_snprintf(params, sizeof(params), " peer_go=%d", + wpa_s->pending_pd_use == AUTO_PD_JOIN); + else + params[0] = '\0'; + + if (config_methods & WPS_CONFIG_DISPLAY) + wpas_prov_disc_local_keypad(wpa_s, peer, params); + else if (config_methods & WPS_CONFIG_KEYPAD) { + generated_pin = wps_generate_pin(); + wpas_prov_disc_local_display(wpa_s, peer, params, + generated_pin); + } else if (config_methods & WPS_CONFIG_PUSHBUTTON) + wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_PBC_RESP MACSTR + "%s", MAC2STR(peer), params); + + wpas_notify_p2p_provision_discovery(wpa_s, peer, 0 /* response */, + P2P_PROV_DISC_SUCCESS, + config_methods, generated_pin); +} + + +static void wpas_prov_disc_fail(void *ctx, const u8 *peer, + enum p2p_prov_disc_status status) +{ + struct wpa_supplicant *wpa_s = ctx; + + if (wpa_s->p2p_fallback_to_go_neg) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: PD for p2p_connect-auto " + "failed - fall back to GO Negotiation"); + wpas_p2p_fallback_to_go_neg(wpa_s, 0); + return; + } + + if (status == P2P_PROV_DISC_TIMEOUT_JOIN) { + wpa_s->pending_pd_before_join = 0; + wpa_printf(MSG_DEBUG, "P2P: Starting pending " + "join-existing-group operation (no ACK for PD " + "Req attempts)"); + wpas_p2p_join_start(wpa_s); + return; + } + + wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE + " p2p_dev_addr=" MACSTR " status=%d", + MAC2STR(peer), status); + + wpas_notify_p2p_provision_discovery(wpa_s, peer, 0 /* response */, + status, 0, 0); +} + + +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) { + int go = s->mode == WPAS_MODE_P2P_GO; + wpas_p2p_group_add_persistent( + wpa_s, s, go, go ? op_freq : 0, 0); + } else if (bssid) { + wpa_s->user_initiated_pd = 0; + wpas_p2p_join(wpa_s, bssid, go_dev_addr, + wpa_s->p2p_wps_method, 0); + } + 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; + } + + /* + * The peer could have missed our ctrl::ack frame for Invitation + * Response and continue retransmitting the frame. To reduce the + * likelihood of the peer not getting successful TX status for the + * Invitation Response frame, wait a short time here before starting + * the persistent group so that we will remain on the current channel to + * acknowledge any possible retransmission from the peer. + */ + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: 50 ms wait on current channel before " + "starting persistent group"); + os_sleep(0, 50000); + + wpas_p2p_group_add_persistent(wpa_s, ssid, + ssid->mode == WPAS_MODE_P2P_GO, + wpa_s->p2p_persistent_go_freq, + wpa_s->p2p_go_ht40); +} + + +static int wpas_p2p_disallowed_freq(struct wpa_global *global, + unsigned int freq) +{ + unsigned int i; + + if (global->p2p_disallow_freq == NULL) + return 0; + + for (i = 0; i < global->num_p2p_disallow_freq; i++) { + if (freq >= global->p2p_disallow_freq[i].min && + freq <= global->p2p_disallow_freq[i].max) + return 1; + } + + return 0; +} + + +static void wpas_p2p_add_chan(struct p2p_reg_class *reg, u8 chan) +{ + reg->channel[reg->channels] = chan; + reg->channels++; +} + + +static int wpas_p2p_default_channels(struct wpa_supplicant *wpa_s, + struct p2p_channels *chan) +{ + 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 = 0; + for (i = 0; i < 11; i++) { + if (!wpas_p2p_disallowed_freq(wpa_s->global, 2412 + i * 5)) + wpas_p2p_add_chan(&chan->reg_class[cla], i + 1); + } + if (chan->reg_class[cla].channels) + cla++; + + wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for lower 5 GHz " + "band"); + + /* Operating class 115 - 5 GHz, channels 36-48 */ + chan->reg_class[cla].reg_class = 115; + chan->reg_class[cla].channels = 0; + if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 36 * 5)) + wpas_p2p_add_chan(&chan->reg_class[cla], 36); + if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 40 * 5)) + wpas_p2p_add_chan(&chan->reg_class[cla], 40); + if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 44 * 5)) + wpas_p2p_add_chan(&chan->reg_class[cla], 44); + if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 48 * 5)) + wpas_p2p_add_chan(&chan->reg_class[cla], 48); + if (chan->reg_class[cla].channels) + cla++; + + wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for higher 5 GHz " + "band"); + + /* Operating class 124 - 5 GHz, channels 149,153,157,161 */ + chan->reg_class[cla].reg_class = 124; + chan->reg_class[cla].channels = 0; + if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 149 * 5)) + wpas_p2p_add_chan(&chan->reg_class[cla], 149); + if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 153 * 5)) + wpas_p2p_add_chan(&chan->reg_class[cla], 153); + if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 156 * 5)) + wpas_p2p_add_chan(&chan->reg_class[cla], 157); + if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 161 * 5)) + wpas_p2p_add_chan(&chan->reg_class[cla], 161); + if (chan->reg_class[cla].channels) + cla++; + + chan->reg_classes = cla; + return 0; +} + + +static struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes, + u16 num_modes, + enum hostapd_hw_mode mode) +{ + u16 i; + + for (i = 0; i < num_modes; i++) { + if (modes[i].mode == mode) + return &modes[i]; + } + + return NULL; +} + + +static int has_channel(struct wpa_global *global, + struct hostapd_hw_modes *mode, u8 chan, int *flags) +{ + int i; + unsigned int freq; + + freq = (mode->mode == HOSTAPD_MODE_IEEE80211A ? 5000 : 2407) + + chan * 5; + if (wpas_p2p_disallowed_freq(global, freq)) + return 0; + + 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 struct p2p_oper_class_map op_class[] = { + { HOSTAPD_MODE_IEEE80211G, 81, 1, 13, 1, BW20 }, +#if 0 /* Do not enable HT40 on 2 GHz for now */ + { HOSTAPD_MODE_IEEE80211G, 83, 1, 9, 1, BW40PLUS }, + { HOSTAPD_MODE_IEEE80211G, 84, 5, 13, 1, BW40MINUS }, +#endif + { HOSTAPD_MODE_IEEE80211A, 115, 36, 48, 4, BW20 }, + { HOSTAPD_MODE_IEEE80211A, 124, 149, 161, 4, BW20 }, + { HOSTAPD_MODE_IEEE80211A, 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 } +}; + + +static int wpas_p2p_verify_channel(struct wpa_supplicant *wpa_s, + struct hostapd_hw_modes *mode, + u8 channel, u8 bw) +{ + int flag; + + if (!has_channel(wpa_s->global, mode, channel, &flag)) + return -1; + if (bw == BW40MINUS && + (!(flag & HOSTAPD_CHAN_HT40MINUS) || + !has_channel(wpa_s->global, mode, channel - 4, NULL))) + return 0; + if (bw == BW40PLUS && + (!(flag & HOSTAPD_CHAN_HT40PLUS) || + !has_channel(wpa_s->global, mode, channel + 4, NULL))) + return 0; + return 1; +} + + +static int wpas_p2p_setup_channels(struct wpa_supplicant *wpa_s, + struct p2p_channels *chan) +{ + struct hostapd_hw_modes *mode; + int cla, op; + + 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) { + if (wpas_p2p_verify_channel(wpa_s, mode, ch, o->bw) < 1) + continue; + if (reg == NULL) { + wpa_printf(MSG_DEBUG, "P2P: Add operating " + "class %u", o->op_class); + reg = &chan->reg_class[cla]; + cla++; + reg->reg_class = o->op_class; + } + 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; +} + + +int wpas_p2p_get_ht40_mode(struct wpa_supplicant *wpa_s, + struct hostapd_hw_modes *mode, u8 channel) +{ + int op, ret; + + for (op = 0; op_class[op].op_class; op++) { + struct p2p_oper_class_map *o = &op_class[op]; + u8 ch; + + for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) { + if (o->mode != HOSTAPD_MODE_IEEE80211A || + o->bw == BW20 || ch != channel) + continue; + ret = wpas_p2p_verify_channel(wpa_s, mode, ch, o->bw); + if (ret < 0) + continue; + else if (ret > 0) + return (o->bw == BW40MINUS) ? -1 : 1; + else + return 0; + } + } + 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(¶ms, 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, ¶ms) < 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; + + p2p.max_listen = wpa_s->max_remain_on_chan; + + global->p2p = p2p_init(&p2p); + if (global->p2p == NULL) + return -1; + global->p2p_init_wpa_s = wpa_s; + + for (i = 0; i < MAX_WPS_VENDOR_EXT; i++) { + if (wpa_s->conf->wps_vendor_ext[i] == NULL) + continue; + p2p_add_wps_vendor_extension( + global->p2p, wpa_s->conf->wps_vendor_ext[i]); + } + + 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); + 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; + + wpa_s = global->ifaces; + if (wpa_s) + wpas_p2p_service_flush(wpa_s); + + if (global->p2p == NULL) + return; + + /* Remove remaining P2P group interfaces */ + while (wpa_s && wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) + wpa_s = wpa_s->next; + while (wpa_s) { + tmp = global->ifaces; + while (tmp && + (tmp == wpa_s || + tmp->p2p_group_interface == NOT_P2P_GROUP_INTERFACE)) { + tmp = tmp->next; + } + if (tmp == NULL) + break; + /* Disconnect from the P2P group and deinit the interface */ + wpas_p2p_disconnect(tmp); + } + + /* + * Deinit GO data on any possibly remaining interface (if main + * interface is used as GO). + */ + for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { + if (wpa_s->ap_iface) + wpas_p2p_group_deinit(wpa_s); + } + + p2p_deinit(global->p2p); + global->p2p = NULL; + global->p2p_init_wpa_s = NULL; +} + + +static int wpas_p2p_create_iface(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->conf->p2p_no_group_iface) + return 0; /* separate interface disabled per configuration */ + if (wpa_s->drv_flags & + (WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE | + WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P)) + return 1; /* P2P group requires a new interface in every case + */ + if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_CONCURRENT)) + return 0; /* driver does not support concurrent operations */ + if (wpa_s->global->ifaces->next) + return 1; /* more that one interface already in use */ + if (wpa_s->wpa_state >= WPA_AUTHENTICATING) + return 1; /* this interface is already in use */ + return 0; +} + + +static int wpas_p2p_start_go_neg(struct wpa_supplicant *wpa_s, + const u8 *peer_addr, + enum p2p_wps_method wps_method, + int go_intent, const u8 *own_interface_addr, + unsigned int force_freq, int persistent_group, + struct wpa_ssid *ssid, unsigned int pref_freq) +{ + if (persistent_group && wpa_s->conf->persistent_reconnect) + persistent_group = 2; + + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) { + return wpa_drv_p2p_connect(wpa_s, peer_addr, wps_method, + go_intent, own_interface_addr, + force_freq, persistent_group); + } + + /* + * Increase GO config timeout if HT40 is used since it takes some time + * to scan channels for coex purposes before the BSS can be started. + */ + p2p_set_config_timeout(wpa_s->global->p2p, + wpa_s->p2p_go_ht40 ? 255 : 100, 20); + + return p2p_connect(wpa_s->global->p2p, peer_addr, wps_method, + go_intent, own_interface_addr, force_freq, + persistent_group, ssid ? ssid->ssid : NULL, + ssid ? ssid->ssid_len : 0, + wpa_s->p2p_pd_before_go_neg, pref_freq); +} + + +static int wpas_p2p_auth_go_neg(struct wpa_supplicant *wpa_s, + const u8 *peer_addr, + enum p2p_wps_method wps_method, + int go_intent, const u8 *own_interface_addr, + unsigned int force_freq, int persistent_group, + struct wpa_ssid *ssid, unsigned int pref_freq) +{ + if (persistent_group && wpa_s->conf->persistent_reconnect) + persistent_group = 2; + + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) + return -1; + + return p2p_authorize(wpa_s->global->p2p, peer_addr, wps_method, + go_intent, own_interface_addr, force_freq, + persistent_group, ssid ? ssid->ssid : NULL, + ssid ? ssid->ssid_len : 0, pref_freq); +} + + +static void wpas_p2p_check_join_scan_limit(struct wpa_supplicant *wpa_s) +{ + wpa_s->p2p_join_scan_count++; + wpa_printf(MSG_DEBUG, "P2P: Join scan attempt %d", + wpa_s->p2p_join_scan_count); + if (wpa_s->p2p_join_scan_count > P2P_MAX_JOIN_SCAN_ATTEMPTS) { + wpa_printf(MSG_DEBUG, "P2P: Failed to find GO " MACSTR + " for join operationg - stop join attempt", + MAC2STR(wpa_s->pending_join_iface_addr)); + eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL); + if (wpa_s->p2p_auto_pd) { + wpa_s->p2p_auto_pd = 0; + wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE + " p2p_dev_addr=" MACSTR " status=N/A", + MAC2STR(wpa_s->pending_join_dev_addr)); + return; + } + wpa_msg(wpa_s->parent, MSG_INFO, + P2P_EVENT_GROUP_FORMATION_FAILURE); + } +} + + +static int wpas_check_freq_conflict(struct wpa_supplicant *wpa_s, int freq) +{ + struct wpa_supplicant *iface; + int shared_freq; + u8 bssid[ETH_ALEN]; + + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT) + return 0; + + for (iface = wpa_s->global->ifaces; iface; iface = iface->next) { + if (!wpas_p2p_create_iface(wpa_s) && iface == wpa_s) + continue; + if (iface->current_ssid == NULL || iface->assoc_freq == 0) + continue; + if (iface->current_ssid->mode == WPAS_MODE_AP || + iface->current_ssid->mode == WPAS_MODE_P2P_GO) + shared_freq = iface->current_ssid->frequency; + else if (wpa_drv_get_bssid(iface, bssid) == 0) + shared_freq = iface->assoc_freq; + else + shared_freq = 0; + + if (shared_freq && freq != shared_freq) { + wpa_printf(MSG_DEBUG, "P2P: Frequency conflict - %s " + "connected on %d MHz - new connection on " + "%d MHz", iface->ifname, shared_freq, freq); + return 1; + } + } + + shared_freq = wpa_drv_shared_freq(wpa_s); + if (shared_freq > 0 && shared_freq != freq) { + wpa_printf(MSG_DEBUG, "P2P: Frequency conflict - shared " + "virtual interface connected on %d MHz - new " + "connection on %d MHz", shared_freq, freq); + return 1; + } + + return 0; +} + + +static int wpas_p2p_peer_go(struct wpa_supplicant *wpa_s, + const u8 *peer_dev_addr) +{ + struct wpa_bss *bss; + int updated; + + bss = wpa_bss_get_p2p_dev_addr(wpa_s, peer_dev_addr); + if (bss == NULL) + return -1; + if (bss->last_update_idx < wpa_s->bss_update_idx) { + wpa_printf(MSG_DEBUG, "P2P: Peer BSS entry not updated in the " + "last scan"); + return 0; + } + + updated = os_time_before(&wpa_s->p2p_auto_started, &bss->last_update); + wpa_printf(MSG_DEBUG, "P2P: Current BSS entry for peer updated at " + "%ld.%06ld (%supdated in last scan)", + bss->last_update.sec, bss->last_update.usec, + updated ? "": "not "); + + return updated; +} + + +static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res) +{ + struct wpa_bss *bss; + int freq; + u8 iface_addr[ETH_ALEN]; + + eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL); + + if (wpa_s->global->p2p_disabled) + return; + + wpa_printf(MSG_DEBUG, "P2P: Scan results received (%d BSS) for %sjoin", + scan_res ? (int) scan_res->num : -1, + wpa_s->p2p_auto_join ? "auto_" : ""); + + if (scan_res) + wpas_p2p_scan_res_handler(wpa_s, scan_res); + + if (wpa_s->p2p_auto_pd) { + int join = wpas_p2p_peer_go(wpa_s, + wpa_s->pending_join_dev_addr); + if (join == 0 && + wpa_s->auto_pd_scan_retry < P2P_AUTO_PD_SCAN_ATTEMPTS) { + wpa_s->auto_pd_scan_retry++; + bss = wpa_bss_get_bssid(wpa_s, + wpa_s->pending_join_dev_addr); + if (bss) { + freq = bss->freq; + wpa_printf(MSG_DEBUG, "P2P: Scan retry %d for " + "the peer " MACSTR " at %d MHz", + wpa_s->auto_pd_scan_retry, + MAC2STR(wpa_s-> + pending_join_dev_addr), + freq); + wpas_p2p_join_scan_req(wpa_s, freq); + return; + } + } + + if (join < 0) + join = 0; + + wpa_s->p2p_auto_pd = 0; + wpa_s->pending_pd_use = join ? AUTO_PD_JOIN : AUTO_PD_GO_NEG; + wpa_printf(MSG_DEBUG, "P2P: Auto PD with " MACSTR " join=%d", + MAC2STR(wpa_s->pending_join_dev_addr), join); + if (p2p_prov_disc_req(wpa_s->global->p2p, + wpa_s->pending_join_dev_addr, + wpa_s->pending_pd_config_methods, join, + 0, wpa_s->user_initiated_pd) < 0) { + wpa_s->p2p_auto_pd = 0; + wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE + " p2p_dev_addr=" MACSTR " status=N/A", + MAC2STR(wpa_s->pending_join_dev_addr)); + } + return; + } + + if (wpa_s->p2p_auto_join) { + int join = wpas_p2p_peer_go(wpa_s, + wpa_s->pending_join_dev_addr); + if (join < 0) { + wpa_printf(MSG_DEBUG, "P2P: Peer was not found to be " + "running a GO -> use GO Negotiation"); + wpas_p2p_connect(wpa_s, wpa_s->pending_join_dev_addr, + wpa_s->p2p_pin, wpa_s->p2p_wps_method, + wpa_s->p2p_persistent_group, 0, 0, 0, + wpa_s->p2p_go_intent, + wpa_s->p2p_connect_freq, + wpa_s->p2p_persistent_id, + wpa_s->p2p_pd_before_go_neg, + wpa_s->p2p_go_ht40); + return; + } + + wpa_printf(MSG_DEBUG, "P2P: Peer was found running GO%s -> " + "try to join the group", join ? "" : + " in older scan"); + if (!join) + wpa_s->p2p_fallback_to_go_neg = 1; + } + + freq = p2p_get_oper_freq(wpa_s->global->p2p, + wpa_s->pending_join_iface_addr); + if (freq < 0 && + p2p_get_interface_addr(wpa_s->global->p2p, + wpa_s->pending_join_dev_addr, + iface_addr) == 0 && + os_memcmp(iface_addr, wpa_s->pending_join_dev_addr, ETH_ALEN) != 0) + { + wpa_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; + + if (wpas_check_freq_conflict(wpa_s, freq) > 0) { + wpa_msg(wpa_s->parent, MSG_INFO, + P2P_EVENT_GROUP_FORMATION_FAILURE + "reason=FREQ_CONFLICT"); + return; + } + + wpa_printf(MSG_DEBUG, "P2P: Send Provision Discovery Request " + "prior to joining an existing group (GO " MACSTR + " freq=%u MHz)", + MAC2STR(wpa_s->pending_join_dev_addr), freq); + wpa_s->pending_pd_before_join = 1; + + switch (wpa_s->pending_join_wps_method) { + case WPS_PIN_DISPLAY: + method = WPS_CONFIG_KEYPAD; + break; + case WPS_PIN_KEYPAD: + method = WPS_CONFIG_DISPLAY; + break; + case WPS_PBC: + method = WPS_CONFIG_PUSHBUTTON; + break; + default: + method = 0; + break; + } + + if ((p2p_get_provisioning_info(wpa_s->global->p2p, + wpa_s->pending_join_dev_addr) == + method)) { + /* + * We have already performed provision discovery for + * joining the group. Proceed directly to join + * operation without duplicated provision discovery. */ + wpa_printf(MSG_DEBUG, "P2P: Provision discovery " + "with " MACSTR " already done - proceed to " + "join", + MAC2STR(wpa_s->pending_join_dev_addr)); + wpa_s->pending_pd_before_join = 0; + goto start; + } + + if (p2p_prov_disc_req(wpa_s->global->p2p, + wpa_s->pending_join_dev_addr, method, 1, + freq, wpa_s->user_initiated_pd) < 0) { + wpa_printf(MSG_DEBUG, "P2P: Failed to send Provision " + "Discovery Request before joining an " + "existing group"); + wpa_s->pending_pd_before_join = 0; + goto start; + } + return; + } + + wpa_printf(MSG_DEBUG, "P2P: Failed to find BSS/GO - try again later"); + eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL); + eloop_register_timeout(1, 0, wpas_p2p_join_scan, wpa_s, NULL); + wpas_p2p_check_join_scan_limit(wpa_s); + return; + +start: + /* Start join operation immediately */ + wpas_p2p_join_start(wpa_s); +} + + +static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq) +{ + int ret; + struct wpa_driver_scan_params params; + struct wpabuf *wps_ie, *ies; + size_t ielen; + int freqs[2] = { 0, 0 }; + + os_memset(¶ms, 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(DEV_PW_DEFAULT, &wpa_s->wps->dev, + wpa_s->wps->uuid, WPS_REQ_ENROLLEE, 0, + NULL); + if (wps_ie == NULL) { + wpas_p2p_scan_res_join(wpa_s, NULL); + return; + } + + ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p); + ies = wpabuf_alloc(wpabuf_len(wps_ie) + ielen); + if (ies == NULL) { + wpabuf_free(wps_ie); + wpas_p2p_scan_res_join(wpa_s, NULL); + return; + } + wpabuf_put_buf(ies, wps_ie); + wpabuf_free(wps_ie); + + p2p_scan_ie(wpa_s->global->p2p, ies, NULL); + + params.p2p_probe = 1; + params.extra_ies = wpabuf_head(ies); + params.extra_ies_len = wpabuf_len(ies); + if (freq > 0) { + freqs[0] = freq; + params.freqs = freqs; + } + + /* + * Run a scan to update BSS table and start Provision Discovery once + * the new scan results become available. + */ + ret = wpa_drv_scan(wpa_s, ¶ms); + if (!ret) + wpa_s->scan_res_handler = wpas_p2p_scan_res_join; + + wpabuf_free(ies); + + if (ret) { + wpa_printf(MSG_DEBUG, "P2P: Failed to start scan for join - " + "try again later"); + eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL); + eloop_register_timeout(1, 0, wpas_p2p_join_scan, wpa_s, NULL); + wpas_p2p_check_join_scan_limit(wpa_s); + } +} + + +static void wpas_p2p_join_scan(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + wpas_p2p_join_scan_req(wpa_s, 0); +} + + +static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr, + const u8 *dev_addr, enum p2p_wps_method wps_method, + int auto_join) +{ + wpa_printf(MSG_DEBUG, "P2P: Request to join existing group (iface " + MACSTR " dev " MACSTR ")%s", + MAC2STR(iface_addr), MAC2STR(dev_addr), + auto_join ? " (auto_join)" : ""); + + wpa_s->p2p_auto_pd = 0; + wpa_s->p2p_auto_join = !!auto_join; + os_memcpy(wpa_s->pending_join_iface_addr, iface_addr, ETH_ALEN); + os_memcpy(wpa_s->pending_join_dev_addr, dev_addr, ETH_ALEN); + wpa_s->pending_join_wps_method = wps_method; + + /* Make sure we are not running find during connection establishment */ + wpas_p2p_stop_find(wpa_s); + + wpa_s->p2p_join_scan_count = 0; + wpas_p2p_join_scan(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; + struct wpa_bss *bss; + + group = wpas_p2p_get_group_iface(wpa_s, 0, 0); + if (group == NULL) + return -1; + if (group != wpa_s) { + os_memcpy(group->p2p_pin, wpa_s->p2p_pin, + sizeof(group->p2p_pin)); + group->p2p_wps_method = wpa_s->p2p_wps_method; + } + + group->p2p_in_provisioning = 1; + wpa_s->global->p2p_group_formation = wpa_s; + group->p2p_fallback_to_go_neg = wpa_s->p2p_fallback_to_go_neg; + + os_memset(&res, 0, sizeof(res)); + os_memcpy(res.peer_interface_addr, wpa_s->pending_join_iface_addr, + ETH_ALEN); + res.wps_method = wpa_s->pending_join_wps_method; + bss = wpa_bss_get_bssid(wpa_s, wpa_s->pending_join_iface_addr); + if (bss) { + res.freq = bss->freq; + res.ssid_len = bss->ssid_len; + os_memcpy(res.ssid, bss->ssid, bss->ssid_len); + } + + if (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 + * @auto_join: Whether to select join vs. GO Negotiation automatically + * @join: Whether to join an existing group (as a client) instead of starting + * Group Owner negotiation; @peer_addr is BSSID in that case + * @auth: Whether to only authorize the connection instead of doing that and + * initiating Group Owner negotiation + * @go_intent: GO Intent or -1 to use default + * @freq: Frequency for the group or 0 for auto-selection + * @persistent_id: Persistent group credentials to use for forcing GO + * parameters or -1 to generate new values (SSID/passphrase) + * @pd: Whether to send Provision Discovery prior to GO Negotiation as an + * interoperability workaround when initiating group formation + * @ht40: Start GO with 40 MHz channel width + * Returns: 0 or new PIN (if pin was %NULL) on success, -1 on unspecified + * failure, -2 on failure due to channel not currently available, + * -3 if forced channel is not supported + */ +int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, + const char *pin, enum p2p_wps_method wps_method, + int persistent_group, int auto_join, int join, int auth, + int go_intent, int freq, int persistent_id, int pd, + int ht40) +{ + int force_freq = 0, pref_freq = 0, oper_freq = 0; + u8 bssid[ETH_ALEN]; + int ret = 0; + enum wpa_driver_if_type iftype; + const u8 *if_addr; + struct wpa_ssid *ssid = NULL; + + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return -1; + + if (persistent_id >= 0) { + ssid = wpa_config_get_network(wpa_s->conf, persistent_id); + if (ssid == NULL || ssid->disabled != 2 || + ssid->mode != WPAS_MODE_P2P_GO) + return -1; + } + + if (go_intent < 0) + go_intent = wpa_s->conf->p2p_go_intent; + + if (!auth) + wpa_s->p2p_long_listen = 0; + + wpa_s->p2p_wps_method = wps_method; + wpa_s->p2p_persistent_group = !!persistent_group; + wpa_s->p2p_persistent_id = persistent_id; + wpa_s->p2p_go_intent = go_intent; + wpa_s->p2p_connect_freq = freq; + wpa_s->p2p_fallback_to_go_neg = 0; + wpa_s->p2p_pd_before_go_neg = !!pd; + wpa_s->p2p_go_ht40 = !!ht40; + + 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 || auto_join) { + u8 iface_addr[ETH_ALEN], dev_addr[ETH_ALEN]; + if (auth) { + wpa_printf(MSG_DEBUG, "P2P: Authorize invitation to " + "connect a running group from " MACSTR, + MAC2STR(peer_addr)); + os_memcpy(wpa_s->p2p_auth_invite, peer_addr, ETH_ALEN); + return ret; + } + os_memcpy(dev_addr, peer_addr, ETH_ALEN); + if (p2p_get_interface_addr(wpa_s->global->p2p, peer_addr, + iface_addr) < 0) { + os_memcpy(iface_addr, peer_addr, ETH_ALEN); + p2p_get_dev_addr(wpa_s->global->p2p, peer_addr, + dev_addr); + } + if (auto_join) { + os_get_time(&wpa_s->p2p_auto_started); + wpa_printf(MSG_DEBUG, "P2P: Auto join started at " + "%ld.%06ld", + wpa_s->p2p_auto_started.sec, + wpa_s->p2p_auto_started.usec); + } + wpa_s->user_initiated_pd = 1; + if (wpas_p2p_join(wpa_s, iface_addr, dev_addr, wps_method, + auto_join) < 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_s->drv_flags & + WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT)) { + wpa_printf(MSG_DEBUG, "P2P: Trying to prefer the channel we " + "are already using (%u MHz) on another interface", + oper_freq); + pref_freq = oper_freq; + } else if (oper_freq > 0) { + wpa_printf(MSG_DEBUG, "P2P: Trying to force us to use the " + "channel we are already using (%u MHz) on another " + "interface", oper_freq); + force_freq = oper_freq; + } + + wpa_s->create_p2p_iface = wpas_p2p_create_iface(wpa_s); + + if (wpa_s->create_p2p_iface) { + /* Prepare to add a new interface for the group */ + iftype = WPA_IF_P2P_GROUP; + if (go_intent == 15) + iftype = WPA_IF_P2P_GO; + if (wpas_p2p_add_group_interface(wpa_s, iftype) < 0) { + wpa_printf(MSG_ERROR, "P2P: Failed to allocate a new " + "interface for the group"); + return -1; + } + + if_addr = wpa_s->pending_interface_addr; + } else + if_addr = wpa_s->own_addr; + + if (auth) { + if (wpas_p2p_auth_go_neg(wpa_s, peer_addr, wps_method, + go_intent, if_addr, + force_freq, persistent_group, ssid, + pref_freq) < 0) + return -1; + return ret; + } + + if (wpas_p2p_start_go_neg(wpa_s, peer_addr, wps_method, + go_intent, if_addr, force_freq, + persistent_group, ssid, pref_freq) < 0) { + if (wpa_s->create_p2p_iface) + wpas_p2p_remove_pending_group_interface(wpa_s); + return -1; + } + return ret; +} + + +/** + * wpas_p2p_remain_on_channel_cb - Indication of remain-on-channel start + * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface() + * @freq: Frequency of the channel in MHz + * @duration: Duration of the stay on the channel in milliseconds + * + * This callback is called when the driver indicates that it has started the + * requested remain-on-channel duration. + */ +void wpas_p2p_remain_on_channel_cb(struct wpa_supplicant *wpa_s, + unsigned int freq, unsigned int duration) +{ + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return; + 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, offchannel_pending_action_tx(wpa_s)); + 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 (offchannel_pending_action_tx(wpa_s)) + return; + if (wpa_s->p2p_long_listen > 0) + wpa_s->p2p_long_listen -= wpa_s->max_remain_on_chan; + if (wpa_s->p2p_long_listen > 0) { + wpa_printf(MSG_DEBUG, "P2P: Continuing long Listen state"); + wpas_p2p_listen_start(wpa_s, wpa_s->p2p_long_listen); + } +} + + +/** + * 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, int ht40) +{ + u8 bssid[ETH_ALEN]; + int res; + + os_memset(params, 0, sizeof(*params)); + params->role_go = 1; + params->ht40 = ht40; + 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 { + int chan; + for (chan = 0; chan < 11; chan++) { + params->freq = 2412 + chan * 5; + if (!wpas_p2p_disallowed_freq(wpa_s->global, + params->freq)) + break; + } + if (chan == 11) { + wpa_printf(MSG_DEBUG, "P2P: No 2.4 GHz channel " + "allowed"); + return -1; + } + wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz (no preference " + "known)", params->freq); + } + + if (wpa_s->current_ssid && wpa_drv_get_bssid(wpa_s, bssid) == 0 && + wpa_s->assoc_freq && !freq) { + 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)) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use same interface for group " + "operations"); + return wpa_s; + } + + if (wpas_p2p_add_group_interface(wpa_s, go ? WPA_IF_P2P_GO : + WPA_IF_P2P_CLIENT) < 0) { + wpa_msg(wpa_s, MSG_ERROR, "P2P: Failed to add group interface"); + return NULL; + } + group_wpa_s = wpas_p2p_init_group_interface(wpa_s, go); + if (group_wpa_s == NULL) { + wpa_msg(wpa_s, MSG_ERROR, "P2P: Failed to initialize group " + "interface"); + wpas_p2p_remove_pending_group_interface(wpa_s); + return NULL; + } + + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use separate group interface %s", + group_wpa_s->ifname); + 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, int ht40) +{ + 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_oper(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, ¶ms, freq, ht40)) + 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, ¶ms); + 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, ¶ms, 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, int ht40) +{ + 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_oper(wpa_s); + + wpa_s->p2p_fallback_to_go_neg = 0; + + 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, ¶ms, freq, ht40)) + return -1; + + params.role_go = 1; + params.psk_set = ssid->psk_set; + if (params.psk_set) + os_memcpy(params.psk, ssid->psk, sizeof(params.psk)); + if (ssid->passphrase) { + if (os_strlen(ssid->passphrase) >= sizeof(params.passphrase)) { + wpa_printf(MSG_ERROR, "P2P: Invalid passphrase in " + "persistent group"); + return -1; + } + os_strlcpy(params.passphrase, ssid->passphrase, + sizeof(params.passphrase)); + } + os_memcpy(params.ssid, ssid->ssid, ssid->ssid_len); + params.ssid_len = ssid->ssid_len; + params.persistent_group = 1; + + wpa_s = wpas_p2p_get_group_iface(wpa_s, addr_allocated, 1); + if (wpa_s == NULL) + return -1; + + wpas_start_wps_go(wpa_s, ¶ms, 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, + struct wpa_ssid *ssid) +{ + 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 (ssid->p2p_persistent_group && wpa_s->conf->persistent_reconnect) + cfg->persistent_group = 2; + else if (ssid->p2p_persistent_group) + cfg->persistent_group = 1; + os_memcpy(cfg->interface_addr, wpa_s->own_addr, ETH_ALEN); + if (wpa_s->max_stations && + wpa_s->max_stations < wpa_s->conf->max_num_sta) + cfg->max_clients = wpa_s->max_stations; + else + cfg->max_clients = wpa_s->conf->max_num_sta; + os_memcpy(cfg->ssid, ssid->ssid, ssid->ssid_len); + cfg->ssid_len = ssid->ssid_len; + cfg->cb_ctx = wpa_s; + cfg->ie_update = wpas_p2p_ie_update; + cfg->idle_update = wpas_p2p_idle_update; + + group = p2p_group_init(wpa_s->global->p2p, cfg); + if (group == NULL) + os_free(cfg); + if (ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION) + p2p_group_notif_formation_done(group); + wpa_s->p2p_group = group; + return group; +} + + +void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr, + int registrar) +{ + struct wpa_ssid *ssid = wpa_s->current_ssid; + + if (!wpa_s->p2p_in_provisioning) { + wpa_printf(MSG_DEBUG, "P2P: Ignore WPS success event - P2P " + "provisioning not in progress"); + return; + } + + if (ssid && ssid->mode == WPAS_MODE_INFRA) { + u8 go_dev_addr[ETH_ALEN]; + os_memcpy(go_dev_addr, wpa_s->bssid, ETH_ALEN); + wpas_p2p_persistent_group(wpa_s, go_dev_addr, ssid->ssid, + ssid->ssid_len); + /* Clear any stored provisioning info */ + p2p_clear_provisioning_info(wpa_s->global->p2p, go_dev_addr); + } + + eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->parent, + NULL); + if (ssid && ssid->mode == WPAS_MODE_INFRA) { + /* + * Use a separate timeout for initial data connection to + * complete to allow the group to be removed automatically if + * something goes wrong in this step before the P2P group idle + * timeout mechanism is taken into use. + */ + eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT, 0, + 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, + enum wpas_p2p_prov_disc_use use) +{ + u16 config_methods; + + wpa_s->p2p_fallback_to_go_neg = 0; + wpa_s->pending_pd_use = NORMAL_PD; + 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 (use == WPAS_P2P_PD_AUTO) { + os_memcpy(wpa_s->pending_join_dev_addr, peer_addr, ETH_ALEN); + wpa_s->pending_pd_config_methods = config_methods; + wpa_s->p2p_auto_pd = 1; + wpa_s->p2p_auto_join = 0; + wpa_s->pending_pd_before_join = 0; + wpa_s->auto_pd_scan_retry = 0; + wpas_p2p_stop_find(wpa_s); + wpa_s->p2p_join_scan_count = 0; + os_get_time(&wpa_s->p2p_auto_started); + wpa_printf(MSG_DEBUG, "P2P: Auto PD started at %ld.%06ld", + wpa_s->p2p_auto_started.sec, + wpa_s->p2p_auto_started.usec); + wpas_p2p_join_scan(wpa_s, NULL); + return 0; + } + + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) { + return wpa_drv_p2p_prov_disc_req(wpa_s, peer_addr, + config_methods, + use == WPAS_P2P_PD_FOR_JOIN); + } + + if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled) + return -1; + + return p2p_prov_disc_req(wpa_s->global->p2p, peer_addr, + config_methods, use == WPAS_P2P_PD_FOR_JOIN, + 0, 1); +} + + +int wpas_p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf, + char *end) +{ + return p2p_scan_result_text(ies, ies_len, buf, end); +} + + +static void wpas_p2p_clear_pending_action_tx(struct wpa_supplicant *wpa_s) +{ + if (!offchannel_pending_action_tx(wpa_s)) + return; + + wpa_printf(MSG_DEBUG, "P2P: Drop pending Action TX due to new " + "operation request"); + offchannel_clear_pending_action_tx(wpa_s); +} + + +int wpas_p2p_find(struct wpa_supplicant *wpa_s, unsigned int timeout, + enum p2p_discovery_type type, + unsigned int num_req_dev_types, const u8 *req_dev_types, + const u8 *dev_id, unsigned int search_delay) +{ + wpas_p2p_clear_pending_action_tx(wpa_s); + wpa_s->p2p_long_listen = 0; + + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) + return wpa_drv_p2p_find(wpa_s, timeout, type); + + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL || + wpa_s->p2p_in_provisioning) + return -1; + + wpa_supplicant_cancel_sched_scan(wpa_s); + + return p2p_find(wpa_s->global->p2p, timeout, type, + num_req_dev_types, req_dev_types, dev_id, + search_delay); +} + + +static int wpas_p2p_stop_find_oper(struct wpa_supplicant *wpa_s) +{ + wpas_p2p_clear_pending_action_tx(wpa_s); + wpa_s->p2p_long_listen = 0; + eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL); + eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL); + wpa_s->global->p2p_cb_on_scan_complete = 0; + + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) { + wpa_drv_p2p_stop_find(wpa_s); + return 1; + } + + if (wpa_s->global->p2p) + p2p_stop_find(wpa_s->global->p2p); + + return 0; +} + + +void wpas_p2p_stop_find(struct wpa_supplicant *wpa_s) +{ + if (wpas_p2p_stop_find_oper(wpa_s) > 0) + return; + wpas_p2p_remove_pending_group_interface(wpa_s); +} + + +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, int ssi_signal) +{ + if (wpa_s->global->p2p_disabled) + return 0; + if (wpa_s->global->p2p == NULL) + return 0; + + switch (p2p_probe_req_rx(wpa_s->global->p2p, addr, dst, bssid, + ie, ie_len)) { + case P2P_PREQ_NOT_P2P: + wpas_notify_preq(wpa_s, addr, dst, bssid, ie, ie_len, + ssi_signal); + /* fall through */ + case P2P_PREQ_MALFORMED: + case P2P_PREQ_NOT_LISTEN: + case P2P_PREQ_NOT_PROCESSED: + default: /* make gcc happy */ + return 0; + case P2P_PREQ_PROCESSED: + return 1; + } +} + + +void wpas_p2p_rx_action(struct wpa_supplicant *wpa_s, const u8 *da, + const u8 *sa, const u8 *bssid, + u8 category, const u8 *data, size_t len, int freq) +{ + if (wpa_s->global->p2p_disabled) + return; + if (wpa_s->global->p2p == NULL) + return; + + p2p_rx_action(wpa_s->global->p2p, da, sa, bssid, category, data, len, + freq); +} + + +void wpas_p2p_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ies) +{ + if (wpa_s->global->p2p_disabled) + return; + if (wpa_s->global->p2p == NULL) + return; + + p2p_scan_ie(wpa_s->global->p2p, ies, NULL); +} + + +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, int freq, + int ht40) +{ + enum p2p_invite_role role; + u8 *bssid = NULL; + + wpa_s->p2p_persistent_go_freq = freq; + wpa_s->p2p_go_ht40 = !!ht40; + if (ssid->mode == WPAS_MODE_P2P_GO) { + role = P2P_INVITE_ROLE_GO; + if (peer_addr == NULL) { + wpa_printf(MSG_DEBUG, "P2P: Missing peer " + "address in invitation command"); + return -1; + } + if (wpas_p2p_create_iface(wpa_s)) { + if (wpas_p2p_add_group_interface(wpa_s, + WPA_IF_P2P_GO) < 0) { + wpa_printf(MSG_ERROR, "P2P: Failed to " + "allocate a new interface for the " + "group"); + return -1; + } + bssid = wpa_s->pending_interface_addr; + } else + bssid = wpa_s->own_addr; + } else { + role = P2P_INVITE_ROLE_CLIENT; + peer_addr = ssid->bssid; + } + wpa_s->pending_invite_ssid_id = ssid->id; + + 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, freq, 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; + + wpa_s->p2p_persistent_go_freq = 0; + wpa_s->p2p_go_ht40 = 0; + + for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { + if (os_strcmp(wpa_s->ifname, ifname) == 0) + break; + } + if (wpa_s == NULL) { + wpa_printf(MSG_DEBUG, "P2P: Interface '%s' not found", ifname); + return -1; + } + + ssid = wpa_s->current_ssid; + if (ssid == NULL) { + wpa_printf(MSG_DEBUG, "P2P: No current SSID to use for " + "invitation"); + return -1; + } + + 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 (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION) { + eloop_cancel_timeout(wpas_p2p_group_formation_timeout, + wpa_s->parent, NULL); + } + + if (!wpa_s->show_group_started || !ssid) + goto done; + + 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); + +done: + if (wpa_s->global->p2p_cb_on_scan_complete && !wpa_s->global->p2p_disabled && + wpa_s->global->p2p != NULL) { + wpa_s->global->p2p_cb_on_scan_complete = 0; + if (p2p_other_scan_completed(wpa_s->global->p2p) == 1) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Pending P2P operation " + "continued after successful connection"); + p2p_increase_search_delay( + wpa_s->global->p2p, + wpas_p2p_search_delay(wpa_s)); + } + } +} + + +int wpas_p2p_presence_req(struct wpa_supplicant *wpa_s, u32 duration1, + u32 interval1, u32 duration2, u32 interval2) +{ + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) + return -1; + 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) +{ + if (wpa_s->current_ssid == NULL) { + /* + * current_ssid can be cleared when P2P client interface gets + * disconnected, so assume this interface was used as P2P + * client. + */ + return 1; + } + return wpa_s->current_ssid->p2p_group && + wpa_s->current_ssid->mode == WPAS_MODE_INFRA; +} + + +static void wpas_p2p_group_idle_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + + if (wpa_s->conf->p2p_group_idle == 0 && !wpas_p2p_is_client(wpa_s)) { + wpa_printf(MSG_DEBUG, "P2P: Ignore group idle timeout - " + "disabled"); + return; + } + + wpa_printf(MSG_DEBUG, "P2P: Group idle timeout reached - terminate " + "group"); + wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_IDLE_TIMEOUT); +} + + +static void wpas_p2p_set_group_idle_timeout(struct wpa_supplicant *wpa_s) +{ + int timeout; + + if (eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL) > 0) + wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group idle timeout"); + + if (wpa_s->current_ssid == NULL || !wpa_s->current_ssid->p2p_group) + return; + + timeout = wpa_s->conf->p2p_group_idle; + if (wpa_s->current_ssid->mode == WPAS_MODE_INFRA && + (timeout == 0 || timeout > P2P_MAX_CLIENT_IDLE)) + timeout = P2P_MAX_CLIENT_IDLE; + + if (timeout == 0) + return; + + if (timeout < 0) { + if (wpa_s->current_ssid->mode == WPAS_MODE_INFRA) + timeout = 0; /* special client mode no-timeout */ + else + return; + } + + if (wpa_s->p2p_in_provisioning) { + /* + * Use the normal group formation timeout during the + * provisioning phase to avoid terminating this process too + * early due to group idle timeout. + */ + wpa_printf(MSG_DEBUG, "P2P: Do not use P2P group idle timeout " + "during provisioning"); + return; + } + + if (wpa_s->show_group_started) { + /* + * Use the normal group formation timeout between the end of + * the provisioning phase and completion of 4-way handshake to + * avoid terminating this process too early due to group idle + * timeout. + */ + wpa_printf(MSG_DEBUG, "P2P: Do not use P2P group idle timeout " + "while waiting for initial 4-way handshake to " + "complete"); + return; + } + + wpa_printf(MSG_DEBUG, "P2P: Set P2P group idle timeout to %u seconds", + timeout); + eloop_register_timeout(timeout, 0, wpas_p2p_group_idle_timeout, + wpa_s, NULL); +} + + +/* Returns 1 if the interface was removed */ +int wpas_p2p_deauth_notif(struct wpa_supplicant *wpa_s, const u8 *bssid, + u16 reason_code, const u8 *ie, size_t ie_len, + int locally_generated) +{ + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return 0; + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) + return 0; + + if (!locally_generated) + p2p_deauth_notif(wpa_s->global->p2p, bssid, reason_code, ie, + ie_len); + + if (reason_code == WLAN_REASON_DEAUTH_LEAVING && !locally_generated && + wpa_s->current_ssid && + wpa_s->current_ssid->p2p_group && + wpa_s->current_ssid->mode == WPAS_MODE_INFRA) { + wpa_printf(MSG_DEBUG, "P2P: GO indicated that the P2P Group " + "session is ending"); + if (wpas_p2p_group_delete(wpa_s, + P2P_GROUP_REMOVAL_GO_ENDING_SESSION) + > 0) + return 1; + } + + return 0; +} + + +void wpas_p2p_disassoc_notif(struct wpa_supplicant *wpa_s, const u8 *bssid, + u16 reason_code, const u8 *ie, size_t ie_len, + int locally_generated) +{ + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return; + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) + return; + + if (!locally_generated) + p2p_disassoc_notif(wpa_s->global->p2p, bssid, reason_code, ie, + ie_len); +} + + +void wpas_p2p_update_config(struct wpa_supplicant *wpa_s) +{ + struct p2p_data *p2p = wpa_s->global->p2p; + + if (p2p == NULL) + return; + + if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE)) + return; + + if (wpa_s->conf->changed_parameters & CFG_CHANGED_DEVICE_NAME) + p2p_set_dev_name(p2p, wpa_s->conf->device_name); + + if (wpa_s->conf->changed_parameters & CFG_CHANGED_DEVICE_TYPE) + p2p_set_pri_dev_type(p2p, wpa_s->conf->device_type); + + if (wpa_s->wps && + (wpa_s->conf->changed_parameters & CFG_CHANGED_CONFIG_METHODS)) + p2p_set_config_methods(p2p, wpa_s->wps->config_methods); + + if (wpa_s->wps && (wpa_s->conf->changed_parameters & CFG_CHANGED_UUID)) + p2p_set_uuid(p2p, wpa_s->wps->uuid); + + if (wpa_s->conf->changed_parameters & CFG_CHANGED_WPS_STRING) { + p2p_set_manufacturer(p2p, wpa_s->conf->manufacturer); + p2p_set_model_name(p2p, wpa_s->conf->model_name); + p2p_set_model_number(p2p, wpa_s->conf->model_number); + p2p_set_serial_number(p2p, wpa_s->conf->serial_number); + } + + if (wpa_s->conf->changed_parameters & CFG_CHANGED_SEC_DEVICE_TYPE) + p2p_set_sec_dev_types(p2p, + (void *) wpa_s->conf->sec_device_type, + wpa_s->conf->num_sec_device_types); + + if (wpa_s->conf->changed_parameters & CFG_CHANGED_VENDOR_EXTENSION) { + int i; + p2p_remove_wps_vendor_extensions(p2p); + for (i = 0; i < MAX_WPS_VENDOR_EXT; i++) { + if (wpa_s->conf->wps_vendor_ext[i] == NULL) + continue; + p2p_add_wps_vendor_extension( + p2p, wpa_s->conf->wps_vendor_ext[i]); + } + } + + if ((wpa_s->conf->changed_parameters & CFG_CHANGED_COUNTRY) && + wpa_s->conf->country[0] && wpa_s->conf->country[1]) { + char country[3]; + country[0] = wpa_s->conf->country[0]; + country[1] = wpa_s->conf->country[1]; + country[2] = 0x04; + p2p_set_country(p2p, country); + } + + if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_SSID_POSTFIX) { + p2p_set_ssid_postfix(p2p, (u8 *) wpa_s->conf->p2p_ssid_postfix, + wpa_s->conf->p2p_ssid_postfix ? + os_strlen(wpa_s->conf->p2p_ssid_postfix) : + 0); + } + + if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_INTRA_BSS) + p2p_set_intra_bss_dist(p2p, wpa_s->conf->p2p_intra_bss); + + if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_LISTEN_CHANNEL) { + u8 reg_class, channel; + int ret; + unsigned int r; + 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) > 0) + wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group idle timeout"); +} + + +void wpas_p2p_notif_disconnected(struct wpa_supplicant *wpa_s) +{ + wpas_p2p_disable_cross_connect(wpa_s); + if (!wpa_s->ap_iface && + !eloop_is_timeout_registered(wpas_p2p_group_idle_timeout, + wpa_s, NULL)) + wpas_p2p_set_group_idle_timeout(wpa_s); +} + + +static void wpas_p2p_cross_connect_setup(struct wpa_supplicant *wpa_s) +{ + struct wpa_supplicant *iface; + + if (!wpa_s->global->cross_connection) + return; + + for (iface = wpa_s->global->ifaces; iface; iface = iface->next) { + if (iface == wpa_s) + continue; + if (iface->drv_flags & + WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE) + continue; + if (iface->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE) + 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); +} + + +static void wpas_p2p_scan_res_ignore(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res) +{ + wpa_printf(MSG_DEBUG, "P2P: Ignore scan results"); +} + + +int wpas_p2p_cancel(struct wpa_supplicant *wpa_s) +{ + struct wpa_global *global = wpa_s->global; + int found = 0; + const u8 *peer; + + if (global->p2p == NULL) + return -1; + + wpa_printf(MSG_DEBUG, "P2P: Request to cancel group formation"); + + if (wpa_s->pending_interface_name[0] && + !is_zero_ether_addr(wpa_s->pending_interface_addr)) + found = 1; + + peer = p2p_get_go_neg_peer(global->p2p); + if (peer) { + wpa_printf(MSG_DEBUG, "P2P: Unauthorize pending GO Neg peer " + MACSTR, MAC2STR(peer)); + p2p_unauthorize(global->p2p, peer); + found = 1; + } + + if (wpa_s->scan_res_handler == wpas_p2p_scan_res_join) { + wpa_printf(MSG_DEBUG, "P2P: Stop pending scan for join"); + wpa_s->scan_res_handler = wpas_p2p_scan_res_ignore; + found = 1; + } + + if (wpa_s->pending_pd_before_join) { + wpa_printf(MSG_DEBUG, "P2P: Stop pending PD before join"); + wpa_s->pending_pd_before_join = 0; + found = 1; + } + + wpas_p2p_stop_find(wpa_s); + + for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { + if (wpa_s == global->p2p_group_formation && + (wpa_s->p2p_in_provisioning || + wpa_s->parent->pending_interface_type == + WPA_IF_P2P_CLIENT)) { + wpa_printf(MSG_DEBUG, "P2P: Interface %s in group " + "formation found - cancelling", + wpa_s->ifname); + found = 1; + eloop_cancel_timeout(wpas_p2p_group_formation_timeout, + wpa_s->parent, NULL); + if (wpa_s->p2p_in_provisioning) { + wpas_group_formation_completed(wpa_s, 0); + break; + } + wpas_p2p_group_delete(wpa_s, + P2P_GROUP_REMOVAL_REQUESTED); + 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"); + wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_UNAVAILABLE); +} + + +void wpas_p2p_update_best_channels(struct wpa_supplicant *wpa_s, + int freq_24, int freq_5, int freq_overall) +{ + struct p2p_data *p2p = wpa_s->global->p2p; + if (p2p == NULL || (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; + + return wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_REQUESTED) < 0 ? + -1 : 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); +} + + +static void wpas_p2p_fallback_to_go_neg(struct wpa_supplicant *wpa_s, + int group_added) +{ + struct wpa_supplicant *group = wpa_s; + if (wpa_s->global->p2p_group_formation) + group = wpa_s->global->p2p_group_formation; + wpa_s = wpa_s->parent; + offchannel_send_action_done(wpa_s); + if (group_added) + wpas_p2p_group_delete(group, P2P_GROUP_REMOVAL_SILENT); + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Fall back to GO Negotiation"); + wpas_p2p_connect(wpa_s, wpa_s->pending_join_dev_addr, wpa_s->p2p_pin, + wpa_s->p2p_wps_method, wpa_s->p2p_persistent_group, 0, + 0, 0, wpa_s->p2p_go_intent, wpa_s->p2p_connect_freq, + wpa_s->p2p_persistent_id, + wpa_s->p2p_pd_before_go_neg, + wpa_s->p2p_go_ht40); +} + + +int wpas_p2p_scan_no_go_seen(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s->p2p_fallback_to_go_neg || + wpa_s->p2p_in_provisioning <= 5) + return 0; + + if (wpas_p2p_peer_go(wpa_s, wpa_s->pending_join_dev_addr) > 0) + return 0; /* peer operating as a GO */ + + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: GO not found for p2p_connect-auto - " + "fallback to GO Negotiation"); + wpas_p2p_fallback_to_go_neg(wpa_s, 1); + + return 1; +} + + +unsigned int wpas_p2p_search_delay(struct wpa_supplicant *wpa_s) +{ + const char *rn, *rn2; + struct wpa_supplicant *ifs; + + if (wpa_s->wpa_state > WPA_SCANNING) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use %u ms search delay due to " + "concurrent operation", + P2P_CONCURRENT_SEARCH_DELAY); + return P2P_CONCURRENT_SEARCH_DELAY; + } + + if (!wpa_s->driver->get_radio_name) + return 0; + rn = wpa_s->driver->get_radio_name(wpa_s->drv_priv); + if (rn == NULL || rn[0] == '\0') + return 0; + + for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) { + if (ifs == wpa_s || !ifs->driver->get_radio_name) + continue; + + rn2 = ifs->driver->get_radio_name(ifs->drv_priv); + if (!rn2 || os_strcmp(rn, rn2) != 0) + continue; + if (ifs->wpa_state > WPA_SCANNING) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use %u ms search " + "delay due to concurrent operation on " + "interface %s", + P2P_CONCURRENT_SEARCH_DELAY, ifs->ifname); + return P2P_CONCURRENT_SEARCH_DELAY; + } + } + + return 0; +} diff --git a/contrib/wpa/wpa_supplicant/p2p_supplicant.h b/contrib/wpa/wpa_supplicant/p2p_supplicant.h new file mode 100644 index 0000000..b6ecf14 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/p2p_supplicant.h @@ -0,0 +1,151 @@ +/* + * wpa_supplicant - P2P + * Copyright (c) 2009-2010, Atheros Communications + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef P2P_SUPPLICANT_H +#define P2P_SUPPLICANT_H + +enum p2p_wps_method; +struct p2p_go_neg_results; +enum p2p_send_action_result; +struct p2p_peer_info; + +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 auto_join, int join, + int auth, int go_intent, int freq, int persistent_id, + int pd, int ht40); +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 ht40); +int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, int addr_allocated, + int freq, int ht40); +struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid); +void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr, + int registrar); +enum wpas_p2p_prov_disc_use { + WPAS_P2P_PD_FOR_GO_NEG, + WPAS_P2P_PD_FOR_JOIN, + WPAS_P2P_PD_AUTO +}; +int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr, + const char *config_method, + enum wpas_p2p_prov_disc_use use); +void wpas_send_action_tx_status(struct wpa_supplicant *wpa_s, const u8 *dst, + const u8 *data, size_t data_len, + enum p2p_send_action_result result); +int wpas_p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf, + char *end); +enum p2p_discovery_type; +int wpas_p2p_find(struct wpa_supplicant *wpa_s, unsigned int timeout, + enum p2p_discovery_type type, + unsigned int num_req_dev_types, const u8 *req_dev_types, + const u8 *dev_id, unsigned int search_delay); +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, + int ssi_signal); +void wpas_p2p_rx_action(struct wpa_supplicant *wpa_s, const u8 *da, + const u8 *sa, const u8 *bssid, + u8 category, const u8 *data, size_t len, int freq); +void wpas_p2p_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ies); +void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s); +void wpas_dev_found(void *ctx, const u8 *addr, + const struct p2p_peer_info *info, + int new_device); +void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res); +void wpas_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id); +void wpas_prov_disc_req(void *ctx, const u8 *peer, u16 config_methods, + const u8 *dev_addr, const u8 *pri_dev_type, + const char *dev_name, u16 supp_config_methods, + u8 dev_capab, u8 group_capab, const u8 *group_id, + size_t group_id_len); +void wpas_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods); +void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token, + u16 update_indic, const u8 *tlvs, size_t tlvs_len); +void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic, + const u8 *tlvs, size_t tlvs_len); +u64 wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst, + const struct wpabuf *tlvs); +u64 wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst, + u8 version, const char *query); +u64 wpas_p2p_sd_request_wifi_display(struct wpa_supplicant *wpa_s, + const u8 *dst, const char *role); +int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, u64 req); +void wpas_p2p_sd_response(struct wpa_supplicant *wpa_s, int freq, + const u8 *dst, u8 dialog_token, + const struct wpabuf *resp_tlvs); +void wpas_p2p_sd_service_update(struct wpa_supplicant *wpa_s); +void wpas_p2p_service_flush(struct wpa_supplicant *wpa_s); +int wpas_p2p_service_add_bonjour(struct wpa_supplicant *wpa_s, + struct wpabuf *query, struct wpabuf *resp); +int wpas_p2p_service_del_bonjour(struct wpa_supplicant *wpa_s, + const struct wpabuf *query); +int wpas_p2p_service_add_upnp(struct wpa_supplicant *wpa_s, u8 version, + const char *service); +int wpas_p2p_service_del_upnp(struct wpa_supplicant *wpa_s, u8 version, + const char *service); +int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *addr); +int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr, + struct wpa_ssid *ssid, const u8 *go_dev_addr, int freq, + int ht40); +int 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); +int wpas_p2p_deauth_notif(struct wpa_supplicant *wpa_s, const u8 *bssid, + u16 reason_code, const u8 *ie, size_t ie_len, + int locally_generated); +void wpas_p2p_disassoc_notif(struct wpa_supplicant *wpa_s, const u8 *bssid, + u16 reason_code, const u8 *ie, size_t ie_len, + int locally_generated); +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); +int wpas_p2p_scan_no_go_seen(struct wpa_supplicant *wpa_s); +int wpas_p2p_get_ht40_mode(struct wpa_supplicant *wpa_s, + struct hostapd_hw_modes *mode, u8 channel); +unsigned int wpas_p2p_search_delay(struct wpa_supplicant *wpa_s); + +#endif /* P2P_SUPPLICANT_H */ diff --git a/contrib/wpa/wpa_supplicant/preauth_test.c b/contrib/wpa/wpa_supplicant/preauth_test.c index d38a6bb..3503e65 100644 --- a/contrib/wpa/wpa_supplicant/preauth_test.c +++ b/contrib/wpa/wpa_supplicant/preauth_test.c @@ -2,14 +2,8 @@ * WPA Supplicant - test code for pre-authentication * Copyright (c) 2003-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. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * IEEE 802.1X Supplicant test code (to be used in place of wpa_supplicant.c. * Not used in production version. @@ -44,12 +38,6 @@ struct preauth_test_data { }; -static void _wpa_supplicant_disassociate(void *wpa_s, int reason_code) -{ - wpa_supplicant_disassociate(wpa_s, reason_code); -} - - static void _wpa_supplicant_deauthenticate(void *wpa_s, int reason_code) { wpa_supplicant_deauthenticate(wpa_s, reason_code); @@ -244,7 +232,6 @@ static void wpa_init_conf(struct wpa_supplicant *wpa_s, const char *ifname) ctx->set_state = _wpa_supplicant_set_state; ctx->get_state = _wpa_supplicant_get_state; ctx->deauthenticate = _wpa_supplicant_deauthenticate; - ctx->disassociate = _wpa_supplicant_disassociate; ctx->set_key = wpa_supplicant_set_key; ctx->get_network_ctx = wpa_supplicant_get_network_ctx; ctx->get_bssid = wpa_supplicant_get_bssid; diff --git a/contrib/wpa/wpa_supplicant/scan.c b/contrib/wpa/wpa_supplicant/scan.c index edc8c83..d2b671a 100644 --- a/contrib/wpa/wpa_supplicant/scan.c +++ b/contrib/wpa/wpa_supplicant/scan.c @@ -1,15 +1,9 @@ /* * WPA Supplicant - Scanning - * 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 - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -20,8 +14,10 @@ #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 "hs20_supplicant.h" #include "notify.h" #include "bss.h" #include "scan.h" @@ -42,21 +38,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,20 +65,50 @@ 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->conf->p2p_disabled) { + wpa_s->wps->dev.p2p = 1; + if (!wps) { + wps = 1; + *req_type = WPS_REQ_ENROLLEE_INFO; + } + } +#endif /* CONFIG_P2P */ + return wps; } #endif /* CONFIG_WPS */ -int wpa_supplicant_enabled_networks(struct wpa_config *conf) +/** + * wpa_supplicant_enabled_networks - Check whether there are enabled networks + * @wpa_s: Pointer to wpa_supplicant data + * Returns: 0 if no networks are enabled, >0 if networks are enabled + * + * This function is used to figure out whether any networks (or Interworking + * with enabled credentials and auto_interworking) are present in the current + * configuration. + */ +int wpa_supplicant_enabled_networks(struct wpa_supplicant *wpa_s) { - struct wpa_ssid *ssid = conf->ssid; + struct wpa_ssid *ssid = wpa_s->conf->ssid; + int count = 0, disabled = 0; while (ssid) { - if (!ssid->disabled) - return 1; + if (!wpas_network_disabled(wpa_s, ssid)) + count++; + else + disabled++; ssid = ssid->next; } - return 0; + if (wpa_s->conf->cred && wpa_s->conf->interworking && + wpa_s->conf->auto_interworking) + count++; + if (count == 0 && disabled > 0) { + wpa_dbg(wpa_s, MSG_DEBUG, "No enabled networks (%d disabled " + "networks)", disabled); + } + return count; } @@ -90,15 +116,15 @@ static void wpa_supplicant_assoc_try(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { while (ssid) { - if (!ssid->disabled) + if (!wpas_network_disabled(wpa_s, ssid)) break; ssid = ssid->next; } /* ap_scan=2 mode - try to associate with each SSID. */ if (ssid == NULL) { - wpa_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; @@ -131,7 +157,7 @@ static void int_array_concat(int **res, const int *a) reslen = int_array_len(*res); alen = int_array_len(a); - n = os_realloc(*res, (reslen + alen + 1) * sizeof(int)); + n = os_realloc_array(*res, reslen + alen + 1, sizeof(int)); if (n == NULL) { os_free(*res); *res = NULL; @@ -182,6 +208,12 @@ static void int_array_sort_unique(int *a) } +/** + * wpa_supplicant_trigger_scan - Request driver to start a scan + * @wpa_s: Pointer to wpa_supplicant data + * @params: Scan parameters + * Returns: 0 on success, -1 on failure + */ int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s, struct wpa_driver_scan_params *params) { @@ -189,16 +221,71 @@ 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); - } else + } else { wpa_s->scan_runs++; + wpa_s->normal_scans++; + } + + return ret; +} + + +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; } @@ -237,36 +324,235 @@ 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(¶ms->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--; + } + + if (params->freqs == NULL && wpa_s->known_wps_freq && wpa_s->wps_freq) + { + /* Optimize provisioning scan based on already known channel */ + wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Scan only frequency %u MHz", + wpa_s->wps_freq); + params->freqs = os_zalloc(2 * sizeof(int)); + if (params->freqs) + params->freqs[0] = wpa_s->wps_freq; + wpa_s->known_wps_freq = 0; /* only do this once */ + } +#endif /* CONFIG_WPS */ +} + + +#ifdef CONFIG_INTERWORKING +static void wpas_add_interworking_elements(struct wpa_supplicant *wpa_s, + struct wpabuf *buf) +{ + 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 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 ? DEV_PW_PUSHBUTTON : + DEV_PW_DEFAULT, + &wpa_s->wps->dev, + wpa_s->wps->uuid, req_type, + 0, NULL); + if (wps_ie) { + if (wpabuf_resize(&extra_ie, wpabuf_len(wps_ie)) == 0) + wpabuf_put_buf(extra_ie, wps_ie); + wpabuf_free(wps_ie); + } + } + +#ifdef CONFIG_P2P + if (wps) { + size_t ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p); + if (wpabuf_resize(&extra_ie, ielen) == 0) + wpas_p2p_scan_ie(wpa_s, extra_ie); + } +#endif /* CONFIG_P2P */ + +#endif /* CONFIG_WPS */ + + return extra_ie; +} + + +#ifdef CONFIG_P2P + +/* + * Check whether there are any enabled networks or credentials that could be + * used for a non-P2P connection. + */ +static int non_p2p_network_enabled(struct wpa_supplicant *wpa_s) +{ + struct wpa_ssid *ssid; + + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { + if (wpas_network_disabled(wpa_s, ssid)) + continue; + if (!ssid->p2p_group) + return 1; + } + + if (wpa_s->conf->cred && wpa_s->conf->interworking && + wpa_s->conf->auto_interworking) + return 1; + + return 0; +} + + +/* + * Find the operating frequency of any other virtual interface that is using + * the same radio concurrently. + */ +static int shared_vif_oper_freq(struct wpa_supplicant *wpa_s) +{ + const char *rn, *rn2; + struct wpa_supplicant *ifs; + u8 bssid[ETH_ALEN]; + + if (!wpa_s->driver->get_radio_name) + return -1; + + rn = wpa_s->driver->get_radio_name(wpa_s->drv_priv); + if (rn == NULL || rn[0] == '\0') + return -1; + + for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) { + if (ifs == wpa_s || !ifs->driver->get_radio_name) + continue; + + rn2 = ifs->driver->get_radio_name(ifs->drv_priv); + if (!rn2 || os_strcmp(rn, rn2) != 0) + continue; + + if (ifs->current_ssid == NULL || ifs->assoc_freq == 0) + continue; + + if (ifs->current_ssid->mode == WPAS_MODE_AP || + ifs->current_ssid->mode == WPAS_MODE_P2P_GO) + return ifs->current_ssid->frequency; + if (wpa_drv_get_bssid(ifs, bssid) == 0) + return ifs->assoc_freq; + } + + return 0; +} + +#endif /* CONFIG_P2P */ + + +static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct wpa_ssid *ssid; + enum scan_req_type scan_req = NORMAL_SCAN_REQ; + int ret; + struct wpabuf *extra_ie = NULL; struct wpa_driver_scan_params params; + struct wpa_driver_scan_params *scan_params; size_t max_ssids; enum wpa_states prev_state; - if (wpa_s->disconnected && !wpa_s->scan_req) { + if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) { + wpa_dbg(wpa_s, MSG_DEBUG, "Skip scan - interface disabled"); + return; + } + + if (wpa_s->disconnected && wpa_s->scan_req == NORMAL_SCAN_REQ) { + wpa_dbg(wpa_s, MSG_DEBUG, "Disconnected - do not scan"); wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); return; } - if (!wpa_supplicant_enabled_networks(wpa_s->conf) && - !wpa_s->scan_req) { - wpa_printf(MSG_DEBUG, "No enabled networks - do not scan"); + if (!wpa_supplicant_enabled_networks(wpa_s) && + wpa_s->scan_req == NORMAL_SCAN_REQ) { + wpa_dbg(wpa_s, MSG_DEBUG, "No enabled networks - do not scan"); wpa_supplicant_set_state(wpa_s, WPA_INACTIVE); +#ifdef CONFIG_P2P + wpa_s->sta_scan_pending = 0; +#endif /* CONFIG_P2P */ 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 +562,24 @@ 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->sta_scan_pending && + wpas_p2p_in_progress(wpa_s) == 2 && + wpa_s->global->p2p_cb_on_scan_complete) { + wpa_dbg(wpa_s, MSG_DEBUG, "Process pending station " + "mode scan during P2P search"); + } else { + wpa_dbg(wpa_s, MSG_DEBUG, "Delay station mode scan " + "while P2P operation is in progress"); + wpa_s->sta_scan_pending = 1; + wpa_supplicant_req_scan(wpa_s, 5, 0); + return; + } + } +#endif /* CONFIG_P2P */ + + if (wpa_s->conf->ap_scan == 2) max_ssids = 1; else { max_ssids = wpa_s->max_scan_ssids; @@ -285,12 +587,8 @@ 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; + wpa_s->scan_req = NORMAL_SCAN_REQ; os_memset(¶ms, 0, sizeof(params)); @@ -299,6 +597,40 @@ 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 autoscan has set its own scanning parameters + */ + if (wpa_s->autoscan_params != NULL) { + scan_params = wpa_s->autoscan_params; + goto scan; + } + + if (scan_req != MANUAL_SCAN_REQ && wpa_s->connect_without_scan) { + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { + if (ssid == wpa_s->connect_without_scan) + break; + } + wpa_s->connect_without_scan = NULL; + if (ssid) { + wpa_printf(MSG_DEBUG, "Start a pre-selected network " + "without scan step"); + wpa_supplicant_associate(wpa_s, NULL, ssid); + return; + } + } + +#ifdef CONFIG_P2P + if ((wpa_s->p2p_in_provisioning || wpa_s->show_group_started) && + wpa_s->go_params) { + wpa_printf(MSG_DEBUG, "P2P: Use specific SSID for scan during " + "P2P group formation"); + params.ssids[0].ssid = wpa_s->go_params->ssid; + params.ssids[0].ssid_len = wpa_s->go_params->ssid_len; + params.num_ssids = 1; + goto ssid_list_set; + } +#endif /* CONFIG_P2P */ + /* Find the starting point from which to continue scanning */ ssid = wpa_s->conf->ssid; if (wpa_s->prev_scan_ssid != WILDCARD_SSID_SCAN) { @@ -311,9 +643,9 @@ 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 != MANUAL_SCAN_REQ && wpa_s->conf->ap_scan == 2) { + wpa_s->connect_without_scan = NULL; + wpa_s->prev_scan_wildcard = 0; wpa_supplicant_assoc_try(wpa_s, ssid); return; } else if (wpa_s->conf->ap_scan == 2) { @@ -328,7 +660,8 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) if (ssid == NULL && max_ssids > 1) ssid = wpa_s->conf->ssid; while (ssid) { - if (!ssid->disabled && ssid->scan_ssid) { + if (!wpas_network_disabled(wpa_s, ssid) && + ssid->scan_ssid) { wpa_hexdump_ascii(MSG_DEBUG, "Scan SSID", ssid->ssid, ssid->ssid_len); params.ssids[params.num_ssids].ssid = @@ -348,7 +681,7 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) } for (tssid = wpa_s->conf->ssid; tssid; tssid = tssid->next) { - if (tssid->disabled) + if (wpas_network_disabled(wpa_s, tssid)) continue; if ((params.freqs || !freqs_set) && tssid->scan_freq) { int_array_concat(¶ms.freqs, @@ -362,58 +695,123 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) int_array_sort_unique(params.freqs); } - if (ssid) { - wpa_s->prev_scan_ssid = ssid; - if (max_ssids > 1) { - wpa_printf(MSG_DEBUG, "Include wildcard SSID in the " - "scan request"); - params.num_ssids++; + if (ssid && max_ssids == 1) { + /* + * If the driver is limited to 1 SSID at a time interleave + * wildcard SSID scans with specific SSID scans to avoid + * waiting a long time for a wildcard scan. + */ + if (!wpa_s->prev_scan_wildcard) { + params.ssids[0].ssid = NULL; + params.ssids[0].ssid_len = 0; + wpa_s->prev_scan_wildcard = 1; + wpa_dbg(wpa_s, MSG_DEBUG, "Starting AP scan for " + "wildcard SSID (Interleave with specific)"); + } else { + wpa_s->prev_scan_ssid = ssid; + wpa_s->prev_scan_wildcard = 0; + wpa_dbg(wpa_s, MSG_DEBUG, + "Starting AP scan for specific SSID: %s", + wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); } - wpa_printf(MSG_DEBUG, "Starting AP scan for specific SSID(s)"); + } else if (ssid) { + /* max_ssids > 1 */ + + wpa_s->prev_scan_ssid = ssid; + wpa_dbg(wpa_s, MSG_DEBUG, "Include wildcard SSID in " + "the scan request"); + params.num_ssids++; } else { 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_P2P +ssid_list_set: +#endif /* CONFIG_P2P */ + + wpa_supplicant_optimize_freqs(wpa_s, ¶ms); + extra_ie = wpa_supplicant_extra_ies(wpa_s); + +#ifdef CONFIG_HS20 + if (wpa_s->conf->hs20 && wpabuf_resize(&extra_ie, 6) == 0) + wpas_hs20_add_indication(extra_ie); +#endif /* CONFIG_HS20 */ + + 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; -#ifdef CONFIG_WPS - if (params.freqs == NULL && wpa_s->after_wps && wpa_s->wps_freq) { + params.filter_ssids = wpa_supplicant_build_filter_ssids( + wpa_s->conf, ¶ms.num_filter_ssids); + if (extra_ie) { + params.extra_ies = wpabuf_head(extra_ie); + params.extra_ies_len = wpabuf_len(extra_ie); + } + +#ifdef CONFIG_P2P + if (wpa_s->p2p_in_provisioning || + (wpa_s->show_group_started && wpa_s->go_params)) { /* - * Optimize post-provisioning scan based on channel used - * during provisioning. + * The interface may not yet be in P2P mode, so we have to + * explicitly request P2P probe to disable CCK rates. */ - 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--; + params.p2p_probe = 1; } - - 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_P2P */ + + scan_params = ¶ms; + +scan: +#ifdef CONFIG_P2P + /* + * If the driver does not support multi-channel concurrency and a + * virtual interface that shares the same radio with the wpa_s interface + * is operating there may not be need to scan other channels apart from + * the current operating channel on the other virtual interface. Filter + * out other channels in case we are trying to find a connection for a + * station interface when we are not configured to prefer station + * connection and a concurrent operation is already in process. + */ + if (wpa_s->scan_for_connection && scan_req == NORMAL_SCAN_REQ && + !scan_params->freqs && !params.freqs && + wpas_is_p2p_prioritized(wpa_s) && + !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT) && + wpa_s->p2p_group_interface == NOT_P2P_GROUP_INTERFACE && + non_p2p_network_enabled(wpa_s)) { + int freq = shared_vif_oper_freq(wpa_s); + if (freq > 0) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only the current " + "operating channel (%d MHz) since driver does " + "not support multi-channel concurrency", freq); + params.freqs = os_zalloc(sizeof(int) * 2); + if (params.freqs) + params.freqs[0] = freq; + scan_params->freqs = params.freqs; } } -#endif /* CONFIG_WPS */ - - params.filter_ssids = wpa_supplicant_build_filter_ssids( - wpa_s->conf, ¶ms.num_filter_ssids); +#endif /* CONFIG_P2P */ - ret = wpa_supplicant_trigger_scan(wpa_s, ¶ms); + ret = wpa_supplicant_trigger_scan(wpa_s, scan_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); + /* Restore scan_req since we will try to scan again */ + wpa_s->scan_req = scan_req; wpa_supplicant_req_scan(wpa_s, 1, 0); + } else { + wpa_s->scan_for_connection = 0; } } @@ -440,18 +838,19 @@ void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec) struct wpa_ssid *ssid = wpa_s->conf->ssid; while (ssid) { - if (!ssid->disabled && ssid->scan_ssid) + if (!wpas_network_disabled(wpa_s, ssid) && + ssid->scan_ssid) break; 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 +858,251 @@ 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 + * Returns: 0 on success or -1 otherwise + * + * This function is used to schedule periodic scans for neighboring + * access points after the specified time. + */ +int wpa_supplicant_delayed_sched_scan(struct wpa_supplicant *wpa_s, + int sec, int usec) +{ + if (!wpa_s->sched_scan_supported) + return -1; + + eloop_register_timeout(sec, usec, + wpa_supplicant_delayed_sched_scan_timeout, + wpa_s, NULL); + + return 0; +} + + +/** + * wpa_supplicant_req_sched_scan - Start a periodic scheduled scan + * @wpa_s: Pointer to wpa_supplicant data + * Returns: 0 is sched_scan was started or -1 otherwise + * + * This function is used to schedule periodic scans for neighboring + * access points repeating the scan continuously. + */ +int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s) +{ + struct wpa_driver_scan_params params; + struct wpa_driver_scan_params *scan_params; + enum wpa_states prev_state; + struct wpa_ssid *ssid = NULL; + struct wpabuf *extra_ie = NULL; + int ret; + unsigned int max_sched_scan_ssids; + int wildcard = 0; + int need_ssids; + + if (!wpa_s->sched_scan_supported) + return -1; + + if (wpa_s->max_sched_scan_ssids > WPAS_MAX_SCAN_SSIDS) + max_sched_scan_ssids = WPAS_MAX_SCAN_SSIDS; + else + max_sched_scan_ssids = wpa_s->max_sched_scan_ssids; + if (max_sched_scan_ssids < 1 || wpa_s->conf->disable_scan_offload) + return -1; + + if (wpa_s->sched_scanning) { + wpa_dbg(wpa_s, MSG_DEBUG, "Already sched scanning"); + return 0; + } + + need_ssids = 0; + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { + if (!wpas_network_disabled(wpa_s, ssid) && !ssid->scan_ssid) { + /* Use wildcard SSID to find this network */ + wildcard = 1; + } else if (!wpas_network_disabled(wpa_s, ssid) && + ssid->ssid_len) + need_ssids++; + +#ifdef CONFIG_WPS + if (!wpas_network_disabled(wpa_s, ssid) && + ssid->key_mgmt == WPA_KEY_MGMT_WPS) { + /* + * Normal scan is more reliable and faster for WPS + * operations and since these are for short periods of + * time, the benefit of trying to use sched_scan would + * be limited. + */ + wpa_dbg(wpa_s, MSG_DEBUG, "Use normal scan instead of " + "sched_scan for WPS"); + return -1; + } +#endif /* CONFIG_WPS */ + } + if (wildcard) + need_ssids++; + + if (wpa_s->normal_scans < 3 && + (need_ssids <= wpa_s->max_scan_ssids || + wpa_s->max_scan_ssids >= (int) max_sched_scan_ssids)) { + /* + * When normal scan can speed up operations, use that for the + * first operations before starting the sched_scan to allow + * user space sleep more. We do this only if the normal scan + * has functionality that is suitable for this or if the + * sched_scan does not have better support for multiple SSIDs. + */ + wpa_dbg(wpa_s, MSG_DEBUG, "Use normal scan instead of " + "sched_scan for initial scans (normal_scans=%d)", + wpa_s->normal_scans); + return -1; + } + + os_memset(¶ms, 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); + + if (wpa_s->autoscan_params != NULL) { + scan_params = wpa_s->autoscan_params; + goto scan; + } + + /* Find the starting point from which to continue scanning */ + ssid = wpa_s->conf->ssid; + if (wpa_s->prev_sched_ssid) { + while (ssid) { + if (ssid == wpa_s->prev_sched_ssid) { + ssid = ssid->next; + break; + } + ssid = ssid->next; + } + } + + if (!ssid || !wpa_s->prev_sched_ssid) { + wpa_dbg(wpa_s, MSG_DEBUG, "Beginning of SSID list"); + + if (wpa_s->sched_scan_interval == 0) + wpa_s->sched_scan_interval = 10; + wpa_s->sched_scan_timeout = max_sched_scan_ssids * 2; + wpa_s->first_sched_scan = 1; + ssid = wpa_s->conf->ssid; + wpa_s->prev_sched_ssid = ssid; + } + + if (wildcard) { + wpa_dbg(wpa_s, MSG_DEBUG, "Add wildcard SSID to sched_scan"); + params.num_ssids++; + } + + while (ssid) { + if (wpas_network_disabled(wpa_s, ssid)) + goto next; + + if (params.num_filter_ssids < wpa_s->max_match_sets && + params.filter_ssids && ssid->ssid && ssid->ssid_len) { + wpa_dbg(wpa_s, MSG_DEBUG, "add to filter ssid: %s", + wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); + os_memcpy(params.filter_ssids[params.num_filter_ssids].ssid, + ssid->ssid, ssid->ssid_len); + params.filter_ssids[params.num_filter_ssids].ssid_len = + ssid->ssid_len; + params.num_filter_ssids++; + } else if (params.filter_ssids && ssid->ssid && ssid->ssid_len) + { + wpa_dbg(wpa_s, MSG_DEBUG, "Not enough room for SSID " + "filter for sched_scan - drop filter"); + os_free(params.filter_ssids); + params.filter_ssids = NULL; + params.num_filter_ssids = 0; + } + + if (ssid->scan_ssid && ssid->ssid && ssid->ssid_len) { + if (params.num_ssids == max_sched_scan_ssids) + break; /* only room for broadcast SSID */ + wpa_dbg(wpa_s, MSG_DEBUG, + "add to active scan ssid: %s", + wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); + params.ssids[params.num_ssids].ssid = + ssid->ssid; + params.ssids[params.num_ssids].ssid_len = + ssid->ssid_len; + params.num_ssids++; + if (params.num_ssids >= max_sched_scan_ssids) { + wpa_s->prev_sched_ssid = ssid; + do { + ssid = ssid->next; + } while (ssid && + (wpas_network_disabled(wpa_s, ssid) || + !ssid->scan_ssid)); + break; + } + } + + next: + wpa_s->prev_sched_ssid = ssid; + ssid = ssid->next; + } + + if (params.num_filter_ssids == 0) { + os_free(params.filter_ssids); + params.filter_ssids = NULL; + } + + extra_ie = wpa_supplicant_extra_ies(wpa_s); + if (extra_ie) { + params.extra_ies = wpabuf_head(extra_ie); + params.extra_ies_len = wpabuf_len(extra_ie); + } + + scan_params = ¶ms; + +scan: + if (ssid || !wpa_s->first_sched_scan) { + wpa_dbg(wpa_s, MSG_DEBUG, + "Starting sched scan: interval %d timeout %d", + wpa_s->sched_scan_interval, wpa_s->sched_scan_timeout); + } else { + wpa_dbg(wpa_s, MSG_DEBUG, + "Starting sched scan: interval %d (no timeout)", + wpa_s->sched_scan_interval); + } + + ret = wpa_supplicant_start_sched_scan(wpa_s, scan_params, + wpa_s->sched_scan_interval); + wpabuf_free(extra_ie); + os_free(params.filter_ssids); + if (ret) { + wpa_msg(wpa_s, MSG_WARNING, "Failed to initiate sched scan"); + if (prev_state != wpa_s->wpa_state) + wpa_supplicant_set_state(wpa_s, prev_state); + return ret; + } + + /* If we have more SSIDs to scan, add a timeout so we scan them too */ + if (ssid || !wpa_s->first_sched_scan) { + wpa_s->sched_scan_timed_out = 0; + eloop_register_timeout(wpa_s->sched_scan_timeout, 0, + wpa_supplicant_sched_scan_timeout, + wpa_s, NULL); + wpa_s->first_sched_scan = 0; + wpa_s->sched_scan_timeout /= 2; + wpa_s->sched_scan_interval *= 2; + } + + return 0; +} + + +/** * wpa_supplicant_cancel_scan - Cancel a scheduled scan request * @wpa_s: Pointer to wpa_supplicant data * @@ -467,11 +1111,38 @@ 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); +} + + +/** + * wpa_supplicant_notify_scanning - Indicate possible scan state change + * @wpa_s: Pointer to wpa_supplicant data + * @scanning: Whether scanning is currently in progress + * + * This function is to generate scanning notifycations. It is called whenever + * there may have been a change in scanning (scan started, completed, stopped). + * wpas_notify_scanning() is called whenever the scanning state changed from the + * previously notified state. + */ void wpa_supplicant_notify_scanning(struct wpa_supplicant *wpa_s, int scanning) { @@ -504,6 +1175,15 @@ static int wpa_scan_get_max_rate(const struct wpa_scan_res *res) } +/** + * wpa_scan_get_ie - Fetch a specified information element from a scan result + * @res: Scan result entry + * @ie: Information element identitifier (WLAN_EID_*) + * Returns: Pointer to the information element (id field) or %NULL if not found + * + * This function returns the first matching information element in the scan + * result. + */ const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie) { const u8 *end, *pos; @@ -523,6 +1203,15 @@ const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie) } +/** + * wpa_scan_get_vendor_ie - Fetch vendor information element from a scan result + * @res: Scan result entry + * @vendor_type: Vendor type (four octets starting the IE payload) + * Returns: Pointer to the information element (id field) or %NULL if not found + * + * This function returns the first matching information element in the scan + * result. + */ const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res, u32 vendor_type) { @@ -544,6 +1233,16 @@ const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res, } +/** + * wpa_scan_get_vendor_ie_multi - Fetch vendor IE data from a scan result + * @res: Scan result entry + * @vendor_type: Vendor type (four octets starting the IE payload) + * Returns: Pointer to the information element payload or %NULL if not found + * + * This function returns concatenated payload of possibly fragmented vendor + * specific information elements in the scan result. The caller is responsible + * for freeing the returned buffer. + */ struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res, u32 vendor_type) { @@ -575,15 +1274,28 @@ struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res, } +/* + * 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 +1316,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 +1395,99 @@ 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) +{ +#ifndef CONFIG_NO_STDOUT_DEBUG + size_t i; + + if (scan_res->res == NULL || scan_res->num == 0) + return; + + wpa_printf(MSG_EXCESSIVE, "Sorted scan results"); + + for (i = 0; i < scan_res->num; i++) { + struct wpa_scan_res *r = scan_res->res[i]; + u8 *pos; + if ((r->flags & (WPA_SCAN_LEVEL_DBM | 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); + } + pos = (u8 *) (r + 1); + if (r->ie_len) + wpa_hexdump(MSG_EXCESSIVE, "IEs", pos, r->ie_len); + pos += r->ie_len; + if (r->beacon_ie_len) + wpa_hexdump(MSG_EXCESSIVE, "Beacon IEs", + pos, r->beacon_ie_len); + } +#endif /* CONFIG_NO_STDOUT_DEBUG */ +} + + +/** + * wpa_supplicant_filter_bssid_match - Is the specified BSSID allowed + * @wpa_s: Pointer to wpa_supplicant data + * @bssid: BSSID to check + * Returns: 0 if the BSSID is filtered or 1 if not + * + * This function is used to filter out specific BSSIDs from scan reslts mainly + * for testing purposes (SET bssid_filter ctrl_iface command). + */ +int wpa_supplicant_filter_bssid_match(struct wpa_supplicant *wpa_s, + const u8 *bssid) +{ + size_t i; + + if (wpa_s->bssid_filter == NULL) + return 1; + + for (i = 0; i < wpa_s->bssid_filter_count; i++) { + if (os_memcmp(wpa_s->bssid_filter + i * ETH_ALEN, bssid, + ETH_ALEN) == 0) + return 1; + } + + return 0; +} + + +static void filter_scan_res(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *res) +{ + size_t i, j; + + if (wpa_s->bssid_filter == NULL) + return; + + for (i = 0, j = 0; i < res->num; i++) { + if (wpa_supplicant_filter_bssid_match(wpa_s, + res->res[i]->bssid)) { + res->res[j++] = res->res[i]; + } else { + os_free(res->res[i]); + res->res[i] = NULL; + } + } + + if (res->num != j) { + wpa_printf(MSG_DEBUG, "Filtered out %d scan results", + (int) (res->num - j)); + res->num = j; + } +} /** @@ -641,18 +1507,26 @@ 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; } + filter_scan_res(wpa_s, scan_res); + +#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++) @@ -663,6 +1537,18 @@ wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s, } +/** + * wpa_supplicant_update_scan_results - Update scan results from the driver + * @wpa_s: Pointer to wpa_supplicant data + * Returns: 0 on success, -1 on failure + * + * This function updates the BSS table within wpa_supplicant based on the + * currently available scan results from the driver without requesting a new + * scan. This is used in cases where the driver indicates an association + * (including roaming within ESS) and wpa_supplicant does not yet have the + * needed information to complete the connection (e.g., to perform validation + * steps in 4-way handshake). + */ int wpa_supplicant_update_scan_results(struct wpa_supplicant *wpa_s) { struct wpa_scan_results *scan_res; @@ -673,17 +1559,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); -} diff --git a/contrib/wpa/wpa_supplicant/scan.h b/contrib/wpa/wpa_supplicant/scan.h index 441fdbb..5096287 100644 --- a/contrib/wpa/wpa_supplicant/scan.h +++ b/contrib/wpa/wpa_supplicant/scan.h @@ -2,22 +2,20 @@ * WPA Supplicant - Scanning * Copyright (c) 2003-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. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef SCAN_H #define SCAN_H -int wpa_supplicant_enabled_networks(struct wpa_config *conf); +int wpa_supplicant_enabled_networks(struct wpa_supplicant *wpa_s); 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 +30,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); +int wpa_supplicant_filter_bssid_match(struct wpa_supplicant *wpa_s, + const u8 *bssid); #endif /* SCAN_H */ diff --git a/contrib/wpa/wpa_supplicant/sme.c b/contrib/wpa/wpa_supplicant/sme.c index 5604e97..77ad1d2 100644 --- a/contrib/wpa/wpa_supplicant/sme.c +++ b/contrib/wpa/wpa_supplicant/sme.c @@ -2,19 +2,14 @@ * wpa_supplicant - SME * 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. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" +#include "utils/eloop.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "eapol_supp/eapol_supp_sm.h" @@ -26,14 +21,68 @@ #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" +#include "hs20_supplicant.h" -void sme_authenticate(struct wpa_supplicant *wpa_s, - struct wpa_bss *bss, struct wpa_ssid *ssid) +#define SME_AUTH_TIMEOUT 5 +#define SME_ASSOC_TIMEOUT 5 + +static void sme_auth_timer(void *eloop_ctx, void *timeout_ctx); +static void sme_assoc_timer(void *eloop_ctx, void *timeout_ctx); +static void sme_obss_scan_timeout(void *eloop_ctx, void *timeout_ctx); +#ifdef CONFIG_IEEE80211W +static void sme_stop_sa_query(struct wpa_supplicant *wpa_s); +#endif /* CONFIG_IEEE80211W */ + + +#ifdef CONFIG_SAE + +static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s) +{ + struct wpabuf *buf; + + buf = wpabuf_alloc(4 + 2); + if (buf == NULL) + return NULL; + + wpabuf_put_le16(buf, 1); /* Transaction seq# */ + wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); + wpabuf_put_le16(buf, 19); /* Finite Cyclic Group */ + /* TODO: Anti-Clogging Token (if requested) */ + /* TODO: Scalar */ + /* TODO: Element */ + + return buf; +} + + +static struct wpabuf * sme_auth_build_sae_confirm(struct wpa_supplicant *wpa_s) +{ + struct wpabuf *buf; + + buf = wpabuf_alloc(4 + 2); + if (buf == NULL) + return NULL; + + wpabuf_put_le16(buf, 2); /* Transaction seq# */ + wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); + wpabuf_put_le16(buf, wpa_s->sme.sae_send_confirm); + wpa_s->sme.sae_send_confirm++; + /* TODO: Confirm */ + + return buf; +} + +#endif /* CONFIG_SAE */ + + +static void sme_send_authentication(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss, struct wpa_ssid *ssid, + int start) { struct wpa_driver_auth_params params; struct wpa_ssid *old_ssid; @@ -44,10 +93,13 @@ void sme_authenticate(struct wpa_supplicant *wpa_s, const u8 *md = NULL; #endif /* CONFIG_IEEE80211R */ int i, bssid_changed; + struct wpabuf *resp = NULL; + u8 ext_capab[10]; + int ext_capab_len; 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 +112,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,13 +133,28 @@ 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); } +#ifdef CONFIG_SAE + if (wpa_key_mgmt_sae(ssid->key_mgmt)) { + const u8 *rsn; + struct wpa_ie_data ied; + + rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN); + if (rsn && + wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ied) == 0) { + if (wpa_key_mgmt_sae(ied.key_mgmt)) { + wpa_dbg(wpa_s, MSG_DEBUG, "Using SAE auth_alg"); + params.auth_alg = WPA_AUTH_ALG_SAE; + } + } + } +#endif /* CONFIG_SAE */ for (i = 0; i < NUM_WEP_KEYS; i++) { if (ssid->wep_key_len[i]) @@ -103,13 +171,11 @@ void sme_authenticate(struct wpa_supplicant *wpa_s, if ((wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE) || wpa_bss_get_ie(bss, WLAN_EID_RSN)) && - (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_key_mgmt_wpa(ssid->key_mgmt)) { int try_opportunistic; - try_opportunistic = ssid->proactive_key_caching && + try_opportunistic = (ssid->proactive_key_caching < 0 ? + wpa_s->conf->okc : + ssid->proactive_key_caching) && (ssid->proto & WPA_PROTO_RSN); if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, wpa_s->current_ssid, @@ -119,22 +185,27 @@ 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 & - (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_IEEE8021X | - WPA_KEY_MGMT_WPA_NONE | WPA_KEY_MGMT_FT_PSK | - WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_PSK_SHA256 | - WPA_KEY_MGMT_IEEE8021X_SHA256)) { + } else if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && + wpa_key_mgmt_wpa_ieee8021x(ssid->key_mgmt)) { + /* + * Both WPA and non-WPA IEEE 802.1X enabled in configuration - + * use non-WPA since the scan results did not indicate that the + * AP is using WPA or WPA2. + */ + wpa_supplicant_set_non_wpa_policy(wpa_s, ssid); + wpa_s->sme.assoc_req_ie_len = 0; + } else if (wpa_key_mgmt_wpa_any(ssid->key_mgmt)) { wpa_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie); if (wpa_supplicant_set_suites(wpa_s, NULL, ssid, wpa_s->sme.assoc_req_ie, &wpa_s->sme.assoc_req_ie_len)) { - wpa_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 @@ -166,8 +237,7 @@ void sme_authenticate(struct wpa_supplicant *wpa_s, wpa_ft_prepare_auth_request(wpa_s->wpa, ie); } - if (md && ssid->key_mgmt & (WPA_KEY_MGMT_FT_PSK | - WPA_KEY_MGMT_FT_IEEE8021X)) { + if (md && wpa_key_mgmt_ft(ssid->key_mgmt)) { if (wpa_s->sme.assoc_req_ie_len + 5 < sizeof(wpa_s->sme.assoc_req_ie)) { struct rsn_mdie *mdie; @@ -185,8 +255,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; @@ -195,23 +265,81 @@ void sme_authenticate(struct wpa_supplicant *wpa_s, #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_IEEE80211W - wpa_s->sme.mfp = ssid->ieee80211w; - if (ssid->ieee80211w != NO_MGMT_FRAME_PROTECTION) { + wpa_s->sme.mfp = ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT ? + wpa_s->conf->pmf : ssid->ieee80211w; + if (wpa_s->sme.mfp != NO_MGMT_FRAME_PROTECTION) { const u8 *rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN); struct wpa_ie_data _ie; if (rsn && wpa_parse_wpa_ie(rsn, 2 + rsn[1], &_ie) == 0 && _ie.capabilities & (WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR)) { - wpa_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_HS20 + if (wpa_s->conf->hs20) { + struct wpabuf *hs20; + hs20 = wpabuf_alloc(20); + if (hs20) { + wpas_hs20_add_indication(hs20); + os_memcpy(wpa_s->sme.assoc_req_ie + + wpa_s->sme.assoc_req_ie_len, + wpabuf_head(hs20), wpabuf_len(hs20)); + wpa_s->sme.assoc_req_ie_len += wpabuf_len(hs20); + wpabuf_free(hs20); + } + } +#endif /* CONFIG_HS20 */ + + ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab); + if (ext_capab_len > 0) { + u8 *pos = wpa_s->sme.assoc_req_ie; + if (wpa_s->sme.assoc_req_ie_len > 0 && pos[0] == WLAN_EID_RSN) + pos += 2 + pos[1]; + os_memmove(pos + ext_capab_len, pos, + wpa_s->sme.assoc_req_ie_len - + (pos - wpa_s->sme.assoc_req_ie)); + wpa_s->sme.assoc_req_ie_len += ext_capab_len; + os_memcpy(pos, ext_capab, ext_capab_len); + } + +#ifdef CONFIG_SAE + if (params.auth_alg == WPA_AUTH_ALG_SAE) { + if (start) + resp = sme_auth_build_sae_commit(wpa_s); + else + resp = sme_auth_build_sae_confirm(wpa_s); + if (resp == NULL) + return; + params.sae_data = wpabuf_head(resp); + params.sae_data_len = wpabuf_len(resp); + wpa_s->sme.sae_state = start ? SME_SAE_COMMIT : SME_SAE_CONFIRM; + } +#endif /* CONFIG_SAE */ + + 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,19 +354,104 @@ void sme_authenticate(struct wpa_supplicant *wpa_s, wpa_s->sme.auth_alg = params.auth_alg; if (wpa_drv_authenticate(wpa_s, ¶ms) < 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); + wpabuf_free(resp); 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 * the driver. */ + + wpabuf_free(resp); +} + + +void sme_authenticate(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss, struct wpa_ssid *ssid) +{ + wpa_s->sme.sae_state = SME_SAE_INIT; + wpa_s->sme.sae_send_confirm = 0; + sme_send_authentication(wpa_s, bss, ssid, 1); +} + + +#ifdef CONFIG_SAE + +static int sme_sae_process_commit(struct wpa_supplicant *wpa_s, const u8 *data, + size_t len) +{ + /* Check Finite Cyclic Group */ + if (len < 2) + return -1; + if (WPA_GET_LE16(data) != 19) { + wpa_printf(MSG_DEBUG, "SAE: Unsupported Finite Cyclic Group %u", + WPA_GET_LE16(data)); + return -1; + } + + /* TODO */ + + return 0; +} + + +static int sme_sae_process_confirm(struct wpa_supplicant *wpa_s, const u8 *data, + size_t len) +{ + u16 rc; + + if (len < 2) + return -1; + rc = WPA_GET_LE16(data); + wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", rc); + + /* TODO */ + return 0; +} + + +static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction, + u16 status_code, const u8 *data, size_t len) +{ + wpa_dbg(wpa_s, MSG_DEBUG, "SME: SAE authentication transaction %u " + "status code %u", auth_transaction, status_code); + wpa_hexdump(MSG_DEBUG, "SME: SAE fields", data, len); + + if (status_code != WLAN_STATUS_SUCCESS) + return -1; + + if (auth_transaction == 1) { + wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE commit"); + if (wpa_s->current_bss == NULL || + wpa_s->current_ssid == NULL) + return -1; + if (wpa_s->sme.sae_state != SME_SAE_COMMIT) + return -1; + if (sme_sae_process_commit(wpa_s, data, len) < 0) + return -1; + sme_send_authentication(wpa_s, wpa_s->current_bss, + wpa_s->current_ssid, 0); + return 0; + } else if (auth_transaction == 2) { + wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE confirm"); + if (wpa_s->sme.sae_state != SME_SAE_CONFIRM) + return -1; + if (sme_sae_process_confirm(wpa_s, data, len) < 0) + return -1; + return 1; + } + + return -1; } +#endif /* CONFIG_SAE */ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data) @@ -246,46 +459,67 @@ 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 auth_transaction=%d status_code=%d", + MAC2STR(data->auth.peer), data->auth.auth_type, + data->auth.auth_transaction, data->auth.status_code); wpa_hexdump(MSG_MSGDUMP, "SME: Authentication response IEs", data->auth.ies, data->auth.ies_len); + eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL); + +#ifdef CONFIG_SAE + if (data->auth.auth_type == WLAN_AUTH_SAE) { + int res; + res = sme_sae_auth(wpa_s, data->auth.auth_transaction, + data->auth.status_code, data->auth.ies, + data->auth.ies_len); + if (res < 0) { + wpas_connection_failed(wpa_s, wpa_s->pending_bssid); + wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); + + } + if (res != 1) + return; + } +#endif /* CONFIG_SAE */ + 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); + wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); 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 +527,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; @@ -324,15 +558,30 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode, { struct wpa_driver_associate_params params; struct ieee802_11_elems elems; +#ifdef CONFIG_HT_OVERRIDES + struct ieee80211_ht_capabilities htcaps; + struct ieee80211_ht_capabilities htcaps_mask; +#endif /* CONFIG_HT_OVERRIDES */ os_memset(¶ms, 0, sizeof(params)); params.bssid = bssid; params.ssid = wpa_s->sme.ssid; params.ssid_len = wpa_s->sme.ssid_len; params.freq = wpa_s->sme.freq; + params.bg_scan_period = wpa_s->current_ssid ? + wpa_s->current_ssid->bg_scan_period : -1; params.wpa_ie = wpa_s->sme.assoc_req_ie_len ? wpa_s->sme.assoc_req_ie : NULL; params.wpa_ie_len = wpa_s->sme.assoc_req_ie_len; + params.pairwise_suite = cipher_suite2driver(wpa_s->pairwise_cipher); + params.group_suite = cipher_suite2driver(wpa_s->group_cipher); +#ifdef CONFIG_HT_OVERRIDES + os_memset(&htcaps, 0, sizeof(htcaps)); + os_memset(&htcaps_mask, 0, sizeof(htcaps_mask)); + params.htcaps = (u8 *) &htcaps; + params.htcaps_mask = (u8 *) &htcaps_mask; + wpa_supplicant_apply_ht_overrides(wpa_s, wpa_s->current_ssid, ¶ms); +#endif /* CONFIG_HT_OVERRIDES */ #ifdef CONFIG_IEEE80211R if (auth_type == WLAN_AUTH_FT && wpa_s->sme.ft_ies) { params.wpa_ie = wpa_s->sme.ft_ies; @@ -354,26 +603,38 @@ 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, ¶ms) < 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); + wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); + 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 +642,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 +662,580 @@ 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); + eloop_cancel_timeout(sme_obss_scan_timeout, wpa_s, NULL); +} + + +static void sme_send_2040_bss_coex(struct wpa_supplicant *wpa_s, + const u8 *chan_list, u8 num_channels, + u8 num_intol) +{ + struct ieee80211_2040_bss_coex_ie *bc_ie; + struct ieee80211_2040_intol_chan_report *ic_report; + struct wpabuf *buf; + + wpa_printf(MSG_DEBUG, "SME: Send 20/40 BSS Coexistence to " MACSTR, + MAC2STR(wpa_s->bssid)); + + buf = wpabuf_alloc(2 + /* action.category + action_code */ + sizeof(struct ieee80211_2040_bss_coex_ie) + + sizeof(struct ieee80211_2040_intol_chan_report) + + num_channels); + if (buf == NULL) + return; + + wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC); + wpabuf_put_u8(buf, WLAN_PA_20_40_BSS_COEX); + + bc_ie = wpabuf_put(buf, sizeof(*bc_ie)); + bc_ie->element_id = WLAN_EID_20_40_BSS_COEXISTENCE; + bc_ie->length = 1; + if (num_intol) + bc_ie->coex_param |= WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ; + + if (num_channels > 0) { + ic_report = wpabuf_put(buf, sizeof(*ic_report)); + ic_report->element_id = WLAN_EID_20_40_BSS_INTOLERANT; + ic_report->length = num_channels + 1; + ic_report->op_class = 0; + os_memcpy(wpabuf_put(buf, num_channels), chan_list, + num_channels); + } + + if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, + wpa_s->own_addr, wpa_s->bssid, + wpabuf_head(buf), wpabuf_len(buf), 0) < 0) { + wpa_msg(wpa_s, MSG_INFO, + "SME: Failed to send 20/40 BSS Coexistence frame"); + } + + wpabuf_free(buf); +} + + +/** + * enum wpas_band - Frequency band + * @WPAS_BAND_2GHZ: 2.4 GHz ISM band + * @WPAS_BAND_5GHZ: around 5 GHz band (4.9 - 5.7 GHz) + */ +enum wpas_band { + WPAS_BAND_2GHZ, + WPAS_BAND_5GHZ, + WPAS_BAND_INVALID +}; + +/** + * freq_to_channel - Convert frequency into channel info + * @channel: Buffer for returning channel number + * Returns: Band (2 or 5 GHz) + */ +static enum wpas_band freq_to_channel(int freq, u8 *channel) +{ + enum wpas_band band = (freq <= 2484) ? WPAS_BAND_2GHZ : WPAS_BAND_5GHZ; + u8 chan = 0; + + if (freq >= 2412 && freq <= 2472) + chan = (freq - 2407) / 5; + else if (freq == 2484) + chan = 14; + else if (freq >= 5180 && freq <= 5805) + chan = (freq - 5000) / 5; + + *channel = chan; + return band; +} + + +int sme_proc_obss_scan(struct wpa_supplicant *wpa_s) +{ + struct wpa_bss *bss; + const u8 *ie; + u16 ht_cap; + u8 chan_list[P2P_MAX_CHANNELS], channel; + u8 num_channels = 0, num_intol = 0, i; + + if (!wpa_s->sme.sched_obss_scan) + return 0; + + wpa_s->sme.sched_obss_scan = 0; + if (!wpa_s->current_bss || wpa_s->wpa_state != WPA_COMPLETED) + return 1; + + /* + * Check whether AP uses regulatory triplet or channel triplet in + * country info. Right now the operating class of the BSS channel + * width trigger event is "unknown" (IEEE Std 802.11-2012 10.15.12), + * based on the assumption that operating class triplet is not used in + * beacon frame. If the First Channel Number/Operating Extension + * Identifier octet has a positive integer value of 201 or greater, + * then its operating class triplet. + * + * TODO: If Supported Operating Classes element is present in beacon + * frame, have to lookup operating class in Annex E and fill them in + * 2040 coex frame. + */ + ie = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_COUNTRY); + if (ie && (ie[1] >= 6) && (ie[5] >= 201)) + return 1; + + os_memset(chan_list, 0, sizeof(chan_list)); + + dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { + /* Skip other band bss */ + if (freq_to_channel(bss->freq, &channel) != WPAS_BAND_2GHZ) + continue; + + ie = wpa_bss_get_ie(bss, WLAN_EID_HT_CAP); + ht_cap = (ie && (ie[1] == 26)) ? WPA_GET_LE16(ie + 2) : 0; + + if (!ht_cap || (ht_cap & HT_CAP_INFO_40MHZ_INTOLERANT)) { + /* Check whether the channel is already considered */ + for (i = 0; i < num_channels; i++) { + if (channel == chan_list[i]) + break; + } + if (i != num_channels) + continue; + + if (ht_cap & HT_CAP_INFO_40MHZ_INTOLERANT) + num_intol++; + + chan_list[num_channels++] = channel; + } + } + + sme_send_2040_bss_coex(wpa_s, chan_list, num_channels, num_intol); + return 1; +} + + +static struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes, + u16 num_modes, + enum hostapd_hw_mode mode) +{ + u16 i; + + for (i = 0; i < num_modes; i++) { + if (modes[i].mode == mode) + return &modes[i]; + } + + return NULL; +} + + +static void wpa_setband_scan_freqs_list(struct wpa_supplicant *wpa_s, + enum hostapd_hw_mode band, + struct wpa_driver_scan_params *params) +{ + /* Include only supported channels for the specified band */ + struct hostapd_hw_modes *mode; + int count, i; + + mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, band); + if (mode == NULL) { + /* No channels supported in this band - use empty list */ + params->freqs = os_zalloc(sizeof(int)); + return; + } + + params->freqs = os_calloc(mode->num_channels + 1, sizeof(int)); + if (params->freqs == NULL) + return; + for (count = 0, i = 0; i < mode->num_channels; i++) { + if (mode->channels[i].flag & HOSTAPD_CHAN_DISABLED) + continue; + params->freqs[count++] = mode->channels[i].freq; + } +} + + +static void sme_obss_scan_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct wpa_driver_scan_params params; + + if (!wpa_s->current_bss) { + wpa_printf(MSG_DEBUG, "SME OBSS: Ignore scan request"); + return; + } + + os_memset(¶ms, 0, sizeof(params)); + wpa_setband_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211G, ¶ms); + wpa_printf(MSG_DEBUG, "SME OBSS: Request an OBSS scan"); + + if (wpa_supplicant_trigger_scan(wpa_s, ¶ms)) + wpa_printf(MSG_DEBUG, "SME OBSS: Failed to trigger scan"); + else + wpa_s->sme.sched_obss_scan = 1; + os_free(params.freqs); + + eloop_register_timeout(wpa_s->sme.obss_scan_int, 0, + sme_obss_scan_timeout, wpa_s, NULL); +} + + +void sme_sched_obss_scan(struct wpa_supplicant *wpa_s, int enable) +{ + const u8 *ie; + struct wpa_bss *bss = wpa_s->current_bss; + struct wpa_ssid *ssid = wpa_s->current_ssid; + struct hostapd_hw_modes *hw_mode = NULL; + int i; + + eloop_cancel_timeout(sme_obss_scan_timeout, wpa_s, NULL); + wpa_s->sme.sched_obss_scan = 0; + if (!enable) + return; + + /* + * Schedule OBSS scan if driver is using station SME in wpa_supplicant + * or it expects OBSS scan to be performed by wpa_supplicant. + */ + if (!((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) || + (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OBSS_SCAN)) || + ssid == NULL || ssid->mode != IEEE80211_MODE_INFRA) + return; + + if (!wpa_s->hw.modes) + return; + + /* only HT caps in 11g mode are relevant */ + for (i = 0; i < wpa_s->hw.num_modes; i++) { + hw_mode = &wpa_s->hw.modes[i]; + if (hw_mode->mode == HOSTAPD_MODE_IEEE80211G) + break; + } + + /* Driver does not support HT40 for 11g or doesn't have 11g. */ + if (i == wpa_s->hw.num_modes || !hw_mode || + !(hw_mode->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) + return; + + if (bss == NULL || bss->freq < 2400 || bss->freq > 2500) + return; /* Not associated on 2.4 GHz band */ + + /* Check whether AP supports HT40 */ + ie = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_HT_CAP); + if (!ie || ie[1] < 2 || + !(WPA_GET_LE16(ie + 2) & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) + return; /* AP does not support HT40 */ + + ie = wpa_bss_get_ie(wpa_s->current_bss, + WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS); + if (!ie || ie[1] < 14) + return; /* AP does not request OBSS scans */ + + wpa_s->sme.obss_scan_int = WPA_GET_LE16(ie + 6); + if (wpa_s->sme.obss_scan_int < 10) { + wpa_printf(MSG_DEBUG, "SME: Invalid OBSS Scan Interval %u " + "replaced with the minimum 10 sec", + wpa_s->sme.obss_scan_int); + wpa_s->sme.obss_scan_int = 10; + } + wpa_printf(MSG_DEBUG, "SME: OBSS Scan Interval %u sec", + wpa_s->sme.obss_scan_int); + eloop_register_timeout(wpa_s->sme.obss_scan_int, 0, + sme_obss_scan_timeout, wpa_s, NULL); +} + + +#ifdef CONFIG_IEEE80211W + +static const unsigned int sa_query_max_timeout = 1000; +static const unsigned int sa_query_retry_timeout = 201; + +static int sme_check_sa_query_timeout(struct wpa_supplicant *wpa_s) +{ + u32 tu; + struct os_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_array(wpa_s->sme.sa_query_trans_id, + wpa_s->sme.sa_query_count + 1, + WLAN_SA_QUERY_TR_ID_LEN); + if (nbuf == NULL) + return; + if (wpa_s->sme.sa_query_count == 0) { + /* Starting a new SA Query procedure */ + os_get_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); +} + + +static void sme_stop_sa_query(struct wpa_supplicant *wpa_s) +{ + eloop_cancel_timeout(sme_sa_query_timer, wpa_s, NULL); + os_free(wpa_s->sme.sa_query_trans_id); + wpa_s->sme.sa_query_trans_id = NULL; + wpa_s->sme.sa_query_count = 0; +} + + +void sme_event_unprot_disconnect(struct wpa_supplicant *wpa_s, const u8 *sa, + const u8 *da, u16 reason_code) +{ + struct wpa_ssid *ssid; + + 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 == MGMT_FRAME_PROTECTION_DEFAULT ? + wpa_s->conf->pmf : ssid->ieee80211w) == NO_MGMT_FRAME_PROTECTION) + return; + if (os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) + return; + if (reason_code != WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA && + reason_code != WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA) + return; + if (wpa_s->sme.sa_query_count > 0) + return; + + 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 */ diff --git a/contrib/wpa/wpa_supplicant/sme.h b/contrib/wpa/wpa_supplicant/sme.h index 3ec8cc9..a7cc507 100644 --- a/contrib/wpa/wpa_supplicant/sme.h +++ b/contrib/wpa/wpa_supplicant/sme.h @@ -2,14 +2,8 @@ * wpa_supplicant - SME * 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. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef SME_H @@ -32,6 +26,17 @@ 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); + +int sme_proc_obss_scan(struct wpa_supplicant *wpa_s); +void sme_sched_obss_scan(struct wpa_supplicant *wpa_s, int enable); #else /* CONFIG_SME */ @@ -73,6 +78,36 @@ 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) +{ +} + +static inline int sme_proc_obss_scan(struct wpa_supplicant *wpa_s) +{ + return 0; +} + +static inline void sme_sched_obss_scan(struct wpa_supplicant *wpa_s, + int enable) +{ +} + #endif /* CONFIG_SME */ #endif /* SME_H */ diff --git a/contrib/wpa/wpa_supplicant/tests/test_eap_sim_common.c b/contrib/wpa/wpa_supplicant/tests/test_eap_sim_common.c index deb19f6..f60b182 100644 --- a/contrib/wpa/wpa_supplicant/tests/test_eap_sim_common.c +++ b/contrib/wpa/wpa_supplicant/tests/test_eap_sim_common.c @@ -2,14 +2,8 @@ * Test program for EAP-SIM PRF * 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. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "eap_common/eap_sim_common.c" @@ -34,7 +28,7 @@ static int test_eap_sim_prf(void) printf("Testing EAP-SIM PRF (FIPS 186-2 + change notice 1)\n"); eap_sim_prf(xkey, buf, sizeof(buf)); - if (memcmp(w, buf, sizeof(w) != 0)) { + if (memcmp(w, buf, sizeof(w)) != 0) { printf("eap_sim_prf failed\n"); return 1; } diff --git a/contrib/wpa/wpa_supplicant/tests/test_wpa.c b/contrib/wpa/wpa_supplicant/tests/test_wpa.c index 7947137..0d659ad 100644 --- a/contrib/wpa/wpa_supplicant/tests/test_wpa.c +++ b/contrib/wpa/wpa_supplicant/tests/test_wpa.c @@ -2,14 +2,8 @@ * Test program for combined WPA authenticator/supplicant * Copyright (c) 2006-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. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/contrib/wpa/wpa_supplicant/utils/log2pcap.py b/contrib/wpa/wpa_supplicant/utils/log2pcap.py new file mode 100755 index 0000000..65e2fa1 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/utils/log2pcap.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python +# +# Copyright (c) 2012, Intel Corporation +# +# Author: Johannes Berg <johannes@sipsolutions.net> +# +# This software may be distributed under the terms of the BSD license. +# See README for more details. + +import sys, struct, re + +def write_pcap_header(pcap_file): + pcap_file.write( + struct.pack('<IHHIIII', + 0xa1b2c3d4, 2, 4, 0, 0, 65535, + 105 # raw 802.11 format + )) + +def pcap_addpacket(pcap_file, ts, data): + # ts in seconds, float + pcap_file.write(struct.pack('<IIII', + int(ts), int(1000000 * ts) % 1000000, + len(data), len(data))) + pcap_file.write(data) + +if __name__ == "__main__": + try: + input = sys.argv[1] + pcap = sys.argv[2] + except IndexError: + print "Usage: %s <log file> <pcap file>" % sys.argv[0] + sys.exit(2) + + input_file = open(input, 'r') + pcap_file = open(pcap, 'w') + frame_re = re.compile(r'(([0-9]+.[0-9]{6}):\s*)?nl80211: MLME event frame - hexdump\(len=[0-9]*\):((\s*[0-9a-fA-F]{2})*)') + + write_pcap_header(pcap_file) + + for line in input_file: + m = frame_re.match(line) + if m is None: + continue + if m.group(2): + ts = float(m.group(2)) + else: + ts = 0 + hexdata = m.group(3) + hexdata = hexdata.split() + data = ''.join([chr(int(x, 16)) for x in hexdata]) + pcap_addpacket(pcap_file, ts, data) + + input_file.close() + pcap_file.close() diff --git a/contrib/wpa/wpa_supplicant/wifi_display.c b/contrib/wpa/wpa_supplicant/wifi_display.c new file mode 100644 index 0000000..92ca536 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/wifi_display.c @@ -0,0 +1,251 @@ +/* + * wpa_supplicant - Wi-Fi Display + * Copyright (c) 2011, Atheros Communications, Inc. + * Copyright (c) 2011-2012, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "p2p/p2p.h" +#include "common/ieee802_11_defs.h" +#include "wpa_supplicant_i.h" +#include "wifi_display.h" + + +int wifi_display_init(struct wpa_global *global) +{ + global->wifi_display = 1; + return 0; +} + + +void wifi_display_deinit(struct wpa_global *global) +{ + int i; + for (i = 0; i < MAX_WFD_SUBELEMS; i++) { + wpabuf_free(global->wfd_subelem[i]); + global->wfd_subelem[i] = NULL; + } +} + + +static int wifi_display_update_wfd_ie(struct wpa_global *global) +{ + struct wpabuf *ie, *buf; + size_t len, plen; + + wpa_printf(MSG_DEBUG, "WFD: Update WFD IE"); + + if (!global->wifi_display) { + wpa_printf(MSG_DEBUG, "WFD: Wi-Fi Display disabled - do not " + "include WFD IE"); + p2p_set_wfd_ie_beacon(global->p2p, NULL); + p2p_set_wfd_ie_probe_req(global->p2p, NULL); + p2p_set_wfd_ie_probe_resp(global->p2p, NULL); + p2p_set_wfd_ie_assoc_req(global->p2p, NULL); + p2p_set_wfd_ie_invitation(global->p2p, NULL); + p2p_set_wfd_ie_prov_disc_req(global->p2p, NULL); + p2p_set_wfd_ie_prov_disc_resp(global->p2p, NULL); + p2p_set_wfd_ie_go_neg(global->p2p, NULL); + p2p_set_wfd_dev_info(global->p2p, NULL); + p2p_set_wfd_assoc_bssid(global->p2p, NULL); + p2p_set_wfd_coupled_sink_info(global->p2p, NULL); + return 0; + } + + p2p_set_wfd_dev_info(global->p2p, + global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO]); + p2p_set_wfd_assoc_bssid( + global->p2p, + global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID]); + p2p_set_wfd_coupled_sink_info( + global->p2p, global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK]); + + /* + * WFD IE is included in number of management frames. Two different + * sets of subelements are included depending on the frame: + * + * Beacon, (Re)Association Request, GO Negotiation Req/Resp/Conf, + * Provision Discovery Req: + * WFD Device Info + * [Associated BSSID] + * [Coupled Sink Info] + * + * Probe Request: + * WFD Device Info + * [Associated BSSID] + * [Coupled Sink Info] + * [WFD Extended Capability] + * + * Probe Response: + * WFD Device Info + * [Associated BSSID] + * [Coupled Sink Info] + * [WFD Extended Capability] + * [WFD Session Info] + * + * (Re)Association Response, P2P Invitation Req/Resp, + * Provision Discovery Resp: + * WFD Device Info + * [Associated BSSID] + * [Coupled Sink Info] + * [WFD Session Info] + */ + len = 0; + if (global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO]) + len += wpabuf_len(global->wfd_subelem[ + WFD_SUBELEM_DEVICE_INFO]); + if (global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID]) + len += wpabuf_len(global->wfd_subelem[ + WFD_SUBELEM_ASSOCIATED_BSSID]); + if (global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK]) + len += wpabuf_len(global->wfd_subelem[ + WFD_SUBELEM_COUPLED_SINK]); + if (global->wfd_subelem[WFD_SUBELEM_SESSION_INFO]) + len += wpabuf_len(global->wfd_subelem[ + WFD_SUBELEM_SESSION_INFO]); + if (global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB]) + len += wpabuf_len(global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB]); + buf = wpabuf_alloc(len); + if (buf == NULL) + return -1; + + if (global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO]) + wpabuf_put_buf(buf, + global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO]); + if (global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID]) + wpabuf_put_buf(buf, global->wfd_subelem[ + WFD_SUBELEM_ASSOCIATED_BSSID]); + if (global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK]) + wpabuf_put_buf(buf, + global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK]); + + ie = wifi_display_encaps(buf); + wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Beacon", ie); + p2p_set_wfd_ie_beacon(global->p2p, ie); + + ie = wifi_display_encaps(buf); + wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for (Re)Association Request", + ie); + p2p_set_wfd_ie_assoc_req(global->p2p, ie); + + ie = wifi_display_encaps(buf); + wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for GO Negotiation", ie); + p2p_set_wfd_ie_go_neg(global->p2p, ie); + + ie = wifi_display_encaps(buf); + wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Provision Discovery " + "Request", ie); + p2p_set_wfd_ie_prov_disc_req(global->p2p, ie); + + plen = buf->used; + if (global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB]) + wpabuf_put_buf(buf, + global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB]); + + ie = wifi_display_encaps(buf); + wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Probe Request", ie); + p2p_set_wfd_ie_probe_req(global->p2p, ie); + + if (global->wfd_subelem[WFD_SUBELEM_SESSION_INFO]) + wpabuf_put_buf(buf, + global->wfd_subelem[WFD_SUBELEM_SESSION_INFO]); + ie = wifi_display_encaps(buf); + wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Probe Response", ie); + p2p_set_wfd_ie_probe_resp(global->p2p, ie); + + /* Remove WFD Extended Capability from buffer */ + buf->used = plen; + if (global->wfd_subelem[WFD_SUBELEM_SESSION_INFO]) + wpabuf_put_buf(buf, + global->wfd_subelem[WFD_SUBELEM_SESSION_INFO]); + + ie = wifi_display_encaps(buf); + wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for P2P Invitation", ie); + p2p_set_wfd_ie_invitation(global->p2p, ie); + + ie = wifi_display_encaps(buf); + wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Provision Discovery " + "Response", ie); + p2p_set_wfd_ie_prov_disc_resp(global->p2p, ie); + + wpabuf_free(buf); + + return 0; +} + + +void wifi_display_enable(struct wpa_global *global, int enabled) +{ + wpa_printf(MSG_DEBUG, "WFD: Wi-Fi Display %s", + enabled ? "enabled" : "disabled"); + global->wifi_display = enabled; + wifi_display_update_wfd_ie(global); +} + + +int wifi_display_subelem_set(struct wpa_global *global, char *cmd) +{ + char *pos; + int subelem; + size_t len; + struct wpabuf *e; + + pos = os_strchr(cmd, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + subelem = atoi(cmd); + if (subelem < 0 || subelem >= MAX_WFD_SUBELEMS) + return -1; + + len = os_strlen(pos); + if (len & 1) + return -1; + len /= 2; + + if (len == 0) { + /* Clear subelement */ + e = NULL; + wpa_printf(MSG_DEBUG, "WFD: Clear subelement %d", subelem); + } else { + e = wpabuf_alloc(1 + len); + if (e == NULL) + return -1; + wpabuf_put_u8(e, subelem); + if (hexstr2bin(pos, wpabuf_put(e, len), len) < 0) { + wpabuf_free(e); + return -1; + } + wpa_printf(MSG_DEBUG, "WFD: Set subelement %d", subelem); + } + + wpabuf_free(global->wfd_subelem[subelem]); + global->wfd_subelem[subelem] = e; + wifi_display_update_wfd_ie(global); + + return 0; +} + + +int wifi_display_subelem_get(struct wpa_global *global, char *cmd, + char *buf, size_t buflen) +{ + int subelem; + + subelem = atoi(cmd); + if (subelem < 0 || subelem >= MAX_WFD_SUBELEMS) + return -1; + + if (global->wfd_subelem[subelem] == NULL) + return 0; + + return wpa_snprintf_hex(buf, buflen, + wpabuf_head_u8(global->wfd_subelem[subelem]) + + 1, + wpabuf_len(global->wfd_subelem[subelem]) - 1); +} diff --git a/contrib/wpa/wpa_supplicant/wifi_display.h b/contrib/wpa/wpa_supplicant/wifi_display.h new file mode 100644 index 0000000..b75d4f2 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/wifi_display.h @@ -0,0 +1,20 @@ +/* + * wpa_supplicant - Wi-Fi Display + * Copyright (c) 2011, Atheros Communications, Inc. + * Copyright (c) 2011-2012, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WIFI_DISPLAY_H +#define WIFI_DISPLAY_H + +int wifi_display_init(struct wpa_global *global); +void wifi_display_deinit(struct wpa_global *global); +void wifi_display_enable(struct wpa_global *global, int enabled); +int wifi_display_subelem_set(struct wpa_global *global, char *cmd); +int wifi_display_subelem_get(struct wpa_global *global, char *cmd, + char *buf, size_t buflen); + +#endif /* WIFI_DISPLAY_H */ diff --git a/contrib/wpa/wpa_supplicant/wnm_sta.c b/contrib/wpa/wpa_supplicant/wnm_sta.c new file mode 100644 index 0000000..4d9e453 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/wnm_sta.c @@ -0,0 +1,423 @@ +/* + * wpa_supplicant - WNM + * Copyright (c) 2011-2012, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "common/ieee802_11_defs.h" +#include "rsn_supp/wpa.h" +#include "wpa_supplicant_i.h" +#include "driver_i.h" +#include "scan.h" + +#define MAX_TFS_IE_LEN 1024 + + +/* get the TFS IE from driver */ +static int ieee80211_11_get_tfs_ie(struct wpa_supplicant *wpa_s, u8 *buf, + u16 *buf_len, enum wnm_oper oper) +{ + wpa_printf(MSG_DEBUG, "%s: TFS get operation %d", __func__, oper); + + return wpa_drv_wnm_oper(wpa_s, oper, wpa_s->bssid, buf, buf_len); +} + + +/* set the TFS IE to driver */ +static int ieee80211_11_set_tfs_ie(struct wpa_supplicant *wpa_s, + const u8 *addr, u8 *buf, u16 *buf_len, + enum wnm_oper oper) +{ + wpa_printf(MSG_DEBUG, "%s: TFS set operation %d", __func__, oper); + + return wpa_drv_wnm_oper(wpa_s, oper, addr, buf, buf_len); +} + + +/* MLME-SLEEPMODE.request */ +int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s, + u8 action, u16 intval, struct wpabuf *tfs_req) +{ + struct ieee80211_mgmt *mgmt; + int res; + size_t len; + struct wnm_sleep_element *wnmsleep_ie; + u8 *wnmtfs_ie; + u8 wnmsleep_ie_len; + u16 wnmtfs_ie_len; /* possibly multiple IE(s) */ + enum wnm_oper tfs_oper = action == 0 ? WNM_SLEEP_TFS_REQ_IE_ADD : + WNM_SLEEP_TFS_REQ_IE_NONE; + + wpa_printf(MSG_DEBUG, "WNM: Request to send WNM-Sleep Mode Request " + "action=%s to " MACSTR, + action == 0 ? "enter" : "exit", + MAC2STR(wpa_s->bssid)); + + /* WNM-Sleep Mode IE */ + wnmsleep_ie_len = sizeof(struct wnm_sleep_element); + wnmsleep_ie = os_zalloc(sizeof(struct wnm_sleep_element)); + if (wnmsleep_ie == NULL) + return -1; + wnmsleep_ie->eid = WLAN_EID_WNMSLEEP; + wnmsleep_ie->len = wnmsleep_ie_len - 2; + wnmsleep_ie->action_type = action; + wnmsleep_ie->status = WNM_STATUS_SLEEP_ACCEPT; + wnmsleep_ie->intval = host_to_le16(intval); + wpa_hexdump(MSG_DEBUG, "WNM: WNM-Sleep Mode element", + (u8 *) wnmsleep_ie, wnmsleep_ie_len); + + /* TFS IE(s) */ + if (tfs_req) { + wnmtfs_ie_len = wpabuf_len(tfs_req); + wnmtfs_ie = os_malloc(wnmtfs_ie_len); + if (wnmtfs_ie == NULL) { + os_free(wnmsleep_ie); + return -1; + } + os_memcpy(wnmtfs_ie, wpabuf_head(tfs_req), wnmtfs_ie_len); + } else { + wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN); + if (wnmtfs_ie == NULL) { + os_free(wnmsleep_ie); + return -1; + } + if (ieee80211_11_get_tfs_ie(wpa_s, wnmtfs_ie, &wnmtfs_ie_len, + tfs_oper)) { + wnmtfs_ie_len = 0; + os_free(wnmtfs_ie); + wnmtfs_ie = NULL; + } + } + wpa_hexdump(MSG_DEBUG, "WNM: TFS Request element", + (u8 *) wnmtfs_ie, wnmtfs_ie_len); + + mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len + wnmtfs_ie_len); + if (mgmt == NULL) { + wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for " + "WNM-Sleep Request action frame"); + os_free(wnmsleep_ie); + os_free(wnmtfs_ie); + return -1; + } + + 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_WNM; + mgmt->u.action.u.wnm_sleep_req.action = WNM_SLEEP_MODE_REQ; + mgmt->u.action.u.wnm_sleep_req.dialogtoken = 1; + os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable, wnmsleep_ie, + wnmsleep_ie_len); + /* copy TFS IE here */ + if (wnmtfs_ie_len > 0) { + os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable + + wnmsleep_ie_len, wnmtfs_ie, wnmtfs_ie_len); + } + + len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_req) + wnmsleep_ie_len + + wnmtfs_ie_len; + + res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, + wpa_s->own_addr, wpa_s->bssid, + &mgmt->u.action.category, len, 0); + if (res < 0) + wpa_printf(MSG_DEBUG, "Failed to send WNM-Sleep Request " + "(action=%d, intval=%d)", action, intval); + + os_free(wnmsleep_ie); + os_free(wnmtfs_ie); + os_free(mgmt); + + return res; +} + + +static void wnm_sleep_mode_enter_success(struct wpa_supplicant *wpa_s, + u8 *tfsresp_ie_start, + u8 *tfsresp_ie_end) +{ + wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_CONFIRM, + wpa_s->bssid, NULL, NULL); + /* remove GTK/IGTK ?? */ + + /* set the TFS Resp IE(s) */ + if (tfsresp_ie_start && tfsresp_ie_end && + tfsresp_ie_end - tfsresp_ie_start >= 0) { + u16 tfsresp_ie_len; + tfsresp_ie_len = (tfsresp_ie_end + tfsresp_ie_end[1] + 2) - + tfsresp_ie_start; + wpa_printf(MSG_DEBUG, "TFS Resp IE(s) found"); + /* pass the TFS Resp IE(s) to driver for processing */ + if (ieee80211_11_set_tfs_ie(wpa_s, wpa_s->bssid, + tfsresp_ie_start, + &tfsresp_ie_len, + WNM_SLEEP_TFS_RESP_IE_SET)) + wpa_printf(MSG_DEBUG, "WNM: Fail to set TFS Resp IE"); + } +} + + +static void wnm_sleep_mode_exit_success(struct wpa_supplicant *wpa_s, + const u8 *frm, u16 key_len_total) +{ + u8 *ptr, *end; + u8 gtk_len; + + wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_CONFIRM, wpa_s->bssid, + NULL, NULL); + + /* Install GTK/IGTK */ + + /* point to key data field */ + ptr = (u8 *) frm + 1 + 1 + 2; + end = ptr + key_len_total; + wpa_hexdump_key(MSG_DEBUG, "WNM: Key Data", ptr, key_len_total); + + while (ptr + 1 < end) { + if (ptr + 2 + ptr[1] > end) { + wpa_printf(MSG_DEBUG, "WNM: Invalid Key Data element " + "length"); + if (end > ptr) { + wpa_hexdump(MSG_DEBUG, "WNM: Remaining data", + ptr, end - ptr); + } + break; + } + if (*ptr == WNM_SLEEP_SUBELEM_GTK) { + if (ptr[1] < 11 + 5) { + wpa_printf(MSG_DEBUG, "WNM: Too short GTK " + "subelem"); + break; + } + gtk_len = *(ptr + 4); + if (ptr[1] < 11 + gtk_len || + gtk_len < 5 || gtk_len > 32) { + wpa_printf(MSG_DEBUG, "WNM: Invalid GTK " + "subelem"); + break; + } + wpa_wnmsleep_install_key( + wpa_s->wpa, + WNM_SLEEP_SUBELEM_GTK, + ptr); + ptr += 13 + gtk_len; +#ifdef CONFIG_IEEE80211W + } else if (*ptr == WNM_SLEEP_SUBELEM_IGTK) { + if (ptr[1] < 2 + 6 + WPA_IGTK_LEN) { + wpa_printf(MSG_DEBUG, "WNM: Too short IGTK " + "subelem"); + break; + } + wpa_wnmsleep_install_key(wpa_s->wpa, + WNM_SLEEP_SUBELEM_IGTK, ptr); + ptr += 10 + WPA_IGTK_LEN; +#endif /* CONFIG_IEEE80211W */ + } else + break; /* skip the loop */ + } +} + + +static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s, + const u8 *frm, int len) +{ + /* + * Action [1] | Diaglog Token [1] | Key Data Len [2] | Key Data | + * WNM-Sleep Mode IE | TFS Response IE + */ + u8 *pos = (u8 *) frm; /* point to action field */ + u16 key_len_total = le_to_host16(*((u16 *)(frm+2))); + struct wnm_sleep_element *wnmsleep_ie = NULL; + /* multiple TFS Resp IE (assuming consecutive) */ + u8 *tfsresp_ie_start = NULL; + u8 *tfsresp_ie_end = NULL; + + wpa_printf(MSG_DEBUG, "action=%d token = %d key_len_total = %d", + frm[0], frm[1], key_len_total); + pos += 4 + key_len_total; + if (pos > frm + len) { + wpa_printf(MSG_INFO, "WNM: Too short frame for Key Data field"); + return; + } + while (pos - frm < len) { + u8 ie_len = *(pos + 1); + if (pos + 2 + ie_len > frm + len) { + wpa_printf(MSG_INFO, "WNM: Invalid IE len %u", ie_len); + break; + } + wpa_hexdump(MSG_DEBUG, "WNM: Element", pos, 2 + ie_len); + if (*pos == WLAN_EID_WNMSLEEP) + wnmsleep_ie = (struct wnm_sleep_element *) pos; + else if (*pos == WLAN_EID_TFS_RESP) { + if (!tfsresp_ie_start) + tfsresp_ie_start = pos; + tfsresp_ie_end = pos; + } else + wpa_printf(MSG_DEBUG, "EID %d not recognized", *pos); + pos += ie_len + 2; + } + + if (!wnmsleep_ie) { + wpa_printf(MSG_DEBUG, "No WNM-Sleep IE found"); + return; + } + + if (wnmsleep_ie->status == WNM_STATUS_SLEEP_ACCEPT || + wnmsleep_ie->status == WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) { + wpa_printf(MSG_DEBUG, "Successfully recv WNM-Sleep Response " + "frame (action=%d, intval=%d)", + wnmsleep_ie->action_type, wnmsleep_ie->intval); + if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER) { + wnm_sleep_mode_enter_success(wpa_s, tfsresp_ie_start, + tfsresp_ie_end); + } else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) { + wnm_sleep_mode_exit_success(wpa_s, frm, key_len_total); + } + } else { + wpa_printf(MSG_DEBUG, "Reject recv WNM-Sleep Response frame " + "(action=%d, intval=%d)", + wnmsleep_ie->action_type, wnmsleep_ie->intval); + if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER) + wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_FAIL, + wpa_s->bssid, NULL, NULL); + else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) + wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_FAIL, + wpa_s->bssid, NULL, NULL); + } +} + + +static void wnm_send_bss_transition_mgmt_resp(struct wpa_supplicant *wpa_s, + u8 dialog_token, u8 status, + u8 delay, const u8 *target_bssid) +{ + u8 buf[1000], *pos; + struct ieee80211_mgmt *mgmt; + size_t len; + + wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Response " + "to " MACSTR " dialog_token=%u status=%u delay=%d", + MAC2STR(wpa_s->bssid), dialog_token, status, delay); + + mgmt = (struct ieee80211_mgmt *) buf; + os_memset(&buf, 0, sizeof(buf)); + 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_WNM; + mgmt->u.action.u.bss_tm_resp.action = WNM_BSS_TRANS_MGMT_RESP; + mgmt->u.action.u.bss_tm_resp.dialog_token = dialog_token; + mgmt->u.action.u.bss_tm_resp.status_code = status; + mgmt->u.action.u.bss_tm_resp.bss_termination_delay = delay; + pos = mgmt->u.action.u.bss_tm_resp.variable; + if (target_bssid) { + os_memcpy(pos, target_bssid, ETH_ALEN); + pos += ETH_ALEN; + } + + len = pos - (u8 *) &mgmt->u.action.category; + + wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, + wpa_s->own_addr, wpa_s->bssid, + &mgmt->u.action.category, len, 0); +} + + +static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, + const u8 *pos, const u8 *end, + int reply) +{ + u8 dialog_token; + u8 mode; + u16 disassoc_timer; + + if (pos + 5 > end) + return; + + dialog_token = pos[0]; + mode = pos[1]; + disassoc_timer = WPA_GET_LE16(pos + 2); + + wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Request: " + "dialog_token=%u request_mode=0x%x " + "disassoc_timer=%u validity_interval=%u", + dialog_token, mode, disassoc_timer, pos[4]); + 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)"); + return; + } + 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); + } + + if (mode & 0x04) { + wpa_msg(wpa_s, MSG_INFO, "WNM: Disassociation Imminent - " + "Disassociation Timer %u", disassoc_timer); + if (disassoc_timer && !wpa_s->scanning) { + /* TODO: mark current BSS less preferred for + * selection */ + wpa_printf(MSG_DEBUG, "Trying to find another BSS"); + wpa_supplicant_req_scan(wpa_s, 0, 0); + } + } + + if (reply) { + /* TODO: add support for reporting Accept */ + wnm_send_bss_transition_mgmt_resp(wpa_s, dialog_token, + 1 /* Reject - unspecified */, + 0, NULL); + } +} + + +void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s, + struct rx_action *action) +{ + const u8 *pos, *end; + u8 act; + + if (action->data == NULL || action->len == 0) + return; + + pos = action->data; + end = pos + action->len; + act = *pos++; + + wpa_printf(MSG_DEBUG, "WNM: RX action %u from " MACSTR, + act, MAC2STR(action->sa)); + if (wpa_s->wpa_state < WPA_ASSOCIATED || + os_memcmp(action->sa, wpa_s->bssid, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "WNM: Ignore unexpected WNM Action " + "frame"); + return; + } + + switch (act) { + case WNM_BSS_TRANS_MGMT_REQ: + ieee802_11_rx_bss_trans_mgmt_req(wpa_s, pos, end, + !(action->da[0] & 0x01)); + break; + case WNM_SLEEP_MODE_RESP: + ieee802_11_rx_wnmsleep_resp(wpa_s, action->data, action->len); + break; + default: + break; + } +} diff --git a/contrib/wpa/wpa_supplicant/wnm_sta.h b/contrib/wpa/wpa_supplicant/wnm_sta.h new file mode 100644 index 0000000..3f9d88b --- /dev/null +++ b/contrib/wpa/wpa_supplicant/wnm_sta.h @@ -0,0 +1,21 @@ +/* + * IEEE 802.11v WNM related functions and structures + * Copyright (c) 2011-2012, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WNM_STA_H +#define WNM_STA_H + +struct rx_action; +struct wpa_supplicant; + +int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s, + u8 action, u16 intval, struct wpabuf *tfs_req); + +void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s, + struct rx_action *action); + +#endif /* WNM_STA_H */ diff --git a/contrib/wpa/wpa_supplicant/wpa_cli.c b/contrib/wpa/wpa_supplicant/wpa_cli.c index 162a0b8..0c6ef5e 100644 --- a/contrib/wpa/wpa_supplicant/wpa_cli.c +++ b/contrib/wpa/wpa_supplicant/wpa_cli.c @@ -1,15 +1,9 @@ /* * WPA Supplicant - command line interface for wpa_supplicant daemon - * Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -19,47 +13,30 @@ #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" +#include "common/ieee802_11_defs.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 = -"This program is free software. You can distribute it and/or modify it\n" -"under the terms of the GNU General Public License version 2.\n" -"\n" -"Alternatively, this software may be distributed under the terms of the\n" -"BSD license. See README and COPYING for more details.\n"; +"This software may be distributed under the terms of the BSD license.\n" +"See README for more details.\n"; static const char *wpa_cli_full_license = -"This program is free software; you can redistribute it and/or modify\n" -"it under the terms of the GNU General Public License version 2 as\n" -"published by the Free Software Foundation.\n" -"\n" -"This program is distributed in the hope that it will be useful,\n" -"but WITHOUT ANY WARRANTY; without even the implied warranty of\n" -"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" -"GNU General Public License for more details.\n" -"\n" -"You should have received a copy of the GNU General Public License\n" -"along with this program; if not, write to the Free Software\n" -"Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n" -"\n" -"Alternatively, this software may be distributed under the terms of the\n" -"BSD license.\n" +"This software may be distributed under the terms of the BSD license.\n" "\n" "Redistribution and use in source and binary forms, with or without\n" "modification, are permitted provided that the following conditions are\n" @@ -91,22 +68,35 @@ 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(const char *cmd); +static void wpa_cli_mon_receive(int sock, void *eloop_ctx, void *sock_ctx); +static void wpa_cli_close_connection(void); +static char * wpa_cli_get_default_ifname(void); +static char ** wpa_list_cmd_list(void); static void usage(void) @@ -121,63 +111,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(); + print_help(NULL); } -#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_calloc(count + 1, sizeof(char *)); + if (res == NULL) + return NULL; + + i = 0; + dl_list_for_each(e, txt_list, struct cli_txt_entry, list) { + res[i] = os_strdup(e->txt); + if (res[i] == NULL) break; - 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 +314,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 +357,16 @@ 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"); + wpa_cli_close_connection(); 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 +378,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 +385,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 +421,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; } @@ -317,10 +434,65 @@ static int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd) } +static int write_cmd(char *buf, size_t buflen, const char *cmd, int argc, + char *argv[]) +{ + int i, res; + char *pos, *end; + + pos = buf; + end = buf + buflen; + + res = os_snprintf(pos, end - pos, "%s", cmd); + if (res < 0 || res >= end - pos) + goto fail; + pos += res; + + for (i = 0; i < argc; i++) { + res = os_snprintf(pos, end - pos, " %s", argv[i]); + if (res < 0 || res >= end - pos) + goto fail; + pos += res; + } + + buf[buflen - 1] = '\0'; + return 0; + +fail: + printf("Too long command\n"); + return -1; +} + + +static int wpa_cli_cmd(struct wpa_ctrl *ctrl, const char *cmd, int min_args, + int argc, char *argv[]) +{ + char buf[256]; + if (argc < min_args) { + printf("Invalid %s command - at least %d argument%s " + "required.\n", cmd, min_args, + min_args > 1 ? "s are" : " is"); + return -1; + } + if (write_cmd(buf, sizeof(buf), cmd, argc, argv) < 0) + return -1; + return wpa_ctrl_command(ctrl, buf); +} + + +static int wpa_cli_cmd_ifname(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "IFNAME"); +} + + static int wpa_cli_cmd_status(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - int verbose = argc > 0 && os_strcmp(argv[0], "verbose") == 0; - return wpa_ctrl_command(ctrl, verbose ? "STATUS-VERBOSE" : "STATUS"); + if (argc > 0 && os_strcmp(argv[0], "verbose") == 0) + return wpa_ctrl_command(ctrl, "STATUS-VERBOSE"); + if (argc > 0 && os_strcmp(argv[0], "wps") == 0) + return wpa_ctrl_command(ctrl, "STATUS-WPS"); + return wpa_ctrl_command(ctrl, "STATUS"); } @@ -330,6 +502,18 @@ 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[]) +{ + return wpa_cli_cmd(ctrl, "NOTE", 1, argc, argv); +} + + static int wpa_cli_cmd_mib(struct wpa_ctrl *ctrl, int argc, char *argv[]) { return wpa_ctrl_command(ctrl, "MIB"); @@ -344,11 +528,26 @@ static int wpa_cli_cmd_pmksa(struct wpa_ctrl *ctrl, int argc, char *argv[]) static int wpa_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - print_help(); + print_help(argc > 0 ? argv[0] : NULL); return 0; } +static char ** wpa_cli_complete_help(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + char **res = NULL; + + switch (arg) { + case 1: + res = wpa_list_cmd_list(); + break; + } + + return res; +} + + static int wpa_cli_cmd_license(struct wpa_ctrl *ctrl, int argc, char *argv[]) { printf("%s\n\n%s\n", wpa_cli_version, wpa_cli_full_license); @@ -359,6 +558,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 +594,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 +613,12 @@ 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[]) +{ + return wpa_cli_cmd(ctrl, "GET", 1, argc, argv); +} + + static int wpa_cli_cmd_logoff(struct wpa_ctrl *ctrl, int argc, char *argv[]) { return wpa_ctrl_command(ctrl, "LOGOFF"); @@ -430,37 +641,48 @@ static int wpa_cli_cmd_reassociate(struct wpa_ctrl *ctrl, int argc, static int wpa_cli_cmd_preauthenticate(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; + return wpa_cli_cmd(ctrl, "PREAUTH", 1, argc, argv); +} - if (argc != 1) { - printf("Invalid PREAUTH command: needs one argument " - "(BSSID)\n"); - return -1; - } - res = os_snprintf(cmd, sizeof(cmd), "PREAUTH %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long PREAUTH command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); +static int wpa_cli_cmd_ap_scan(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_cli_cmd(ctrl, "AP_SCAN", 1, argc, argv); } -static int wpa_cli_cmd_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[]) +{ + return wpa_cli_cmd(ctrl, "SCAN_INTERVAL", 1, argc, argv); +} + + +static int wpa_cli_cmd_bss_expire_age(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "BSS_EXPIRE_AGE", 1, argc, argv); +} + + +static int wpa_cli_cmd_bss_expire_count(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "BSS_EXPIRE_COUNT", 1, argc, argv); +} + + +static int wpa_cli_cmd_bss_flush(struct wpa_ctrl *ctrl, int argc, char *argv[]) { char cmd[256]; int res; - if (argc != 1) { - printf("Invalid AP_SCAN command: needs one argument (ap_scan " - "value)\n"); - return -1; - } - res = os_snprintf(cmd, sizeof(cmd), "AP_SCAN %s", argv[0]); + if (argc < 1) + res = os_snprintf(cmd, sizeof(cmd), "BSS_FLUSH 0"); + else + res = os_snprintf(cmd, sizeof(cmd), "BSS_FLUSH %s", argv[0]); if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long AP_SCAN command.\n"); + printf("Too long BSS_FLUSH command.\n"); return -1; } return wpa_ctrl_command(ctrl, cmd); @@ -470,128 +692,157 @@ static int wpa_cli_cmd_ap_scan(struct wpa_ctrl *ctrl, int argc, char *argv[]) static int wpa_cli_cmd_stkstart(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; + return wpa_cli_cmd(ctrl, "STKSTART", 1, argc, argv); +} - if (argc != 1) { - printf("Invalid STKSTART command: needs one argument " - "(Peer STA MAC address)\n"); - return -1; - } - res = os_snprintf(cmd, sizeof(cmd), "STKSTART %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long STKSTART command.\n"); +static int wpa_cli_cmd_ft_ds(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_cli_cmd(ctrl, "FT_DS", 1, argc, argv); +} + + +static int wpa_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_cli_cmd(ctrl, "WPS_PBC", 0, argc, argv); +} + + +static int wpa_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + if (argc == 0) { + printf("Invalid WPS_PIN command: need one or two arguments:\n" + "- BSSID: use 'any' to select any\n" + "- PIN: optional, used only with devices that have no " + "display\n"); return -1; } - return wpa_ctrl_command(ctrl, cmd); + + return wpa_cli_cmd(ctrl, "WPS_PIN", 1, argc, argv); } -static int wpa_cli_cmd_ft_ds(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; + return wpa_cli_cmd(ctrl, "WPS_CHECK_PIN", 1, argc, argv); +} + + +static int wpa_cli_cmd_wps_cancel(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "WPS_CANCEL"); +} + + +#ifdef CONFIG_WPS_NFC + +static int wpa_cli_cmd_wps_nfc(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_cli_cmd(ctrl, "WPS_NFC", 0, argc, argv); +} + + +static int wpa_cli_cmd_wps_nfc_token(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "WPS_NFC_TOKEN", 1, argc, argv); +} + + +static int wpa_cli_cmd_wps_nfc_tag_read(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + int ret; + char *buf; + size_t buflen; if (argc != 1) { - printf("Invalid FT_DS command: needs one argument " - "(Target AP MAC address)\n"); + printf("Invalid 'wps_nfc_tag_read' command - one argument " + "is required.\n"); return -1; } - res = os_snprintf(cmd, sizeof(cmd), "FT_DS %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long FT_DS command.\n"); + buflen = 18 + os_strlen(argv[0]); + buf = os_malloc(buflen); + if (buf == NULL) return -1; - } - return wpa_ctrl_command(ctrl, cmd); + os_snprintf(buf, buflen, "WPS_NFC_TAG_READ %s", argv[0]); + + ret = wpa_ctrl_command(ctrl, buf); + os_free(buf); + + return ret; } -static int wpa_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc, char *argv[]) +static int wpa_cli_cmd_nfc_get_handover_req(struct wpa_ctrl *ctrl, int argc, + char *argv[]) { - char cmd[256]; - int res; + return wpa_cli_cmd(ctrl, "NFC_GET_HANDOVER_REQ", 2, argc, argv); +} - if (argc == 0) { - /* Any BSSID */ - return wpa_ctrl_command(ctrl, "WPS_PBC"); - } - /* Specific BSSID */ - res = os_snprintf(cmd, sizeof(cmd), "WPS_PBC %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long WPS_PBC command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); +static int wpa_cli_cmd_nfc_get_handover_sel(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "NFC_GET_HANDOVER_SEL", 2, argc, argv); } -static int wpa_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc, char *argv[]) +static int wpa_cli_cmd_nfc_rx_handover_req(struct wpa_ctrl *ctrl, int argc, + char *argv[]) { - char cmd[256]; - int res; + int ret; + char *buf; + size_t buflen; - if (argc == 0) { - printf("Invalid WPS_PIN command: need one or two arguments:\n" - "- BSSID: use 'any' to select any\n" - "- PIN: optional, used only with devices that have no " - "display\n"); + if (argc != 1) { + printf("Invalid 'nfc_rx_handover_req' command - one argument " + "is required.\n"); return -1; } - if (argc == 1) { - /* Use dynamically generated PIN (returned as reply) */ - res = os_snprintf(cmd, sizeof(cmd), "WPS_PIN %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long WPS_PIN command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); - } - - /* Use hardcoded PIN from a label */ - res = os_snprintf(cmd, sizeof(cmd), "WPS_PIN %s %s", argv[0], argv[1]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long WPS_PIN command.\n"); + buflen = 21 + os_strlen(argv[0]); + buf = os_malloc(buflen); + if (buf == NULL) return -1; - } - return wpa_ctrl_command(ctrl, cmd); + os_snprintf(buf, buflen, "NFC_RX_HANDOVER_REQ %s", argv[0]); + + ret = wpa_ctrl_command(ctrl, buf); + os_free(buf); + + return ret; } -#ifdef CONFIG_WPS_OOB -static int wpa_cli_cmd_wps_oob(struct wpa_ctrl *ctrl, int argc, char *argv[]) +static int wpa_cli_cmd_nfc_rx_handover_sel(struct wpa_ctrl *ctrl, int argc, + char *argv[]) { - char cmd[256]; - int res; + int ret; + char *buf; + size_t buflen; - if (argc != 3 && argc != 4) { - printf("Invalid WPS_OOB command: need three or four " - "arguments:\n" - "- DEV_TYPE: use 'ufd' or 'nfc'\n" - "- PATH: path of OOB device like '/mnt'\n" - "- METHOD: OOB method 'pin-e' or 'pin-r', " - "'cred'\n" - "- DEV_NAME: (only for NFC) device name like " - "'pn531'\n"); + if (argc != 1) { + printf("Invalid 'nfc_rx_handover_sel' command - one argument " + "is required.\n"); return -1; } - if (argc == 3) - res = os_snprintf(cmd, sizeof(cmd), "WPS_OOB %s %s %s", - argv[0], argv[1], argv[2]); - else - res = os_snprintf(cmd, sizeof(cmd), "WPS_OOB %s %s %s %s", - argv[0], argv[1], argv[2], argv[3]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long WPS_OOB command.\n"); + buflen = 21 + os_strlen(argv[0]); + buf = os_malloc(buflen); + if (buf == NULL) return -1; - } - return wpa_ctrl_command(ctrl, cmd); + os_snprintf(buf, buflen, "NFC_RX_HANDOVER_SEL %s", argv[0]); + + ret = wpa_ctrl_command(ctrl, buf); + os_free(buf); + + return ret; } -#endif /* CONFIG_WPS_OOB */ + +#endif /* CONFIG_WPS_NFC */ static int wpa_cli_cmd_wps_reg(struct wpa_ctrl *ctrl, int argc, char *argv[]) @@ -602,7 +853,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 +866,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 +881,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 +902,17 @@ 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[]) +{ + return wpa_cli_cmd(ctrl, "WPS_AP_PIN", 1, argc, argv); +} + + static int wpa_cli_cmd_wps_er_start(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - return wpa_ctrl_command(ctrl, "WPS_ER_START"); - + return wpa_cli_cmd(ctrl, "WPS_ER_START", 0, argc, argv); } @@ -667,54 +927,29 @@ static int wpa_cli_cmd_wps_er_stop(struct wpa_ctrl *ctrl, int argc, static int wpa_cli_cmd_wps_er_pin(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - 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 (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long WPS_ER_PIN command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "WPS_ER_PIN", 2, argc, argv); } static int wpa_cli_cmd_wps_er_pbc(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - - if (argc != 1) { - printf("Invalid WPS_ER_PBC command: need one argument:\n" - "- UUID: Specify the Enrollee\n"); - return -1; - } - - res = os_snprintf(cmd, sizeof(cmd), "WPS_ER_PBC %s", - argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long WPS_ER_PBC command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "WPS_ER_PBC", 1, argc, argv); } static int wpa_cli_cmd_wps_er_learn(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - if (argc != 2) { printf("Invalid WPS_ER_LEARN command: need two arguments:\n" "- UUID: specify which AP to use\n" @@ -722,52 +957,102 @@ static int wpa_cli_cmd_wps_er_learn(struct wpa_ctrl *ctrl, int argc, return -1; } - res = os_snprintf(cmd, sizeof(cmd), "WPS_ER_LEARN %s %s", - argv[0], argv[1]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long WPS_ER_LEARN command.\n"); + return wpa_cli_cmd(ctrl, "WPS_ER_LEARN", 2, argc, argv); +} + + +static int wpa_cli_cmd_wps_er_set_config(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + if (argc != 2) { + printf("Invalid WPS_ER_SET_CONFIG command: need two " + "arguments:\n" + "- UUID: specify which AP to use\n" + "- Network configuration id\n"); return -1; } - return wpa_ctrl_command(ctrl, cmd); + + return wpa_cli_cmd(ctrl, "WPS_ER_SET_CONFIG", 2, argc, argv); } -static int wpa_cli_cmd_ibss_rsn(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 IBSS_RSN command: needs one argument " - "(Peer STA MAC address)\n"); + 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; } - 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"); + printf("Too long WPS_ER_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[]) +#ifdef CONFIG_WPS_NFC +static int wpa_cli_cmd_wps_er_nfc_config_token(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"); + if (argc != 2) { + printf("Invalid WPS_ER_NFC_CONFIG_TOKEN command: need two " + "arguments:\n" + "- WPS/NDEF: token format\n" + "- UUID: specify which AP to use\n"); return -1; } - return wpa_ctrl_command(ctrl, cmd); + + return wpa_cli_cmd(ctrl, "WPS_ER_NFC_CONFIG_TOKEN", 2, argc, argv); +} +#endif /* CONFIG_WPS_NFC */ + + +static int wpa_cli_cmd_ibss_rsn(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_cli_cmd(ctrl, "IBSS_RSN", 1, argc, argv); +} + + +static int wpa_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_cli_cmd(ctrl, "LEVEL", 1, argc, argv); } @@ -972,33 +1257,25 @@ static int wpa_cli_cmd_passphrase(struct wpa_ctrl *ctrl, int argc, static int wpa_cli_cmd_bssid(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256], *pos, *end; - int i, ret; - if (argc < 2) { printf("Invalid BSSID command: needs two arguments (network " "id and BSSID)\n"); return -1; } - end = cmd + sizeof(cmd); - pos = cmd; - ret = os_snprintf(pos, end - pos, "BSSID"); - if (ret < 0 || ret >= end - pos) { - printf("Too long BSSID 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 BSSID command.\n"); - return -1; - } - pos += ret; - } + return wpa_cli_cmd(ctrl, "BSSID", 2, argc, argv); +} - return wpa_ctrl_command(ctrl, cmd); + +static int wpa_cli_cmd_blacklist(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_cli_cmd(ctrl, "BLACKLIST", 0, argc, argv); +} + + +static int wpa_cli_cmd_log_level(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_cli_cmd(ctrl, "LOG_LEVEL", 0, argc, argv); } @@ -1012,63 +1289,21 @@ static int wpa_cli_cmd_list_networks(struct wpa_ctrl *ctrl, int argc, static int wpa_cli_cmd_select_network(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[32]; - int res; - - if (argc < 1) { - printf("Invalid SELECT_NETWORK command: needs one argument " - "(network id)\n"); - return -1; - } - - res = os_snprintf(cmd, sizeof(cmd), "SELECT_NETWORK %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "SELECT_NETWORK", 1, argc, argv); } static int wpa_cli_cmd_enable_network(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[32]; - int res; - - if (argc < 1) { - printf("Invalid ENABLE_NETWORK command: needs one argument " - "(network id)\n"); - return -1; - } - - res = os_snprintf(cmd, sizeof(cmd), "ENABLE_NETWORK %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "ENABLE_NETWORK", 1, argc, argv); } static int wpa_cli_cmd_disable_network(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[32]; - int res; - - if (argc < 1) { - printf("Invalid DISABLE_NETWORK command: needs one argument " - "(network id)\n"); - return -1; - } - - res = os_snprintf(cmd, sizeof(cmd), "DISABLE_NETWORK %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "DISABLE_NETWORK", 1, argc, argv); } @@ -1082,21 +1317,7 @@ static int wpa_cli_cmd_add_network(struct wpa_ctrl *ctrl, int argc, static int wpa_cli_cmd_remove_network(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[32]; - int res; - - if (argc < 1) { - printf("Invalid REMOVE_NETWORK command: needs one argument " - "(network id)\n"); - return -1; - } - - res = os_snprintf(cmd, sizeof(cmd), "REMOVE_NETWORK %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "REMOVE_NETWORK", 1, argc, argv); } @@ -1124,36 +1345,24 @@ static void wpa_cli_show_network_variables(void) static int wpa_cli_cmd_set_network(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - if (argc == 0) { wpa_cli_show_network_variables(); return 0; } - if (argc != 3) { + if (argc < 3) { printf("Invalid SET_NETWORK command: needs three arguments\n" "(network id, variable name, and value)\n"); return -1; } - res = os_snprintf(cmd, sizeof(cmd), "SET_NETWORK %s %s %s", - argv[0], argv[1], argv[2]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long SET_NETWORK command.\n"); - return -1; - } - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "SET_NETWORK", 3, argc, argv); } static int wpa_cli_cmd_get_network(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; - int res; - if (argc == 0) { wpa_cli_show_network_variables(); return 0; @@ -1165,13 +1374,39 @@ static int wpa_cli_cmd_get_network(struct wpa_ctrl *ctrl, int argc, return -1; } - res = os_snprintf(cmd, sizeof(cmd), "GET_NETWORK %s %s", - argv[0], argv[1]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long GET_NETWORK command.\n"); + return wpa_cli_cmd(ctrl, "GET_NETWORK", 2, argc, argv); +} + + +static int wpa_cli_cmd_list_creds(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "LIST_CREDS"); +} + + +static int wpa_cli_cmd_add_cred(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "ADD_CRED"); +} + + +static int wpa_cli_cmd_remove_cred(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "REMOVE_CRED", 1, argc, argv); +} + + +static int wpa_cli_cmd_set_cred(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + if (argc != 3) { + printf("Invalid SET_CRED command: needs three arguments\n" + "(cred id, variable name, and value)\n"); return -1; } - return wpa_ctrl_command(ctrl, cmd); + + return wpa_cli_cmd(ctrl, "SET_CRED", 3, argc, argv); } @@ -1211,30 +1446,28 @@ static int wpa_cli_cmd_scan_results(struct wpa_ctrl *ctrl, int argc, static int wpa_cli_cmd_bss(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[64]; - int res; + return wpa_cli_cmd(ctrl, "BSS", 1, argc, argv); +} - if (argc != 1) { - printf("Invalid BSS command: need one argument (index or " - "BSSID)\n"); - return -1; - } - res = os_snprintf(cmd, sizeof(cmd), "BSS %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; +static char ** wpa_cli_complete_bss(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + char **res = NULL; - return wpa_ctrl_command(ctrl, cmd); + 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[]) { - char cmd[64]; - int res; - if (argc < 1 || argc > 2) { printf("Invalid GET_CAPABILITY command: need either one or " "two arguments\n"); @@ -1247,13 +1480,7 @@ static int wpa_cli_cmd_get_capability(struct wpa_ctrl *ctrl, int argc, return -1; } - res = os_snprintf(cmd, sizeof(cmd), "GET_CAPABILITY %s%s", argv[0], - (argc == 2) ? " strict" : ""); - if (res < 0 || (size_t) res >= sizeof(cmd)) - return -1; - cmd[sizeof(cmd) - 1] = '\0'; - - return wpa_ctrl_command(ctrl, cmd); + return wpa_cli_cmd(ctrl, "GET_CAPABILITY", 1, argc, argv); } @@ -1333,20 +1560,7 @@ static int wpa_cli_cmd_interface_add(struct wpa_ctrl *ctrl, int argc, static int wpa_cli_cmd_interface_remove(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[128]; - int res; - - if (argc != 1) { - printf("Invalid INTERFACE_REMOVE command: needs one argument " - "(interface name)\n"); - return -1; - } - - res = os_snprintf(cmd, sizeof(cmd), "INTERFACE_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); + return wpa_cli_cmd(ctrl, "INTERFACE_REMOVE", 1, argc, argv); } @@ -1360,14 +1574,7 @@ static int wpa_cli_cmd_interface_list(struct wpa_ctrl *ctrl, int argc, #ifdef CONFIG_AP static int wpa_cli_cmd_sta(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char buf[64]; - if (argc != 1) { - printf("Invalid 'sta' command - exactly one argument, STA " - "address, is required.\n"); - return -1; - } - os_snprintf(buf, sizeof(buf), "STA %s", argv[0]); - return wpa_ctrl_command(ctrl, buf); + return wpa_cli_cmd(ctrl, "STA", 1, argc, argv); } @@ -1383,7 +1590,7 @@ static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd, return -1; } len = sizeof(buf) - 1; - ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len, + ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len, wpa_cli_msg_cb); if (ret == -2) { printf("'%s' command timed out.\n", cmd); @@ -1394,7 +1601,7 @@ static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd, } buf[len] = '\0'; - if (memcmp(buf, "FAIL", 4) == 0) + if (os_memcmp(buf, "FAIL", 4) == 0) return -1; printf("%s", buf); @@ -1419,6 +1626,20 @@ static int wpa_cli_cmd_all_sta(struct wpa_ctrl *ctrl, int argc, char *argv[]) return -1; } + + +static int wpa_cli_cmd_deauthenticate(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "DEAUTHENTICATE", 1, argc, argv); +} + + +static int wpa_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "DISASSOCIATE", 1, argc, argv); +} #endif /* CONFIG_AP */ @@ -1442,23 +1663,607 @@ static int wpa_cli_cmd_drop_sa(struct wpa_ctrl *ctrl, int argc, char *argv[]) static int wpa_cli_cmd_roam(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[128]; + return wpa_cli_cmd(ctrl, "ROAM", 1, argc, argv); +} + + +#ifdef CONFIG_P2P + +static int wpa_cli_cmd_p2p_find(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_cli_cmd(ctrl, "P2P_FIND", 0, argc, argv); +} + + +static char ** wpa_cli_complete_p2p_find(const char *str, int pos) +{ + char **res = NULL; + int arg = get_cmd_arg_num(str, pos); + + res = os_calloc(6, sizeof(char *)); + if (res == NULL) + return NULL; + res[0] = os_strdup("type=social"); + if (res[0] == NULL) { + os_free(res); + return NULL; + } + res[1] = os_strdup("type=progressive"); + if (res[1] == NULL) + return res; + res[2] = os_strdup("delay="); + if (res[2] == NULL) + return res; + res[3] = os_strdup("dev_id="); + if (res[3] == NULL) + return res; + if (arg == 1) + res[4] = os_strdup("[timeout]"); + + return res; +} + + +static int wpa_cli_cmd_p2p_stop_find(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "P2P_STOP_FIND"); +} + + +static int wpa_cli_cmd_p2p_connect(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "P2P_CONNECT", 2, argc, argv); +} + + +static char ** wpa_cli_complete_p2p_connect(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + char **res = NULL; + + switch (arg) { + case 1: + res = cli_txt_list_array(&p2p_peers); + break; + } + + return res; +} + + +static int wpa_cli_cmd_p2p_listen(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "P2P_LISTEN", 0, argc, argv); +} + + +static int wpa_cli_cmd_p2p_group_remove(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "P2P_GROUP_REMOVE", 1, argc, argv); +} + + +static char ** wpa_cli_complete_p2p_group_remove(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + char **res = NULL; + + switch (arg) { + case 1: + res = cli_txt_list_array(&p2p_groups); + break; + } + + return res; +} + + +static int wpa_cli_cmd_p2p_group_add(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "P2P_GROUP_ADD", 0, argc, argv); +} + + +static int wpa_cli_cmd_p2p_prov_disc(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + if (argc != 2 && argc != 3) { + printf("Invalid P2P_PROV_DISC command: needs at least " + "two arguments, address and config method\n" + "(display, keypad, or pbc) and an optional join\n"); + return -1; + } + + return wpa_cli_cmd(ctrl, "P2P_PROV_DISC", 2, argc, argv); +} + + +static int wpa_cli_cmd_p2p_get_passphrase(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "P2P_GET_PASSPHRASE"); +} + + +static int wpa_cli_cmd_p2p_serv_disc_req(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[4096]; + + if (argc != 2 && 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 (write_cmd(cmd, sizeof(cmd), "P2P_SERV_DISC_REQ", argc, argv) < 0) + return -1; + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_p2p_serv_disc_cancel_req(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + return wpa_cli_cmd(ctrl, "P2P_SERV_DISC_CANCEL_REQ", 1, argc, argv); +} + + +static int wpa_cli_cmd_p2p_serv_disc_resp(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[4096]; + int res; + + if (argc != 4) { + printf("Invalid P2P_SERV_DISC_RESP command: needs four " + "arguments (freq, address, dialog token, and TLVs)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "P2P_SERV_DISC_RESP %s %s %s %s", + argv[0], argv[1], argv[2], argv[3]); + if (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[]) +{ + return wpa_cli_cmd(ctrl, "P2P_SERV_DISC_EXTERNAL", 1, argc, argv); +} + + +static int wpa_cli_cmd_p2p_service_flush(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "P2P_SERVICE_FLUSH"); +} + + +static int wpa_cli_cmd_p2p_service_add(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + 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[]) +{ + return wpa_cli_cmd(ctrl, "P2P_REJECT", 1, argc, argv); +} + + +static int wpa_cli_cmd_p2p_invite(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + return wpa_cli_cmd(ctrl, "P2P_INVITE", 1, argc, argv); +} + + +static int wpa_cli_cmd_p2p_peer(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_cli_cmd(ctrl, "P2P_PEER", 1, argc, argv); +} + + +static char ** wpa_cli_complete_p2p_peer(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + char **res = NULL; + + switch (arg) { + case 1: + res = cli_txt_list_array(&p2p_peers); + break; + } + + return res; +} + + +static int wpa_ctrl_command_p2p_peer(struct wpa_ctrl *ctrl, char *cmd, + char *addr, size_t addr_len, + int discovered) +{ + char buf[4096], *pos; + size_t len; + int ret; + + if (ctrl_conn == NULL) + return -1; + len = sizeof(buf) - 1; + ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len, + wpa_cli_msg_cb); + if (ret == -2) { + printf("'%s' command timed out.\n", cmd); + return -2; + } else if (ret < 0) { + printf("'%s' command failed.\n", cmd); + return -1; + } + + buf[len] = '\0'; + if (os_memcmp(buf, "FAIL", 4) == 0) + return -1; + + pos = buf; + while (*pos != '\0' && *pos != '\n') + pos++; + *pos++ = '\0'; + os_strlcpy(addr, buf, addr_len); + if (!discovered || os_strstr(pos, "[PROBE_REQ_ONLY]") == NULL) + printf("%s\n", addr); + return 0; +} + + +static int wpa_cli_cmd_p2p_peers(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char addr[32], cmd[64]; + int discovered; + + discovered = argc > 0 && os_strcmp(argv[0], "discovered") == 0; + + if (wpa_ctrl_command_p2p_peer(ctrl, "P2P_PEER FIRST", + addr, sizeof(addr), discovered)) + return -1; + do { + os_snprintf(cmd, sizeof(cmd), "P2P_PEER NEXT-%s", addr); + } while (wpa_ctrl_command_p2p_peer(ctrl, cmd, addr, sizeof(addr), + discovered) == 0); + + return 0; +} + + +static int wpa_cli_cmd_p2p_set(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_cli_cmd(ctrl, "P2P_SET", 2, argc, argv); +} + + +static int wpa_cli_cmd_p2p_flush(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "P2P_FLUSH"); +} + + +static int wpa_cli_cmd_p2p_cancel(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "P2P_CANCEL"); +} + + +static int wpa_cli_cmd_p2p_unauthorize(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "P2P_UNAUTHORIZE", 1, argc, argv); +} + + +static int wpa_cli_cmd_p2p_presence_req(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + if (argc != 0 && argc != 2 && argc != 4) { + printf("Invalid P2P_PRESENCE_REQ command: needs two arguments " + "(preferred duration, interval; in microsecods).\n" + "Optional second pair can be used to provide " + "acceptable values.\n"); + return -1; + } + + return wpa_cli_cmd(ctrl, "P2P_PRESENCE_REQ", 0, argc, argv); +} + + +static int wpa_cli_cmd_p2p_ext_listen(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + if (argc != 0 && argc != 2) { + printf("Invalid P2P_EXT_LISTEN command: needs two arguments " + "(availability period, availability interval; in " + "millisecods).\n" + "Extended Listen Timing can be cancelled with this " + "command when used without parameters.\n"); + return -1; + } + + return wpa_cli_cmd(ctrl, "P2P_EXT_LISTEN", 0, argc, argv); +} + +#endif /* CONFIG_P2P */ + +#ifdef CONFIG_WIFI_DISPLAY + +static int wpa_cli_cmd_wfd_subelem_set(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[100]; + int res; + + if (argc != 1 && argc != 2) { + printf("Invalid WFD_SUBELEM_SET command: needs one or two " + "arguments (subelem, hexdump)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "WFD_SUBELEM_SET %s %s", + argv[0], argc > 1 ? argv[1] : ""); + if (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_wfd_subelem_get(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[100]; int res; if (argc != 1) { - printf("Invalid ROAM command: needs one argument " - "(target AP's BSSID)\n"); + printf("Invalid WFD_SUBELEM_GET command: needs one " + "argument (subelem)\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"); + res = os_snprintf(cmd, sizeof(cmd), "WFD_SUBELEM_GET %s", + argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd)) + return -1; + cmd[sizeof(cmd) - 1] = '\0'; + return wpa_ctrl_command(ctrl, cmd); +} +#endif /* CONFIG_WIFI_DISPLAY */ + + +#ifdef CONFIG_INTERWORKING +static int wpa_cli_cmd_fetch_anqp(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "FETCH_ANQP"); +} + + +static int wpa_cli_cmd_stop_fetch_anqp(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "STOP_FETCH_ANQP"); +} + + +static int wpa_cli_cmd_interworking_select(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "INTERWORKING_SELECT", 0, argc, argv); +} + + +static int wpa_cli_cmd_interworking_connect(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "INTERWORKING_CONNECT", 1, argc, argv); +} + + +static int wpa_cli_cmd_anqp_get(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_cli_cmd(ctrl, "ANQP_GET", 2, argc, argv); +} + + +static int wpa_cli_cmd_gas_request(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "GAS_REQUEST", 2, argc, argv); +} + + +static int wpa_cli_cmd_gas_response_get(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "GAS_RESPONSE_GET", 2, argc, argv); +} +#endif /* CONFIG_INTERWORKING */ + + +#ifdef CONFIG_HS20 + +static int wpa_cli_cmd_hs20_anqp_get(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "HS20_ANQP_GET", 2, argc, argv); +} + + +static int wpa_cli_cmd_get_nai_home_realm_list(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[512]; + + if (argc == 0) { + printf("Command needs one or two arguments (dst mac addr and " + "optional home realm)\n"); return -1; } + + if (write_cmd(cmd, sizeof(cmd), "HS20_GET_NAI_HOME_REALM_LIST", + argc, argv) < 0) + return -1; + return wpa_ctrl_command(ctrl, cmd); } +#endif /* CONFIG_HS20 */ + + +static int wpa_cli_cmd_sta_autoconnect(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "STA_AUTOCONNECT", 1, argc, argv); +} + + +static int wpa_cli_cmd_tdls_discover(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "TDLS_DISCOVER", 1, argc, argv); +} + + +static int wpa_cli_cmd_tdls_setup(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "TDLS_SETUP", 1, argc, argv); +} + + +static int wpa_cli_cmd_tdls_teardown(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "TDLS_TEARDOWN", 1, argc, argv); +} + + +static int wpa_cli_cmd_signal_poll(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "SIGNAL_POLL"); +} + + +static int wpa_cli_cmd_pktcnt_poll(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "PKTCNT_POLL"); +} + + +static int wpa_cli_cmd_reauthenticate(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "REAUTHENTICATE"); +} + + +#ifdef CONFIG_AUTOSCAN + +static int wpa_cli_cmd_autoscan(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + if (argc == 0) + return wpa_ctrl_command(ctrl, "AUTOSCAN "); + + return wpa_cli_cmd(ctrl, "AUTOSCAN", 0, argc, argv); +} + +#endif /* CONFIG_AUTOSCAN */ + + +#ifdef CONFIG_WNM + +static int wpa_cli_cmd_wnm_sleep(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_cli_cmd(ctrl, "WNM_SLEEP", 0, argc, argv); +} + +#endif /* CONFIG_WNM */ + + +static int wpa_cli_cmd_raw(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + if (argc == 0) + return -1; + return wpa_cli_cmd(ctrl, argv[0], 0, argc - 1, &argv[1]); +} + enum wpa_cli_cmd_flags { cli_cmd_flag_none = 0x00, @@ -1468,202 +2273,439 @@ enum wpa_cli_cmd_flags { struct wpa_cli_cmd { const char *cmd; int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]); + char ** (*completion)(const char *str, int pos); enum wpa_cli_cmd_flags flags; const char *usage; }; static struct wpa_cli_cmd wpa_cli_commands[] = { - { "status", wpa_cli_cmd_status, + { "status", wpa_cli_cmd_status, NULL, cli_cmd_flag_none, "[verbose] = get current WPA/EAPOL/EAP status" }, - { "ping", wpa_cli_cmd_ping, + { "ifname", wpa_cli_cmd_ifname, NULL, + cli_cmd_flag_none, + "= get current interface name" }, + { "ping", wpa_cli_cmd_ping, NULL, cli_cmd_flag_none, "= pings wpa_supplicant" }, - { "mib", wpa_cli_cmd_mib, + { "relog", wpa_cli_cmd_relog, NULL, + cli_cmd_flag_none, + "= re-open log-file (allow rolling logs)" }, + { "note", wpa_cli_cmd_note, NULL, + cli_cmd_flag_none, + "<text> = add a note to wpa_supplicant debug log" }, + { "mib", wpa_cli_cmd_mib, NULL, cli_cmd_flag_none, "= get MIB variables (dot1x, dot11)" }, - { "help", wpa_cli_cmd_help, + { "help", wpa_cli_cmd_help, wpa_cli_complete_help, cli_cmd_flag_none, - "= show this usage help" }, - { "interface", wpa_cli_cmd_interface, + "[command] = show usage help" }, + { "interface", wpa_cli_cmd_interface, NULL, cli_cmd_flag_none, "[ifname] = show interfaces/select interface" }, - { "level", wpa_cli_cmd_level, + { "level", wpa_cli_cmd_level, NULL, cli_cmd_flag_none, "<debug level> = change debug level" }, - { "license", wpa_cli_cmd_license, + { "license", wpa_cli_cmd_license, NULL, cli_cmd_flag_none, "= show full wpa_cli license" }, - { "quit", wpa_cli_cmd_quit, + { "quit", wpa_cli_cmd_quit, NULL, cli_cmd_flag_none, "= exit wpa_cli" }, - { "set", wpa_cli_cmd_set, + { "set", wpa_cli_cmd_set, NULL, cli_cmd_flag_none, "= set variables (shows list of variables when run without " "arguments)" }, - { "logon", wpa_cli_cmd_logon, + { "get", wpa_cli_cmd_get, NULL, + cli_cmd_flag_none, + "<name> = get information" }, + { "logon", wpa_cli_cmd_logon, NULL, cli_cmd_flag_none, "= IEEE 802.1X EAPOL state machine logon" }, - { "logoff", wpa_cli_cmd_logoff, + { "logoff", wpa_cli_cmd_logoff, NULL, cli_cmd_flag_none, "= IEEE 802.1X EAPOL state machine logoff" }, - { "pmksa", wpa_cli_cmd_pmksa, + { "pmksa", wpa_cli_cmd_pmksa, NULL, cli_cmd_flag_none, "= show PMKSA cache" }, - { "reassociate", wpa_cli_cmd_reassociate, + { "reassociate", wpa_cli_cmd_reassociate, NULL, cli_cmd_flag_none, "= force reassociation" }, - { "preauthenticate", wpa_cli_cmd_preauthenticate, + { "preauthenticate", wpa_cli_cmd_preauthenticate, wpa_cli_complete_bss, cli_cmd_flag_none, "<BSSID> = force preauthentication" }, - { "identity", wpa_cli_cmd_identity, + { "identity", wpa_cli_cmd_identity, NULL, cli_cmd_flag_none, "<network id> <identity> = configure identity for an SSID" }, - { "password", wpa_cli_cmd_password, + { "password", wpa_cli_cmd_password, NULL, cli_cmd_flag_sensitive, "<network id> <password> = configure password for an SSID" }, - { "new_password", wpa_cli_cmd_new_password, + { "new_password", wpa_cli_cmd_new_password, NULL, cli_cmd_flag_sensitive, "<network id> <password> = change password for an SSID" }, - { "pin", wpa_cli_cmd_pin, + { "pin", wpa_cli_cmd_pin, NULL, cli_cmd_flag_sensitive, "<network id> <pin> = configure pin for an SSID" }, - { "otp", wpa_cli_cmd_otp, + { "otp", wpa_cli_cmd_otp, NULL, cli_cmd_flag_sensitive, "<network id> <password> = configure one-time-password for an SSID" }, - { "passphrase", wpa_cli_cmd_passphrase, + { "passphrase", wpa_cli_cmd_passphrase, NULL, cli_cmd_flag_sensitive, "<network id> <passphrase> = configure private key passphrase\n" " for an SSID" }, - { "bssid", wpa_cli_cmd_bssid, + { "bssid", wpa_cli_cmd_bssid, NULL, cli_cmd_flag_none, "<network id> <BSSID> = set preferred BSSID for an SSID" }, - { "list_networks", wpa_cli_cmd_list_networks, + { "blacklist", wpa_cli_cmd_blacklist, wpa_cli_complete_bss, + cli_cmd_flag_none, + "<BSSID> = add a BSSID to the blacklist\n" + "blacklist clear = clear the blacklist\n" + "blacklist = display the blacklist" }, + { "log_level", wpa_cli_cmd_log_level, NULL, + cli_cmd_flag_none, + "<level> [<timestamp>] = update the log level/timestamp\n" + "log_level = display the current log level and log options" }, + { "list_networks", wpa_cli_cmd_list_networks, NULL, cli_cmd_flag_none, "= list configured networks" }, - { "select_network", wpa_cli_cmd_select_network, + { "select_network", wpa_cli_cmd_select_network, NULL, cli_cmd_flag_none, "<network id> = select a network (disable others)" }, - { "enable_network", wpa_cli_cmd_enable_network, + { "enable_network", wpa_cli_cmd_enable_network, NULL, cli_cmd_flag_none, "<network id> = enable a network" }, - { "disable_network", wpa_cli_cmd_disable_network, + { "disable_network", wpa_cli_cmd_disable_network, NULL, cli_cmd_flag_none, "<network id> = disable a network" }, - { "add_network", wpa_cli_cmd_add_network, + { "add_network", wpa_cli_cmd_add_network, NULL, cli_cmd_flag_none, "= add a network" }, - { "remove_network", wpa_cli_cmd_remove_network, + { "remove_network", wpa_cli_cmd_remove_network, NULL, cli_cmd_flag_none, "<network id> = remove a network" }, - { "set_network", wpa_cli_cmd_set_network, + { "set_network", wpa_cli_cmd_set_network, NULL, cli_cmd_flag_sensitive, "<network id> <variable> <value> = set network variables (shows\n" " list of variables when run without arguments)" }, - { "get_network", wpa_cli_cmd_get_network, + { "get_network", wpa_cli_cmd_get_network, NULL, cli_cmd_flag_none, "<network id> <variable> = get network variables" }, - { "save_config", wpa_cli_cmd_save_config, + { "list_creds", wpa_cli_cmd_list_creds, NULL, + cli_cmd_flag_none, + "= list configured credentials" }, + { "add_cred", wpa_cli_cmd_add_cred, NULL, + cli_cmd_flag_none, + "= add a credential" }, + { "remove_cred", wpa_cli_cmd_remove_cred, NULL, + cli_cmd_flag_none, + "<cred id> = remove a credential" }, + { "set_cred", wpa_cli_cmd_set_cred, NULL, + cli_cmd_flag_sensitive, + "<cred id> <variable> <value> = set credential variables" }, + { "save_config", wpa_cli_cmd_save_config, NULL, cli_cmd_flag_none, "= save the current configuration" }, - { "disconnect", wpa_cli_cmd_disconnect, + { "disconnect", wpa_cli_cmd_disconnect, NULL, cli_cmd_flag_none, "= disconnect and wait for reassociate/reconnect command before\n" " connecting" }, - { "reconnect", wpa_cli_cmd_reconnect, + { "reconnect", wpa_cli_cmd_reconnect, NULL, cli_cmd_flag_none, "= like reassociate, but only takes effect if already disconnected" }, - { "scan", wpa_cli_cmd_scan, + { "scan", wpa_cli_cmd_scan, NULL, cli_cmd_flag_none, "= request new BSS scan" }, - { "scan_results", wpa_cli_cmd_scan_results, + { "scan_results", wpa_cli_cmd_scan_results, NULL, cli_cmd_flag_none, "= get latest scan results" }, - { "bss", wpa_cli_cmd_bss, + { "bss", wpa_cli_cmd_bss, wpa_cli_complete_bss, cli_cmd_flag_none, "<<idx> | <bssid>> = get detailed scan result info" }, - { "get_capability", wpa_cli_cmd_get_capability, + { "get_capability", wpa_cli_cmd_get_capability, NULL, cli_cmd_flag_none, - "<eap/pairwise/group/key_mgmt/proto/auth_alg> = get capabilies" }, - { "reconfigure", wpa_cli_cmd_reconfigure, + "<eap/pairwise/group/key_mgmt/proto/auth_alg/channels> " + "= get capabilies" }, + { "reconfigure", wpa_cli_cmd_reconfigure, NULL, cli_cmd_flag_none, "= force wpa_supplicant to re-read its configuration file" }, - { "terminate", wpa_cli_cmd_terminate, + { "terminate", wpa_cli_cmd_terminate, NULL, cli_cmd_flag_none, "= terminate wpa_supplicant" }, - { "interface_add", wpa_cli_cmd_interface_add, + { "interface_add", wpa_cli_cmd_interface_add, NULL, cli_cmd_flag_none, "<ifname> <confname> <driver> <ctrl_interface> <driver_param>\n" " <bridge_name> = adds new interface, all parameters but <ifname>\n" " are optional" }, - { "interface_remove", wpa_cli_cmd_interface_remove, + { "interface_remove", wpa_cli_cmd_interface_remove, NULL, cli_cmd_flag_none, "<ifname> = removes the interface" }, - { "interface_list", wpa_cli_cmd_interface_list, + { "interface_list", wpa_cli_cmd_interface_list, NULL, cli_cmd_flag_none, "= list available interfaces" }, - { "ap_scan", wpa_cli_cmd_ap_scan, + { "ap_scan", wpa_cli_cmd_ap_scan, NULL, cli_cmd_flag_none, "<value> = set ap_scan parameter" }, - { "stkstart", wpa_cli_cmd_stkstart, + { "scan_interval", wpa_cli_cmd_scan_interval, NULL, + cli_cmd_flag_none, + "<value> = set scan_interval parameter (in seconds)" }, + { "bss_expire_age", wpa_cli_cmd_bss_expire_age, NULL, + cli_cmd_flag_none, + "<value> = set BSS expiration age parameter" }, + { "bss_expire_count", wpa_cli_cmd_bss_expire_count, NULL, + cli_cmd_flag_none, + "<value> = set BSS expiration scan count parameter" }, + { "bss_flush", wpa_cli_cmd_bss_flush, NULL, + cli_cmd_flag_none, + "<value> = set BSS flush age (0 by default)" }, + { "stkstart", wpa_cli_cmd_stkstart, NULL, cli_cmd_flag_none, "<addr> = request STK negotiation with <addr>" }, - { "ft_ds", wpa_cli_cmd_ft_ds, + { "ft_ds", wpa_cli_cmd_ft_ds, wpa_cli_complete_bss, cli_cmd_flag_none, "<addr> = request over-the-DS FT with <addr>" }, - { "wps_pbc", wpa_cli_cmd_wps_pbc, + { "wps_pbc", wpa_cli_cmd_wps_pbc, wpa_cli_complete_bss, cli_cmd_flag_none, "[BSSID] = start Wi-Fi Protected Setup: Push Button Configuration" }, - { "wps_pin", wpa_cli_cmd_wps_pin, + { "wps_pin", wpa_cli_cmd_wps_pin, wpa_cli_complete_bss, cli_cmd_flag_sensitive, "<BSSID> [PIN] = start WPS PIN method (returns PIN, if not " "hardcoded)" }, -#ifdef CONFIG_WPS_OOB - { "wps_oob", wpa_cli_cmd_wps_oob, + { "wps_check_pin", wpa_cli_cmd_wps_check_pin, NULL, + cli_cmd_flag_sensitive, + "<PIN> = verify PIN checksum" }, + { "wps_cancel", wpa_cli_cmd_wps_cancel, NULL, cli_cmd_flag_none, + "Cancels the pending WPS operation" }, +#ifdef CONFIG_WPS_NFC + { "wps_nfc", wpa_cli_cmd_wps_nfc, wpa_cli_complete_bss, + cli_cmd_flag_none, + "[BSSID] = start Wi-Fi Protected Setup: NFC" }, + { "wps_nfc_token", wpa_cli_cmd_wps_nfc_token, NULL, + cli_cmd_flag_none, + "<WPS|NDEF> = create password token" }, + { "wps_nfc_tag_read", wpa_cli_cmd_wps_nfc_tag_read, NULL, cli_cmd_flag_sensitive, - "<DEV_TYPE> <PATH> <METHOD> [DEV_NAME] = start WPS OOB" }, -#endif /* CONFIG_WPS_OOB */ - { "wps_reg", wpa_cli_cmd_wps_reg, + "<hexdump of payload> = report read NFC tag with WPS data" }, + { "nfc_get_handover_req", wpa_cli_cmd_nfc_get_handover_req, NULL, + cli_cmd_flag_none, + "<NDEF> <WPS> = create NFC handover request" }, + { "nfc_get_handover_sel", wpa_cli_cmd_nfc_get_handover_sel, NULL, + cli_cmd_flag_none, + "<NDEF> <WPS> = create NFC handover select" }, + { "nfc_rx_handover_req", wpa_cli_cmd_nfc_rx_handover_req, NULL, + cli_cmd_flag_none, + "<hexdump of payload> = report received NFC handover request" }, + { "nfc_rx_handover_sel", wpa_cli_cmd_nfc_rx_handover_sel, NULL, + cli_cmd_flag_none, + "<hexdump of payload> = report received NFC handover select" }, +#endif /* CONFIG_WPS_NFC */ + { "wps_reg", wpa_cli_cmd_wps_reg, wpa_cli_complete_bss, cli_cmd_flag_sensitive, "<BSSID> <AP PIN> = start WPS Registrar to configure an AP" }, - { "wps_er_start", wpa_cli_cmd_wps_er_start, + { "wps_ap_pin", wpa_cli_cmd_wps_ap_pin, NULL, + cli_cmd_flag_sensitive, + "[params..] = enable/disable AP PIN" }, + { "wps_er_start", wpa_cli_cmd_wps_er_start, NULL, cli_cmd_flag_none, - "= start Wi-Fi Protected Setup External Registrar" }, - { "wps_er_stop", wpa_cli_cmd_wps_er_stop, + "[IP address] = start Wi-Fi Protected Setup External Registrar" }, + { "wps_er_stop", wpa_cli_cmd_wps_er_stop, NULL, cli_cmd_flag_none, "= stop Wi-Fi Protected Setup External Registrar" }, - { "wps_er_pin", wpa_cli_cmd_wps_er_pin, + { "wps_er_pin", wpa_cli_cmd_wps_er_pin, NULL, cli_cmd_flag_sensitive, "<UUID> <PIN> = add an Enrollee PIN to External Registrar" }, - { "wps_er_pbc", wpa_cli_cmd_wps_er_pbc, + { "wps_er_pbc", wpa_cli_cmd_wps_er_pbc, NULL, cli_cmd_flag_none, "<UUID> = accept an Enrollee PBC using External Registrar" }, - { "wps_er_learn", wpa_cli_cmd_wps_er_learn, + { "wps_er_learn", wpa_cli_cmd_wps_er_learn, NULL, cli_cmd_flag_sensitive, "<UUID> <PIN> = learn AP configuration" }, - { "ibss_rsn", wpa_cli_cmd_ibss_rsn, + { "wps_er_set_config", wpa_cli_cmd_wps_er_set_config, NULL, + cli_cmd_flag_none, + "<UUID> <network id> = set AP configuration for enrolling" }, + { "wps_er_config", wpa_cli_cmd_wps_er_config, NULL, + cli_cmd_flag_sensitive, + "<UUID> <PIN> <SSID> <auth> <encr> <key> = configure AP" }, +#ifdef CONFIG_WPS_NFC + { "wps_er_nfc_config_token", wpa_cli_cmd_wps_er_nfc_config_token, NULL, + cli_cmd_flag_none, + "<WPS/NDEF> <UUID> = build NFC configuration token" }, +#endif /* CONFIG_WPS_NFC */ + { "ibss_rsn", wpa_cli_cmd_ibss_rsn, NULL, cli_cmd_flag_none, "<addr> = request RSN authentication with <addr> in IBSS" }, #ifdef CONFIG_AP - { "sta", wpa_cli_cmd_sta, + { "sta", wpa_cli_cmd_sta, NULL, cli_cmd_flag_none, "<addr> = get information about an associated station (AP)" }, - { "all_sta", wpa_cli_cmd_all_sta, + { "all_sta", wpa_cli_cmd_all_sta, NULL, cli_cmd_flag_none, "= get information about all associated stations (AP)" }, + { "deauthenticate", wpa_cli_cmd_deauthenticate, NULL, + cli_cmd_flag_none, + "<addr> = deauthenticate a station" }, + { "disassociate", wpa_cli_cmd_disassociate, NULL, + cli_cmd_flag_none, + "<addr> = disassociate a station" }, #endif /* CONFIG_AP */ - { "suspend", wpa_cli_cmd_suspend, cli_cmd_flag_none, + { "suspend", wpa_cli_cmd_suspend, NULL, cli_cmd_flag_none, "= notification of suspend/hibernate" }, - { "resume", wpa_cli_cmd_resume, cli_cmd_flag_none, + { "resume", wpa_cli_cmd_resume, NULL, cli_cmd_flag_none, "= notification of resume/thaw" }, - { "drop_sa", wpa_cli_cmd_drop_sa, cli_cmd_flag_none, + { "drop_sa", wpa_cli_cmd_drop_sa, NULL, cli_cmd_flag_none, "= drop SA without deauth/disassoc (test command)" }, - { "roam", wpa_cli_cmd_roam, + { "roam", wpa_cli_cmd_roam, wpa_cli_complete_bss, cli_cmd_flag_none, "<addr> = roam to the specified BSS" }, - { NULL, NULL, cli_cmd_flag_none, NULL } +#ifdef CONFIG_P2P + { "p2p_find", wpa_cli_cmd_p2p_find, wpa_cli_complete_p2p_find, + cli_cmd_flag_none, + "[timeout] [type=*] = find P2P Devices for up-to timeout seconds" }, + { "p2p_stop_find", wpa_cli_cmd_p2p_stop_find, NULL, cli_cmd_flag_none, + "= stop P2P Devices search" }, + { "p2p_connect", wpa_cli_cmd_p2p_connect, wpa_cli_complete_p2p_connect, + cli_cmd_flag_none, + "<addr> <\"pbc\"|PIN> [ht40] = connect to a P2P Device" }, + { "p2p_listen", wpa_cli_cmd_p2p_listen, NULL, cli_cmd_flag_none, + "[timeout] = listen for P2P Devices for up-to timeout seconds" }, + { "p2p_group_remove", wpa_cli_cmd_p2p_group_remove, + wpa_cli_complete_p2p_group_remove, cli_cmd_flag_none, + "<ifname> = remove P2P group interface (terminate group if GO)" }, + { "p2p_group_add", wpa_cli_cmd_p2p_group_add, NULL, cli_cmd_flag_none, + "[ht40] = add a new P2P group (local end as GO)" }, + { "p2p_prov_disc", wpa_cli_cmd_p2p_prov_disc, + wpa_cli_complete_p2p_peer, cli_cmd_flag_none, + "<addr> <method> = request provisioning discovery" }, + { "p2p_get_passphrase", wpa_cli_cmd_p2p_get_passphrase, NULL, + cli_cmd_flag_none, + "= get the passphrase for a group (GO only)" }, + { "p2p_serv_disc_req", wpa_cli_cmd_p2p_serv_disc_req, + wpa_cli_complete_p2p_peer, cli_cmd_flag_none, + "<addr> <TLVs> = schedule service discovery request" }, + { "p2p_serv_disc_cancel_req", wpa_cli_cmd_p2p_serv_disc_cancel_req, + NULL, cli_cmd_flag_none, + "<id> = cancel pending service discovery request" }, + { "p2p_serv_disc_resp", wpa_cli_cmd_p2p_serv_disc_resp, NULL, + cli_cmd_flag_none, + "<freq> <addr> <dialog token> <TLVs> = service discovery response" }, + { "p2p_service_update", wpa_cli_cmd_p2p_service_update, NULL, + cli_cmd_flag_none, + "= indicate change in local services" }, + { "p2p_serv_disc_external", wpa_cli_cmd_p2p_serv_disc_external, NULL, + cli_cmd_flag_none, + "<external> = set external processing of service discovery" }, + { "p2p_service_flush", wpa_cli_cmd_p2p_service_flush, NULL, + cli_cmd_flag_none, + "= remove all stored service entries" }, + { "p2p_service_add", wpa_cli_cmd_p2p_service_add, NULL, + cli_cmd_flag_none, + "<bonjour|upnp> <query|version> <response|service> = add a local " + "service" }, + { "p2p_service_del", wpa_cli_cmd_p2p_service_del, NULL, + cli_cmd_flag_none, + "<bonjour|upnp> <query|version> [|service] = remove a local " + "service" }, + { "p2p_reject", wpa_cli_cmd_p2p_reject, wpa_cli_complete_p2p_peer, + cli_cmd_flag_none, + "<addr> = reject connection attempts from a specific peer" }, + { "p2p_invite", wpa_cli_cmd_p2p_invite, NULL, + cli_cmd_flag_none, + "<cmd> [peer=addr] = invite peer" }, + { "p2p_peers", wpa_cli_cmd_p2p_peers, NULL, cli_cmd_flag_none, + "[discovered] = list known (optionally, only fully discovered) P2P " + "peers" }, + { "p2p_peer", wpa_cli_cmd_p2p_peer, wpa_cli_complete_p2p_peer, + cli_cmd_flag_none, + "<address> = show information about known P2P peer" }, + { "p2p_set", wpa_cli_cmd_p2p_set, NULL, cli_cmd_flag_none, + "<field> <value> = set a P2P parameter" }, + { "p2p_flush", wpa_cli_cmd_p2p_flush, NULL, cli_cmd_flag_none, + "= flush P2P state" }, + { "p2p_cancel", wpa_cli_cmd_p2p_cancel, NULL, cli_cmd_flag_none, + "= cancel P2P group formation" }, + { "p2p_unauthorize", wpa_cli_cmd_p2p_unauthorize, + wpa_cli_complete_p2p_peer, cli_cmd_flag_none, + "<address> = unauthorize a peer" }, + { "p2p_presence_req", wpa_cli_cmd_p2p_presence_req, NULL, + cli_cmd_flag_none, + "[<duration> <interval>] [<duration> <interval>] = request GO " + "presence" }, + { "p2p_ext_listen", wpa_cli_cmd_p2p_ext_listen, NULL, + cli_cmd_flag_none, + "[<period> <interval>] = set extended listen timing" }, +#endif /* CONFIG_P2P */ +#ifdef CONFIG_WIFI_DISPLAY + { "wfd_subelem_set", wpa_cli_cmd_wfd_subelem_set, NULL, + cli_cmd_flag_none, + "<subelem> [contents] = set Wi-Fi Display subelement" }, + { "wfd_subelem_get", wpa_cli_cmd_wfd_subelem_get, NULL, + cli_cmd_flag_none, + "<subelem> = get Wi-Fi Display subelement" }, +#endif /* CONFIG_WIFI_DISPLAY */ +#ifdef CONFIG_INTERWORKING + { "fetch_anqp", wpa_cli_cmd_fetch_anqp, NULL, cli_cmd_flag_none, + "= fetch ANQP information for all APs" }, + { "stop_fetch_anqp", wpa_cli_cmd_stop_fetch_anqp, NULL, + cli_cmd_flag_none, + "= stop fetch_anqp operation" }, + { "interworking_select", wpa_cli_cmd_interworking_select, NULL, + cli_cmd_flag_none, + "[auto] = perform Interworking network selection" }, + { "interworking_connect", wpa_cli_cmd_interworking_connect, + wpa_cli_complete_bss, cli_cmd_flag_none, + "<BSSID> = connect using Interworking credentials" }, + { "anqp_get", wpa_cli_cmd_anqp_get, wpa_cli_complete_bss, + cli_cmd_flag_none, + "<addr> <info id>[,<info id>]... = request ANQP information" }, + { "gas_request", wpa_cli_cmd_gas_request, wpa_cli_complete_bss, + cli_cmd_flag_none, + "<addr> <AdvProtoID> [QueryReq] = GAS request" }, + { "gas_response_get", wpa_cli_cmd_gas_response_get, + wpa_cli_complete_bss, cli_cmd_flag_none, + "<addr> <dialog token> [start,len] = Fetch last GAS response" }, +#endif /* CONFIG_INTERWORKING */ +#ifdef CONFIG_HS20 + { "hs20_anqp_get", wpa_cli_cmd_hs20_anqp_get, wpa_cli_complete_bss, + cli_cmd_flag_none, + "<addr> <subtype>[,<subtype>]... = request HS 2.0 ANQP information" + }, + { "nai_home_realm_list", wpa_cli_cmd_get_nai_home_realm_list, + wpa_cli_complete_bss, cli_cmd_flag_none, + "<addr> <home realm> = get HS20 nai home realm list" }, +#endif /* CONFIG_HS20 */ + { "sta_autoconnect", wpa_cli_cmd_sta_autoconnect, NULL, + cli_cmd_flag_none, + "<0/1> = disable/enable automatic reconnection" }, + { "tdls_discover", wpa_cli_cmd_tdls_discover, NULL, + cli_cmd_flag_none, + "<addr> = request TDLS discovery with <addr>" }, + { "tdls_setup", wpa_cli_cmd_tdls_setup, NULL, + cli_cmd_flag_none, + "<addr> = request TDLS setup with <addr>" }, + { "tdls_teardown", wpa_cli_cmd_tdls_teardown, NULL, + cli_cmd_flag_none, + "<addr> = tear down TDLS with <addr>" }, + { "signal_poll", wpa_cli_cmd_signal_poll, NULL, + cli_cmd_flag_none, + "= get signal parameters" }, + { "pktcnt_poll", wpa_cli_cmd_pktcnt_poll, NULL, + cli_cmd_flag_none, + "= get TX/RX packet counters" }, + { "reauthenticate", wpa_cli_cmd_reauthenticate, NULL, + cli_cmd_flag_none, + "= trigger IEEE 802.1X/EAPOL reauthentication" }, +#ifdef CONFIG_AUTOSCAN + { "autoscan", wpa_cli_cmd_autoscan, NULL, cli_cmd_flag_none, + "[params] = Set or unset (if none) autoscan parameters" }, +#endif /* CONFIG_AUTOSCAN */ +#ifdef CONFIG_WNM + { "wnm_sleep", wpa_cli_cmd_wnm_sleep, NULL, cli_cmd_flag_none, + "<enter/exit> [interval=#] = enter/exit WNM-Sleep mode" }, +#endif /* CONFIG_WNM */ + { "raw", wpa_cli_cmd_raw, NULL, cli_cmd_flag_sensitive, + "<params..> = Sent unprocessed command" }, + { NULL, NULL, NULL, cli_cmd_flag_none, NULL } }; @@ -1685,17 +2727,18 @@ static void print_cmd_help(struct wpa_cli_cmd *cmd, const char *pad) } -static void print_help(void) +static void print_help(const char *cmd) { int n; printf("commands:\n"); - for (n = 0; wpa_cli_commands[n].cmd; n++) - print_cmd_help(&wpa_cli_commands[n], " "); + for (n = 0; wpa_cli_commands[n].cmd; n++) { + if (cmd == NULL || str_starts(wpa_cli_commands[n].cmd, cmd)) + print_cmd_help(&wpa_cli_commands[n], " "); + } } -#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 +2757,68 @@ 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_calloc(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; + + for (i = 0; wpa_cli_commands[i].cmd; i++) { + if (os_strcasecmp(wpa_cli_commands[i].cmd, cmd) == 0) { + if (wpa_cli_commands[i].completion) + return wpa_cli_commands[i].completion(str, + pos); + edit_clear_line(); + printf("\r%s\n", wpa_cli_commands[i].usage); + edit_redraw(); + break; + } + } + + return NULL; +} + + +static char ** wpa_cli_edit_completion_cb(void *ctx, const char *str, int pos) +{ + char **res; + const char *end; + char *cmd; + + 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 +2956,24 @@ 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, P2P_EVENT_GO_NEG_FAILURE)) { + 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, AP_STA_CONNECTED)) { + wpa_cli_exec(action_file, ctrl_ifname, pos); + } else if (str_match(pos, AP_STA_DISCONNECTED)) { + 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; @@ -1870,14 +2992,114 @@ static void wpa_cli_action_cb(char *msg, size_t len) static void wpa_cli_reconnect(void) { wpa_cli_close_connection(); - wpa_cli_open_connection(ctrl_ifname, 1); + if (wpa_cli_open_connection(ctrl_ifname, 1) < 0) + return; + + if (interactive) { + edit_clear_line(); + printf("\rConnection to wpa_supplicant re-established\n"); + edit_redraw(); + } } -static void 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 int check_terminating(const char *msg) +{ + const char *pos = msg; + + if (*pos == '<') { + /* skip priority */ + pos = os_strchr(pos, '>'); + if (pos) + pos++; + else + pos = msg; + } + + if (str_match(pos, WPA_EVENT_TERMINATING) && ctrl_conn) { + edit_clear_line(); + printf("\rConnection to wpa_supplicant lost - trying to " + "reconnect\n"); + edit_redraw(); + wpa_cli_attached = 0; + wpa_cli_close_connection(); + return 1; + } + + return 0; +} + + +static void wpa_cli_recv_pending(struct wpa_ctrl *ctrl, int action_monitor) { - int first = 1; if (ctrl_conn == NULL) { wpa_cli_reconnect(); return; @@ -1890,14 +3112,15 @@ 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(); + } + + if (interactive && check_terminating(buf) > 0) + return; } } else { printf("Could not read pending message.\n"); @@ -1912,214 +3135,144 @@ 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_mon_receive(int sock, void *eloop_ctx, void *sock_ctx) { - static int i, len; - char *options[] = { - "verbose", NULL - }; - char *t; - - if (state == 0) { - i = 0; - len = os_strlen(text); - } + wpa_cli_recv_pending(mon_conn, 0); +} - 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_edit_cmd_cb(void *ctx, char *cmd) +{ + char *argv[max_args]; + int argc; + argc = tokenize_cmd(cmd, argv); + if (argc) + wpa_request(ctrl_conn, argc, argv); } -static char ** wpa_cli_completion(const char *text, int start, int end) +static void wpa_cli_edit_eof_cb(void *ctx) { - char * (*func)(const char *text, int state); - - 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); + eloop_terminate(); } -#endif /* CONFIG_READLINE */ -static void wpa_cli_interactive(void) +static int warning_displayed = 0; +static char *hfile = NULL; +static int edit_started = 0; + +static void start_edit(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 */ + char *home; + char *ps = NULL; - printf("\nInteractive mode\n\n"); +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE + ps = wpa_ctrl_get_remote_ifname(ctrl_conn); +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ -#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(); + if (edit_init(wpa_cli_edit_cmd_cb, wpa_cli_edit_eof_cb, + wpa_cli_edit_completion_cb, NULL, hfile, ps) < 0) { + eloop_terminate(); + return; + } + + edit_started = 1; + eloop_register_timeout(ping_interval, 0, wpa_cli_ping, NULL, NULL); +} + + +static void try_connection(void *eloop_ctx, void *timeout_ctx) +{ + if (ctrl_ifname == NULL) + ctrl_ifname = wpa_cli_get_default_ifname(); + + if (!wpa_cli_open_connection(ctrl_ifname, 1) == 0) { + if (!warning_displayed) { + printf("Could not connect to wpa_supplicant: " + "%s - re-trying\n", ctrl_ifname); + warning_displayed = 1; } - write_history(hfile); - os_free(hfile); + eloop_register_timeout(1, 0, try_connection, NULL, NULL); + return; } -#endif /* CONFIG_READLINE */ + + if (warning_displayed) + printf("Connection established.\n"); + + start_edit(); +} + + +static void wpa_cli_interactive(void) +{ + printf("\nInteractive mode\n\n"); + + eloop_register_timeout(0, 0, try_connection, NULL, NULL); + eloop_run(); + eloop_cancel_timeout(try_connection, NULL, NULL); + + cli_txt_list_flush(&p2p_peers); + cli_txt_list_flush(&p2p_groups); + cli_txt_list_flush(&bsses); + if (edit_started) + edit_deinit(hfile, wpa_cli_edit_filter_history_cb); + os_free(hfile); + eloop_cancel_timeout(wpa_cli_ping, NULL, NULL); + wpa_cli_close_connection(); } @@ -2149,7 +3302,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; @@ -2175,39 +3328,11 @@ static void wpa_cli_cleanup(void) os_program_deinit(); } -static void wpa_cli_terminate(int sig) -{ - wpa_cli_cleanup(); - exit(0); -} - -#ifdef CONFIG_WPA_CLI_FORK -static void wpa_cli_usr1(int sig) +static void wpa_cli_terminate(int sig, void *ctx) { -#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); + eloop_terminate(); } -#endif /* CONFIG_NATIVE_WINDOWS */ static char * wpa_cli_get_default_ifname(void) @@ -2217,8 +3342,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 /* @@ -2267,7 +3401,6 @@ static char * wpa_cli_get_default_ifname(void) int main(int argc, char *argv[]) { - int warning_displayed = 0; int c; int daemonize = 0; int ret = 0; @@ -2320,6 +3453,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); @@ -2327,47 +3463,26 @@ int main(int argc, char *argv[]) ctrl_conn = wpa_ctrl_open(global); #endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */ if (ctrl_conn == NULL) { - perror("Failed to connect to wpa_supplicant - " - "wpa_ctrl_open"); + fprintf(stderr, "Failed to connect to wpa_supplicant " + "global interface: %s error: %s\n", + global, strerror(errno)); return -1; } } -#ifndef _WIN32_WCE - 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 */ + eloop_register_signal_terminate(wpa_cli_terminate, NULL); if (ctrl_ifname == NULL) ctrl_ifname = wpa_cli_get_default_ifname(); if (interactive) { - for (; !global;) { - if (wpa_cli_open_connection(ctrl_ifname, 1) == 0) { - if (warning_displayed) - printf("Connection established.\n"); - break; - } - - if (!warning_displayed) { - printf("Could not connect to wpa_supplicant - " - "re-trying\n"); - warning_displayed = 1; - } - os_sleep(1, 0); - continue; - } + wpa_cli_interactive(); } else { if (!global && wpa_cli_open_connection(ctrl_ifname, 0) < 0) { - perror("Failed to connect to wpa_supplicant - " - "wpa_ctrl_open"); + fprintf(stderr, "Failed to connect to non-global " + "ctrl_ifname: %s error: %s\n", + ctrl_ifname, strerror(errno)); return -1; } @@ -2380,19 +3495,19 @@ int main(int argc, char *argv[]) return -1; } } - } - if (daemonize && os_daemonize(pid_file)) - return -1; + if (daemonize && os_daemonize(pid_file)) + return -1; - if (interactive) - wpa_cli_interactive(); - else if (action_file) - wpa_cli_action(ctrl_conn); - else - ret = wpa_request(ctrl_conn, argc - optind, &argv[optind]); + if (action_file) + wpa_cli_action(ctrl_conn); + else + ret = wpa_request(ctrl_conn, argc - optind, + &argv[optind]); + } os_free(ctrl_ifname); + eloop_destroy(); wpa_cli_cleanup(); return ret; diff --git a/contrib/wpa/wpa_supplicant/wpa_passphrase.c b/contrib/wpa/wpa_supplicant/wpa_passphrase.c index 67465aa..9b568f0 100644 --- a/contrib/wpa/wpa_supplicant/wpa_passphrase.c +++ b/contrib/wpa/wpa_supplicant/wpa_passphrase.c @@ -2,14 +2,8 @@ * WPA Supplicant - ASCII passphrase to WPA PSK tool * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -58,7 +52,7 @@ int main(int argc, char *argv[]) return 1; } - pbkdf2_sha1(passphrase, ssid, os_strlen(ssid), 4096, psk, 32); + pbkdf2_sha1(passphrase, (u8 *) ssid, os_strlen(ssid), 4096, psk, 32); printf("network={\n"); printf("\tssid=\"%s\"\n", ssid); diff --git a/contrib/wpa/wpa_supplicant/wpa_priv.c b/contrib/wpa/wpa_supplicant/wpa_priv.c index d2a991b..ad6a080 100644 --- a/contrib/wpa/wpa_supplicant/wpa_priv.c +++ b/contrib/wpa/wpa_supplicant/wpa_priv.c @@ -2,14 +2,8 @@ * WPA Supplicant / privileged helper program * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -649,7 +643,7 @@ wpa_priv_interface_init(const char *dir, const char *params) } if (bind(iface->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("bind(PF_UNIX)"); + perror("wpa-priv-iface-init: bind(PF_UNIX)"); goto fail; } wpa_printf(MSG_DEBUG, "Successfully replaced leftover " @@ -825,7 +819,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 +909,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(); diff --git a/contrib/wpa/wpa_supplicant/wpa_supplicant.c b/contrib/wpa/wpa_supplicant/wpa_supplicant.c index 37a539d..0fb4d0f 100644 --- a/contrib/wpa/wpa_supplicant/wpa_supplicant.c +++ b/contrib/wpa/wpa_supplicant/wpa_supplicant.c @@ -1,15 +1,9 @@ /* * 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 - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This file implements functions for registering and unregistering * %wpa_supplicant interfaces. In addition, this file contains number of @@ -19,12 +13,15 @@ #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/wpa.h" #include "eloop.h" #include "config.h" +#include "utils/ext_password.h" #include "l2_packet/l2_packet.h" #include "wpa_supplicant_i.h" #include "driver_i.h" @@ -34,29 +31,32 @@ #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 "wifi_display.h" #include "notify.h" #include "bgscan.h" +#include "autoscan.h" #include "bss.h" #include "scan.h" +#include "offchannel.h" +#include "hs20_supplicant.h" 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" -"under the terms of the GNU General Public License version 2.\n" -"\n" -"Alternatively, this software may be distributed under the terms of the\n" -"BSD license. See README and COPYING for more details.\n" +"This software may be distributed under the terms of the BSD license.\n" +"See README for more details.\n" #ifdef EAP_TLS_OPENSSL "\nThis product includes software developed by the OpenSSL Project\n" "for use in the OpenSSL Toolkit (http://www.openssl.org/)\n" @@ -66,22 +66,9 @@ const char *wpa_supplicant_license = #ifndef CONFIG_NO_STDOUT_DEBUG /* Long text divided into parts in order to fit in C89 strings size limits. */ const char *wpa_supplicant_full_license1 = -"This program is free software; you can redistribute it and/or modify\n" -"it under the terms of the GNU General Public License version 2 as\n" -"published by the Free Software Foundation.\n" -"\n" -"This program is distributed in the hope that it will be useful,\n" -"but WITHOUT ANY WARRANTY; without even the implied warranty of\n" -"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" -"GNU General Public License for more details.\n" -"\n"; +""; const char *wpa_supplicant_full_license2 = -"You should have received a copy of the GNU General Public License\n" -"along with this program; if not, write to the Free Software\n" -"Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n" -"\n" -"Alternatively, this software may be distributed under the terms of the\n" -"BSD license.\n" +"This software may be distributed under the terms of the BSD license.\n" "\n" "Redistribution and use in source and binary forms, with or without\n" "modification, are permitted provided that the following conditions are\n" @@ -130,9 +117,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 +138,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; } @@ -168,6 +155,11 @@ static int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s, keylen = 16; alg = WPA_ALG_CCMP; break; + case WPA_CIPHER_GCMP: + os_memcpy(key, ssid->psk, 16); + keylen = 16; + alg = WPA_ALG_GCMP; + break; case WPA_CIPHER_TKIP: /* WPA-None uses the same Michael MIC key for both TX and RX */ os_memcpy(key, ssid->psk, 16 + 8); @@ -176,16 +168,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); } @@ -199,9 +190,25 @@ static void wpa_supplicant_timeout(void *eloop_ctx, void *timeout_ctx) MAC2STR(bssid)); wpa_blacklist_add(wpa_s, bssid); wpa_sm_notify_disassoc(wpa_s->wpa); - wpa_supplicant_disassociate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); + wpa_supplicant_deauthenticate(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); + +#ifdef CONFIG_P2P + if (wpa_s->global->p2p_cb_on_scan_complete && !wpa_s->global->p2p_disabled && + wpa_s->global->p2p != NULL) { + wpa_s->global->p2p_cb_on_scan_complete = 0; + if (p2p_other_scan_completed(wpa_s->global->p2p) == 1) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Pending P2P operation " + "continued after timed out authentication"); + } + } +#endif /* CONFIG_P2P */ } @@ -221,7 +228,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 +245,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,9 +368,26 @@ 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); + autoscan_deinit(wpa_s); scard_deinit(wpa_s->scard); wpa_s->scard = NULL; wpa_sm_set_scard_ctx(wpa_s->wpa, NULL); @@ -375,16 +399,10 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) wpa_s->l2_br = NULL; } - if (wpa_s->ctrl_iface) { - wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface); - wpa_s->ctrl_iface = NULL; - } if (wpa_s->conf != NULL) { struct wpa_ssid *ssid; for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) wpas_notify_network_removed(wpa_s, ssid); - wpa_config_free(wpa_s->conf); - wpa_s->conf = NULL; } os_free(wpa_s->confname); @@ -396,6 +414,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 +427,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 +443,47 @@ 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); + + os_free(wpa_s->bssid_filter); + wpa_s->bssid_filter = NULL; + + os_free(wpa_s->disallow_aps_bssid); + wpa_s->disallow_aps_bssid = NULL; + os_free(wpa_s->disallow_aps_ssid); + wpa_s->disallow_aps_ssid = NULL; + + wnm_bss_keep_alive_deinit(wpa_s); + + ext_password_deinit(wpa_s->ext_pw); + wpa_s->ext_pw = NULL; + + wpabuf_free(wpa_s->last_gas_resp); + + os_free(wpa_s->last_scan_res); + wpa_s->last_scan_res = NULL; } @@ -440,8 +497,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 +505,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 +544,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 +566,74 @@ 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 { + struct wpa_scan_results *scan_res; + wpa_s->bgscan_ssid = wpa_s->current_ssid; + scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL, + 0); + if (scan_res) { + bgscan_notify_scan(wpa_s, scan_res); + wpa_scan_results_free(scan_res); + } + } + } else + wpa_s->bgscan_ssid = NULL; +} + + +static void wpa_supplicant_stop_bgscan(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->bgscan_ssid != NULL) { + bgscan_deinit(wpa_s); + wpa_s->bgscan_ssid = NULL; + } +} + +#endif /* CONFIG_BGSCAN */ + + +static void wpa_supplicant_start_autoscan(struct wpa_supplicant *wpa_s) +{ + if (autoscan_init(wpa_s, 0)) + wpa_dbg(wpa_s, MSG_DEBUG, "Failed to initialize autoscan"); +} + + +static void wpa_supplicant_stop_autoscan(struct wpa_supplicant *wpa_s) +{ + autoscan_deinit(wpa_s); +} + + +void wpa_supplicant_reinit_autoscan(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->wpa_state == WPA_DISCONNECTED || + wpa_s->wpa_state == WPA_SCANNING) { + autoscan_deinit(wpa_s); + wpa_supplicant_start_autoscan(wpa_s); + } +} + + /** * wpa_supplicant_set_state - Set current connection state * @wpa_s: Pointer to wpa_supplicant data @@ -522,9 +647,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); @@ -533,25 +658,55 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, #if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) struct wpa_ssid *ssid = wpa_s->current_ssid; wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_CONNECTED "- Connection to " - MACSTR " completed %s [id=%d id_str=%s]", - MAC2STR(wpa_s->bssid), wpa_s->reassociated_connection ? - "(reauth)" : "(auth)", + MACSTR " completed [id=%d id_str=%s]", + MAC2STR(wpa_s->bssid), ssid ? ssid->id : -1, ssid && ssid->id_str ? ssid->id_str : ""); #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ + wpas_clear_temp_disabled(wpa_s, ssid, 1); + wpa_s->extra_blacklist_count = 0; 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 */ + + sme_sched_obss_scan(wpa_s, 1); } else if (state == WPA_DISCONNECTED || state == WPA_ASSOCIATING || state == WPA_ASSOCIATED) { wpa_s->new_connection = 1; wpa_drv_set_operstate(wpa_s, 0); +#ifndef IEEE8021X_EAPOL + wpa_drv_set_supp_port(wpa_s, 0); +#endif /* IEEE8021X_EAPOL */ + sme_sched_obss_scan(wpa_s, 0); } wpa_s->wpa_state = state; - 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 (state == WPA_AUTHENTICATING) + wpa_supplicant_stop_autoscan(wpa_s); + + if (state == WPA_DISCONNECTED || state == WPA_INACTIVE) + wpa_supplicant_start_autoscan(wpa_s); + + 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); + } } @@ -575,16 +730,11 @@ void wpa_supplicant_terminate_proc(struct wpa_global *global) static void wpa_supplicant_terminate(int sig, void *signal_ctx) { struct wpa_global *global = signal_ctx; - struct wpa_supplicant *wpa_s; - for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { - wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_TERMINATING "- signal %d " - "received", sig); - } wpa_supplicant_terminate_proc(global); } -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 +742,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_supplicant_set_state(wpa_s, WPA_DISCONNECTED); if (wpa_s->wpa_state != old_state) wpas_notify_state_changed(wpa_s, wpa_s->wpa_state, old_state); @@ -613,7 +764,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 +775,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 +788,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 +806,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 +819,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)) { + wpa_s->reassociate = 1; + wpa_supplicant_req_scan(wpa_s, 0, 0); + } + wpa_dbg(wpa_s, MSG_DEBUG, "Reconfiguration completed"); return 0; } @@ -679,8 +835,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); } @@ -699,6 +856,8 @@ enum wpa_cipher cipher_suite2driver(int cipher) return CIPHER_WEP104; case WPA_CIPHER_CCMP: return CIPHER_CCMP; + case WPA_CIPHER_GCMP: + return CIPHER_GCMP; case WPA_CIPHER_TKIP: default: return CIPHER_TKIP; @@ -747,8 +906,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", @@ -770,7 +929,9 @@ static int wpa_supplicant_suites_from_ai(struct wpa_supplicant *wpa_s, #ifdef CONFIG_IEEE80211W if (!(ie->capabilities & WPA_CAPABILITY_MFPC) && - ssid->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED) { + (ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT ? + wpa_s->conf->pmf : ssid->ieee80211w) == + MGMT_FRAME_PROTECTION_REQUIRED) { wpa_msg(wpa_s, MSG_INFO, "WPA: Driver associated with an AP " "that does not support management frame protection - " "reject"); @@ -815,14 +976,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 +1003,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,69 +1035,88 @@ 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_GCMP) { + wpa_s->group_cipher = WPA_CIPHER_GCMP; + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK GCMP"); } 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_GCMP) { + wpa_s->pairwise_cipher = WPA_CIPHER_GCMP; + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK GCMP"); } 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; } sel = ie.key_mgmt & ssid->key_mgmt; +#ifdef CONFIG_SAE + if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE)) + sel &= ~(WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE); +#endif /* CONFIG_SAE */ if (0) { #ifdef CONFIG_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_SAE + } else if (sel & WPA_KEY_MGMT_SAE) { + wpa_s->key_mgmt = WPA_KEY_MGMT_SAE; + wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT SAE"); + } else if (sel & WPA_KEY_MGMT_FT_SAE) { + wpa_s->key_mgmt = WPA_KEY_MGMT_FT_SAE; + wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT FT/SAE"); +#endif /* CONFIG_SAE */ #ifdef CONFIG_IEEE80211W } else if (sel & WPA_KEY_MGMT_IEEE8021X_SHA256) { wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_SHA256; - wpa_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; } @@ -946,37 +1127,134 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, #ifdef CONFIG_IEEE80211W sel = ie.mgmt_group_cipher; - if (ssid->ieee80211w == NO_MGMT_FRAME_PROTECTION || + if ((ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT ? + wpa_s->conf->pmf : ssid->ieee80211w) == NO_MGMT_FRAME_PROTECTION || !(ie.capabilities & WPA_CAPABILITY_MFPC)) sel = 0; if (sel & WPA_CIPHER_AES_128_CMAC) { wpa_s->mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC; - wpa_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); - wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MFP, ssid->ieee80211w); + wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MFP, + (ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT ? + wpa_s->conf->pmf : ssid->ieee80211w)); #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)) + if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt)) { 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, 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 */ +#ifdef CONFIG_EXT_PASSWORD + if (ssid->ext_psk) { + struct wpabuf *pw = ext_password_get(wpa_s->ext_pw, + ssid->ext_psk); + char pw_str[64 + 1]; + u8 psk[PMK_LEN]; + + if (pw == NULL) { + wpa_msg(wpa_s, MSG_INFO, "EXT PW: No PSK " + "found from external storage"); + return -1; + } + + if (wpabuf_len(pw) < 8 || wpabuf_len(pw) > 64) { + wpa_msg(wpa_s, MSG_INFO, "EXT PW: Unexpected " + "PSK length %d in external storage", + (int) wpabuf_len(pw)); + ext_password_free(pw); + return -1; + } + + os_memcpy(pw_str, wpabuf_head(pw), wpabuf_len(pw)); + pw_str[wpabuf_len(pw)] = '\0'; + +#ifndef CONFIG_NO_PBKDF2 + if (wpabuf_len(pw) >= 8 && wpabuf_len(pw) < 64 && bss) + { + pbkdf2_sha1(pw_str, bss->ssid, bss->ssid_len, + 4096, psk, PMK_LEN); + os_memset(pw_str, 0, sizeof(pw_str)); + wpa_hexdump_key(MSG_MSGDUMP, "PSK (from " + "external passphrase)", + psk, PMK_LEN); + wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN); + } else +#endif /* CONFIG_NO_PBKDF2 */ + if (wpabuf_len(pw) == 2 * PMK_LEN) { + if (hexstr2bin(pw_str, psk, PMK_LEN) < 0) { + wpa_msg(wpa_s, MSG_INFO, "EXT PW: " + "Invalid PSK hex string"); + os_memset(pw_str, 0, sizeof(pw_str)); + ext_password_free(pw); + return -1; + } + wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN); + } else { + wpa_msg(wpa_s, MSG_INFO, "EXT PW: No suitable " + "PSK available"); + os_memset(pw_str, 0, sizeof(pw_str)); + ext_password_free(pw); + return -1; + } + + os_memset(pw_str, 0, sizeof(pw_str)); + ext_password_free(pw); + } +#endif /* CONFIG_EXT_PASSWORD */ + } else wpa_sm_set_pmk_from_pmksa(wpa_s->wpa); return 0; } +int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf) +{ + u32 ext_capab = 0; + u8 *pos = buf; + +#ifdef CONFIG_INTERWORKING + if (wpa_s->conf->interworking) + ext_capab |= BIT(31); /* Interworking */ +#endif /* CONFIG_INTERWORKING */ + +#ifdef CONFIG_WNM + ext_capab |= BIT(17); /* WNM-Sleep Mode */ + ext_capab |= BIT(19); /* BSS Transition */ +#endif /* CONFIG_WNM */ + + if (!ext_capab) + return 0; + + *pos++ = WLAN_EID_EXT_CAPAB; + *pos++ = 4; + WPA_PUT_LE32(pos, ext_capab); + pos += 4; + + return pos - buf; +} + + /** * wpa_supplicant_associate - Request association * @wpa_s: Pointer to wpa_supplicant data @@ -988,7 +1266,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; @@ -998,31 +1276,53 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, struct wpa_driver_capa capa; int assoc_failed = 0; struct wpa_ssid *old_ssid; + u8 ext_capab[10]; + int ext_capab_len; +#ifdef CONFIG_HT_OVERRIDES + struct ieee80211_ht_capabilities htcaps; + struct ieee80211_ht_capabilities htcaps_mask; +#endif /* CONFIG_HT_OVERRIDES */ + +#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) { + 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; + } + if (wpa_supplicant_create_ap(wpa_s, ssid) < 0) { + wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); 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(¶ms, 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 */ @@ -1050,7 +1350,7 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) { /* Use ap_scan==1 style network selection to find the network */ - wpa_s->scan_req = 2; + wpa_s->scan_req = MANUAL_SCAN_REQ; wpa_s->reassociate = 1; wpa_supplicant_req_scan(wpa_s, 0, 0); return; @@ -1060,6 +1360,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,22 +1377,20 @@ 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) || wpa_bss_get_ie(bss, WLAN_EID_RSN)) && - (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_key_mgmt_wpa(ssid->key_mgmt)) { int try_opportunistic; - try_opportunistic = ssid->proactive_key_caching && + try_opportunistic = (ssid->proactive_key_caching < 0 ? + wpa_s->conf->okc : + ssid->proactive_key_caching) && (ssid->proto & WPA_PROTO_RSN); if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, wpa_s->current_ssid, @@ -1100,21 +1399,27 @@ 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 & - (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_IEEE8021X | - WPA_KEY_MGMT_WPA_NONE | WPA_KEY_MGMT_FT_PSK | - WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_PSK_SHA256 | - WPA_KEY_MGMT_IEEE8021X_SHA256)) { + } else if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && bss && + wpa_key_mgmt_wpa_ieee8021x(ssid->key_mgmt)) { + /* + * Both WPA and non-WPA IEEE 802.1X enabled in configuration - + * use non-WPA since the scan results did not indicate that the + * AP is using WPA or WPA2. + */ + wpa_supplicant_set_non_wpa_policy(wpa_s, ssid); + wpa_ie_len = 0; + wpa_s->wpa_proto = 0; + } else if (wpa_key_mgmt_wpa_any(ssid->key_mgmt)) { wpa_ie_len = sizeof(wpa_ie); if (wpa_supplicant_set_suites(wpa_s, NULL, ssid, wpa_ie, &wpa_ie_len)) { - wpa_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,10 +1433,70 @@ 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_HS20 + if (wpa_s->conf->hs20) { + struct wpabuf *hs20; + hs20 = wpabuf_alloc(20); + if (hs20) { + wpas_hs20_add_indication(hs20); + os_memcpy(wpa_ie + wpa_ie_len, wpabuf_head(hs20), + wpabuf_len(hs20)); + wpa_ie_len += wpabuf_len(hs20); + wpabuf_free(hs20); + } + } +#endif /* CONFIG_HS20 */ + + ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab); + if (ext_capab_len > 0) { + u8 *pos = wpa_ie; + if (wpa_ie_len > 0 && pos[0] == WLAN_EID_RSN) + pos += 2 + pos[1]; + os_memmove(pos + ext_capab_len, pos, + wpa_ie_len - (pos - wpa_ie)); + wpa_ie_len += ext_capab_len; + os_memcpy(pos, ext_capab, ext_capab_len); } wpa_clear_keys(wpa_s, bss ? bss->bssid : NULL); @@ -1172,16 +1537,29 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, } wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATING); - os_memset(¶ms, 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) || ssid->bssid_set) { + wpa_printf(MSG_DEBUG, "Limit connection to BSSID " + MACSTR " freq=%u MHz based on scan results " + "(bssid_set=%d)", + MAC2STR(bss->bssid), bss->freq, + ssid->bssid_set); + params.bssid = bss->bssid; + params.freq = bss->freq; + } } else { params.ssid = ssid->ssid; params.ssid_len = ssid->ssid_len; } + + if (ssid->mode == WPAS_MODE_IBSS && ssid->bssid_set && + wpa_s->conf->ap_scan == 2) { + params.bssid = ssid->bssid; + params.fixed_bssid = 1; + } + if (ssid->mode == WPAS_MODE_IBSS && ssid->frequency > 0 && params.freq == 0) params.freq = ssid->frequency; /* Initial channel for IBSS */ @@ -1190,8 +1568,10 @@ 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; + params.bg_scan_period = ssid->bg_scan_period; for (i = 0; i < NUM_WEP_KEYS; i++) { if (ssid->wep_key_len[i]) params.wep_key[i] = ssid->wep_key[i]; @@ -1210,28 +1590,53 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, params.drop_unencrypted = use_crypt; #ifdef CONFIG_IEEE80211W - params.mgmt_frame_protection = ssid->ieee80211w; - if (ssid->ieee80211w != NO_MGMT_FRAME_PROTECTION && bss) { + params.mgmt_frame_protection = + ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT ? + wpa_s->conf->pmf : ssid->ieee80211w; + if (params.mgmt_frame_protection != NO_MGMT_FRAME_PROTECTION && bss) { const u8 *rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN); struct wpa_ie_data ie; if (rsn && wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ie) == 0 && ie.capabilities & (WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR)) { - wpa_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, ¶ms); + 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, ¶ms); + params.uapsd = -1; + +#ifdef CONFIG_HT_OVERRIDES + os_memset(&htcaps, 0, sizeof(htcaps)); + os_memset(&htcaps_mask, 0, sizeof(htcaps_mask)); + params.htcaps = (u8 *) &htcaps; + params.htcaps_mask = (u8 *) &htcaps_mask; + wpa_supplicant_apply_ht_overrides(wpa_s, ssid, ¶ms); +#endif /* CONFIG_HT_OVERRIDES */ + + ret = wpa_drv_associate(wpa_s, ¶ms); if (ret < 0) { wpa_msg(wpa_s, MSG_INFO, "Association request to the driver " "failed"); + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SANE_ERROR_CODES) { + /* + * The driver is known to mean what is saying, so we + * can stop right here; the association will not + * succeed. + */ + wpas_connection_failed(wpa_s, wpa_s->pending_bssid); + wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); + os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); + return; + } /* try to continue anyway; new association will be tried again * after timeout */ assoc_failed = 1; @@ -1249,7 +1654,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,32 +1697,14 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, } -/** - * wpa_supplicant_disassociate - Disassociate the current connection - * @wpa_s: Pointer to wpa_supplicant data - * @reason_code: IEEE 802.11 reason code for the disassociate frame - * - * This function is used to request %wpa_supplicant to disassociate with the - * current AP. - */ -void wpa_supplicant_disassociate(struct wpa_supplicant *wpa_s, - int reason_code) +static void wpa_supplicant_clear_connection(struct wpa_supplicant *wpa_s, + const u8 *addr) { 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); - 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_supplicant_mark_disassoc(wpa_s); wpa_sm_set_config(wpa_s->wpa, NULL); eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); if (old_ssid != wpa_s->current_ssid) @@ -1338,27 +1724,43 @@ 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; + union wpa_event_data event; + int zero_addr = 0; - 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_dbg(wpa_s, MSG_DEBUG, "Request to deauthenticate - bssid=" MACSTR + " pending_bssid=" MACSTR " reason=%d state=%s", + MAC2STR(wpa_s->bssid), MAC2STR(wpa_s->pending_bssid), + reason_code, wpa_supplicant_state_txt(wpa_s->wpa_state)); + + if (!is_zero_ether_addr(wpa_s->bssid)) + addr = wpa_s->bssid; + else if (!is_zero_ether_addr(wpa_s->pending_bssid) && + (wpa_s->wpa_state == WPA_AUTHENTICATING || + wpa_s->wpa_state == WPA_ASSOCIATING)) + addr = wpa_s->pending_bssid; + else if (wpa_s->wpa_state == WPA_ASSOCIATING) { + /* + * When using driver-based BSS selection, we may not know the + * BSSID with which we are currently trying to associate. We + * need to notify the driver of this disconnection even in such + * a case, so use the all zeros address here. + */ addr = wpa_s->bssid; + zero_addr = 1; } - 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); + + if (addr) { + wpa_drv_deauthenticate(wpa_s, addr, reason_code); + os_memset(&event, 0, sizeof(event)); + event.deauth_info.reason_code = (u16) reason_code; + event.deauth_info.locally_generated = 1; + wpa_supplicant_event(wpa_s, EVENT_DEAUTH, &event); + if (zero_addr) + addr = NULL; + } + + wpa_supplicant_clear_connection(wpa_s, addr); } @@ -1376,8 +1778,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; @@ -1385,16 +1790,16 @@ void wpa_supplicant_enable_network(struct wpa_supplicant *wpa_s, was_disabled = other_ssid->disabled; other_ssid->disabled = 0; + if (was_disabled) + wpas_clear_temp_disabled(wpa_s, other_ssid, 0); if (was_disabled != other_ssid->disabled) wpas_notify_network_enabled_changed( wpa_s, other_ssid); - - 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 @@ -1407,6 +1812,7 @@ void wpa_supplicant_enable_network(struct wpa_supplicant *wpa_s, was_disabled = ssid->disabled; ssid->disabled = 0; + wpas_clear_temp_disabled(wpa_s, ssid, 1); if (was_disabled != ssid->disabled) wpas_notify_network_enabled_changed(wpa_s, ssid); @@ -1428,24 +1834,25 @@ 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_supplicant_deauthenticate( wpa_s, WLAN_REASON_DEAUTH_LEAVING); - } else { + } else if (ssid->disabled != 2) { if (ssid == wpa_s->current_ssid) - wpa_supplicant_disassociate( + wpa_supplicant_deauthenticate( wpa_s, WLAN_REASON_DEAUTH_LEAVING); was_disabled = ssid->disabled; @@ -1468,29 +1875,48 @@ 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) - wpa_supplicant_disassociate( + if (ssid && ssid != wpa_s->current_ssid && wpa_s->current_ssid) { + wpa_supplicant_deauthenticate( wpa_s, WLAN_REASON_DEAUTH_LEAVING); + disconnected = 1; + } + + if (ssid) + wpas_clear_temp_disabled(wpa_s, ssid, 1); /* * Mark all other networks disabled or mark all networks enabled if no * network specified. */ - 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_clear_temp_disabled(wpa_s, other_ssid, 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; } + + if (ssid) + wpa_s->current_ssid = ssid; + 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 +1938,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 +1959,75 @@ 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_scan_interval - Set scan interval + * @wpa_s: wpa_supplicant structure for a network interface + * @scan_interval: scan interval in seconds + * Returns: 0 if succeed or -1 if scan_interval has an invalid value + * + */ +int wpa_supplicant_set_scan_interval(struct wpa_supplicant *wpa_s, + int scan_interval) +{ + if (scan_interval < 0) { + wpa_msg(wpa_s, MSG_ERROR, "Invalid scan interval %d", + scan_interval); + return -1; + } + wpa_msg(wpa_s, MSG_DEBUG, "Setting scan interval: %d sec", + scan_interval); + wpa_s->scan_interval = scan_interval; + + return 0; +} + + +/** * wpa_supplicant_set_debug_params - Set global debug params * @global: wpa_global structure * @debug_level: debug level @@ -1537,7 +2042,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 +2083,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; } @@ -1605,20 +2102,26 @@ struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s) entry = wpa_s->conf->ssid; while (entry) { - if (!entry->disabled && + if (!wpas_network_disabled(wpa_s, entry) && ((ssid_len == entry->ssid_len && os_memcmp(ssid, entry->ssid, ssid_len) == 0) || wired) && (!entry->bssid_set || os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0)) return entry; #ifdef CONFIG_WPS - if (!entry->disabled && + if (!wpas_network_disabled(wpa_s, entry) && (entry->key_mgmt & WPA_KEY_MGMT_WPS) && (entry->ssid == NULL || entry->ssid_len == 0) && (!entry->bssid_set || os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0)) return entry; #endif /* CONFIG_WPS */ + + if (!wpas_network_disabled(wpa_s, entry) && entry->bssid_set && + entry->ssid_len == 0 && + os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0) + return entry; + entry = entry->next; } @@ -1626,45 +2129,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,20 +2214,31 @@ 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) { + if (wpa_s->wpa_state < WPA_ASSOCIATED || + (wpa_s->last_eapol_matches_bssid && +#ifdef CONFIG_AP + !wpa_s->ap_iface && +#endif /* CONFIG_AP */ + os_memcmp(src_addr, wpa_s->bssid, ETH_ALEN) != 0)) { /* * There is possible race condition between receiving the * association event and the EAPOL frame since they are coming * through different paths from the driver. In order to avoid * issues in trying to process the EAPOL frame before receiving * association information, lets queue it for processing until - * the association event is received. + * the association event is received. This may also be needed in + * driver-based roaming case, so also use src_addr != BSSID as a + * trigger if we have previously confirmed that the + * Authenticator uses BSSID as the src_addr (which is not the + * case with wired IEEE 802.1X). */ - wpa_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 (state=%s bssid=" MACSTR ")", + wpa_supplicant_state_txt(wpa_s->wpa_state), + MAC2STR(wpa_s->bssid)); wpabuf_free(wpa_s->pending_eapol_rx); wpa_s->pending_eapol_rx = wpabuf_alloc_copy(buf, len); if (wpa_s->pending_eapol_rx) { @@ -1712,6 +2249,9 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, return; } + wpa_s->last_eapol_matches_bssid = + os_memcmp(src_addr, wpa_s->bssid, ETH_ALEN) == 0; + #ifdef CONFIG_AP if (wpa_s->ap_iface) { wpa_supplicant_ap_rx_eapol(wpa_s, src_addr, buf, len); @@ -1720,8 +2260,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 +2282,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 +2320,93 @@ 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; +} + + +static void wpa_supplicant_rx_eapol_bridge(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len) +{ + struct wpa_supplicant *wpa_s = ctx; + const struct l2_ethhdr *eth; + + if (len < sizeof(*eth)) + return; + eth = (const struct l2_ethhdr *) buf; + + if (os_memcmp(eth->h_dest, wpa_s->own_addr, ETH_ALEN) != 0 && + !(eth->h_dest[0] & 0x01)) { + wpa_dbg(wpa_s, MSG_DEBUG, "RX EAPOL from " MACSTR " to " MACSTR + " (bridge - not for this interface - ignore)", + MAC2STR(src_addr), MAC2STR(eth->h_dest)); + return; + } + + wpa_dbg(wpa_s, MSG_DEBUG, "RX EAPOL from " MACSTR " to " MACSTR + " (bridge)", MAC2STR(src_addr), MAC2STR(eth->h_dest)); + wpa_supplicant_rx_eapol(wpa_s, src_addr, buf + sizeof(*eth), + len - sizeof(*eth)); +} + + +/** + * wpa_supplicant_driver_init - Initialize driver interface parameters + * @wpa_s: Pointer to wpa_supplicant data + * Returns: 0 on success, -1 on failure + * + * This function is called to initialize driver interface parameters. + * wpa_drv_init() must have been called before this function to initialize the + * driver interface. + */ +int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s) +{ + static int interface_count = 0; + + if (wpa_supplicant_update_mac_addr(wpa_s) < 0) + return -1; 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); + wpa_supplicant_rx_eapol_bridge, + wpa_s, 1); 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 +2417,17 @@ 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); + wpa_s->prev_scan_wildcard = 0; + + if (wpa_supplicant_enabled_networks(wpa_s)) { + 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); @@ -1864,13 +2450,290 @@ static struct wpa_supplicant * wpa_supplicant_alloc(void) wpa_s = os_zalloc(sizeof(*wpa_s)); if (wpa_s == NULL) return NULL; - wpa_s->scan_req = 1; + wpa_s->scan_req = INITIAL_SCAN_REQ; + wpa_s->scan_interval = 5; wpa_s->new_connection = 1; + wpa_s->parent = wpa_s; + wpa_s->sched_scanning = 0; return wpa_s; } +#ifdef CONFIG_HT_OVERRIDES + +static int wpa_set_htcap_mcs(struct wpa_supplicant *wpa_s, + struct ieee80211_ht_capabilities *htcaps, + struct ieee80211_ht_capabilities *htcaps_mask, + const char *ht_mcs) +{ + /* parse ht_mcs into hex array */ + int i; + const char *tmp = ht_mcs; + char *end = NULL; + + /* If ht_mcs is null, do not set anything */ + if (!ht_mcs) + return 0; + + /* This is what we are setting in the kernel */ + os_memset(&htcaps->supported_mcs_set, 0, IEEE80211_HT_MCS_MASK_LEN); + + wpa_msg(wpa_s, MSG_DEBUG, "set_htcap, ht_mcs -:%s:-", ht_mcs); + + for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) { + errno = 0; + long v = strtol(tmp, &end, 16); + if (errno == 0) { + wpa_msg(wpa_s, MSG_DEBUG, + "htcap value[%i]: %ld end: %p tmp: %p", + i, v, end, tmp); + if (end == tmp) + break; + + htcaps->supported_mcs_set[i] = v; + tmp = end; + } else { + wpa_msg(wpa_s, MSG_ERROR, + "Failed to parse ht-mcs: %s, error: %s\n", + ht_mcs, strerror(errno)); + return -1; + } + } + + /* + * If we were able to parse any values, then set mask for the MCS set. + */ + if (i) { + os_memset(&htcaps_mask->supported_mcs_set, 0xff, + IEEE80211_HT_MCS_MASK_LEN - 1); + /* skip the 3 reserved bits */ + htcaps_mask->supported_mcs_set[IEEE80211_HT_MCS_MASK_LEN - 1] = + 0x1f; + } + + return 0; +} + + +static int wpa_disable_max_amsdu(struct wpa_supplicant *wpa_s, + struct ieee80211_ht_capabilities *htcaps, + struct ieee80211_ht_capabilities *htcaps_mask, + int disabled) +{ + u16 msk; + + wpa_msg(wpa_s, MSG_DEBUG, "set_disable_max_amsdu: %d", disabled); + + if (disabled == -1) + return 0; + + msk = host_to_le16(HT_CAP_INFO_MAX_AMSDU_SIZE); + htcaps_mask->ht_capabilities_info |= msk; + if (disabled) + htcaps->ht_capabilities_info &= msk; + else + htcaps->ht_capabilities_info |= msk; + + return 0; +} + + +static int wpa_set_ampdu_factor(struct wpa_supplicant *wpa_s, + struct ieee80211_ht_capabilities *htcaps, + struct ieee80211_ht_capabilities *htcaps_mask, + int factor) +{ + wpa_msg(wpa_s, MSG_DEBUG, "set_ampdu_factor: %d", factor); + + if (factor == -1) + return 0; + + if (factor < 0 || factor > 3) { + wpa_msg(wpa_s, MSG_ERROR, "ampdu_factor: %d out of range. " + "Must be 0-3 or -1", factor); + return -EINVAL; + } + + htcaps_mask->a_mpdu_params |= 0x3; /* 2 bits for factor */ + htcaps->a_mpdu_params &= ~0x3; + htcaps->a_mpdu_params |= factor & 0x3; + + return 0; +} + + +static int wpa_set_ampdu_density(struct wpa_supplicant *wpa_s, + struct ieee80211_ht_capabilities *htcaps, + struct ieee80211_ht_capabilities *htcaps_mask, + int density) +{ + wpa_msg(wpa_s, MSG_DEBUG, "set_ampdu_density: %d", density); + + if (density == -1) + return 0; + + if (density < 0 || density > 7) { + wpa_msg(wpa_s, MSG_ERROR, + "ampdu_density: %d out of range. Must be 0-7 or -1.", + density); + return -EINVAL; + } + + htcaps_mask->a_mpdu_params |= 0x1C; + htcaps->a_mpdu_params &= ~(0x1C); + htcaps->a_mpdu_params |= (density << 2) & 0x1C; + + return 0; +} + + +static int wpa_set_disable_ht40(struct wpa_supplicant *wpa_s, + struct ieee80211_ht_capabilities *htcaps, + struct ieee80211_ht_capabilities *htcaps_mask, + int disabled) +{ + /* Masking these out disables HT40 */ + u16 msk = host_to_le16(HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET | + HT_CAP_INFO_SHORT_GI40MHZ); + + wpa_msg(wpa_s, MSG_DEBUG, "set_disable_ht40: %d", disabled); + + if (disabled) + htcaps->ht_capabilities_info &= ~msk; + else + htcaps->ht_capabilities_info |= msk; + + htcaps_mask->ht_capabilities_info |= msk; + + return 0; +} + + +static int wpa_set_disable_sgi(struct wpa_supplicant *wpa_s, + struct ieee80211_ht_capabilities *htcaps, + struct ieee80211_ht_capabilities *htcaps_mask, + int disabled) +{ + /* Masking these out disables SGI */ + u16 msk = host_to_le16(HT_CAP_INFO_SHORT_GI20MHZ | + HT_CAP_INFO_SHORT_GI40MHZ); + + wpa_msg(wpa_s, MSG_DEBUG, "set_disable_sgi: %d", disabled); + + if (disabled) + htcaps->ht_capabilities_info &= ~msk; + else + htcaps->ht_capabilities_info |= msk; + + htcaps_mask->ht_capabilities_info |= msk; + + return 0; +} + + +void wpa_supplicant_apply_ht_overrides( + struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, + struct wpa_driver_associate_params *params) +{ + struct ieee80211_ht_capabilities *htcaps; + struct ieee80211_ht_capabilities *htcaps_mask; + + if (!ssid) + return; + + params->disable_ht = ssid->disable_ht; + if (!params->htcaps || !params->htcaps_mask) + return; + + htcaps = (struct ieee80211_ht_capabilities *) params->htcaps; + htcaps_mask = (struct ieee80211_ht_capabilities *) params->htcaps_mask; + wpa_set_htcap_mcs(wpa_s, htcaps, htcaps_mask, ssid->ht_mcs); + wpa_disable_max_amsdu(wpa_s, htcaps, htcaps_mask, + ssid->disable_max_amsdu); + wpa_set_ampdu_factor(wpa_s, htcaps, htcaps_mask, ssid->ampdu_factor); + wpa_set_ampdu_density(wpa_s, htcaps, htcaps_mask, ssid->ampdu_density); + wpa_set_disable_ht40(wpa_s, htcaps, htcaps_mask, ssid->disable_ht40); + wpa_set_disable_sgi(wpa_s, htcaps, htcaps_mask, ssid->disable_sgi); +} + +#endif /* CONFIG_HT_OVERRIDES */ + + +static int pcsc_reader_init(struct wpa_supplicant *wpa_s) +{ +#ifdef PCSC_FUNCS + size_t len; + + if (!wpa_s->conf->pcsc_reader) + return 0; + + wpa_s->scard = scard_init(SCARD_TRY_BOTH, wpa_s->conf->pcsc_reader); + if (!wpa_s->scard) + return 1; + + if (wpa_s->conf->pcsc_pin && + scard_set_pin(wpa_s->scard, wpa_s->conf->pcsc_pin) < 0) { + scard_deinit(wpa_s->scard); + wpa_s->scard = NULL; + wpa_msg(wpa_s, MSG_ERROR, "PC/SC PIN validation failed"); + return -1; + } + + len = sizeof(wpa_s->imsi) - 1; + if (scard_get_imsi(wpa_s->scard, wpa_s->imsi, &len)) { + scard_deinit(wpa_s->scard); + wpa_s->scard = NULL; + wpa_msg(wpa_s, MSG_ERROR, "Could not read IMSI"); + return -1; + } + wpa_s->imsi[len] = '\0'; + + wpa_s->mnc_len = scard_get_mnc_len(wpa_s->scard); + + wpa_printf(MSG_DEBUG, "SCARD: IMSI %s (MNC length %d)", + wpa_s->imsi, wpa_s->mnc_len); + + wpa_sm_set_scard_ctx(wpa_s->wpa, wpa_s->scard); + eapol_sm_register_scard_ctx(wpa_s->eapol, wpa_s->scard); +#endif /* PCSC_FUNCS */ + + return 0; +} + + +int wpas_init_ext_pw(struct wpa_supplicant *wpa_s) +{ + char *val, *pos; + + ext_password_deinit(wpa_s->ext_pw); + wpa_s->ext_pw = NULL; + eapol_sm_set_ext_pw_ctx(wpa_s->eapol, NULL); + + if (!wpa_s->conf->ext_password_backend) + return 0; + + val = os_strdup(wpa_s->conf->ext_password_backend); + if (val == NULL) + return -1; + pos = os_strchr(val, ':'); + if (pos) + *pos++ = '\0'; + + wpa_printf(MSG_DEBUG, "EXT PW: Initialize backend '%s'", val); + + wpa_s->ext_pw = ext_password_init(val, pos); + os_free(val); + if (wpa_s->ext_pw == NULL) { + wpa_printf(MSG_DEBUG, "EXT PW: Failed to initialize backend"); + return -1; + } + eapol_sm_set_ext_pw_ctx(wpa_s->eapol, wpa_s->ext_pw); + + return 0; +} + + static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, struct wpa_interface *iface) { @@ -1969,24 +2832,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 +2865,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 +2881,26 @@ 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->drv_enc = capa.enc; + wpa_s->probe_resp_offloads = capa.probe_resp_offloads; wpa_s->max_scan_ssids = capa.max_scan_ssids; + wpa_s->max_sched_scan_ssids = capa.max_sched_scan_ssids; + wpa_s->sched_scan_supported = capa.sched_scan_supported; + wpa_s->max_match_sets = capa.max_match_sets; wpa_s->max_remain_on_chan = capa.max_remain_on_chan; + wpa_s->max_stations = capa.max_stations; } if (wpa_s->max_remain_on_chan == 0) wpa_s->max_remain_on_chan = 1000; @@ -2037,14 +2908,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,23 +2940,34 @@ 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; + if (pcsc_reader_init(wpa_s) < 0) + return -1; + + if (wpas_init_ext_pw(wpa_s) < 0) + return -1; + return 0; } static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s, - int notify) + int notify, int terminate) { if (wpa_s->drv_priv) { wpa_supplicant_deauthenticate(wpa_s, @@ -2094,11 +2979,32 @@ static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s, wpa_supplicant_cleanup(wpa_s); - if (notify) - wpas_notify_iface_removed(wpa_s); +#ifdef CONFIG_P2P + if (wpa_s == wpa_s->global->p2p_init_wpa_s && wpa_s->global->p2p) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Disable P2P since removing " + "the management interface is being removed"); + wpas_p2p_deinit_global(wpa_s->global); + } +#endif /* CONFIG_P2P */ if (wpa_s->drv_priv) wpa_drv_deinit(wpa_s); + + if (notify) + wpas_notify_iface_removed(wpa_s); + + if (terminate) + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_TERMINATING); + + if (wpa_s->ctrl_iface) { + wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface); + wpa_s->ctrl_iface = NULL; + } + + if (wpa_s->conf != NULL) { + wpa_config_free(wpa_s->conf); + wpa_s->conf = NULL; + } } @@ -2148,14 +3054,14 @@ struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global, if (wpa_supplicant_init_iface(wpa_s, &t_iface)) { wpa_printf(MSG_DEBUG, "Failed to add interface %s", iface->ifname); - wpa_supplicant_deinit_iface(wpa_s, 0); + wpa_supplicant_deinit_iface(wpa_s, 0, 0); os_free(wpa_s); return NULL; } /* Notify the control interfaces about new iface */ if (wpas_notify_iface_added(wpa_s)) { - wpa_supplicant_deinit_iface(wpa_s, 1); + wpa_supplicant_deinit_iface(wpa_s, 1, 0); os_free(wpa_s); return NULL; } @@ -2166,7 +3072,8 @@ 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); + wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); return wpa_s; } @@ -2184,7 +3091,8 @@ struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global, * %wpa_supplicant is terminated. */ int wpa_supplicant_remove_iface(struct wpa_global *global, - struct wpa_supplicant *wpa_s) + struct wpa_supplicant *wpa_s, + int terminate) { struct wpa_supplicant *prev; @@ -2200,9 +3108,11 @@ 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); - wpa_supplicant_deinit_iface(wpa_s, 1); + if (global->p2p_group_formation == wpa_s) + global->p2p_group_formation = NULL; + wpa_supplicant_deinit_iface(wpa_s, 1, terminate); os_free(wpa_s); return 0; @@ -2210,6 +3120,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 +3160,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,9 +3188,28 @@ 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(); + if (params->wpa_debug_tracing) { + ret = wpa_debug_open_linux_tracing(); + if (ret) { + wpa_printf(MSG_ERROR, + "Failed to enable trace logging"); + return NULL; + } + } ret = eap_register_methods(); if (ret) { @@ -2261,6 +3223,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 +3246,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 +3279,14 @@ 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; - } + +#ifdef CONFIG_WIFI_DISPLAY + if (wifi_display_init(global) < 0) { + wpa_printf(MSG_ERROR, "Failed to initialize Wi-Fi Display"); + wpa_supplicant_deinit(global); + return NULL; } +#endif /* CONFIG_WIFI_DISPLAY */ return global; } @@ -2374,8 +3339,15 @@ void wpa_supplicant_deinit(struct wpa_global *global) if (global == NULL) return; +#ifdef CONFIG_WIFI_DISPLAY + wifi_display_deinit(global); +#endif /* CONFIG_WIFI_DISPLAY */ +#ifdef CONFIG_P2P + wpas_p2p_deinit_global(global); +#endif /* CONFIG_P2P */ + while (global->ifaces) - wpa_supplicant_remove_iface(global, global->ifaces); + wpa_supplicant_remove_iface(global, global->ifaces, 1); if (global->ctrl_iface) wpa_supplicant_global_ctrl_iface_deinit(global->ctrl_iface); @@ -2394,6 +3366,8 @@ void wpa_supplicant_deinit(struct wpa_global *global) } os_free(global->drv_priv); + random_deinit(); + eloop_destroy(); if (global->params.pid_file) { @@ -2404,7 +3378,419 @@ void wpa_supplicant_deinit(struct wpa_global *global) os_free(global->params.override_driver); os_free(global->params.override_ctrl_interface); + os_free(global->p2p_disallow_freq); + os_free(global); wpa_debug_close_syslog(); wpa_debug_close_file(); + wpa_debug_close_linux_tracing(); +} + + +void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s) +{ + if ((wpa_s->conf->changed_parameters & CFG_CHANGED_COUNTRY) && + wpa_s->conf->country[0] && wpa_s->conf->country[1]) { + char country[3]; + country[0] = wpa_s->conf->country[0]; + country[1] = wpa_s->conf->country[1]; + country[2] = '\0'; + if (wpa_drv_set_country(wpa_s, country) < 0) { + wpa_printf(MSG_ERROR, "Failed to set country code " + "'%s'", country); + } + } + + if (wpa_s->conf->changed_parameters & CFG_CHANGED_EXT_PW_BACKEND) + wpas_init_ext_pw(wpa_s); + +#ifdef CONFIG_WPS + wpas_wps_update_config(wpa_s); +#endif /* CONFIG_WPS */ + +#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; + } + } + + /* + * Add previous failure count in case the temporary blacklist was + * cleared due to no other BSSes being available. + */ + count += wpa_s->extra_blacklist_count; + + switch (count) { + case 1: + timeout = 100; + break; + case 2: + timeout = 500; + break; + case 3: + timeout = 1000; + break; + case 4: + timeout = 5000; + break; + default: + timeout = 10000; + break; + } + + wpa_dbg(wpa_s, MSG_DEBUG, "Blacklist count %d --> request scan in %d " + "ms", count, timeout); + + /* + * TODO: if more than one possible AP is available in scan results, + * could try the other ones before requesting a new scan. + */ + wpa_supplicant_req_scan(wpa_s, timeout / 1000, + 1000 * (timeout % 1000)); + +#ifdef CONFIG_P2P + if (wpa_s->global->p2p_cb_on_scan_complete && !wpa_s->global->p2p_disabled && + wpa_s->global->p2p != NULL) { + wpa_s->global->p2p_cb_on_scan_complete = 0; + if (p2p_other_scan_completed(wpa_s->global->p2p) == 1) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Pending P2P operation " + "continued after failed association"); + } + } +#endif /* CONFIG_P2P */ +} + + +int wpas_driver_bss_selection(struct wpa_supplicant *wpa_s) +{ + return wpa_s->conf->ap_scan == 2 || + (wpa_s->drv_flags & WPA_DRIVER_FLAGS_BSS_SELECTION); +} + + +#if defined(CONFIG_CTRL_IFACE) || defined(CONFIG_CTRL_IFACE_DBUS_NEW) +int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + const char *field, + const char *value) +{ +#ifdef IEEE8021X_EAPOL + struct eap_peer_config *eap = &ssid->eap; + + wpa_printf(MSG_DEBUG, "CTRL_IFACE: response handle field=%s", field); + wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: response value", + (const u8 *) value, os_strlen(value)); + + switch (wpa_supplicant_ctrl_req_from_string(field)) { + case WPA_CTRL_REQ_EAP_IDENTITY: + os_free(eap->identity); + eap->identity = (u8 *) os_strdup(value); + eap->identity_len = os_strlen(value); + eap->pending_req_identity = 0; + if (ssid == wpa_s->current_ssid) + wpa_s->reassociate = 1; + break; + case WPA_CTRL_REQ_EAP_PASSWORD: + os_free(eap->password); + eap->password = (u8 *) os_strdup(value); + eap->password_len = os_strlen(value); + eap->pending_req_password = 0; + if (ssid == wpa_s->current_ssid) + wpa_s->reassociate = 1; + break; + case WPA_CTRL_REQ_EAP_NEW_PASSWORD: + os_free(eap->new_password); + eap->new_password = (u8 *) os_strdup(value); + eap->new_password_len = os_strlen(value); + eap->pending_req_new_password = 0; + if (ssid == wpa_s->current_ssid) + wpa_s->reassociate = 1; + break; + case WPA_CTRL_REQ_EAP_PIN: + os_free(eap->pin); + eap->pin = os_strdup(value); + eap->pending_req_pin = 0; + if (ssid == wpa_s->current_ssid) + wpa_s->reassociate = 1; + break; + case WPA_CTRL_REQ_EAP_OTP: + os_free(eap->otp); + eap->otp = (u8 *) os_strdup(value); + eap->otp_len = os_strlen(value); + os_free(eap->pending_req_otp); + eap->pending_req_otp = NULL; + eap->pending_req_otp_len = 0; + break; + case WPA_CTRL_REQ_EAP_PASSPHRASE: + os_free(eap->private_key_passwd); + eap->private_key_passwd = (u8 *) os_strdup(value); + eap->pending_req_passphrase = 0; + if (ssid == wpa_s->current_ssid) + wpa_s->reassociate = 1; + break; + default: + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown field '%s'", field); + return -1; + } + + return 0; +#else /* IEEE8021X_EAPOL */ + wpa_printf(MSG_DEBUG, "CTRL_IFACE: IEEE 802.1X not included"); + return -1; +#endif /* IEEE8021X_EAPOL */ +} +#endif /* CONFIG_CTRL_IFACE || CONFIG_CTRL_IFACE_DBUS_NEW */ + + +int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) +{ + int i; + unsigned int drv_enc; + + if (ssid == NULL) + return 1; + + if (ssid->disabled) + return 1; + + if (wpa_s && wpa_s->drv_capa_known) + drv_enc = wpa_s->drv_enc; + else + drv_enc = (unsigned int) -1; + + for (i = 0; i < NUM_WEP_KEYS; i++) { + size_t len = ssid->wep_key_len[i]; + if (len == 0) + continue; + if (len == 5 && (drv_enc & WPA_DRIVER_CAPA_ENC_WEP40)) + continue; + if (len == 13 && (drv_enc & WPA_DRIVER_CAPA_ENC_WEP104)) + continue; + if (len == 16 && (drv_enc & WPA_DRIVER_CAPA_ENC_WEP128)) + continue; + return 1; /* invalid WEP key */ + } + + if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt) && !ssid->psk_set && + !ssid->ext_psk) + return 1; + + return 0; +} + + +int wpas_is_p2p_prioritized(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->global->conc_pref == WPA_CONC_PREF_P2P) + return 1; + if (wpa_s->global->conc_pref == WPA_CONC_PREF_STA) + return 0; + return -1; +} + + +void wpas_auth_failed(struct wpa_supplicant *wpa_s) +{ + struct wpa_ssid *ssid = wpa_s->current_ssid; + int dur; + struct os_time now; + + if (ssid == NULL) { + wpa_printf(MSG_DEBUG, "Authentication failure but no known " + "SSID block"); + return; + } + + if (ssid->key_mgmt == WPA_KEY_MGMT_WPS) + return; + + ssid->auth_failures++; + if (ssid->auth_failures > 50) + dur = 300; + else if (ssid->auth_failures > 20) + dur = 120; + else if (ssid->auth_failures > 10) + dur = 60; + else if (ssid->auth_failures > 5) + dur = 30; + else if (ssid->auth_failures > 1) + dur = 20; + else + dur = 10; + + os_get_time(&now); + if (now.sec + dur <= ssid->disabled_until.sec) + return; + + ssid->disabled_until.sec = now.sec + dur; + + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_TEMP_DISABLED + "id=%d ssid=\"%s\" auth_failures=%u duration=%d", + ssid->id, wpa_ssid_txt(ssid->ssid, ssid->ssid_len), + ssid->auth_failures, dur); +} + + +void wpas_clear_temp_disabled(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, int clear_failures) +{ + if (ssid == NULL) + return; + + if (ssid->disabled_until.sec) { + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_REENABLED + "id=%d ssid=\"%s\"", + ssid->id, wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); + } + ssid->disabled_until.sec = 0; + ssid->disabled_until.usec = 0; + if (clear_failures) + ssid->auth_failures = 0; +} + + +int disallowed_bssid(struct wpa_supplicant *wpa_s, const u8 *bssid) +{ + size_t i; + + if (wpa_s->disallow_aps_bssid == NULL) + return 0; + + for (i = 0; i < wpa_s->disallow_aps_bssid_count; i++) { + if (os_memcmp(wpa_s->disallow_aps_bssid + i * ETH_ALEN, + bssid, ETH_ALEN) == 0) + return 1; + } + + return 0; +} + + +int disallowed_ssid(struct wpa_supplicant *wpa_s, const u8 *ssid, + size_t ssid_len) +{ + size_t i; + + if (wpa_s->disallow_aps_ssid == NULL || ssid == NULL) + return 0; + + for (i = 0; i < wpa_s->disallow_aps_ssid_count; i++) { + struct wpa_ssid_value *s = &wpa_s->disallow_aps_ssid[i]; + if (ssid_len == s->ssid_len && + os_memcmp(ssid, s->ssid, ssid_len) == 0) + return 1; + } + + return 0; +} + + +/** + * wpas_request_connection - Request a new connection + * @wpa_s: Pointer to the network interface + * + * This function is used to request a new connection to be found. It will mark + * the interface to allow reassociation and request a new scan to find a + * suitable network to connect to. + */ +void wpas_request_connection(struct wpa_supplicant *wpa_s) +{ + wpa_s->normal_scans = 0; + wpa_supplicant_reinit_autoscan(wpa_s); + wpa_s->extra_blacklist_count = 0; + wpa_s->disconnected = 0; + wpa_s->reassociate = 1; + wpa_supplicant_req_scan(wpa_s, 0, 0); } diff --git a/contrib/wpa/wpa_supplicant/wpa_supplicant.conf b/contrib/wpa/wpa_supplicant/wpa_supplicant.conf index 1b175ad..9ad3f8b 100644 --- a/contrib/wpa/wpa_supplicant/wpa_supplicant.conf +++ b/contrib/wpa/wpa_supplicant/wpa_supplicant.conf @@ -30,7 +30,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. # @@ -190,8 +190,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) @@ -201,6 +205,22 @@ fast_reauth=1 # to external program(s) #wps_cred_processing=0 +# Vendor attribute in WPS M1, e.g., Windows 7 Vertical Pairing +# The vendor attribute contents to be added in M1 (hex string) +#wps_vendor_ext_m1=000137100100020001 + +# NFC password token for WPS +# These parameters can be used to configure a fixed NFC password token for the +# station. This can be generated, e.g., with nfc_pw_token. When these +# parameters are used, the station is assumed to be deployed with a NFC tag +# that includes the matching NFC password token (e.g., written based on the +# NDEF record from nfc_pw_token). +# +#wps_nfc_dev_pw_id: Device Password ID (16..65535) +#wps_nfc_dh_pubkey: Hexdump of DH Public Key +#wps_nfc_dh_privkey: Hexdump of DH Private Key +#wps_nfc_dev_pw: Hexdump of Device Password + # Maximum number of BSS entries to keep in memory # Default: 200 # This can be used to limit memory use on the BSS entries (cached scan @@ -208,12 +228,191 @@ fast_reauth=1 # of APs when using ap_scan=1 mode. #bss_max_count=200 +# Automatic scan +# This is an optional set of parameters for automatic scanning +# within an interface in following format: +#autoscan=<autoscan module name>:<module parameters> +# autoscan is like bgscan but on disconnected or inactive state. +# For instance, on exponential module parameters would be <base>:<limit> +#autoscan=exponential:3:300 +# Which means a delay between scans on a base exponential of 3, +# up to the limit of 300 seconds (3, 9, 27 ... 300) +# For periodic module, parameters would be <fixed interval> +#autoscan=periodic:30 +# So a delay of 30 seconds will be applied between each scan # filter_ssids - SSID-based scan result filtering # 0 = do not filter scan results (default) # 1 = only include configured SSIDs in scan results/BSS table #filter_ssids=0 +# Password (and passphrase, etc.) backend for external storage +# format: <backend name>[:<optional backend parameters>] +#ext_password_backend=test:pw1=password|pw2=testing + +# Timeout in seconds to detect STA inactivity (default: 300 seconds) +# +# This timeout value is used in P2P GO mode to clean up +# inactive stations. +#p2p_go_max_inactivity=300 + +# Opportunistic Key Caching (also known as Proactive Key Caching) default +# This parameter can be used to set the default behavior for the +# proactive_key_caching parameter. By default, OKC is disabled unless enabled +# with the global okc=1 parameter or with the per-network +# proactive_key_caching=1 parameter. With okc=1, OKC is enabled by default, but +# can be disabled with per-network proactive_key_caching=0 parameter. +#okc=0 + +# Protected Management Frames default +# This parameter can be used to set the default behavior for the ieee80211w +# parameter. By default, PMF is disabled unless enabled with the global pmf=1/2 +# parameter or with the per-network ieee80211w=1/2 parameter. With pmf=1/2, PMF +# is enabled/required by default, but can be disabled with the per-network +# ieee80211w parameter. +#pmf=0 + +# Interworking (IEEE 802.11u) + +# Enable Interworking +# interworking=1 + +# Homogenous ESS identifier +# If this is set, scans will be used to request response only from BSSes +# belonging to the specified Homogeneous ESS. This is used only if interworking +# is enabled. +# hessid=00:11:22:33:44:55 + +# Automatic network selection behavior +# 0 = do not automatically go through Interworking network selection +# (i.e., require explicit interworking_select command for this; default) +# 1 = perform Interworking network selection if one or more +# credentials have been configured and scan did not find a +# matching network block +#auto_interworking=0 + +# credential block +# +# Each credential used for automatic network selection is configured as a set +# of parameters that are compared to the information advertised by the APs when +# interworking_select and interworking_connect commands are used. +# +# credential fields: +# +# priority: Priority group +# By default, all networks and credentials get the same priority group +# (0). This field can be used to give higher priority for credentials +# (and similarly in struct wpa_ssid for network blocks) to change the +# Interworking automatic networking selection behavior. The matching +# network (based on either an enabled network block or a credential) +# with the highest priority value will be selected. +# +# pcsc: Use PC/SC and SIM/USIM card +# +# realm: Home Realm for Interworking +# +# username: Username for Interworking network selection +# +# password: Password for Interworking network selection +# +# ca_cert: CA certificate for Interworking network selection +# +# client_cert: File path to client certificate file (PEM/DER) +# This field is used with Interworking networking selection for a case +# where client certificate/private key is used for authentication +# (EAP-TLS). Full path to the file should be used since working +# directory may change when wpa_supplicant is run in the background. +# +# Alternatively, a named configuration blob can be used by setting +# this to blob://blob_name. +# +# private_key: File path to client private key file (PEM/DER/PFX) +# When PKCS#12/PFX file (.p12/.pfx) is used, client_cert should be +# commented out. Both the private key and certificate will be read +# from the PKCS#12 file in this case. Full path to the file should be +# used since working directory may change when wpa_supplicant is run +# in the background. +# +# Windows certificate store can be used by leaving client_cert out and +# configuring private_key in one of the following formats: +# +# cert://substring_to_match +# +# hash://certificate_thumbprint_in_hex +# +# For example: private_key="hash://63093aa9c47f56ae88334c7b65a4" +# +# Note that when running wpa_supplicant as an application, the user +# certificate store (My user account) is used, whereas computer store +# (Computer account) is used when running wpasvc as a service. +# +# Alternatively, a named configuration blob can be used by setting +# this to blob://blob_name. +# +# private_key_passwd: Password for private key file +# +# imsi: IMSI in <MCC> | <MNC> | '-' | <MSIN> format +# +# milenage: Milenage parameters for SIM/USIM simulator in <Ki>:<OPc>:<SQN> +# format +# +# domain: Home service provider FQDN +# This is used to compare against the Domain Name List to figure out +# whether the AP is operated by the Home SP. +# +# roaming_consortium: Roaming Consortium OI +# If roaming_consortium_len is non-zero, this field contains the +# Roaming Consortium OI that can be used to determine which access +# points support authentication with this credential. This is an +# alternative to the use of the realm parameter. When using Roaming +# Consortium to match the network, the EAP parameters need to be +# pre-configured with the credential since the NAI Realm information +# may not be available or fetched. +# +# eap: Pre-configured EAP method +# This optional field can be used to specify which EAP method will be +# used with this credential. If not set, the EAP method is selected +# automatically based on ANQP information (e.g., NAI Realm). +# +# phase1: Pre-configure Phase 1 (outer authentication) parameters +# This optional field is used with like the 'eap' parameter. +# +# phase2: Pre-configure Phase 2 (inner authentication) parameters +# This optional field is used with like the 'eap' parameter. +# +# excluded_ssid: Excluded SSID +# This optional field can be used to excluded specific SSID(s) from +# matching with the network. Multiple entries can be used to specify more +# than one SSID. +# +# for example: +# +#cred={ +# realm="example.com" +# username="user@example.com" +# password="password" +# ca_cert="/etc/wpa_supplicant/ca.pem" +# domain="example.com" +#} +# +#cred={ +# imsi="310026-000000000" +# milenage="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82" +#} +# +#cred={ +# realm="example.com" +# username="user" +# password="password" +# ca_cert="/etc/wpa_supplicant/ca.pem" +# domain="example.com" +# roaming_consortium=223344 +# eap=TTLS +# phase2="auth=MSCHAPV2" +#} + +# Hotspot 2.0 +# hs20=1 # network block # @@ -232,8 +431,10 @@ fast_reauth=1 # to external action script through wpa_cli as WPA_ID_STR environment # variable to make it easier to do network specific configuration. # -# ssid: SSID (mandatory); either as an ASCII string with double quotation or -# as hex string; network name +# ssid: SSID (mandatory); network name in one of the optional formats: +# - an ASCII string with double quotation +# - a hex string (two characters per octet of SSID) +# - a printf-escaped ASCII string P"<escaped string>" # # scan_ssid: # 0 = do not scan this SSID with specific Probe Request frames (default) @@ -284,6 +485,23 @@ fast_reauth=1 # set, scan results that do not match any of the specified frequencies are not # considered when selecting a BSS. # +# bgscan: Background scanning +# wpa_supplicant behavior for background scanning can be specified by +# configuring a bgscan module. These modules are responsible for requesting +# background scans for the purpose of roaming within an ESS (i.e., within a +# single network block with all the APs using the same SSID). The bgscan +# parameter uses following format: "<bgscan module name>:<module parameters>" +# Following bgscan modules are available: +# simple - Periodic background scans based on signal strength +# bgscan="simple:<short bgscan interval in seconds>:<signal strength threshold>: +# <long interval>" +# bgscan="simple:30:-45:300" +# learn - Learn channels used by the network and try to avoid bgscans on other +# channels (experimental) +# bgscan="learn:<short bgscan interval in seconds>:<signal strength threshold>: +# <long interval>[:<database file name>]" +# bgscan="learn:30:-45:300:/etc/wpa_supplicant/network1.bgscan" +# # proto: list of accepted protocols # WPA = WPA/IEEE 802.11i/D3.0 # RSN = WPA2/IEEE 802.11i (also WPA2 can be used as an alias for RSN) @@ -299,6 +517,16 @@ fast_reauth=1 # WPA-EAP-SHA256 = Like WPA-EAP but using stronger SHA256-based algorithms # If not set, this defaults to: WPA-PSK WPA-EAP # +# ieee80211w: whether management frame protection is enabled +# 0 = disabled (default unless changed with the global pmf parameter) +# 1 = optional +# 2 = required +# The most common configuration options for this based on the PMF (protected +# management frames) certification program are: +# PMF enabled: ieee80211w=1 and key_mgmt=WPA-EAP WPA-EAP-SHA256 +# PMF required: ieee80211w=2 and key_mgmt=WPA-EAP-SHA256 +# (and similarly for WPA-PSK and WPA-WPSK-SHA256 if WPA2-Personal is used) +# # auth_alg: list of allowed IEEE 802.11 authentication algorithms # OPEN = Open System authentication (required for WPA/WPA2) # SHARED = Shared Key authentication (requires static WEP keys) @@ -324,7 +552,8 @@ fast_reauth=1 # The key used in WPA-PSK mode can be entered either as 64 hex-digits, i.e., # 32 bytes or as an ASCII passphrase (in which case, the real PSK will be # generated using the passphrase and SSID). ASCII passphrase must be between -# 8 and 63 characters (inclusive). +# 8 and 63 characters (inclusive). ext:<name of external PSK field> format can +# be used to indicate that the PSK/passphrase is stored in external storage. # This field is not needed, if WPA-EAP is used. # Note: Separate tool, wpa_passphrase, can be used to generate 256-bit keys # from ASCII passphrase. This process uses lot of CPU and wpa_supplicant @@ -341,13 +570,13 @@ 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 # # proactive_key_caching: # Enable/disable opportunistic PMKSA caching for WPA2. -# 0 = disabled (default) +# 0 = disabled (default unless changed with the global okc parameter) # 1 = enabled # # wep_key0..3: Static WEP key (ASCII in double quotation, e.g. "abcde" or @@ -385,7 +614,8 @@ fast_reauth=1 # EAP-PSK/PAX/SAKE/GPSK. # anonymous_identity: Anonymous identity string for EAP (to be used as the # unencrypted identity with EAP types that support different tunnelled -# identity, e.g., EAP-TTLS) +# identity, e.g., EAP-TTLS). This field can also be used with +# EAP-SIM/AKA/AKA' to store the pseudonym identity. # password: Password string for EAP. This field can include either the # plaintext password (using ASCII or hex string) or a NtPasswordHash # (16-byte MD4 hash of password) in hash:<32 hex digits> format. @@ -393,7 +623,8 @@ fast_reauth=1 # MSCHAP (EAP-MSCHAPv2, EAP-TTLS/MSCHAPv2, EAP-TTLS/MSCHAP, LEAP). # EAP-PSK (128-bit PSK), EAP-PAX (128-bit PSK), and EAP-SAKE (256-bit # PSK) is also configured using this field. For EAP-GPSK, this is a -# variable length PSK. +# variable length PSK. ext:<name of external password field> format can +# be used to indicate that the password is stored in external storage. # ca_cert: File path to CA certificate file (PEM/DER). This file can have one # or more trusted CA certificates. If ca_cert and ca_path are not # included, server certificate will not be verified. This is insecure and @@ -496,6 +727,25 @@ fast_reauth=1 # phase2: Phase2 (inner authentication with TLS tunnel) parameters # (string with field-value pairs, e.g., "auth=MSCHAPV2" for EAP-PEAP or # "autheap=MSCHAPV2 autheap=MD5" for EAP-TTLS) +# +# TLS-based methods can use the following parameters to control TLS behavior +# (these are normally in the phase1 parameter, but can be used also in the +# phase2 parameter when EAP-TLS is used within the inner tunnel): +# tls_allow_md5=1 - allow MD5-based certificate signatures (depending on the +# TLS library, these may be disabled by default to enforce stronger +# security) +# tls_disable_time_checks=1 - ignore certificate validity time (this requests +# the TLS library to accept certificates even if they are not currently +# valid, i.e., have expired or have not yet become valid; this should be +# used only for testing purposes) +# tls_disable_session_ticket=1 - disable TLS Session Ticket extension +# tls_disable_session_ticket=0 - allow TLS Session Ticket extension to be used +# Note: If not set, this is automatically set to 1 for EAP-TLS/PEAP/TTLS +# as a workaround for broken authentication server implementations unless +# EAP workarounds are disabled with eap_workarounds=0. +# For EAP-FAST, this must be set to 0 (or left unconfigured for the +# default value to be used automatically). +# # Following certificate/private key fields are used in inner Phase2 # authentication when using EAP-TTLS or EAP-PEAP. # ca_cert2: File path to CA certificate file. This file can have one or more @@ -545,6 +795,54 @@ fast_reauth=1 # number of authentication servers. Strict EAP conformance mode can be # configured by disabling workarounds with eap_workaround=0. +# Station inactivity limit +# +# If a station does not send anything in ap_max_inactivity seconds, an +# empty data frame is sent to it in order to verify whether it is +# still in range. If this frame is not ACKed, the station will be +# disassociated and then deauthenticated. This feature is used to +# clear station table of old entries when the STAs move out of the +# range. +# +# The station can associate again with the AP if it is still in range; +# this inactivity poll is just used as a nicer way of verifying +# inactivity; i.e., client will not report broken connection because +# disassociation frame is not sent immediately without first polling +# the STA with a data frame. +# default: 300 (i.e., 5 minutes) +#ap_max_inactivity=300 + +# DTIM period in Beacon intervals for AP mode (default: 2) +#dtim_period=2 + +# disable_ht: Whether HT (802.11n) should be disabled. +# 0 = HT enabled (if AP supports it) +# 1 = HT disabled +# +# disable_ht40: Whether HT-40 (802.11n) should be disabled. +# 0 = HT-40 enabled (if AP supports it) +# 1 = HT-40 disabled +# +# disable_sgi: Whether SGI (short guard interval) should be disabled. +# 0 = SGI enabled (if AP supports it) +# 1 = SGI disabled +# +# ht_mcs: Configure allowed MCS rates. +# Parsed as an array of bytes, in base-16 (ascii-hex) +# ht_mcs="" // Use all available (default) +# ht_mcs="0xff 00 00 00 00 00 00 00 00 00 " // Use MCS 0-7 only +# ht_mcs="0xff ff 00 00 00 00 00 00 00 00 " // Use MCS 0-15 only +# +# disable_max_amsdu: Whether MAX_AMSDU should be disabled. +# -1 = Do not make any changes. +# 0 = Enable MAX-AMSDU if hardware supports it. +# 1 = Disable AMSDU +# +# ampdu_density: Allow overriding AMPDU density configuration. +# Treated as hint by the kernel. +# -1 = Do not make any changes. +# 0-3 = Set AMPDU density (aka factor) to specified value. + # Example blocks: # Simple case: WPA-PSK, PSK as an ASCII passphrase, allow all valid ciphers diff --git a/contrib/wpa/wpa_supplicant/wpa_supplicant.nsi b/contrib/wpa/wpa_supplicant/wpa_supplicant.nsi deleted file mode 100644 index b9f0162..0000000 --- a/contrib/wpa/wpa_supplicant/wpa_supplicant.nsi +++ /dev/null @@ -1,112 +0,0 @@ -!define PRODUCT_NAME "wpa_supplicant" -!define PRODUCT_VERSION "@WPAVER@" -!define PRODUCT_PUBLISHER "Jouni Malinen" - -Name "${PRODUCT_NAME} ${PRODUCT_VERSION}" -outfile "../wpa_supplicant-@WPAVER@.exe" - -installDir "$PROGRAMFILES\wpa_supplicant" - -Page Directory -Page InstFiles - -section -Prerequisites - SetOutPath $INSTDIR\Prerequisites - MessageBox MB_YESNO "Install WinPcap?" /SD IDYES IDNO endWinPcap - File "/opt/Qt-Win/files/WinPcap_4_1_2.exe" - ExecWait "$INSTDIR\Prerequisites\WinPcap_4_1_2.exe" - Goto endWinPcap - endWinPcap: -sectionEnd - - -section - setOutPath $INSTDIR - - File wpa_gui.exe - File wpa_gui_de.qm - File wpa_cli.exe - File COPYING - File README - File README-Windows.txt - File win_example.reg - File win_if_list.exe - File wpa_passphrase.exe - File wpa_supplicant.conf - File wpa_supplicant.exe - File wpasvc.exe - - File /opt/Qt-Win/files/mingwm10.dll - File /opt/Qt-Win/files/libgcc_s_dw2-1.dll - File /opt/Qt-Win/files/QtCore4.dll - File /opt/Qt-Win/files/QtGui4.dll - - WriteRegDWORD HKLM "Software\wpa_supplicant" "debug_level" 0 - WriteRegDWORD HKLM "Software\wpa_supplicant" "debug_show_keys" 0 - WriteRegDWORD HKLM "Software\wpa_supplicant" "debug_timestamp" 0 - WriteRegDWORD HKLM "Software\wpa_supplicant" "debug_use_file" 0 - - WriteRegDWORD HKLM "Software\wpa_supplicant\configs\default" "ap_scan" 2 - WriteRegDWORD HKLM "Software\wpa_supplicant\configs\default" "update_config" 1 - WriteRegDWORD HKLM "Software\wpa_supplicant\configs\default\networks" "dummy" 1 - DeleteRegValue HKLM "Software\wpa_supplicant\configs\default\networks" "dummy" - - WriteRegDWORD HKLM "Software\wpa_supplicant\interfaces" "dummy" 1 - DeleteRegValue HKLM "Software\wpa_supplicant\interfaces" "dummy" - - writeUninstaller "$INSTDIR\uninstall.exe" - - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\wpa_supplicant" \ - "DisplayName" "wpa_supplicant" -WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\wpa_supplicant" \ - "UninstallString" "$INSTDIR\uninstall.exe" - - CreateDirectory "$SMPROGRAMS\wpa_supplicant" - CreateShortCut "$SMPROGRAMS\wpa_supplicant\wpa_gui.lnk" "$INSTDIR\wpa_gui.exe" - CreateShortCut "$SMPROGRAMS\wpa_supplicant\Uninstall.lnk" "$INSTDIR\uninstall.exe" - - ExecWait "$INSTDIR\wpasvc.exe reg" -sectionEnd - - -Function un.onInit - MessageBox MB_YESNO "This will uninstall wpa_supplicant. Continue?" IDYES NoAbort - Abort - NoAbort: -FunctionEnd - -section "uninstall" - DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\wpa_supplicant" - delete "$INSTDIR\uninstall.exe" - - ExecWait "$INSTDIR\wpasvc.exe unreg" - - DeleteRegKey HKLM "Software\wpa_supplicant" - - delete "$INSTDIR\wpa_gui.exe" - delete "$INSTDIR\wpa_gui_de.qm" - delete "$INSTDIR\wpa_cli.exe" - delete "$INSTDIR\COPYING" - delete "$INSTDIR\README" - delete "$INSTDIR\README-Windows.txt" - delete "$INSTDIR\win_example.reg" - delete "$INSTDIR\win_if_list.exe" - delete "$INSTDIR\wpa_passphrase.exe" - delete "$INSTDIR\wpa_supplicant.conf" - delete "$INSTDIR\wpa_supplicant.exe" - delete "$INSTDIR\wpasvc.exe" - - delete "$INSTDIR\mingwm10.dll" - delete "$INSTDIR\libgcc_s_dw2-1.dll" - delete "$INSTDIR\QtCore4.dll" - delete "$INSTDIR\QtGui4.dll" - - delete "$INSTDIR\Prerequisites\WinPcap_4_1_2.exe" - rmdir "$INSTDIR\Prerequisites" - - rmdir "$INSTDIR" - - delete "$SMPROGRAMS\wpa_supplicant\wpa_gui.lnk" - delete "$SMPROGRAMS\wpa_supplicant\Uninstall.lnk" - rmdir "$SMPROGRAMS\wpa_supplicant" -sectionEnd diff --git a/contrib/wpa/wpa_supplicant/wpa_supplicant_conf.mk b/contrib/wpa/wpa_supplicant/wpa_supplicant_conf.mk new file mode 100644 index 0000000..74986ea --- /dev/null +++ b/contrib/wpa/wpa_supplicant/wpa_supplicant_conf.mk @@ -0,0 +1,34 @@ +# +# Copyright (C) 2010 The Android Open Source Project +# +# This software may be distributed under the terms of the BSD license. +# See README for more details. +# + +# Include this makefile to generate your hardware specific wpa_supplicant.conf +# Requires: WIFI_DRIVER_SOCKET_IFACE + +LOCAL_PATH := $(call my-dir) + +######################## +include $(CLEAR_VARS) + +LOCAL_MODULE := wpa_supplicant.conf +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/wifi + +include $(BUILD_SYSTEM)/base_rules.mk + +WPA_SUPPLICANT_CONF_TEMPLATE := $(LOCAL_PATH)/wpa_supplicant_template.conf +WPA_SUPPLICANT_CONF_SCRIPT := $(LOCAL_PATH)/wpa_supplicant_conf.sh +$(LOCAL_BUILT_MODULE): PRIVATE_WIFI_DRIVER_SOCKET_IFACE := $(WIFI_DRIVER_SOCKET_IFACE) +$(LOCAL_BUILT_MODULE): PRIVATE_WPA_SUPPLICANT_CONF_TEMPLATE := $(WPA_SUPPLICANT_CONF_TEMPLATE) +$(LOCAL_BUILT_MODULE): PRIVATE_WPA_SUPPLICANT_CONF_SCRIPT := $(WPA_SUPPLICANT_CONF_SCRIPT) +$(LOCAL_BUILT_MODULE) : $(WPA_SUPPLICANT_CONF_TEMPLATE) $(WPA_SUPPLICANT_CONF_SCRIPT) + @echo Target wpa_supplicant.conf: $@ + @mkdir -p $(dir $@) + $(hide) WIFI_DRIVER_SOCKET_IFACE="$(PRIVATE_WIFI_DRIVER_SOCKET_IFACE)" \ + bash $(PRIVATE_WPA_SUPPLICANT_CONF_SCRIPT) $(PRIVATE_WPA_SUPPLICANT_CONF_TEMPLATE) > $@ + +######################## diff --git a/contrib/wpa/wpa_supplicant/wpa_supplicant_conf.sh b/contrib/wpa/wpa_supplicant/wpa_supplicant_conf.sh new file mode 100755 index 0000000..f36eef1 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/wpa_supplicant_conf.sh @@ -0,0 +1,16 @@ +#!/bin/bash +# +# Copyright (C) 2010 The Android Open Source Project +# +# This software may be distributed under the terms of the BSD license. +# See README for more details. +# + +# Generate a wpa_supplicant.conf from the template. +# $1: the template file name +if [ -n "$WIFI_DRIVER_SOCKET_IFACE" ] +then + sed -e 's/#.*$//' -e 's/[ \t]*$//' -e '/^$/d' < $1 | sed -e "s/wlan0/$WIFI_DRIVER_SOCKET_IFACE/" +else + sed -e 's/#.*$//' -e 's/[ \t]*$//' -e '/^$/d' < $1 +fi diff --git a/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h b/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h index 6c36a1a..544977b 100644 --- a/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h +++ b/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h @@ -1,15 +1,9 @@ /* * wpa_supplicant - Internal definitions - * 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 - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPA_SUPPLICANT_I_H @@ -17,6 +11,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 +29,8 @@ struct ibss_rsn; struct scan_info; struct wpa_bss; struct wpa_scan_results; +struct hostapd_hw_modes; +struct wpa_driver_associate_params; /* * Forward declarations of private structures used within the ctrl_iface @@ -164,6 +161,11 @@ struct wpa_params { int wpa_debug_syslog; /** + * wpa_debug_tracing - Enable log output through Linux tracing + */ + int wpa_debug_tracing; + + /** * override_driver - Optional driver parameter override * * This parameter can be used to override the driver parameter in @@ -180,8 +182,34 @@ 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; +}; + +struct wpa_freq_range { + unsigned int min; + unsigned int max; +}; + + /** * struct wpa_global - Internal, global data for all %wpa_supplicant interfaces * @@ -196,101 +224,56 @@ struct wpa_global { void **drv_priv; size_t drv_count; struct os_time suspend_time; + struct p2p_data *p2p; + struct wpa_supplicant *p2p_init_wpa_s; + struct wpa_supplicant *p2p_group_formation; + 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_freq_range *p2p_disallow_freq; + unsigned int num_p2p_disallow_freq; + enum wpa_conc_pref { + WPA_CONC_PREF_NOT_SET, + WPA_CONC_PREF_STA, + WPA_CONC_PREF_P2P + } conc_pref; + unsigned int p2p_cb_on_scan_complete:1; + +#ifdef CONFIG_WIFI_DISPLAY + int wifi_display; +#define MAX_WFD_SUBELEMS 10 + struct wpabuf *wfd_subelem[MAX_WFD_SUBELEMS]; +#endif /* CONFIG_WIFI_DISPLAY */ }; -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]; +/** + * offchannel_send_action_result - Result of offchannel send Action frame + */ +enum offchannel_send_action_result { + OFFCHANNEL_SEND_ACTION_SUCCESS /**< Frame was send and acknowledged */, + OFFCHANNEL_SEND_ACTION_NO_ACK /**< Frame was sent, but not acknowledged + */, + OFFCHANNEL_SEND_ACTION_FAILED /**< Frame was not sent due to a failure + */ +}; + +struct wps_ap_info { + u8 bssid[ETH_ALEN]; + enum wps_ap_info_type { + WPS_AP_NOT_SEL_REG, + WPS_AP_SEL_REG, + WPS_AP_SEL_REG_OUR + } type; + unsigned int tries; + struct os_time last_attempt; +}; + +struct wpa_ssid_value { 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 */ }; /** @@ -303,6 +286,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 +297,10 @@ struct wpa_supplicant { #endif /* CONFIG_CTRL_IFACE_DBUS */ #ifdef CONFIG_CTRL_IFACE_DBUS_NEW char *dbus_new_path; + char *dbus_groupobj_path; +#ifdef CONFIG_AP + char *preq_notify_peer; +#endif /* CONFIG_AP */ #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */ char bridge_ifname[16]; @@ -322,7 +310,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,11 +323,23 @@ 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 */ void *global_drv_priv; + u8 *bssid_filter; + size_t bssid_filter_count; + + u8 *disallow_aps_bssid; + size_t disallow_aps_bssid_count; + struct wpa_ssid_value *disallow_aps_ssid; + size_t disallow_aps_ssid_count; + + /* previous scan was wildcard when interleaving between + * wildcard scans and specific SSID scan when max_ssids=1 */ + int prev_scan_wildcard; struct wpa_ssid *prev_scan_ssid; /* previously scanned SSID; * NULL = not yet initialized (start * with wildcard SSID) @@ -348,6 +348,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 */ @@ -356,6 +362,16 @@ struct wpa_supplicant { unsigned int bss_update_idx; unsigned int bss_next_id; + /* + * Pointers to BSS entries in the order they were in the last scan + * results. + */ + struct wpa_bss **last_scan_res; + unsigned int last_scan_res_used; + unsigned int last_scan_res_size; + int last_scan_full; + struct os_time last_scan; + struct wpa_driver_ops *driver; int interface_removed; /* whether the network interface has been * removed */ @@ -366,13 +382,17 @@ struct wpa_supplicant { enum wpa_states wpa_state; int scanning; + int sched_scanning; int new_connection; - int reassociated_connection; int eapol_received; /* number of EAPOL packets received after the * previous association event */ struct scard_data *scard; +#ifdef PCSC_FUNCS + char imsi[20]; + int mnc_len; +#endif /* PCSC_FUNCS */ unsigned char last_eapol_src[ETH_ALEN]; @@ -380,14 +400,70 @@ struct wpa_supplicant { struct wpa_blacklist *blacklist; - int scan_req; /* manual scan request; this forces a scan even if there - * are no enabled networks in the configuration */ + /** + * extra_blacklist_count - Sum of blacklist counts after last connection + * + * This variable is used to maintain a count of temporary blacklisting + * failures (maximum number for any BSS) over blacklist clear + * operations. This is needed for figuring out whether there has been + * failures prior to the last blacklist clear operation which happens + * whenever no other not-blacklisted BSS candidates are available. This + * gets cleared whenever a connection has been established successfully. + */ + int extra_blacklist_count; + + /** + * scan_req - Type of the scan request + */ + enum scan_req_type { + /** + * NORMAL_SCAN_REQ - Normal scan request + * + * This is used for scans initiated by wpa_supplicant to find an + * AP for a connection. + */ + NORMAL_SCAN_REQ, + + /** + * INITIAL_SCAN_REQ - Initial scan request + * + * This is used for the first scan on an interface to force at + * least one scan to be run even if the configuration does not + * include any enabled networks. + */ + INITIAL_SCAN_REQ, + + /** + * MANUAL_SCAN_REQ - Manual scan request + * + * This is used for scans where the user request a scan or + * a specific wpa_supplicant operation (e.g., WPS) requires scan + * to be run. + */ + MANUAL_SCAN_REQ + } scan_req; 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 */ + int normal_scans; /* normal scans run before sched_scan */ + int scan_for_connection; /* whether the scan request was triggered for + * finding a connection */ - struct wpa_client_mlme mlme; unsigned int drv_flags; + unsigned int drv_enc; + + /* + * A bitmap of supported protocols for probe response offload. See + * struct wpa_driver_capa in driver.h + */ + unsigned int probe_resp_offloads; + 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; @@ -401,15 +477,21 @@ struct wpa_supplicant { struct wpabuf *pending_eapol_rx; struct os_time pending_eapol_rx_time; u8 pending_eapol_rx_src[ETH_ALEN]; + unsigned int last_eapol_matches_bssid:1; 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 +501,24 @@ 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; + u8 sched_obss_scan; + u16 obss_scan_int; + u16 bss_max_idle_period; + enum { + SME_SAE_INIT, + SME_SAE_COMMIT, + SME_SAE_CONFIRM + } sae_state; + u16 sae_send_confirm; } sme; #endif /* CONFIG_SME */ @@ -429,23 +529,165 @@ 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 auto_pd_scan_retry; + int force_long_sd; + u16 pending_pd_config_methods; + enum { + NORMAL_PD, AUTO_PD_GO_NEG, AUTO_PD_JOIN + } pending_pd_use; + + /* + * Whether cross connection is disallowed by the AP to which this + * interface is associated (only valid if there is an association). + */ + int cross_connect_disallowed; + + /* + * Whether this P2P group is configured to use cross connection (only + * valid if this is P2P GO interface). The actual cross connect packet + * forwarding may not be configured depending on the uplink status. + */ + int cross_connect_enabled; + + /* Whether cross connection forwarding is in use at the moment. */ + int cross_connect_in_use; + + /* + * Uplink interface name for cross connection + */ + char cross_connect_uplink[100]; + + unsigned int sta_scan_pending:1; + unsigned int p2p_auto_join:1; + unsigned int p2p_auto_pd:1; + unsigned int p2p_persistent_group:1; + unsigned int p2p_fallback_to_go_neg:1; + unsigned int p2p_pd_before_go_neg:1; + unsigned int p2p_go_ht40:1; + unsigned int user_initiated_pd:1; + int p2p_persistent_go_freq; + int p2p_persistent_id; + int p2p_go_intent; + int p2p_connect_freq; + struct os_time p2p_auto_started; +#endif /* CONFIG_P2P */ + struct wpa_ssid *bgscan_ssid; const struct bgscan_ops *bgscan; void *bgscan_priv; - int connect_without_scan; + const struct autoscan_ops *autoscan; + struct wpa_driver_scan_params *autoscan_params; + void *autoscan_priv; + + struct wpa_ssid *connect_without_scan; + + struct wps_ap_info *wps_ap; + size_t num_wps_ap; + int wps_ap_iter; int after_wps; + int known_wps_freq; unsigned int wps_freq; + u16 wps_ap_channel; + int wps_fragment_size; + int auto_reconnect_disabled; + + /* Channel preferences for AP/P2P GO use */ + int best_24_freq; + int best_5_freq; + int best_overall_freq; + + struct gas_query *gas; + +#ifdef CONFIG_INTERWORKING + unsigned int fetch_anqp_in_progress:1; + unsigned int network_select:1; + unsigned int auto_select:1; + unsigned int auto_network_select:1; + unsigned int fetch_all_anqp:1; +#endif /* CONFIG_INTERWORKING */ + unsigned int drv_capa_known; + + struct { + struct hostapd_hw_modes *modes; + u16 num_modes; + u16 flags; + } hw; + + int pno; + + /* WLAN_REASON_* reason codes. Negative if locally generated. */ + int disconnect_reason; + + struct ext_password_data *ext_pw; + + struct wpabuf *last_gas_resp; + u8 last_gas_addr[ETH_ALEN]; + u8 last_gas_dialog_token; + + unsigned int no_keep_alive:1; }; /* wpa_supplicant.c */ +void wpa_supplicant_apply_ht_overrides( + struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, + struct wpa_driver_associate_params *params); + int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); int wpa_supplicant_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, @@ -459,14 +701,14 @@ void wpa_supplicant_initiate_eapol(struct wpa_supplicant *wpa_s); void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr); void wpa_supplicant_req_auth_timeout(struct wpa_supplicant *wpa_s, int sec, int usec); +void wpa_supplicant_reinit_autoscan(struct wpa_supplicant *wpa_s); void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, enum wpa_states state); struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s); +const char * wpa_supplicant_get_eap_mode(struct wpa_supplicant *wpa_s); void wpa_supplicant_cancel_auth_timeout(struct wpa_supplicant *wpa_s); void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s, int reason_code); -void wpa_supplicant_disassociate(struct wpa_supplicant *wpa_s, - int reason_code); void wpa_supplicant_enable_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); @@ -476,16 +718,24 @@ 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_scan_interval(struct wpa_supplicant *wpa_s, + int scan_interval); int wpa_supplicant_set_debug_params(struct wpa_global *global, int debug_level, int debug_timestamp, int debug_show_keys); +void free_hw_features(struct wpa_supplicant *wpa_s); void wpa_show_license(void); struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global, struct wpa_interface *iface); int wpa_supplicant_remove_iface(struct wpa_global *global, - struct wpa_supplicant *wpa_s); + struct wpa_supplicant *wpa_s, + int terminate); struct wpa_supplicant * wpa_supplicant_get_iface(struct wpa_global *global, const char *ifname); struct wpa_global * wpa_supplicant_init(struct wpa_params *params); @@ -499,14 +749,60 @@ 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); +int wpas_is_p2p_prioritized(struct wpa_supplicant *wpa_s); +void wpas_auth_failed(struct wpa_supplicant *wpa_s); +void wpas_clear_temp_disabled(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, int clear_failures); +int disallowed_bssid(struct wpa_supplicant *wpa_s, const u8 *bssid); +int disallowed_ssid(struct wpa_supplicant *wpa_s, const u8 *ssid, + size_t ssid_len); +void wpas_request_connection(struct wpa_supplicant *wpa_s); +int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf); + +/** + * 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); /* 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); +void wnm_bss_keep_alive_deinit(struct wpa_supplicant *wpa_s); +int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s); /* 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); +} + +int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); + +int wpas_init_ext_pw(struct wpa_supplicant *wpa_s); + #endif /* WPA_SUPPLICANT_I_H */ diff --git a/contrib/wpa/wpa_supplicant/wpa_supplicant_template.conf b/contrib/wpa/wpa_supplicant/wpa_supplicant_template.conf new file mode 100644 index 0000000..a08eb33 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/wpa_supplicant_template.conf @@ -0,0 +1,6 @@ +##### wpa_supplicant configuration file template ##### +update_config=1 +ctrl_interface=wlan0 +eapol_version=1 +ap_scan=1 +fast_reauth=1 diff --git a/contrib/wpa/wpa_supplicant/wpas_glue.c b/contrib/wpa/wpa_supplicant/wpas_glue.c index 4af0cd0..6f69ddb 100644 --- a/contrib/wpa/wpa_supplicant/wpas_glue.c +++ b/contrib/wpa/wpa_supplicant/wpas_glue.c @@ -1,15 +1,9 @@ /* * WPA Supplicant - Glue code to setup EAPOL and RSN modules - * Copyright (c) 2003-2008, 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 - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -24,7 +18,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 +25,7 @@ #include "wps_supplicant.h" #include "bss.h" #include "scan.h" +#include "notify.h" #ifndef CONFIG_NO_CONFIG_BLOBS @@ -210,9 +204,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 +248,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 +279,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"); @@ -395,14 +406,6 @@ static enum wpa_states _wpa_supplicant_get_state(void *wpa_s) } -static void _wpa_supplicant_disassociate(void *wpa_s, int reason_code) -{ - wpa_supplicant_disassociate(wpa_s, reason_code); - /* Schedule a scan to make sure we continue looking for networks */ - wpa_supplicant_req_scan(wpa_s, 5, 0); -} - - static void _wpa_supplicant_deauthenticate(void *wpa_s, int reason_code) { wpa_supplicant_deauthenticate(wpa_s, reason_code); @@ -420,10 +423,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 +470,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 +481,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 +491,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 +509,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, ¶ms); +} + +#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 +652,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 +702,63 @@ 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); +} + + +static void wpa_supplicant_status_cb(void *ctx, const char *status, + const char *parameter) +{ + struct wpa_supplicant *wpa_s = ctx; + + wpas_notify_eap_status(wpa_s, status, parameter); +} + + +static void wpa_supplicant_set_anon_id(void *ctx, const u8 *id, size_t len) +{ + struct wpa_supplicant *wpa_s = ctx; + char *str; + int res; + + wpa_hexdump_ascii(MSG_DEBUG, "EAP method updated anonymous_identity", + id, len); + + if (wpa_s->current_ssid == NULL) + return; + + if (id == NULL) { + if (wpa_config_set(wpa_s->current_ssid, "anonymous_identity", + "NULL", 0) < 0) + return; + } else { + str = os_malloc(len * 2 + 1); + if (str == NULL) + return; + wpa_snprintf_hex(str, len * 2 + 1, id, len); + res = wpa_config_set(wpa_s->current_ssid, "anonymous_identity", + str, 0); + os_free(str); + if (res < 0) + return; + } + + if (wpa_s->conf->update_config) { + res = wpa_config_write(wpa_s->confname, wpa_s->conf); + if (res) { + wpa_printf(MSG_DEBUG, "Failed to update config after " + "anonymous_id update"); + } + } +} #endif /* IEEE8021X_EAPOL */ @@ -602,6 +789,9 @@ 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->status_cb = wpa_supplicant_status_cb; + ctx->set_anon_id = wpa_supplicant_set_anon_id; ctx->cb_ctx = wpa_s; wpa_s->eapol = eapol_sm_init(ctx); if (wpa_s->eapol == NULL) { @@ -616,6 +806,18 @@ int wpa_supplicant_init_eapol(struct wpa_supplicant *wpa_s) } +#ifndef CONFIG_NO_WPA +static void wpa_supplicant_set_rekey_offload(void *ctx, const u8 *kek, + const u8 *kck, + const u8 *replay_ctr) +{ + struct wpa_supplicant *wpa_s = ctx; + + wpa_drv_set_rekey_info(wpa_s, kek, kck, replay_ctr); +} +#endif /* CONFIG_NO_WPA */ + + int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s) { #ifndef CONFIG_NO_WPA @@ -631,7 +833,6 @@ int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s) ctx->set_state = _wpa_supplicant_set_state; ctx->get_state = _wpa_supplicant_get_state; ctx->deauthenticate = _wpa_supplicant_deauthenticate; - ctx->disassociate = _wpa_supplicant_disassociate; ctx->set_key = wpa_supplicant_set_key; ctx->get_network_ctx = wpa_supplicant_get_network_ctx; ctx->get_bssid = wpa_supplicant_get_bssid; @@ -651,6 +852,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 +882,8 @@ 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 < 0 ? + wpa_s->conf->okc : ssid->proactive_key_caching; conf.eap_workaround = ssid->eap_workaround; conf.eap_conf_ctx = &ssid->eap; #endif /* IEEE8021X_EAPOL */ diff --git a/contrib/wpa/wpa_supplicant/wpas_glue.h b/contrib/wpa/wpa_supplicant/wpas_glue.h index b571e4d..9808c22 100644 --- a/contrib/wpa/wpa_supplicant/wpas_glue.h +++ b/contrib/wpa/wpa_supplicant/wpas_glue.h @@ -2,22 +2,24 @@ * WPA Supplicant - Glue code to setup EAPOL and RSN modules * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef 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 */ diff --git a/contrib/wpa/wpa_supplicant/wps_supplicant.c b/contrib/wpa/wpa_supplicant/wps_supplicant.c index ba94d33..8ab5f64 100644 --- a/contrib/wpa/wpa_supplicant/wps_supplicant.c +++ b/contrib/wpa/wpa_supplicant/wps_supplicant.c @@ -1,15 +1,9 @@ /* * wpa_supplicant / WPS integration - * Copyright (c) 2008-2010, Jouni Malinen <j@w1.fi> + * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -17,6 +11,7 @@ #include "common.h" #include "eloop.h" #include "uuid.h" +#include "crypto/random.h" #include "crypto/dh_group5.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" @@ -24,7 +19,9 @@ #include "common/wpa_ctrl.h" #include "eap_common/eap_wsc_common.h" #include "eap_peer/eap.h" +#include "eapol_supp/eapol_supp_sm.h" #include "rsn_supp/wpa.h" +#include "wps/wps_attr_parse.h" #include "config.h" #include "wpa_supplicant_i.h" #include "driver_i.h" @@ -32,15 +29,29 @@ #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); +static void wpas_wps_clear_ap_info(struct wpa_supplicant *wpa_s) +{ + os_free(wpa_s->wps_ap); + wpa_s->wps_ap = NULL; + wpa_s->num_wps_ap = 0; + wpa_s->wps_ap_iter = 0; +} + + int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s) { if (!wpa_s->wps_success && @@ -64,16 +75,28 @@ int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s) return 1; } + wpas_wps_clear_ap_info(wpa_s); eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL); + if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS && !wpa_s->wps_success) + wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_FAIL); if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS && wpa_s->current_ssid && !(wpa_s->current_ssid->key_mgmt & WPA_KEY_MGMT_WPS)) { + int disabled = wpa_s->current_ssid->disabled; + unsigned int freq = wpa_s->assoc_freq; 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->normal_scans = 0; wpa_s->reassociate = 1; wpa_supplicant_req_scan(wpa_s, 0, 0); return 1; @@ -113,6 +136,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 +203,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) { @@ -228,9 +256,25 @@ static int wpa_supplicant_wps_cred(void *ctx, return 0; } + if (auth_type == WPS_AUTH_WPAPSK || auth_type == WPS_AUTH_WPA2PSK) { + if (cred->key_len < 8 || cred->key_len > 2 * PMK_LEN) { + wpa_printf(MSG_ERROR, "WPS: Reject PSK credential with " + "invalid Network Key length %lu", + (unsigned long) cred->key_len); + return -1; + } + } + if (ssid && (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) { wpa_printf(MSG_DEBUG, "WPS: Replace WPS network block based " "on the received credential"); +#ifdef CONFIG_WPS_REG_DISABLE_OPEN + if (ssid->eap.identity && + ssid->eap.identity_len == WSC_ID_REGISTRAR_LEN && + os_memcmp(ssid->eap.identity, WSC_ID_REGISTRAR, + WSC_ID_REGISTRAR_LEN) == 0) + registrar = 1; +#endif /* CONFIG_WPS_REG_DISABLE_OPEN */ os_free(ssid->eap.identity); ssid->eap.identity = NULL; ssid->eap.identity_len = 0; @@ -238,6 +282,13 @@ 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; + ssid->bssid_set = 0; + } + ssid->disabled_until.sec = 0; + ssid->disabled_until.usec = 0; + ssid->auth_failures = 0; } else { wpa_printf(MSG_DEBUG, "WPS: Create a new network based on the " "received credential"); @@ -304,6 +355,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; @@ -315,16 +376,6 @@ static int wpa_supplicant_wps_cred(void *ctx, ssid->key_mgmt = WPA_KEY_MGMT_PSK; ssid->proto = WPA_PROTO_WPA; break; - case WPS_AUTH_WPA: - ssid->auth_alg = WPA_AUTH_ALG_OPEN; - ssid->key_mgmt = WPA_KEY_MGMT_IEEE8021X; - ssid->proto = WPA_PROTO_WPA; - break; - case WPS_AUTH_WPA2: - ssid->auth_alg = WPA_AUTH_ALG_OPEN; - ssid->key_mgmt = WPA_KEY_MGMT_IEEE8021X; - ssid->proto = WPA_PROTO_RSN; - break; case WPS_AUTH_WPA2PSK: ssid->auth_alg = WPA_AUTH_ALG_OPEN; ssid->key_mgmt = WPA_KEY_MGMT_PSK; @@ -341,6 +392,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 +401,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", @@ -359,6 +412,9 @@ static int wpa_supplicant_wps_cred(void *ctx, wpas_wps_security_workaround(wpa_s, ssid, cred); + if (cred->ap_channel) + wpa_s->wps_ap_channel = cred->ap_channel; + #ifndef CONFIG_NO_CONFIG_WRITE if (wpa_s->conf->update_config && wpa_config_write(wpa_s->confname, wpa_s->conf)) { @@ -367,10 +423,26 @@ static int wpa_supplicant_wps_cred(void *ctx, } #endif /* CONFIG_NO_CONFIG_WRITE */ + /* + * Optimize the post-WPS scan based on the channel used during + * the provisioning in case EAP-Failure is not received. + */ + wpa_s->after_wps = 5; + wpa_s->wps_freq = wpa_s->assoc_freq; + return 0; } +#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 +450,97 @@ 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 */ +} + + +static void wpas_wps_reenable_networks_cb(void *eloop_ctx, void *timeout_ctx); + +static void wpas_wps_reenable_networks(struct wpa_supplicant *wpa_s) +{ + struct wpa_ssid *ssid; + int changed = 0; + + eloop_cancel_timeout(wpas_wps_reenable_networks_cb, wpa_s, NULL); + + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { + if (ssid->disabled_for_connect && ssid->disabled) { + ssid->disabled_for_connect = 0; + ssid->disabled = 0; + wpas_notify_network_enabled_changed(wpa_s, ssid); + changed++; + } + } + + if (changed) { +#ifndef CONFIG_NO_CONFIG_WRITE + if (wpa_s->conf->update_config && + wpa_config_write(wpa_s->confname, wpa_s->conf)) { + wpa_printf(MSG_DEBUG, "WPS: Failed to update " + "configuration"); + } +#endif /* CONFIG_NO_CONFIG_WRITE */ + } +} + + +static void wpas_wps_reenable_networks_cb(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + /* Enable the networks disabled during wpas_wps_reassoc */ + wpas_wps_reenable_networks(wpa_s); } @@ -395,6 +549,18 @@ 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); + + /* + * Enable the networks disabled during wpas_wps_reassoc after 10 + * seconds. The 10 seconds timer is to allow the data connection to be + * formed before allowing other networks to be selected. + */ + eloop_register_timeout(10, 0, wpas_wps_reenable_networks_cb, wpa_s, + NULL); + +#ifdef CONFIG_P2P + wpas_p2p_wps_success(wpa_s, wpa_s->bssid, 0); +#endif /* CONFIG_P2P */ } @@ -468,6 +634,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 +702,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 +725,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 +752,12 @@ 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; + + /* Enable the networks disabled during wpas_wps_reassoc */ + wpas_wps_reenable_networks(wpa_s); eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL); @@ -538,18 +776,25 @@ 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); } } + + wpas_wps_clear_ap_info(wpa_s); } static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; - 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 +809,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 +821,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 +858,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; @@ -611,45 +866,90 @@ static struct wpa_ssid * wpas_wps_add_network(struct wpa_supplicant *wpa_s, static void wpas_wps_reassoc(struct wpa_supplicant *wpa_s, - struct wpa_ssid *selected) + struct wpa_ssid *selected, const u8 *bssid) { struct wpa_ssid *ssid; + struct wpa_bss *bss; + + wpa_s->known_wps_freq = 0; + if (bssid) { + bss = wpa_bss_get_bssid(wpa_s, bssid); + if (bss && bss->freq > 0) { + wpa_s->known_wps_freq = 1; + wpa_s->wps_freq = bss->freq; + } + } + + 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); + ssid->disabled_for_connect = 0; + /* + * In case the network object corresponds to a persistent group + * then do not send out network disabled signal. In addition, + * do not change disabled status of persistent network objects + * from 2 to 1 should we connect to another network. + */ + if (was_disabled != 2) { + ssid->disabled = ssid != selected; + if (was_disabled != ssid->disabled) { + if (ssid->disabled) + ssid->disabled_for_connect = 1; + wpas_notify_network_enabled_changed(wpa_s, + ssid); + } + } ssid = ssid->next; } wpa_s->disconnected = 0; wpa_s->reassociate = 1; wpa_s->scan_runs = 0; + wpa_s->normal_scans = 0; wpa_s->wps_success = 0; wpa_s->blacklist_cleared = 0; wpa_supplicant_req_scan(wpa_s, 0, 0); } -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); + wpas_wps_reassoc(wpa_s, ssid, bssid); return 0; } 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,68 +959,67 @@ 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); + wpa_s->wps_ap_iter = 1; + wpas_wps_reassoc(wpa_s, ssid, bssid); return rpin; } -#ifdef CONFIG_WPS_OOB -int wpas_wps_start_oob(struct wpa_supplicant *wpa_s, char *device_type, - char *path, char *method, char *name) +/* Cancel the wps pbc/pin requests */ +int wpas_wps_cancel(struct wpa_supplicant *wpa_s) { - struct wps_context *wps = wpa_s->wps; - struct oob_device_data *oob_dev; - - oob_dev = wps_get_oob_device(device_type); - if (oob_dev == NULL) - return -1; - oob_dev->device_path = path; - oob_dev->device_name = name; - wps->oob_conf.oob_method = wps_get_oob_method(method); - - if (wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_E) { - /* - * Use pre-configured DH keys in order to be able to write the - * key hash into the OOB file. - */ - wpabuf_free(wps->dh_pubkey); - wpabuf_free(wps->dh_privkey); - wps->dh_privkey = NULL; - wps->dh_pubkey = NULL; - dh5_free(wps->dh_ctx); - wps->dh_ctx = dh5_init(&wps->dh_privkey, &wps->dh_pubkey); - wps->dh_pubkey = wpabuf_zeropad(wps->dh_pubkey, 192); - if (wps->dh_ctx == NULL || wps->dh_pubkey == NULL) { - wpa_printf(MSG_ERROR, "WPS: Failed to initialize " - "Diffie-Hellman handshake"); - return -1; - } +#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 (wps->oob_conf.oob_method == OOB_METHOD_CRED) + if (wpa_s->wpa_state == WPA_SCANNING || + wpa_s->wpa_state == WPA_DISCONNECTED) { + wpa_printf(MSG_DEBUG, "WPS: Cancel operation - cancel scan"); + wpa_supplicant_cancel_scan(wpa_s); wpas_clear_wps(wpa_s); - - if (wps_process_oob(wps, oob_dev, 0) < 0) - return -1; - - 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) - return -1; + } 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); + } else { + wpas_wps_reenable_networks(wpa_s); + wpas_wps_clear_ap_info(wpa_s); + } return 0; } -#endif /* CONFIG_WPS_OOB */ int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid, @@ -737,6 +1036,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,9 +1056,11 @@ 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); + wpas_wps_reassoc(wpa_s, ssid, bssid); return 0; } @@ -805,16 +1107,90 @@ 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) { + if (wps != wpa_s->global->ifaces->wps) + 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); + } +} + + +static void wpas_wps_set_vendor_ext_m1(struct wpa_supplicant *wpa_s, + struct wps_context *wps) +{ + wpabuf_free(wps->dev.vendor_ext_m1); + wps->dev.vendor_ext_m1 = NULL; + + if (wpa_s->conf->wps_vendor_ext_m1) { + wps->dev.vendor_ext_m1 = + wpabuf_dup(wpa_s->conf->wps_vendor_ext_m1); + if (!wps->dev.vendor_ext_m1) { + wpa_printf(MSG_ERROR, "WPS: Cannot " + "allocate memory for vendor_ext_m1"); + } + } +} + + int wpas_wps_init(struct wpa_supplicant *wpa_s) { struct wps_context *wps; struct wps_registrar_config rcfg; + struct hostapd_hw_modes *modes; + u16 m; wps = os_zalloc(sizeof(*wps)); if (wps == NULL) @@ -831,22 +1207,44 @@ 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); + + wpas_wps_set_vendor_ext_m1(wpa_s, wps); + 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; @@ -873,6 +1271,8 @@ int wpas_wps_init(struct wpa_supplicant *wpa_s) void wpas_wps_deinit(struct wpa_supplicant *wpa_s) { eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL); + eloop_cancel_timeout(wpas_wps_reenable_networks_cb, wpa_s, NULL); + wpas_wps_clear_ap_info(wpa_s); if (wpa_s->wps == NULL) return; @@ -885,8 +1285,7 @@ void wpas_wps_deinit(struct wpa_supplicant *wpa_s) wps_registrar_deinit(wpa_s->wps->registrar); wpabuf_free(wpa_s->wps->dh_pubkey); wpabuf_free(wpa_s->wps->dh_privkey); - wpabuf_free(wpa_s->wps->oob_conf.pubkey_hash); - wpabuf_free(wpa_s->wps->oob_conf.dev_password); + wpabuf_free(wpa_s->wps->dev.vendor_ext_m1); os_free(wpa_s->wps->network_key); os_free(wpa_s->wps); wpa_s->wps = NULL; @@ -894,14 +1293,14 @@ void wpas_wps_deinit(struct wpa_supplicant *wpa_s) int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s, - struct wpa_ssid *ssid, struct wpa_scan_res *bss) + struct wpa_ssid *ssid, struct wpa_bss *bss) { struct wpabuf *wps_ie; if (!(ssid->key_mgmt & WPA_KEY_MGMT_WPS)) return -1; - wps_ie = wpa_scan_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); + wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); if (eap_is_wps_pbc_enrollee(&ssid->eap)) { if (!wps_ie) { wpa_printf(MSG_DEBUG, " skip - non-WPS AP"); @@ -929,12 +1328,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 +1344,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; @@ -962,21 +1362,21 @@ int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s, int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, - struct wpa_scan_res *bss) + struct wpa_bss *bss) { struct wpabuf *wps_ie = NULL; int ret = 0; if (eap_is_wps_pbc_enrollee(&ssid->eap)) { - wps_ie = wpa_scan_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); + wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); if (wps_ie && wps_is_selected_pbc_registrar(wps_ie)) { /* allow wildcard SSID for WPS PBC */ ret = 1; } } else if (eap_is_wps_pin_enrollee(&ssid->eap)) { - wps_ie = wpa_scan_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); + wps_ie = wpa_bss_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 +1389,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_bss_get_vendor_ie_multi_beacon( + bss, WPS_IE_VENDOR_TYPE); + if (bcn_wps == NULL) { + wpa_printf(MSG_DEBUG, "WPS: Mandatory WPS IE " + "missing from AP Beacon"); + ret = 0; + } else { + if (wps_validate_beacon(wps_ie) < 0) + ret = 0; + wpabuf_free(bcn_wps); + } + } + } +#endif /* CONFIG_WPS_STRICT */ + wpabuf_free(wps_ie); return ret; @@ -1006,12 +1428,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 +1455,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 +1485,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 +1496,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 +1546,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 +1574,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 +1584,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 +1612,135 @@ 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); +} + + +#ifdef CONFIG_WPS_NFC +struct wpabuf * wpas_wps_er_nfc_config_token(struct wpa_supplicant *wpa_s, + int ndef, const char *uuid) +{ + struct wpabuf *ret; + u8 u[UUID_LEN]; + + if (!wpa_s->wps_er) + return NULL; + + if (uuid_str2bin(uuid, u)) + return NULL; + + ret = wps_er_nfc_config_token(wpa_s->wps_er, u); + if (ndef && ret) { + struct wpabuf *tmp; + tmp = ndef_build_wifi(ret); + wpabuf_free(ret); + if (tmp == NULL) + return NULL; + ret = tmp; + } + + return ret; +} +#endif /* CONFIG_WPS_NFC */ + + +static int callbacks_pending = 0; + static void wpas_wps_terminate_cb(void *ctx) { wpa_printf(MSG_DEBUG, "WPS ER: Terminated"); - eloop_terminate(); + if (--callbacks_pending <= 0) + eloop_terminate(); } #endif /* CONFIG_WPS_ER */ @@ -1176,6 +1749,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 +1757,386 @@ 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); + wps->dev.config_methods = wps->config_methods; + + if (wpa_s->conf->changed_parameters & CFG_CHANGED_DEVICE_TYPE) + os_memcpy(wps->dev.pri_dev_type, wpa_s->conf->device_type, + WPS_DEV_TYPE_LEN); + + if (wpa_s->conf->changed_parameters & CFG_CHANGED_SEC_DEVICE_TYPE) { + wps->dev.num_sec_dev_types = wpa_s->conf->num_sec_device_types; + os_memcpy(wps->dev.sec_dev_type, wpa_s->conf->sec_device_type, + wps->dev.num_sec_dev_types * WPS_DEV_TYPE_LEN); + } + + if (wpa_s->conf->changed_parameters & CFG_CHANGED_VENDOR_EXTENSION) + wpas_wps_set_vendor_ext_m1(wpa_s, wps); + + if (wpa_s->conf->changed_parameters & CFG_CHANGED_OS_VERSION) + wps->dev.os_version = WPA_GET_BE32(wpa_s->conf->os_version); + + if (wpa_s->conf->changed_parameters & CFG_CHANGED_UUID) + wpas_wps_set_uuid(wpa_s, wps); + + if (wpa_s->conf->changed_parameters & + (CFG_CHANGED_DEVICE_NAME | CFG_CHANGED_WPS_STRING)) { + /* Update pointers to make sure they refer current values */ + wps->dev.device_name = wpa_s->conf->device_name; + wps->dev.manufacturer = wpa_s->conf->manufacturer; + wps->dev.model_name = wpa_s->conf->model_name; + wps->dev.model_number = wpa_s->conf->model_number; + wps->dev.serial_number = wpa_s->conf->serial_number; + } +} + + +#ifdef CONFIG_WPS_NFC + +struct wpabuf * wpas_wps_nfc_token(struct wpa_supplicant *wpa_s, int ndef) +{ + return wps_nfc_token_gen(ndef, &wpa_s->conf->wps_nfc_dev_pw_id, + &wpa_s->conf->wps_nfc_dh_pubkey, + &wpa_s->conf->wps_nfc_dh_privkey, + &wpa_s->conf->wps_nfc_dev_pw); +} + + +int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *bssid) +{ + struct wps_context *wps = wpa_s->wps; + char pw[32 * 2 + 1]; + + if (wpa_s->conf->wps_nfc_dh_pubkey == NULL || + wpa_s->conf->wps_nfc_dh_privkey == NULL || + wpa_s->conf->wps_nfc_dev_pw == NULL) + return -1; + + dh5_free(wps->dh_ctx); + wpabuf_free(wps->dh_pubkey); + wpabuf_free(wps->dh_privkey); + wps->dh_privkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_privkey); + wps->dh_pubkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_pubkey); + if (wps->dh_privkey == NULL || wps->dh_pubkey == NULL) { + wps->dh_ctx = NULL; + wpabuf_free(wps->dh_pubkey); + wps->dh_pubkey = NULL; + wpabuf_free(wps->dh_privkey); + wps->dh_privkey = NULL; + return -1; + } + wps->dh_ctx = dh5_init_fixed(wps->dh_privkey, wps->dh_pubkey); + if (wps->dh_ctx == NULL) + return -1; + + wpa_snprintf_hex_uppercase(pw, sizeof(pw), + wpabuf_head(wpa_s->conf->wps_nfc_dev_pw), + wpabuf_len(wpa_s->conf->wps_nfc_dev_pw)); + return wpas_wps_start_pin(wpa_s, bssid, pw, 0, + wpa_s->conf->wps_nfc_dev_pw_id); +} + + +static int wpas_wps_use_cred(struct wpa_supplicant *wpa_s, + struct wps_parse_attr *attr) +{ + wpa_s->wps_ap_channel = 0; + + if (wps_oob_use_cred(wpa_s->wps, attr) < 0) + return -1; + + if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) + return 0; + + wpa_printf(MSG_DEBUG, "WPS: Request reconnection with new network " + "based on the received credential added"); + wpa_s->normal_scans = 0; + wpa_supplicant_reinit_autoscan(wpa_s); + if (wpa_s->wps_ap_channel) { + u16 chan = wpa_s->wps_ap_channel; + int freq = 0; + + if (chan >= 1 && chan <= 13) + freq = 2407 + 5 * chan; + else if (chan == 14) + freq = 2484; + else if (chan >= 30) + freq = 5000 + 5 * chan; + + if (freq) { + wpa_printf(MSG_DEBUG, "WPS: Credential indicated " + "AP channel %u -> %u MHz", chan, freq); + wpa_s->after_wps = 5; + wpa_s->wps_freq = freq; + } + } + wpa_s->disconnected = 0; + wpa_s->reassociate = 1; + wpa_supplicant_req_scan(wpa_s, 0, 0); + + return 0; +} + + +#ifdef CONFIG_WPS_ER +static int wpas_wps_add_nfc_password_token(struct wpa_supplicant *wpa_s, + struct wps_parse_attr *attr) +{ + return wps_registrar_add_nfc_password_token( + wpa_s->wps->registrar, attr->oob_dev_password, + attr->oob_dev_password_len); +} +#endif /* CONFIG_WPS_ER */ + + +static int wpas_wps_nfc_tag_process(struct wpa_supplicant *wpa_s, + const struct wpabuf *wps) +{ + struct wps_parse_attr attr; + + wpa_hexdump_buf(MSG_DEBUG, "WPS: Received NFC tag payload", wps); + + if (wps_parse_msg(wps, &attr)) { + wpa_printf(MSG_DEBUG, "WPS: Ignore invalid data from NFC tag"); + return -1; + } + + if (attr.num_cred) + return wpas_wps_use_cred(wpa_s, &attr); + +#ifdef CONFIG_WPS_ER + if (attr.oob_dev_password) + return wpas_wps_add_nfc_password_token(wpa_s, &attr); +#endif /* CONFIG_WPS_ER */ + + wpa_printf(MSG_DEBUG, "WPS: Ignore unrecognized NFC tag"); + return -1; +} + + +int wpas_wps_nfc_tag_read(struct wpa_supplicant *wpa_s, + const struct wpabuf *data) +{ + const struct wpabuf *wps = data; + struct wpabuf *tmp = NULL; + int ret; + + if (wpabuf_len(data) < 4) + return -1; + + if (*wpabuf_head_u8(data) != 0x10) { + /* Assume this contains full NDEF record */ + tmp = ndef_parse_wifi(data); + if (tmp == NULL) { + wpa_printf(MSG_DEBUG, "WPS: Could not parse NDEF"); + return -1; + } + wps = tmp; + } + + ret = wpas_wps_nfc_tag_process(wpa_s, wps); + wpabuf_free(tmp); + return ret; +} + + +struct wpabuf * wpas_wps_nfc_handover_req(struct wpa_supplicant *wpa_s) +{ + return ndef_build_wifi_hr(); +} + + +struct wpabuf * wpas_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s) +{ + return NULL; +} + + +int wpas_wps_nfc_rx_handover_req(struct wpa_supplicant *wpa_s, + const struct wpabuf *data) +{ + /* TODO */ + return -1; +} + + +int wpas_wps_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s, + const struct wpabuf *data) +{ + struct wpabuf *wps; + int ret; + + wps = ndef_parse_wifi(data); + if (wps == NULL) + return -1; + wpa_printf(MSG_DEBUG, "WPS: Received application/vnd.wfa.wsc " + "payload from NFC connection handover"); + wpa_hexdump_buf_key(MSG_DEBUG, "WPS: NFC payload", wps); + ret = wpas_wps_nfc_tag_process(wpa_s, wps); + wpabuf_free(wps); + + return ret; +} + +#endif /* CONFIG_WPS_NFC */ + + +extern int wpa_debug_level; + +static void wpas_wps_dump_ap_info(struct wpa_supplicant *wpa_s) +{ + size_t i; + struct os_time now; + + if (wpa_debug_level > MSG_DEBUG) + return; + + if (wpa_s->wps_ap == NULL) + return; + + os_get_time(&now); + + for (i = 0; i < wpa_s->num_wps_ap; i++) { + struct wps_ap_info *ap = &wpa_s->wps_ap[i]; + struct wpa_blacklist *e = wpa_blacklist_get(wpa_s, ap->bssid); + + wpa_printf(MSG_DEBUG, "WPS: AP[%d] " MACSTR " type=%d " + "tries=%d last_attempt=%d sec ago blacklist=%d", + (int) i, MAC2STR(ap->bssid), ap->type, ap->tries, + ap->last_attempt.sec > 0 ? + (int) now.sec - (int) ap->last_attempt.sec : -1, + e ? e->count : 0); + } +} + + +static struct wps_ap_info * wpas_wps_get_ap_info(struct wpa_supplicant *wpa_s, + const u8 *bssid) +{ + size_t i; + + if (wpa_s->wps_ap == NULL) + return NULL; + + for (i = 0; i < wpa_s->num_wps_ap; i++) { + struct wps_ap_info *ap = &wpa_s->wps_ap[i]; + if (os_memcmp(ap->bssid, bssid, ETH_ALEN) == 0) + return ap; + } + + return NULL; +} + + +static void wpas_wps_update_ap_info_bss(struct wpa_supplicant *wpa_s, + struct wpa_scan_res *res) +{ + struct wpabuf *wps; + enum wps_ap_info_type type; + struct wps_ap_info *ap; + int r; + + if (wpa_scan_get_vendor_ie(res, WPS_IE_VENDOR_TYPE) == NULL) + return; + + wps = wpa_scan_get_vendor_ie_multi(res, WPS_IE_VENDOR_TYPE); + if (wps == NULL) + return; + + r = wps_is_addr_authorized(wps, wpa_s->own_addr, 1); + if (r == 2) + type = WPS_AP_SEL_REG_OUR; + else if (r == 1) + type = WPS_AP_SEL_REG; + else + type = WPS_AP_NOT_SEL_REG; + + wpabuf_free(wps); + + ap = wpas_wps_get_ap_info(wpa_s, res->bssid); + if (ap) { + if (ap->type != type) { + wpa_printf(MSG_DEBUG, "WPS: AP " MACSTR + " changed type %d -> %d", + MAC2STR(res->bssid), ap->type, type); + ap->type = type; + if (type != WPS_AP_NOT_SEL_REG) + wpa_blacklist_del(wpa_s, ap->bssid); + } + return; + } + + ap = os_realloc_array(wpa_s->wps_ap, wpa_s->num_wps_ap + 1, + sizeof(struct wps_ap_info)); + if (ap == NULL) + return; + + wpa_s->wps_ap = ap; + ap = &wpa_s->wps_ap[wpa_s->num_wps_ap]; + wpa_s->num_wps_ap++; + + os_memset(ap, 0, sizeof(*ap)); + os_memcpy(ap->bssid, res->bssid, ETH_ALEN); + ap->type = type; + wpa_printf(MSG_DEBUG, "WPS: AP " MACSTR " type %d added", + MAC2STR(ap->bssid), ap->type); +} + + +void wpas_wps_update_ap_info(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res) +{ + size_t i; + + for (i = 0; i < scan_res->num; i++) + wpas_wps_update_ap_info_bss(wpa_s, scan_res->res[i]); + + wpas_wps_dump_ap_info(wpa_s); +} + + +void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *bssid) +{ + struct wps_ap_info *ap; + if (!wpa_s->wps_ap_iter) + return; + ap = wpas_wps_get_ap_info(wpa_s, bssid); + if (ap == NULL) + return; + ap->tries++; + os_get_time(&ap->last_attempt); +} diff --git a/contrib/wpa/wpa_supplicant/wps_supplicant.h b/contrib/wpa/wpa_supplicant/wps_supplicant.h index ba2fb16..dd0dc60 100644 --- a/contrib/wpa/wpa_supplicant/wps_supplicant.h +++ b/contrib/wpa/wpa_supplicant/wps_supplicant.h @@ -1,21 +1,15 @@ /* * wpa_supplicant / WPS integration - * Copyright (c) 2008-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPS_SUPPLICANT_H #define WPS_SUPPLICANT_H -struct wpa_scan_res; +struct wpa_scan_results; #ifdef CONFIG_WPS @@ -35,31 +29,52 @@ 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); -int wpas_wps_start_oob(struct wpa_supplicant *wpa_s, char *device_type, - char *path, char *method, char *name); + const char *pin, int p2p_group, u16 dev_pw_id); +int wpas_wps_cancel(struct wpa_supplicant *wpa_s); int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid, const char *pin, struct wps_new_ap_settings *settings); int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s, - struct wpa_ssid *ssid, struct wpa_scan_res *bss); + struct wpa_ssid *ssid, struct wpa_bss *bss); int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s, - struct wpa_ssid *ssid, struct wpa_scan_res *bss); + struct wpa_ssid *ssid, struct wpa_bss *bss); int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s, struct wpa_bss *selected, struct wpa_ssid *ssid); void wpas_wps_notify_scan_results(struct wpa_supplicant *wpa_s); int wpas_wps_searching(struct wpa_supplicant *wpa_s); int wpas_wps_scan_result_text(const u8 *ies, size_t ies_len, char *pos, char *end); -int wpas_wps_er_start(struct wpa_supplicant *wpa_s); +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); +struct wpabuf * wpas_wps_er_nfc_config_token(struct wpa_supplicant *wpa_s, + int ndef, const char *uuid); int wpas_wps_terminate_pending(struct wpa_supplicant *wpa_s); +int wpas_wps_in_progress(struct wpa_supplicant *wpa_s); +void wpas_wps_update_config(struct wpa_supplicant *wpa_s); +struct wpabuf * wpas_wps_nfc_token(struct wpa_supplicant *wpa_s, int ndef); +int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *bssid); +int wpas_wps_nfc_tag_read(struct wpa_supplicant *wpa_s, + const struct wpabuf *data); +struct wpabuf * wpas_wps_nfc_handover_req(struct wpa_supplicant *wpa_s); +struct wpabuf * wpas_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s); +int wpas_wps_nfc_rx_handover_req(struct wpa_supplicant *wpa_s, + const struct wpabuf *data); +int wpas_wps_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s, + const struct wpabuf *data); +void wpas_wps_update_ap_info(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res); +void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *bssid); #else /* CONFIG_WPS */ @@ -84,14 +99,14 @@ static inline u8 wpas_wps_get_req_type(struct wpa_ssid *ssid) static inline int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, - struct wpa_scan_res *bss) + struct wpa_bss *bss) { return -1; } static inline int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, - struct wpa_scan_res *bss) + struct wpa_bss *bss) { return 0; } @@ -112,6 +127,16 @@ static inline int wpas_wps_searching(struct wpa_supplicant *wpa_s) return 0; } +static inline void wpas_wps_update_ap_info(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res) +{ +} + +static inline void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s, + const u8 *bssid) +{ +} + #endif /* CONFIG_WPS */ #endif /* WPS_SUPPLICANT_H */ |