diff options
Diffstat (limited to 'wpa_supplicant')
168 files changed, 27517 insertions, 3411 deletions
diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk new file mode 100644 index 0000000000000..a8d6a7f944e9f --- /dev/null +++ b/wpa_supplicant/Android.mk @@ -0,0 +1,1680 @@ +# +# Copyright (C) 2008 The Android Open Source Project +# +# This software may be distributed under the terms of the BSD license. +# See README for more details. +# + +LOCAL_PATH := $(call my-dir) +PKG_CONFIG ?= pkg-config + +ifneq ($(BOARD_WPA_SUPPLICANT_DRIVER),) + CONFIG_DRIVER_$(BOARD_WPA_SUPPLICANT_DRIVER) := y +endif + +include $(LOCAL_PATH)/android.config + +# To ignore possible wrong network configurations +L_CFLAGS = -DWPA_IGNORE_CONFIG_ERRORS + +L_CFLAGS += -DVERSION_STR_POSTFIX=\"-$(PLATFORM_VERSION)\" + +# Set Android log name +L_CFLAGS += -DANDROID_LOG_NAME=\"wpa_supplicant\" + +# Disable unused parameter warnings +L_CFLAGS += -Wno-unused-parameter + +# Set Android extended P2P functionality +L_CFLAGS += -DANDROID_P2P + +ifeq ($(BOARD_WPA_SUPPLICANT_PRIVATE_LIB),) +L_CFLAGS += -DANDROID_LIB_STUB +endif + +# Disable roaming in wpa_supplicant +ifdef CONFIG_NO_ROAMING +L_CFLAGS += -DCONFIG_NO_ROAMING +endif + +# Use Android specific directory for control interface sockets +L_CFLAGS += -DCONFIG_CTRL_IFACE_CLIENT_DIR=\"/data/misc/wifi/sockets\" +L_CFLAGS += -DCONFIG_CTRL_IFACE_DIR=\"/data/misc/wifi/sockets\" + +# Use Android specific directory for wpa_cli command completion history +L_CFLAGS += -DCONFIG_WPA_CLI_HISTORY_DIR=\"/data/misc/wifi\" + +# To force sizeof(enum) = 4 +ifeq ($(TARGET_ARCH),arm) +L_CFLAGS += -mabi=aapcs-linux +endif + +# C++ flags for binder interface +L_CPPFLAGS := -std=c++11 -Wall -Werror +# TODO: Remove these allowed warnings later. +L_CPPFLAGS += -Wno-unused-variable -Wno-unused-parameter +L_CPPFLAGS += -Wno-unused-private-field + +INCLUDES = $(LOCAL_PATH) +INCLUDES += $(LOCAL_PATH)/src +INCLUDES += $(LOCAL_PATH)/src/common +# INCLUDES += $(LOCAL_PATH)/src/crypto # To force proper includes +INCLUDES += $(LOCAL_PATH)/src/drivers +INCLUDES += $(LOCAL_PATH)/src/eap_common +INCLUDES += $(LOCAL_PATH)/src/eapol_supp +INCLUDES += $(LOCAL_PATH)/src/eap_peer +INCLUDES += $(LOCAL_PATH)/src/eap_server +INCLUDES += $(LOCAL_PATH)/src/hlr_auc_gw +INCLUDES += $(LOCAL_PATH)/src/l2_packet +INCLUDES += $(LOCAL_PATH)/src/radius +INCLUDES += $(LOCAL_PATH)/src/rsn_supp +INCLUDES += $(LOCAL_PATH)/src/tls +INCLUDES += $(LOCAL_PATH)/src/utils +INCLUDES += $(LOCAL_PATH)/src/wps +INCLUDES += system/security/keystore/include +ifdef CONFIG_DRIVER_NL80211 +ifneq ($(wildcard external/libnl),) +INCLUDES += external/libnl/include +else +INCLUDES += external/libnl-headers +endif +endif + +ifdef CONFIG_FIPS +CONFIG_NO_RANDOM_POOL= +CONFIG_OPENSSL_CMAC=y +endif + +OBJS = config.c +OBJS += notify.c +OBJS += bss.c +OBJS += eap_register.c +OBJS += src/utils/common.c +OBJS += src/utils/wpa_debug.c +OBJS += src/utils/wpabuf.c +OBJS += wmm_ac.c +OBJS_p = wpa_passphrase.c +OBJS_p += src/utils/common.c +OBJS_p += src/utils/wpa_debug.c +OBJS_p += src/utils/wpabuf.c +OBJS_c = wpa_cli.c src/common/wpa_ctrl.c +OBJS_c += src/utils/wpa_debug.c +OBJS_c += src/utils/common.c +OBJS_c += src/common/cli.c +OBJS_d = +OBJS_priv = + +ifndef CONFIG_OS +ifdef CONFIG_NATIVE_WINDOWS +CONFIG_OS=win32 +else +CONFIG_OS=unix +endif +endif + +ifeq ($(CONFIG_OS), internal) +L_CFLAGS += -DOS_NO_C_LIB_DEFINES +endif + +OBJS += src/utils/os_$(CONFIG_OS).c +OBJS_p += src/utils/os_$(CONFIG_OS).c +OBJS_c += src/utils/os_$(CONFIG_OS).c + +ifdef CONFIG_WPA_TRACE +L_CFLAGS += -DWPA_TRACE +OBJS += src/utils/trace.c +OBJS_p += src/utils/trace.c +OBJS_c += src/utils/trace.c +LDFLAGS += -rdynamic +L_CFLAGS += -funwind-tables +ifdef CONFIG_WPA_TRACE_BFD +L_CFLAGS += -DWPA_TRACE_BFD +LIBS += -lbfd +LIBS_p += -lbfd +LIBS_c += -lbfd +endif +endif + +ifndef CONFIG_ELOOP +CONFIG_ELOOP=eloop +endif +OBJS += src/utils/$(CONFIG_ELOOP).c +OBJS_c += src/utils/$(CONFIG_ELOOP).c + +ifdef CONFIG_ELOOP_POLL +L_CFLAGS += -DCONFIG_ELOOP_POLL +endif + +ifdef CONFIG_ELOOP_EPOLL +L_CFLAGS += -DCONFIG_ELOOP_EPOLL +endif + +ifdef CONFIG_EAPOL_TEST +L_CFLAGS += -Werror -DEAPOL_TEST +endif + +ifdef CONFIG_HT_OVERRIDES +L_CFLAGS += -DCONFIG_HT_OVERRIDES +endif + +ifdef CONFIG_VHT_OVERRIDES +L_CFLAGS += -DCONFIG_VHT_OVERRIDES +endif + +ifndef CONFIG_BACKEND +CONFIG_BACKEND=file +endif + +ifeq ($(CONFIG_BACKEND), file) +OBJS += config_file.c +ifndef CONFIG_NO_CONFIG_BLOBS +NEED_BASE64=y +endif +L_CFLAGS += -DCONFIG_BACKEND_FILE +endif + +ifeq ($(CONFIG_BACKEND), winreg) +OBJS += config_winreg.c +endif + +ifeq ($(CONFIG_BACKEND), none) +OBJS += config_none.c +endif + +ifdef CONFIG_NO_CONFIG_WRITE +L_CFLAGS += -DCONFIG_NO_CONFIG_WRITE +endif + +ifdef CONFIG_NO_CONFIG_BLOBS +L_CFLAGS += -DCONFIG_NO_CONFIG_BLOBS +endif + +ifdef CONFIG_NO_SCAN_PROCESSING +L_CFLAGS += -DCONFIG_NO_SCAN_PROCESSING +endif + +ifdef CONFIG_SUITEB +L_CFLAGS += -DCONFIG_SUITEB +NEED_SHA256=y +NEED_AES_OMAC1=y +endif + +ifdef CONFIG_SUITEB192 +L_CFLAGS += -DCONFIG_SUITEB192 +NEED_SHA384=y +endif + +ifdef CONFIG_IEEE80211W +L_CFLAGS += -DCONFIG_IEEE80211W +NEED_SHA256=y +NEED_AES_OMAC1=y +endif + +ifdef CONFIG_IEEE80211R +L_CFLAGS += -DCONFIG_IEEE80211R +OBJS += src/rsn_supp/wpa_ft.c +NEED_SHA256=y +NEED_AES_OMAC1=y +endif + +ifdef CONFIG_MESH +NEED_80211_COMMON=y +NEED_SHA256=y +NEED_AES_SIV=y +NEED_AES_OMAC1=y +NEED_AES_CTR=y +CONFIG_SAE=y +CONFIG_AP=y +L_CFLAGS += -DCONFIG_MESH +OBJS += mesh.c +OBJS += mesh_mpm.c +OBJS += mesh_rsn.c +endif + +ifdef CONFIG_SAE +L_CFLAGS += -DCONFIG_SAE +OBJS += src/common/sae.c +NEED_ECC=y +NEED_DH_GROUPS=y +endif + +ifdef CONFIG_WNM +L_CFLAGS += -DCONFIG_WNM +OBJS += wnm_sta.c +endif + +ifdef CONFIG_TDLS +L_CFLAGS += -DCONFIG_TDLS +OBJS += src/rsn_supp/tdls.c +NEED_SHA256=y +NEED_AES_OMAC1=y +endif + +ifdef CONFIG_TDLS_TESTING +L_CFLAGS += -DCONFIG_TDLS_TESTING +endif + +ifdef CONFIG_PEERKEY +L_CFLAGS += -DCONFIG_PEERKEY +endif + +ifndef CONFIG_NO_WPA +OBJS += src/rsn_supp/wpa.c +OBJS += src/rsn_supp/preauth.c +OBJS += src/rsn_supp/pmksa_cache.c +OBJS += src/rsn_supp/peerkey.c +OBJS += src/rsn_supp/wpa_ie.c +OBJS += src/common/wpa_common.c +NEED_AES=y +NEED_SHA1=y +NEED_MD5=y +NEED_RC4=y +else +L_CFLAGS += -DCONFIG_NO_WPA +endif + +ifdef CONFIG_IBSS_RSN +NEED_RSN_AUTHENTICATOR=y +L_CFLAGS += -DCONFIG_IBSS_RSN +L_CFLAGS += -DCONFIG_NO_VLAN +OBJS += ibss_rsn.c +endif + +ifdef CONFIG_P2P +OBJS += p2p_supplicant.c +OBJS += p2p_supplicant_sd.c +OBJS += src/p2p/p2p.c +OBJS += src/p2p/p2p_utils.c +OBJS += src/p2p/p2p_parse.c +OBJS += src/p2p/p2p_build.c +OBJS += src/p2p/p2p_go_neg.c +OBJS += src/p2p/p2p_sd.c +OBJS += src/p2p/p2p_pd.c +OBJS += src/p2p/p2p_invitation.c +OBJS += src/p2p/p2p_dev_disc.c +OBJS += src/p2p/p2p_group.c +OBJS += src/ap/p2p_hostapd.c +OBJS += src/utils/bitfield.c +L_CFLAGS += -DCONFIG_P2P +NEED_GAS=y +NEED_OFFCHANNEL=y +CONFIG_WPS=y +CONFIG_AP=y +ifdef CONFIG_P2P_STRICT +L_CFLAGS += -DCONFIG_P2P_STRICT +endif +endif + +ifdef CONFIG_WIFI_DISPLAY +L_CFLAGS += -DCONFIG_WIFI_DISPLAY +OBJS += wifi_display.c +endif + +ifdef CONFIG_HS20 +OBJS += hs20_supplicant.c +L_CFLAGS += -DCONFIG_HS20 +CONFIG_INTERWORKING=y +NEED_AES_OMAC1=y +endif + +ifdef CONFIG_INTERWORKING +OBJS += interworking.c +L_CFLAGS += -DCONFIG_INTERWORKING +NEED_GAS=y +endif + +ifdef CONFIG_FST +L_CFLAGS += -DCONFIG_FST +OBJS += src/fst/fst.c +OBJS += src/fst/fst_session.c +OBJS += src/fst/fst_iface.c +OBJS += src/fst/fst_group.c +OBJS += src/fst/fst_ctrl_aux.c +ifdef CONFIG_FST_TEST +L_CFLAGS += -DCONFIG_FST_TEST +endif +ifdef CONFIG_CTRL_IFACE +OBJS += src/fst/fst_ctrl_iface.c +endif +endif + + +include $(LOCAL_PATH)/src/drivers/drivers.mk + +ifdef CONFIG_AP +OBJS_d += $(DRV_BOTH_OBJS) +L_CFLAGS += $(DRV_BOTH_CFLAGS) +LDFLAGS += $(DRV_BOTH_LDFLAGS) +LIBS += $(DRV_BOTH_LIBS) +else +NEED_AP_MLME= +OBJS_d += $(DRV_WPA_OBJS) +L_CFLAGS += $(DRV_WPA_CFLAGS) +LDFLAGS += $(DRV_WPA_LDFLAGS) +LIBS += $(DRV_WPA_LIBS) +endif + +ifndef CONFIG_L2_PACKET +CONFIG_L2_PACKET=linux +endif + +OBJS_l2 += src/l2_packet/l2_packet_$(CONFIG_L2_PACKET).c + +ifeq ($(CONFIG_L2_PACKET), pcap) +ifdef CONFIG_WINPCAP +L_CFLAGS += -DCONFIG_WINPCAP +LIBS += -lwpcap -lpacket +LIBS_w += -lwpcap +else +LIBS += -ldnet -lpcap +endif +endif + +ifeq ($(CONFIG_L2_PACKET), winpcap) +LIBS += -lwpcap -lpacket +LIBS_w += -lwpcap +endif + +ifeq ($(CONFIG_L2_PACKET), freebsd) +LIBS += -lpcap +endif + +ifdef CONFIG_ERP +L_CFLAGS += -DCONFIG_ERP +NEED_SHA256=y +NEED_HMAC_SHA256_KDF=y +endif + +ifdef CONFIG_EAP_TLS +# EAP-TLS +ifeq ($(CONFIG_EAP_TLS), dyn) +L_CFLAGS += -DEAP_TLS_DYNAMIC +EAPDYN += src/eap_peer/eap_tls.so +else +L_CFLAGS += -DEAP_TLS +OBJS += src/eap_peer/eap_tls.c +endif +TLS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_UNAUTH_TLS +# EAP-UNAUTH-TLS +L_CFLAGS += -DEAP_UNAUTH_TLS +ifndef CONFIG_EAP_TLS +OBJS += src/eap_peer/eap_tls.c +TLS_FUNCS=y +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_PEAP +# EAP-PEAP +ifeq ($(CONFIG_EAP_PEAP), dyn) +L_CFLAGS += -DEAP_PEAP_DYNAMIC +EAPDYN += src/eap_peer/eap_peap.so +else +L_CFLAGS += -DEAP_PEAP +OBJS += src/eap_peer/eap_peap.c +OBJS += src/eap_common/eap_peap_common.c +endif +TLS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_TTLS +# EAP-TTLS +ifeq ($(CONFIG_EAP_TTLS), dyn) +L_CFLAGS += -DEAP_TTLS_DYNAMIC +EAPDYN += src/eap_peer/eap_ttls.so +else +L_CFLAGS += -DEAP_TTLS +OBJS += src/eap_peer/eap_ttls.c +endif +TLS_FUNCS=y +ifndef CONFIG_FIPS +MS_FUNCS=y +CHAP=y +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_MD5 +# EAP-MD5 +ifeq ($(CONFIG_EAP_MD5), dyn) +L_CFLAGS += -DEAP_MD5_DYNAMIC +EAPDYN += src/eap_peer/eap_md5.so +else +L_CFLAGS += -DEAP_MD5 +OBJS += src/eap_peer/eap_md5.c +endif +CHAP=y +CONFIG_IEEE8021X_EAPOL=y +endif + +# backwards compatibility for old spelling +ifdef CONFIG_MSCHAPV2 +ifndef CONFIG_EAP_MSCHAPV2 +CONFIG_EAP_MSCHAPV2=y +endif +endif + +ifdef CONFIG_EAP_MSCHAPV2 +# EAP-MSCHAPv2 +ifeq ($(CONFIG_EAP_MSCHAPV2), dyn) +L_CFLAGS += -DEAP_MSCHAPv2_DYNAMIC +EAPDYN += src/eap_peer/eap_mschapv2.so +EAPDYN += src/eap_peer/mschapv2.so +else +L_CFLAGS += -DEAP_MSCHAPv2 +OBJS += src/eap_peer/eap_mschapv2.c +OBJS += src/eap_peer/mschapv2.c +endif +MS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_GTC +# EAP-GTC +ifeq ($(CONFIG_EAP_GTC), dyn) +L_CFLAGS += -DEAP_GTC_DYNAMIC +EAPDYN += src/eap_peer/eap_gtc.so +else +L_CFLAGS += -DEAP_GTC +OBJS += src/eap_peer/eap_gtc.c +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_OTP +# EAP-OTP +ifeq ($(CONFIG_EAP_OTP), dyn) +L_CFLAGS += -DEAP_OTP_DYNAMIC +EAPDYN += src/eap_peer/eap_otp.so +else +L_CFLAGS += -DEAP_OTP +OBJS += src/eap_peer/eap_otp.c +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_SIM +# EAP-SIM +ifeq ($(CONFIG_EAP_SIM), dyn) +L_CFLAGS += -DEAP_SIM_DYNAMIC +EAPDYN += src/eap_peer/eap_sim.so +else +L_CFLAGS += -DEAP_SIM +OBJS += src/eap_peer/eap_sim.c +endif +CONFIG_IEEE8021X_EAPOL=y +CONFIG_EAP_SIM_COMMON=y +NEED_AES_CBC=y +endif + +ifdef CONFIG_EAP_LEAP +# EAP-LEAP +ifeq ($(CONFIG_EAP_LEAP), dyn) +L_CFLAGS += -DEAP_LEAP_DYNAMIC +EAPDYN += src/eap_peer/eap_leap.so +else +L_CFLAGS += -DEAP_LEAP +OBJS += src/eap_peer/eap_leap.c +endif +MS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_PSK +# EAP-PSK +ifeq ($(CONFIG_EAP_PSK), dyn) +L_CFLAGS += -DEAP_PSK_DYNAMIC +EAPDYN += src/eap_peer/eap_psk.so +else +L_CFLAGS += -DEAP_PSK +OBJS += src/eap_peer/eap_psk.c src/eap_common/eap_psk_common.c +endif +CONFIG_IEEE8021X_EAPOL=y +NEED_AES=y +NEED_AES_OMAC1=y +NEED_AES_ENCBLOCK=y +NEED_AES_EAX=y +endif + +ifdef CONFIG_EAP_AKA +# EAP-AKA +ifeq ($(CONFIG_EAP_AKA), dyn) +L_CFLAGS += -DEAP_AKA_DYNAMIC +EAPDYN += src/eap_peer/eap_aka.so +else +L_CFLAGS += -DEAP_AKA +OBJS += src/eap_peer/eap_aka.c +endif +CONFIG_IEEE8021X_EAPOL=y +CONFIG_EAP_SIM_COMMON=y +NEED_AES_CBC=y +endif + +ifdef CONFIG_EAP_PROXY +L_CFLAGS += -DCONFIG_EAP_PROXY +OBJS += src/eap_peer/eap_proxy_$(CONFIG_EAP_PROXY).c +include $(LOCAL_PATH)/eap_proxy_$(CONFIG_EAP_PROXY).mk +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_AKA_PRIME +# EAP-AKA' +ifeq ($(CONFIG_EAP_AKA_PRIME), dyn) +L_CFLAGS += -DEAP_AKA_PRIME_DYNAMIC +else +L_CFLAGS += -DEAP_AKA_PRIME +endif +NEED_SHA256=y +endif + +ifdef CONFIG_EAP_SIM_COMMON +OBJS += src/eap_common/eap_sim_common.c +NEED_AES=y +NEED_FIPS186_2_PRF=y +endif + +ifdef CONFIG_EAP_FAST +# EAP-FAST +ifeq ($(CONFIG_EAP_FAST), dyn) +L_CFLAGS += -DEAP_FAST_DYNAMIC +EAPDYN += src/eap_peer/eap_fast.so +EAPDYN += src/eap_common/eap_fast_common.c +else +L_CFLAGS += -DEAP_FAST +OBJS += src/eap_peer/eap_fast.c src/eap_peer/eap_fast_pac.c +OBJS += src/eap_common/eap_fast_common.c +endif +TLS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +NEED_T_PRF=y +endif + +ifdef CONFIG_EAP_PAX +# EAP-PAX +ifeq ($(CONFIG_EAP_PAX), dyn) +L_CFLAGS += -DEAP_PAX_DYNAMIC +EAPDYN += src/eap_peer/eap_pax.so +else +L_CFLAGS += -DEAP_PAX +OBJS += src/eap_peer/eap_pax.c src/eap_common/eap_pax_common.c +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_SAKE +# EAP-SAKE +ifeq ($(CONFIG_EAP_SAKE), dyn) +L_CFLAGS += -DEAP_SAKE_DYNAMIC +EAPDYN += src/eap_peer/eap_sake.so +else +L_CFLAGS += -DEAP_SAKE +OBJS += src/eap_peer/eap_sake.c src/eap_common/eap_sake_common.c +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_GPSK +# EAP-GPSK +ifeq ($(CONFIG_EAP_GPSK), dyn) +L_CFLAGS += -DEAP_GPSK_DYNAMIC +EAPDYN += src/eap_peer/eap_gpsk.so +else +L_CFLAGS += -DEAP_GPSK +OBJS += src/eap_peer/eap_gpsk.c src/eap_common/eap_gpsk_common.c +endif +CONFIG_IEEE8021X_EAPOL=y +ifdef CONFIG_EAP_GPSK_SHA256 +L_CFLAGS += -DEAP_GPSK_SHA256 +endif +NEED_SHA256=y +NEED_AES_OMAC1=y +endif + +ifdef CONFIG_EAP_PWD +L_CFLAGS += -DEAP_PWD +OBJS += src/eap_peer/eap_pwd.c src/eap_common/eap_pwd_common.c +CONFIG_IEEE8021X_EAPOL=y +NEED_SHA256=y +endif + +ifdef CONFIG_EAP_EKE +# EAP-EKE +ifeq ($(CONFIG_EAP_EKE), dyn) +L_CFLAGS += -DEAP_EKE_DYNAMIC +EAPDYN += src/eap_peer/eap_eke.so +else +L_CFLAGS += -DEAP_EKE +OBJS += src/eap_peer/eap_eke.c src/eap_common/eap_eke_common.c +endif +CONFIG_IEEE8021X_EAPOL=y +NEED_DH_GROUPS=y +NEED_DH_GROUPS_ALL=y +NEED_SHA256=y +NEED_AES_CBC=y +endif + +ifdef CONFIG_WPS +# EAP-WSC +L_CFLAGS += -DCONFIG_WPS -DEAP_WSC +OBJS += wps_supplicant.c +OBJS += src/utils/uuid.c +OBJS += src/eap_peer/eap_wsc.c src/eap_common/eap_wsc_common.c +OBJS += src/wps/wps.c +OBJS += src/wps/wps_common.c +OBJS += src/wps/wps_attr_parse.c +OBJS += src/wps/wps_attr_build.c +OBJS += src/wps/wps_attr_process.c +OBJS += src/wps/wps_dev_attr.c +OBJS += src/wps/wps_enrollee.c +OBJS += src/wps/wps_registrar.c +CONFIG_IEEE8021X_EAPOL=y +NEED_DH_GROUPS=y +NEED_SHA256=y +NEED_BASE64=y +NEED_AES_CBC=y +NEED_MODEXP=y + +ifdef CONFIG_WPS_NFC +L_CFLAGS += -DCONFIG_WPS_NFC +OBJS += src/wps/ndef.c +NEED_WPS_OOB=y +endif + +ifdef NEED_WPS_OOB +L_CFLAGS += -DCONFIG_WPS_OOB +endif + +ifdef CONFIG_WPS_ER +CONFIG_WPS_UPNP=y +L_CFLAGS += -DCONFIG_WPS_ER +OBJS += src/wps/wps_er.c +OBJS += src/wps/wps_er_ssdp.c +endif + +ifdef CONFIG_WPS_UPNP +L_CFLAGS += -DCONFIG_WPS_UPNP +OBJS += src/wps/wps_upnp.c +OBJS += src/wps/wps_upnp_ssdp.c +OBJS += src/wps/wps_upnp_web.c +OBJS += src/wps/wps_upnp_event.c +OBJS += src/wps/wps_upnp_ap.c +OBJS += src/wps/upnp_xml.c +OBJS += src/wps/httpread.c +OBJS += src/wps/http_client.c +OBJS += src/wps/http_server.c +endif + +ifdef CONFIG_WPS_STRICT +L_CFLAGS += -DCONFIG_WPS_STRICT +OBJS += src/wps/wps_validate.c +endif + +ifdef CONFIG_WPS_TESTING +L_CFLAGS += -DCONFIG_WPS_TESTING +endif + +ifdef CONFIG_WPS_REG_DISABLE_OPEN +L_CFLAGS += -DCONFIG_WPS_REG_DISABLE_OPEN +endif + +endif + +ifdef CONFIG_EAP_IKEV2 +# EAP-IKEv2 +ifeq ($(CONFIG_EAP_IKEV2), dyn) +L_CFLAGS += -DEAP_IKEV2_DYNAMIC +EAPDYN += src/eap_peer/eap_ikev2.so src/eap_peer/ikev2.c +EAPDYN += src/eap_common/eap_ikev2_common.c src/eap_common/ikev2_common.c +else +L_CFLAGS += -DEAP_IKEV2 +OBJS += src/eap_peer/eap_ikev2.c src/eap_peer/ikev2.c +OBJS += src/eap_common/eap_ikev2_common.c src/eap_common/ikev2_common.c +endif +CONFIG_IEEE8021X_EAPOL=y +NEED_DH_GROUPS=y +NEED_DH_GROUPS_ALL=y +NEED_MODEXP=y +NEED_CIPHER=y +endif + +ifdef CONFIG_EAP_VENDOR_TEST +ifeq ($(CONFIG_EAP_VENDOR_TEST), dyn) +L_CFLAGS += -DEAP_VENDOR_TEST_DYNAMIC +EAPDYN += src/eap_peer/eap_vendor_test.so +else +L_CFLAGS += -DEAP_VENDOR_TEST +OBJS += src/eap_peer/eap_vendor_test.c +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_TNC +# EAP-TNC +L_CFLAGS += -DEAP_TNC +OBJS += src/eap_peer/eap_tnc.c +OBJS += src/eap_peer/tncc.c +NEED_BASE64=y +ifndef CONFIG_NATIVE_WINDOWS +ifndef CONFIG_DRIVER_BSD +LIBS += -ldl +endif +endif +endif + +ifdef CONFIG_IEEE8021X_EAPOL +# IEEE 802.1X/EAPOL state machines (e.g., for RADIUS authentication) +L_CFLAGS += -DIEEE8021X_EAPOL +OBJS += src/eapol_supp/eapol_supp_sm.c +OBJS += src/eap_peer/eap.c src/eap_peer/eap_methods.c +NEED_EAP_COMMON=y +ifdef CONFIG_DYNAMIC_EAP_METHODS +L_CFLAGS += -DCONFIG_DYNAMIC_EAP_METHODS +LIBS += -ldl -rdynamic +endif +endif + +ifdef CONFIG_AP +NEED_EAP_COMMON=y +NEED_RSN_AUTHENTICATOR=y +L_CFLAGS += -DCONFIG_AP +OBJS += ap.c +L_CFLAGS += -DCONFIG_NO_RADIUS +L_CFLAGS += -DCONFIG_NO_ACCOUNTING +L_CFLAGS += -DCONFIG_NO_VLAN +OBJS += src/ap/hostapd.c +OBJS += src/ap/wpa_auth_glue.c +OBJS += src/ap/utils.c +OBJS += src/ap/authsrv.c +OBJS += src/ap/ap_config.c +OBJS += src/utils/ip_addr.c +OBJS += src/ap/sta_info.c +OBJS += src/ap/tkip_countermeasures.c +OBJS += src/ap/ap_mlme.c +OBJS += src/ap/ieee802_1x.c +OBJS += src/eapol_auth/eapol_auth_sm.c +OBJS += src/ap/ieee802_11_auth.c +OBJS += src/ap/ieee802_11_shared.c +OBJS += src/ap/drv_callbacks.c +OBJS += src/ap/ap_drv_ops.c +OBJS += src/ap/beacon.c +OBJS += src/ap/bss_load.c +OBJS += src/ap/eap_user_db.c +OBJS += src/ap/neighbor_db.c +OBJS += src/ap/rrm.c +ifdef CONFIG_IEEE80211N +OBJS += src/ap/ieee802_11_ht.c +ifdef CONFIG_IEEE80211AC +OBJS += src/ap/ieee802_11_vht.c +endif +endif +ifdef CONFIG_WNM +OBJS += src/ap/wnm_ap.c +endif +ifdef CONFIG_MBO +OBJS += src/ap/mbo_ap.c +endif +ifdef CONFIG_CTRL_IFACE +OBJS += src/ap/ctrl_iface_ap.c +endif + +L_CFLAGS += -DEAP_SERVER -DEAP_SERVER_IDENTITY +OBJS += src/eap_server/eap_server.c +OBJS += src/eap_server/eap_server_identity.c +OBJS += src/eap_server/eap_server_methods.c + +ifdef CONFIG_IEEE80211N +L_CFLAGS += -DCONFIG_IEEE80211N +ifdef CONFIG_IEEE80211AC +L_CFLAGS += -DCONFIG_IEEE80211AC +endif +endif + +ifdef CONFIG_MBO +OBJS += mbo.c +L_CFLAGS += -DCONFIG_MBO +endif + +ifdef NEED_AP_MLME +OBJS += src/ap/wmm.c +OBJS += src/ap/ap_list.c +OBJS += src/ap/ieee802_11.c +OBJS += src/ap/hw_features.c +OBJS += src/ap/dfs.c +L_CFLAGS += -DNEED_AP_MLME +endif +ifdef CONFIG_WPS +L_CFLAGS += -DEAP_SERVER_WSC +OBJS += src/ap/wps_hostapd.c +OBJS += src/eap_server/eap_server_wsc.c +endif +ifdef CONFIG_INTERWORKING +OBJS += src/ap/gas_serv.c +endif +ifdef CONFIG_HS20 +OBJS += src/ap/hs20.c +endif +endif + +ifdef NEED_RSN_AUTHENTICATOR +L_CFLAGS += -DCONFIG_NO_RADIUS +NEED_AES_WRAP=y +OBJS += src/ap/wpa_auth.c +OBJS += src/ap/wpa_auth_ie.c +OBJS += src/ap/pmksa_cache_auth.c +ifdef CONFIG_IEEE80211R +OBJS += src/ap/wpa_auth_ft.c +endif +ifdef CONFIG_PEERKEY +OBJS += src/ap/peerkey_auth.c +endif +endif + +ifdef CONFIG_ACS +L_CFLAGS += -DCONFIG_ACS +OBJS += src/ap/acs.c +LIBS += -lm +endif + +ifdef CONFIG_PCSC +# PC/SC interface for smartcards (USIM, GSM SIM) +L_CFLAGS += -DPCSC_FUNCS -I/usr/include/PCSC +OBJS += src/utils/pcsc_funcs.c +# -lpthread may not be needed depending on how pcsc-lite was configured +ifdef CONFIG_NATIVE_WINDOWS +#Once MinGW gets support for WinScard, -lwinscard could be used instead of the +#dynamic symbol loading that is now used in pcsc_funcs.c +#LIBS += -lwinscard +else +LIBS += -lpcsclite -lpthread +endif +endif + +ifdef CONFIG_SIM_SIMULATOR +L_CFLAGS += -DCONFIG_SIM_SIMULATOR +NEED_MILENAGE=y +endif + +ifdef CONFIG_USIM_SIMULATOR +L_CFLAGS += -DCONFIG_USIM_SIMULATOR +NEED_MILENAGE=y +endif + +ifdef NEED_MILENAGE +OBJS += src/crypto/milenage.c +NEED_AES_ENCBLOCK=y +endif + +ifdef CONFIG_PKCS12 +L_CFLAGS += -DPKCS12_FUNCS +endif + +ifdef CONFIG_SMARTCARD +L_CFLAGS += -DCONFIG_SMARTCARD +endif + +ifdef MS_FUNCS +OBJS += src/crypto/ms_funcs.c +NEED_DES=y +NEED_MD4=y +endif + +ifdef CHAP +OBJS += src/eap_common/chap.c +endif + +ifdef TLS_FUNCS +NEED_DES=y +# Shared TLS functions (needed for EAP_TLS, EAP_PEAP, EAP_TTLS, and EAP_FAST) +OBJS += src/eap_peer/eap_tls_common.c +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 +L_CFLAGS += -DCONFIG_TLSV11 +endif + +ifdef CONFIG_TLSV12 +L_CFLAGS += -DCONFIG_TLSV12 +NEED_SHA256=y +endif + +ifeq ($(CONFIG_TLS), openssl) +ifdef TLS_FUNCS +L_CFLAGS += -DEAP_TLS_OPENSSL +OBJS += src/crypto/tls_openssl.c +OBJS += src/crypto/tls_openssl_ocsp.c +LIBS += -lssl +endif +OBJS += src/crypto/crypto_openssl.c +OBJS_p += src/crypto/crypto_openssl.c +ifdef NEED_FIPS186_2_PRF +OBJS += src/crypto/fips_prf_openssl.c +endif +NEED_SHA256=y +NEED_TLS_PRF_SHA256=y +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.c +LIBS += -lgnutls -lgpg-error +endif +OBJS += src/crypto/crypto_gnutls.c +OBJS_p += src/crypto/crypto_gnutls.c +ifdef NEED_FIPS186_2_PRF +OBJS += src/crypto/fips_prf_internal.c +OBJS += src/crypto/sha1-internal.c +endif +LIBS += -lgcrypt +LIBS_p += -lgcrypt +CONFIG_INTERNAL_SHA256=y +CONFIG_INTERNAL_RC4=y +CONFIG_INTERNAL_DH_GROUP5=y +endif + +ifeq ($(CONFIG_TLS), internal) +ifndef CONFIG_CRYPTO +CONFIG_CRYPTO=internal +endif +ifdef TLS_FUNCS +OBJS += src/crypto/crypto_internal-rsa.c +OBJS += src/crypto/tls_internal.c +OBJS += src/tls/tlsv1_common.c +OBJS += src/tls/tlsv1_record.c +OBJS += src/tls/tlsv1_cred.c +OBJS += src/tls/tlsv1_client.c +OBJS += src/tls/tlsv1_client_write.c +OBJS += src/tls/tlsv1_client_read.c +OBJS += src/tls/tlsv1_client_ocsp.c +OBJS += src/tls/asn1.c +OBJS += src/tls/rsa.c +OBJS += src/tls/x509v3.c +OBJS += src/tls/pkcs1.c +OBJS += src/tls/pkcs5.c +OBJS += src/tls/pkcs8.c +NEED_SHA256=y +NEED_BASE64=y +NEED_TLS_PRF=y +ifdef CONFIG_TLSV12 +NEED_TLS_PRF_SHA256=y +endif +NEED_MODEXP=y +NEED_CIPHER=y +L_CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT +endif +ifdef NEED_CIPHER +NEED_DES=y +OBJS += src/crypto/crypto_internal-cipher.c +endif +ifdef NEED_MODEXP +OBJS += src/crypto/crypto_internal-modexp.c +OBJS += src/tls/bignum.c +endif +ifeq ($(CONFIG_CRYPTO), libtomcrypt) +OBJS += src/crypto/crypto_libtomcrypt.c +OBJS_p += src/crypto/crypto_libtomcrypt.c +LIBS += -ltomcrypt -ltfm +LIBS_p += -ltomcrypt -ltfm +CONFIG_INTERNAL_SHA256=y +CONFIG_INTERNAL_RC4=y +CONFIG_INTERNAL_DH_GROUP5=y +endif +ifeq ($(CONFIG_CRYPTO), internal) +OBJS += src/crypto/crypto_internal.c +OBJS_p += src/crypto/crypto_internal.c +NEED_AES_ENC=y +L_CFLAGS += -DCONFIG_CRYPTO_INTERNAL +ifdef CONFIG_INTERNAL_LIBTOMMATH +L_CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH +ifdef CONFIG_INTERNAL_LIBTOMMATH_FAST +L_CFLAGS += -DLTM_FAST +endif +else +LIBS += -ltommath +LIBS_p += -ltommath +endif +CONFIG_INTERNAL_AES=y +CONFIG_INTERNAL_DES=y +CONFIG_INTERNAL_SHA1=y +CONFIG_INTERNAL_MD4=y +CONFIG_INTERNAL_MD5=y +CONFIG_INTERNAL_SHA256=y +CONFIG_INTERNAL_SHA384=y +CONFIG_INTERNAL_SHA512=y +CONFIG_INTERNAL_RC4=y +CONFIG_INTERNAL_DH_GROUP5=y +endif +ifeq ($(CONFIG_CRYPTO), cryptoapi) +OBJS += src/crypto/crypto_cryptoapi.c +OBJS_p += src/crypto/crypto_cryptoapi.c +L_CFLAGS += -DCONFIG_CRYPTO_CRYPTOAPI +CONFIG_INTERNAL_SHA256=y +CONFIG_INTERNAL_RC4=y +endif +endif + +ifeq ($(CONFIG_TLS), none) +ifdef TLS_FUNCS +OBJS += src/crypto/tls_none.c +L_CFLAGS += -DEAP_TLS_NONE +CONFIG_INTERNAL_AES=y +CONFIG_INTERNAL_SHA1=y +CONFIG_INTERNAL_MD5=y +endif +OBJS += src/crypto/crypto_none.c +OBJS_p += src/crypto/crypto_none.c +CONFIG_INTERNAL_SHA256=y +CONFIG_INTERNAL_RC4=y +endif + +ifdef TLS_FUNCS +ifdef CONFIG_SMARTCARD +ifndef CONFIG_NATIVE_WINDOWS +ifneq ($(CONFIG_L2_PACKET), freebsd) +LIBS += -ldl +endif +endif +endif +endif + +ifndef TLS_FUNCS +OBJS += src/crypto/tls_none.c +ifeq ($(CONFIG_TLS), internal) +CONFIG_INTERNAL_AES=y +CONFIG_INTERNAL_SHA1=y +CONFIG_INTERNAL_MD5=y +CONFIG_INTERNAL_RC4=y +endif +endif + +AESOBJS = # none so far (see below) +ifdef CONFIG_INTERNAL_AES +AESOBJS += src/crypto/aes-internal.c src/crypto/aes-internal-dec.c +endif + +ifneq ($(CONFIG_TLS), openssl) +NEED_INTERNAL_AES_WRAP=y +endif +ifdef CONFIG_OPENSSL_INTERNAL_AES_WRAP +# Seems to be needed at least with BoringSSL +NEED_INTERNAL_AES_WRAP=y +L_CFLAGS += -DCONFIG_OPENSSL_INTERNAL_AES_WRAP +endif +ifdef CONFIG_FIPS +# Have to use internal AES key wrap routines to use OpenSSL EVP since the +# OpenSSL AES_wrap_key()/AES_unwrap_key() API is not available in FIPS mode. +NEED_INTERNAL_AES_WRAP=y +endif + +ifdef NEED_INTERNAL_AES_WRAP +AESOBJS += src/crypto/aes-unwrap.c +endif +ifdef NEED_AES_EAX +AESOBJS += src/crypto/aes-eax.c +NEED_AES_CTR=y +endif +ifdef NEED_AES_CTR +AESOBJS += src/crypto/aes-ctr.c +endif +ifdef NEED_AES_ENCBLOCK +AESOBJS += src/crypto/aes-encblock.c +endif +ifdef NEED_AES_OMAC1 +NEED_AES_ENC=y +ifdef CONFIG_OPENSSL_CMAC +L_CFLAGS += -DCONFIG_OPENSSL_CMAC +else +AESOBJS += src/crypto/aes-omac1.c +endif +endif +ifdef NEED_AES_WRAP +NEED_AES_ENC=y +ifdef NEED_INTERNAL_AES_WRAP +AESOBJS += src/crypto/aes-wrap.c +endif +endif +ifdef NEED_AES_CBC +NEED_AES_ENC=y +ifneq ($(CONFIG_TLS), openssl) +AESOBJS += src/crypto/aes-cbc.c +endif +endif +ifdef NEED_AES_ENC +ifdef CONFIG_INTERNAL_AES +AESOBJS += src/crypto/aes-internal-enc.c +endif +endif +ifdef NEED_AES_SIV +AESOBJS += src/crypto/aes-siv.c +endif +ifdef NEED_AES +OBJS += $(AESOBJS) +endif + +SHA1OBJS = +ifdef NEED_SHA1 +ifneq ($(CONFIG_TLS), openssl) +SHA1OBJS += src/crypto/sha1.c +endif +SHA1OBJS += src/crypto/sha1-prf.c +ifdef CONFIG_INTERNAL_SHA1 +SHA1OBJS += src/crypto/sha1-internal.c +ifdef NEED_FIPS186_2_PRF +SHA1OBJS += src/crypto/fips_prf_internal.c +endif +endif +ifdef CONFIG_NO_WPA_PASSPHRASE +L_CFLAGS += -DCONFIG_NO_PBKDF2 +else +ifneq ($(CONFIG_TLS), openssl) +SHA1OBJS += src/crypto/sha1-pbkdf2.c +endif +endif +ifdef NEED_T_PRF +SHA1OBJS += src/crypto/sha1-tprf.c +endif +ifdef NEED_TLS_PRF +SHA1OBJS += src/crypto/sha1-tlsprf.c +endif +endif + +MD5OBJS = +ifndef CONFIG_FIPS +ifneq ($(CONFIG_TLS), openssl) +MD5OBJS += src/crypto/md5.c +endif +endif +ifdef NEED_MD5 +ifdef CONFIG_INTERNAL_MD5 +MD5OBJS += src/crypto/md5-internal.c +endif +OBJS += $(MD5OBJS) +OBJS_p += $(MD5OBJS) +endif + +ifdef NEED_MD4 +ifdef CONFIG_INTERNAL_MD4 +OBJS += src/crypto/md4-internal.c +endif +endif + +DESOBJS = # none needed when not internal +ifdef NEED_DES +ifdef CONFIG_INTERNAL_DES +DESOBJS += src/crypto/des-internal.c +endif +endif + +ifdef CONFIG_NO_RC4 +L_CFLAGS += -DCONFIG_NO_RC4 +endif + +ifdef NEED_RC4 +ifdef CONFIG_INTERNAL_RC4 +ifndef CONFIG_NO_RC4 +OBJS += src/crypto/rc4.c +endif +endif +endif + +SHA256OBJS = # none by default +ifdef NEED_SHA256 +L_CFLAGS += -DCONFIG_SHA256 +ifneq ($(CONFIG_TLS), openssl) +SHA256OBJS += src/crypto/sha256.c +endif +SHA256OBJS += src/crypto/sha256-prf.c +ifdef CONFIG_INTERNAL_SHA256 +SHA256OBJS += src/crypto/sha256-internal.c +endif +ifdef CONFIG_INTERNAL_SHA384 +L_CFLAGS += -DCONFIG_INTERNAL_SHA384 +SHA256OBJS += src/crypto/sha384-internal.c +endif +ifdef CONFIG_INTERNAL_SHA512 +L_CFLAGS += -DCONFIG_INTERNAL_SHA512 +SHA256OBJS += src/crypto/sha512-internal.c +endif +ifdef NEED_TLS_PRF_SHA256 +SHA256OBJS += src/crypto/sha256-tlsprf.c +endif +ifdef NEED_HMAC_SHA256_KDF +L_CFLAGS += -DCONFIG_HMAC_SHA256_KDF +SHA256OBJS += src/crypto/sha256-kdf.c +endif +OBJS += $(SHA256OBJS) +endif +ifdef NEED_SHA384 +L_CFLAGS += -DCONFIG_SHA384 +OBJS += src/crypto/sha384-prf.c +endif + +ifdef NEED_DH_GROUPS +OBJS += src/crypto/dh_groups.c +endif +ifdef NEED_DH_GROUPS_ALL +L_CFLAGS += -DALL_DH_GROUPS +endif +ifdef CONFIG_INTERNAL_DH_GROUP5 +ifdef NEED_DH_GROUPS +OBJS += src/crypto/dh_group5.c +endif +endif + +ifdef NEED_ECC +L_CFLAGS += -DCONFIG_ECC +endif + +ifdef CONFIG_NO_RANDOM_POOL +L_CFLAGS += -DCONFIG_NO_RANDOM_POOL +else +OBJS += src/crypto/random.c +endif + +ifdef CONFIG_CTRL_IFACE +ifeq ($(CONFIG_CTRL_IFACE), y) +ifdef CONFIG_NATIVE_WINDOWS +CONFIG_CTRL_IFACE=named_pipe +else +CONFIG_CTRL_IFACE=unix +endif +endif +L_CFLAGS += -DCONFIG_CTRL_IFACE +ifeq ($(CONFIG_CTRL_IFACE), unix) +L_CFLAGS += -DCONFIG_CTRL_IFACE_UNIX +OBJS += src/common/ctrl_iface_common.c +endif +ifeq ($(CONFIG_CTRL_IFACE), udp) +L_CFLAGS += -DCONFIG_CTRL_IFACE_UDP +endif +ifeq ($(CONFIG_CTRL_IFACE), named_pipe) +L_CFLAGS += -DCONFIG_CTRL_IFACE_NAMED_PIPE +endif +ifeq ($(CONFIG_CTRL_IFACE), udp-remote) +CONFIG_CTRL_IFACE=udp +L_CFLAGS += -DCONFIG_CTRL_IFACE_UDP +L_CFLAGS += -DCONFIG_CTRL_IFACE_UDP_REMOTE +endif +OBJS += ctrl_iface.c ctrl_iface_$(CONFIG_CTRL_IFACE).c +endif + +ifdef CONFIG_CTRL_IFACE_DBUS +DBUS=y +DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS -DDBUS_API_SUBJECT_TO_CHANGE +DBUS_OBJS += dbus/dbus_old.c dbus/dbus_old_handlers.c +ifdef CONFIG_WPS +DBUS_OBJS += dbus/dbus_old_handlers_wps.c +endif +DBUS_OBJS += dbus/dbus_dict_helpers.c +DBUS_CFLAGS += $(DBUS_INCLUDE) +endif + +ifdef CONFIG_CTRL_IFACE_DBUS_NEW +DBUS=y +DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_NEW +DBUS_OBJS ?= dbus/dbus_dict_helpers.c +DBUS_OBJS += dbus/dbus_new_helpers.c +DBUS_OBJS += dbus/dbus_new.c dbus/dbus_new_handlers.c +ifdef CONFIG_WPS +DBUS_OBJS += dbus/dbus_new_handlers_wps.c +endif +ifdef CONFIG_P2P +DBUS_OBJS += dbus/dbus_new_handlers_p2p.c +endif +ifdef CONFIG_CTRL_IFACE_DBUS_INTRO +DBUS_OBJS += dbus/dbus_new_introspect.c +DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_INTRO +endif +DBUS_CFLAGS += $(DBUS_INCLUDE) +endif + +ifdef DBUS +DBUS_CFLAGS += -DCONFIG_DBUS +DBUS_OBJS += dbus/dbus_common.c +endif + +OBJS += $(DBUS_OBJS) +L_CFLAGS += $(DBUS_CFLAGS) + +ifdef CONFIG_CTRL_IFACE_BINDER +WPA_SUPPLICANT_USE_BINDER=y +L_CFLAGS += -DCONFIG_BINDER -DCONFIG_CTRL_IFACE_BINDER +endif + +ifdef CONFIG_READLINE +OBJS_c += src/utils/edit_readline.c +LIBS_c += -lncurses -lreadline +else +ifdef CONFIG_WPA_CLI_EDIT +OBJS_c += src/utils/edit.c +else +OBJS_c += src/utils/edit_simple.c +endif +endif + +ifdef CONFIG_NATIVE_WINDOWS +L_CFLAGS += -DCONFIG_NATIVE_WINDOWS +LIBS += -lws2_32 -lgdi32 -lcrypt32 +LIBS_c += -lws2_32 +LIBS_p += -lws2_32 -lgdi32 +ifeq ($(CONFIG_CRYPTO), cryptoapi) +LIBS_p += -lcrypt32 +endif +endif + +ifdef CONFIG_NO_STDOUT_DEBUG +L_CFLAGS += -DCONFIG_NO_STDOUT_DEBUG +ifndef CONFIG_CTRL_IFACE +L_CFLAGS += -DCONFIG_NO_WPA_MSG +endif +endif + +ifdef CONFIG_ANDROID_LOG +L_CFLAGS += -DCONFIG_ANDROID_LOG +endif + +ifdef CONFIG_IPV6 +# for eapol_test only +L_CFLAGS += -DCONFIG_IPV6 +endif + +ifdef NEED_BASE64 +OBJS += src/utils/base64.c +endif + +ifdef NEED_SME +OBJS += sme.c +L_CFLAGS += -DCONFIG_SME +endif + +OBJS += src/common/ieee802_11_common.c +OBJS += src/common/hw_features_common.c + +ifdef NEED_EAP_COMMON +OBJS += src/eap_common/eap_common.c +endif + +ifndef CONFIG_MAIN +CONFIG_MAIN=main +endif + +ifdef CONFIG_DEBUG_SYSLOG +L_CFLAGS += -DCONFIG_DEBUG_SYSLOG +ifdef CONFIG_DEBUG_SYSLOG_FACILITY +L_CFLAGS += -DLOG_HOSTAPD="$(CONFIG_DEBUG_SYSLOG_FACILITY)" +endif +endif + +ifdef CONFIG_DEBUG_LINUX_TRACING +L_CFLAGS += -DCONFIG_DEBUG_LINUX_TRACING +endif + +ifdef CONFIG_DEBUG_FILE +L_CFLAGS += -DCONFIG_DEBUG_FILE +endif + +ifdef CONFIG_DELAYED_MIC_ERROR_REPORT +L_CFLAGS += -DCONFIG_DELAYED_MIC_ERROR_REPORT +endif + +ifdef CONFIG_FIPS +L_CFLAGS += -DCONFIG_FIPS +endif + +OBJS += $(SHA1OBJS) $(DESOBJS) + +OBJS_p += $(SHA1OBJS) +OBJS_p += $(SHA256OBJS) + +ifdef CONFIG_BGSCAN_SIMPLE +L_CFLAGS += -DCONFIG_BGSCAN_SIMPLE +OBJS += bgscan_simple.c +NEED_BGSCAN=y +endif + +ifdef CONFIG_BGSCAN_LEARN +L_CFLAGS += -DCONFIG_BGSCAN_LEARN +OBJS += bgscan_learn.c +NEED_BGSCAN=y +endif + +ifdef NEED_BGSCAN +L_CFLAGS += -DCONFIG_BGSCAN +OBJS += bgscan.c +endif + +ifdef CONFIG_AUTOSCAN_EXPONENTIAL +L_CFLAGS += -DCONFIG_AUTOSCAN_EXPONENTIAL +OBJS += autoscan_exponential.c +NEED_AUTOSCAN=y +endif + +ifdef CONFIG_AUTOSCAN_PERIODIC +L_CFLAGS += -DCONFIG_AUTOSCAN_PERIODIC +OBJS += autoscan_periodic.c +NEED_AUTOSCAN=y +endif + +ifdef NEED_AUTOSCAN +L_CFLAGS += -DCONFIG_AUTOSCAN +OBJS += autoscan.c +endif + +ifdef CONFIG_EXT_PASSWORD_TEST +OBJS += src/utils/ext_password_test.c +L_CFLAGS += -DCONFIG_EXT_PASSWORD_TEST +NEED_EXT_PASSWORD=y +endif + +ifdef NEED_EXT_PASSWORD +OBJS += src/utils/ext_password.c +L_CFLAGS += -DCONFIG_EXT_PASSWORD +endif + +ifdef NEED_GAS +OBJS += src/common/gas.c +OBJS += gas_query.c +L_CFLAGS += -DCONFIG_GAS +NEED_OFFCHANNEL=y +endif + +ifdef NEED_OFFCHANNEL +OBJS += offchannel.c +L_CFLAGS += -DCONFIG_OFFCHANNEL +endif + +OBJS += src/drivers/driver_common.c + +OBJS += wpa_supplicant.c events.c blacklist.c wpas_glue.c scan.c +OBJS_t := $(OBJS) $(OBJS_l2) eapol_test.c +OBJS_t += src/radius/radius_client.c +OBJS_t += src/radius/radius.c +ifndef CONFIG_AP +OBJS_t += src/utils/ip_addr.c +endif +OBJS_t2 := $(OBJS) $(OBJS_l2) preauth_test.c +OBJS += $(CONFIG_MAIN).c + +ifdef CONFIG_PRIVSEP +OBJS_priv += $(OBJS_d) src/drivers/drivers.c +OBJS_priv += $(OBJS_l2) +OBJS_priv += src/utils/os_$(CONFIG_OS).c +OBJS_priv += src/utils/$(CONFIG_ELOOP).c +OBJS_priv += src/utils/common.c +OBJS_priv += src/utils/wpa_debug.c +OBJS_priv += src/utils/wpabuf.c +OBJS_priv += wpa_priv.c +ifdef CONFIG_DRIVER_NL80211 +OBJS_priv += src/common/ieee802_11_common.c +endif +OBJS += src/l2_packet/l2_packet_privsep.c +OBJS += src/drivers/driver_privsep.c +EXTRA_progs += wpa_priv +else +OBJS += $(OBJS_d) src/drivers/drivers.c +OBJS += $(OBJS_l2) +endif + +ifdef CONFIG_NDIS_EVENTS_INTEGRATED +L_CFLAGS += -DCONFIG_NDIS_EVENTS_INTEGRATED +OBJS += src/drivers/ndis_events.c +EXTRALIBS += -loleaut32 -lole32 -luuid +ifdef PLATFORMSDKLIB +EXTRALIBS += $(PLATFORMSDKLIB)/WbemUuid.Lib +else +EXTRALIBS += WbemUuid.Lib +endif +endif + +ifndef LDO +LDO=$(CC) +endif + +######################## + +include $(CLEAR_VARS) +LOCAL_MODULE := wpa_cli +LOCAL_MODULE_TAGS := debug +LOCAL_SHARED_LIBRARIES := libc libcutils liblog +LOCAL_CFLAGS := $(L_CFLAGS) +LOCAL_SRC_FILES := $(OBJS_c) +LOCAL_C_INCLUDES := $(INCLUDES) +include $(BUILD_EXECUTABLE) + +######################## +include $(CLEAR_VARS) +LOCAL_MODULE := wpa_supplicant +ifdef CONFIG_DRIVER_CUSTOM +LOCAL_STATIC_LIBRARIES := libCustomWifi +endif +ifneq ($(BOARD_WPA_SUPPLICANT_PRIVATE_LIB),) +LOCAL_STATIC_LIBRARIES += $(BOARD_WPA_SUPPLICANT_PRIVATE_LIB) +endif +LOCAL_SHARED_LIBRARIES := libc libcutils liblog +ifdef CONFIG_EAP_PROXY +LOCAL_STATIC_LIBRARIES += $(LIB_STATIC_EAP_PROXY) +LOCAL_SHARED_LIBRARIES += $(LIB_SHARED_EAP_PROXY) +endif +ifeq ($(CONFIG_TLS), openssl) +LOCAL_SHARED_LIBRARIES += libcrypto libssl libkeystore_binder +endif + +# With BoringSSL we need libkeystore-engine in order to provide access to +# keystore keys. +ifneq (,$(wildcard external/boringssl/flavor.mk)) +LOCAL_SHARED_LIBRARIES += libkeystore-engine +endif + +ifdef CONFIG_DRIVER_NL80211 +ifneq ($(wildcard external/libnl),) +LOCAL_SHARED_LIBRARIES += libnl +else +LOCAL_STATIC_LIBRARIES += libnl_2 +endif +endif +LOCAL_CFLAGS := $(L_CFLAGS) +LOCAL_SRC_FILES := $(OBJS) +LOCAL_C_INCLUDES := $(INCLUDES) +ifeq ($(DBUS), y) +LOCAL_SHARED_LIBRARIES += libdbus +endif +ifeq ($(WPA_SUPPLICANT_USE_BINDER), y) +LOCAL_SHARED_LIBRARIES += libbinder libutils +LOCAL_STATIC_LIBRARIES += libwpa_binder libwpa_binder_interface +endif +include $(BUILD_EXECUTABLE) + +######################## +# +#include $(CLEAR_VARS) +#LOCAL_MODULE := eapol_test +#ifdef CONFIG_DRIVER_CUSTOM +#LOCAL_STATIC_LIBRARIES := libCustomWifi +#endif +#LOCAL_SHARED_LIBRARIES := libc libcrypto libssl +#LOCAL_CFLAGS := $(L_CFLAGS) +#LOCAL_SRC_FILES := $(OBJS_t) +#LOCAL_C_INCLUDES := $(INCLUDES) +#include $(BUILD_EXECUTABLE) +# +######################## +# +#local_target_dir := $(TARGET_OUT)/etc/wifi +# +#include $(CLEAR_VARS) +#LOCAL_MODULE := wpa_supplicant.conf +#LOCAL_MODULE_CLASS := ETC +#LOCAL_MODULE_PATH := $(local_target_dir) +#LOCAL_SRC_FILES := $(LOCAL_MODULE) +#include $(BUILD_PREBUILT) +# +######################## + +include $(CLEAR_VARS) +LOCAL_MODULE = libwpa_client +LOCAL_CFLAGS = $(L_CFLAGS) +LOCAL_SRC_FILES = src/common/wpa_ctrl.c src/utils/os_$(CONFIG_OS).c +LOCAL_C_INCLUDES = $(INCLUDES) +LOCAL_SHARED_LIBRARIES := libcutils liblog +LOCAL_COPY_HEADERS_TO := libwpa_client +LOCAL_COPY_HEADERS := src/common/wpa_ctrl.h +LOCAL_COPY_HEADERS += src/common/qca-vendor.h +include $(BUILD_SHARED_LIBRARY) + +ifeq ($(WPA_SUPPLICANT_USE_BINDER), y) +### Binder interface library ### +######################## + +include $(CLEAR_VARS) +LOCAL_MODULE := libwpa_binder_interface +LOCAL_AIDL_INCLUDES := \ + $(LOCAL_PATH)/binder \ + frameworks/native/aidl/binder +LOCAL_EXPORT_C_INCLUDE_DIRS := \ + $(LOCAL_PATH)/binder +LOCAL_CPPFLAGS := $(L_CPPFLAGS) +LOCAL_SRC_FILES := \ + binder/binder_constants.cpp \ + binder/fi/w1/wpa_supplicant/ISupplicant.aidl \ + binder/fi/w1/wpa_supplicant/ISupplicantCallbacks.aidl \ + binder/fi/w1/wpa_supplicant/IIface.aidl +LOCAL_SHARED_LIBRARIES := libbinder +include $(BUILD_STATIC_LIBRARY) + +### Binder service library ### +######################## + +include $(CLEAR_VARS) +LOCAL_MODULE := libwpa_binder +LOCAL_CPPFLAGS := $(L_CPPFLAGS) +LOCAL_CFLAGS := $(L_CFLAGS) +LOCAL_C_INCLUDES := $(INCLUDES) +LOCAL_SRC_FILES := \ + binder/binder.cpp binder/binder_manager.cpp \ + binder/supplicant.cpp binder/iface.cpp +LOCAL_SHARED_LIBRARIES := \ + libbinder \ + libutils +LOCAL_STATIC_LIBRARIES := libwpa_binder_interface +include $(BUILD_STATIC_LIBRARY) + +endif # BINDER == y diff --git a/wpa_supplicant/ChangeLog b/wpa_supplicant/ChangeLog index facd90eea30c1..f28055f4093e0 100644 --- a/wpa_supplicant/ChangeLog +++ b/wpa_supplicant/ChangeLog @@ -1,5 +1,149 @@ ChangeLog for wpa_supplicant +2016-10-02 - v2.6 + * fixed WNM Sleep Mode processing when PMF is not enabled + [http://w1.fi/security/2015-6/] (CVE-2015-5310) + * fixed EAP-pwd last fragment validation + [http://w1.fi/security/2015-7/] (CVE-2015-5315) + * fixed EAP-pwd unexpected Confirm message processing + [http://w1.fi/security/2015-8/] (CVE-2015-5316) + * fixed WPS configuration update vulnerability with malformed passphrase + [http://w1.fi/security/2016-1/] (CVE-2016-4476) + * fixed configuration update vulnerability with malformed parameters set + over the local control interface + [http://w1.fi/security/2016-1/] (CVE-2016-4477) + * fixed TK configuration to the driver in EAPOL-Key 3/4 retry case + * extended channel switch support for P2P GO + * started to throttle control interface event message bursts to avoid + issues with monitor sockets running out of buffer space + * mesh mode fixes/improvements + - generate proper AID for peer + - enable WMM by default + - add VHT support + - fix PMKID derivation + - improve robustness on various exchanges + - fix peer link counting in reconnect case + - improve mesh joining behavior + - allow DTIM period to be configured + - allow HT to be disabled (disable_ht=1) + - add MESH_PEER_ADD and MESH_PEER_REMOVE commands + - add support for PMKSA caching + - add minimal support for SAE group negotiation + - allow pairwise/group cipher to be configured in the network profile + - use ieee80211w profile parameter to enable/disable PMF and derive + a separate TX IGTK if PMF is enabled instead of using MGTK + incorrectly + - fix AEK and MTK derivation + - remove GTKdata and IGTKdata from Mesh Peering Confirm/Close + - note: these changes are not fully backwards compatible for secure + (RSN) mesh network + * fixed PMKID derivation with SAE + * added support for requesting and fetching arbitrary ANQP-elements + without internal support in wpa_supplicant for the specific element + (anqp[265]=<hexdump> in "BSS <BSSID>" command output) + * P2P + - filter control characters in group client device names to be + consistent with other P2P peer cases + - support VHT 80+80 MHz and 160 MHz + - indicate group completion in P2P Client role after data association + instead of already after the WPS provisioning step + - improve group-join operation to use SSID, if known, to filter BSS + entries + - added optional ssid=<hexdump> argument to P2P_CONNECT for join case + - added P2P_GROUP_MEMBER command to fetch client interface address + * P2PS + - fix follow-on PD Response behavior + - fix PD Response generation for unknown peer + - fix persistent group reporting + - add channel policy to PD Request + - add group SSID to the P2PS-PROV-DONE event + - allow "P2P_CONNECT <addr> p2ps" to be used without specifying the + default PIN + * BoringSSL + - support for OCSP stapling + - support building of h20-osu-client + * D-Bus + - add ExpectDisconnect() + - add global config parameters as properties + - add SaveConfig() + - add VendorElemAdd(), VendorElemGet(), VendorElemRem() + * fixed Suite B 192-bit AKM to use proper PMK length + (note: this makes old releases incompatible with the fixed behavior) + * improved PMF behavior for cases where the AP and STA has different + configuration by not trying to connect in some corner cases where the + connection cannot succeed + * added option to reopen debug log (e.g., to rotate the file) upon + receipt of SIGHUP signal + * EAP-pwd: added support for Brainpool Elliptic Curves + (with OpenSSL 1.0.2 and newer) + * fixed EAPOL reauthentication after FT protocol run + * fixed FTIE generation for 4-way handshake after FT protocol run + * extended INTERFACE_ADD command to allow certain type (sta/ap) + interface to be created + * fixed and improved various FST operations + * added 80+80 MHz and 160 MHz VHT support for IBSS/mesh + * fixed SIGNAL_POLL in IBSS and mesh cases + * added an option to abort an ongoing scan (used to speed up connection + and can also be done with the new ABORT_SCAN command) + * TLS client + - do not verify CA certificates when ca_cert is not specified + - support validating server certificate hash + - support SHA384 and SHA512 hashes + - add signature_algorithms extension into ClientHello + - support TLS v1.2 signature algorithm with SHA384 and SHA512 + - support server certificate probing + - allow specific TLS versions to be disabled with phase2 parameter + - support extKeyUsage + - support PKCS #5 v2.0 PBES2 + - support PKCS #5 with PKCS #12 style key decryption + - minimal support for PKCS #12 + - support OCSP stapling (including ocsp_multi) + * OpenSSL + - support OpenSSL 1.1 API changes + - drop support for OpenSSL 0.9.8 + - drop support for OpenSSL 1.0.0 + * added support for multiple schedule scan plans (sched_scan_plans) + * added support for external server certificate chain validation + (tls_ext_cert_check=1 in the network profile phase1 parameter) + * made phase2 parser more strict about correct use of auth=<val> and + autheap=<val> values + * improved GAS offchannel operations with comeback request + * added SIGNAL_MONITOR command to request signal strength monitoring + events + * added command for retrieving HS 2.0 icons with in-memory storage + (REQ_HS20_ICON, GET_HS20_ICON, DEL_HS20_ICON commands and + RX-HS20-ICON event) + * enabled ACS support for AP mode operations with wpa_supplicant + * EAP-PEAP: fixed interoperability issue with Windows 2012r2 server + ("Invalid Compound_MAC in cryptobinding TLV") + * EAP-TTLS: fixed success after fragmented final Phase 2 message + * VHT: added interoperability workaround for 80+80 and 160 MHz channels + * WNM: workaround for broken AP operating class behavior + * added kqueue(2) support for eloop (CONFIG_ELOOP_KQUEUE) + * nl80211: + - add support for full station state operations + - do not add NL80211_ATTR_SMPS_MODE attribute if HT is disabled + - add NL80211_ATTR_PREV_BSSID with Connect command + - fix IEEE 802.1X/WEP EAP reauthentication and rekeying to use + unencrypted EAPOL frames + * added initial MBO support; number of extensions to WNM BSS Transition + Management + * added support for PBSS/PCP and P2P on 60 GHz + * Interworking: add credential realm to EAP-TLS identity + * fixed EAPOL-Key Request Secure bit to be 1 if PTK is set + * HS 2.0: add support for configuring frame filters + * added POLL_STA command to check connectivity in AP mode + * added initial functionality for location related operations + * started to ignore pmf=1/2 parameter for non-RSN networks + * added wps_disabled=1 network profile parameter to allow AP mode to + be started without enabling WPS + * wpa_cli: added action script support for AP-ENABLED and AP-DISABLED + events + * improved Public Action frame addressing + - add gas_address3 configuration parameter to control Address 3 + behavior + * number of small fixes + 2015-09-27 - v2.5 * fixed P2P validation of SSID element length before copying it [http://w1.fi/security/2015-1/] (CVE-2015-1863) diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile index ad9ead90a8a99..f3e86c1de6c05 100644 --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile @@ -6,6 +6,17 @@ ifndef CFLAGS CFLAGS = -MMD -O2 -Wall -g endif +ifdef LIBS +# If LIBS is set with some global build system defaults, clone those for +# LIBS_c and LIBS_p to cover wpa_passphrase and wpa_cli as well. +ifndef LIBS_c +LIBS_c := $(LIBS) +endif +ifndef LIBS_p +LIBS_p := $(LIBS) +endif +endif + export LIBDIR ?= /usr/local/lib/ export INCDIR ?= /usr/local/include/ export BINDIR ?= /usr/local/sbin/ @@ -17,6 +28,16 @@ CFLAGS += -I$(abspath ../src/utils) -include .config +ifndef CONFIG_NO_GITVER +# Add VERSION_STR postfix for builds from a git repository +ifeq ($(wildcard ../.git),../.git) +GITVER := $(shell git describe --dirty=+) +ifneq ($(GITVER),) +CFLAGS += -DGIT_VERSION_STR_POSTFIX=\"-$(GITVER)\" +endif +endif +endif + ifdef CONFIG_TESTING_OPTIONS CFLAGS += -DCONFIG_TESTING_OPTIONS CONFIG_WPS_TESTING=y @@ -89,6 +110,7 @@ OBJS_p += ../src/utils/wpabuf.o OBJS_c = wpa_cli.o ../src/common/wpa_ctrl.o OBJS_c += ../src/utils/wpa_debug.o OBJS_c += ../src/utils/common.o +OBJS_c += ../src/common/cli.o OBJS += wmm_ac.o ifndef CONFIG_OS @@ -149,6 +171,10 @@ ifdef CONFIG_ELOOP_EPOLL CFLAGS += -DCONFIG_ELOOP_EPOLL endif +ifdef CONFIG_ELOOP_KQUEUE +CFLAGS += -DCONFIG_ELOOP_KQUEUE +endif + ifdef CONFIG_EAPOL_TEST CFLAGS += -Werror -DEAPOL_TEST endif @@ -278,14 +304,23 @@ NEED_MD5=y NEED_RC4=y else CFLAGS += -DCONFIG_NO_WPA +ifeq ($(CONFIG_TLS), internal) +NEED_SHA1=y +NEED_MD5=y +endif endif ifdef CONFIG_IBSS_RSN NEED_RSN_AUTHENTICATOR=y CFLAGS += -DCONFIG_IBSS_RSN +CFLAGS += -DCONFIG_NO_VLAN OBJS += ibss_rsn.o endif +ifdef CONFIG_MATCH_IFACE +CFLAGS += -DCONFIG_MATCH_IFACE +endif + ifdef CONFIG_P2P OBJS += p2p_supplicant.o OBJS += p2p_supplicant_sd.o @@ -386,7 +421,6 @@ EAPDYN += ../src/eap_peer/eap_tls.so else CFLAGS += -DEAP_TLS OBJS += ../src/eap_peer/eap_tls.o -OBJS_h += ../src/eap_server/eap_server_tls.o endif TLS_FUNCS=y CONFIG_IEEE8021X_EAPOL=y @@ -397,7 +431,6 @@ ifdef CONFIG_EAP_UNAUTH_TLS CFLAGS += -DEAP_UNAUTH_TLS ifndef CONFIG_EAP_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 @@ -412,7 +445,6 @@ else CFLAGS += -DEAP_PEAP OBJS += ../src/eap_peer/eap_peap.o OBJS += ../src/eap_common/eap_peap_common.o -OBJS_h += ../src/eap_server/eap_server_peap.o endif TLS_FUNCS=y CONFIG_IEEE8021X_EAPOL=y @@ -426,7 +458,6 @@ EAPDYN += ../src/eap_peer/eap_ttls.so else CFLAGS += -DEAP_TTLS OBJS += ../src/eap_peer/eap_ttls.o -OBJS_h += ../src/eap_server/eap_server_ttls.o endif TLS_FUNCS=y ifndef CONFIG_FIPS @@ -444,7 +475,6 @@ EAPDYN += ../src/eap_peer/eap_md5.so else CFLAGS += -DEAP_MD5 OBJS += ../src/eap_peer/eap_md5.o -OBJS_h += ../src/eap_server/eap_server_md5.o endif CHAP=y CONFIG_IEEE8021X_EAPOL=y @@ -467,7 +497,6 @@ else CFLAGS += -DEAP_MSCHAPv2 OBJS += ../src/eap_peer/eap_mschapv2.o OBJS += ../src/eap_peer/mschapv2.o -OBJS_h += ../src/eap_server/eap_server_mschapv2.o endif MS_FUNCS=y CONFIG_IEEE8021X_EAPOL=y @@ -481,7 +510,6 @@ EAPDYN += ../src/eap_peer/eap_gtc.so else CFLAGS += -DEAP_GTC OBJS += ../src/eap_peer/eap_gtc.o -OBJS_h += ../src/eap_server/eap_server_gtc.o endif CONFIG_IEEE8021X_EAPOL=y endif @@ -506,7 +534,6 @@ EAPDYN += ../src/eap_peer/eap_sim.so else CFLAGS += -DEAP_SIM OBJS += ../src/eap_peer/eap_sim.o -OBJS_h += ../src/eap_server/eap_server_sim.o endif CONFIG_IEEE8021X_EAPOL=y CONFIG_EAP_SIM_COMMON=y @@ -534,7 +561,6 @@ EAPDYN += ../src/eap_peer/eap_psk.so else CFLAGS += -DEAP_PSK OBJS += ../src/eap_peer/eap_psk.o ../src/eap_common/eap_psk_common.o -OBJS_h += ../src/eap_server/eap_server_psk.o endif CONFIG_IEEE8021X_EAPOL=y NEED_AES=y @@ -551,7 +577,6 @@ EAPDYN += ../src/eap_peer/eap_aka.so else CFLAGS += -DEAP_AKA OBJS += ../src/eap_peer/eap_aka.o -OBJS_h += ../src/eap_server/eap_server_aka.o endif CONFIG_IEEE8021X_EAPOL=y CONFIG_EAP_SIM_COMMON=y @@ -577,7 +602,6 @@ endif ifdef CONFIG_EAP_SIM_COMMON OBJS += ../src/eap_common/eap_sim_common.o -OBJS_h += ../src/eap_server/eap_sim_db.o NEED_AES=y NEED_FIPS186_2_PRF=y endif @@ -592,7 +616,6 @@ else CFLAGS += -DEAP_FAST OBJS += ../src/eap_peer/eap_fast.o ../src/eap_peer/eap_fast_pac.o OBJS += ../src/eap_common/eap_fast_common.o -OBJS_h += ../src/eap_server/eap_server_fast.o endif TLS_FUNCS=y CONFIG_IEEE8021X_EAPOL=y @@ -607,7 +630,6 @@ EAPDYN += ../src/eap_peer/eap_pax.so else CFLAGS += -DEAP_PAX OBJS += ../src/eap_peer/eap_pax.o ../src/eap_common/eap_pax_common.o -OBJS_h += ../src/eap_server/eap_server_pax.o endif CONFIG_IEEE8021X_EAPOL=y endif @@ -620,7 +642,6 @@ EAPDYN += ../src/eap_peer/eap_sake.so else CFLAGS += -DEAP_SAKE OBJS += ../src/eap_peer/eap_sake.o ../src/eap_common/eap_sake_common.o -OBJS_h += ../src/eap_server/eap_server_sake.o endif CONFIG_IEEE8021X_EAPOL=y endif @@ -633,7 +654,6 @@ EAPDYN += ../src/eap_peer/eap_gpsk.so else CFLAGS += -DEAP_GPSK OBJS += ../src/eap_peer/eap_gpsk.o ../src/eap_common/eap_gpsk_common.o -OBJS_h += ../src/eap_server/eap_server_gpsk.o endif CONFIG_IEEE8021X_EAPOL=y ifdef CONFIG_EAP_GPSK_SHA256 @@ -646,7 +666,6 @@ 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_server_pwd.o CONFIG_IEEE8021X_EAPOL=y NEED_SHA256=y endif @@ -659,7 +678,6 @@ EAPDYN += ../src/eap_peer/eap_eke.so else CFLAGS += -DEAP_EKE OBJS += ../src/eap_peer/eap_eke.o ../src/eap_common/eap_eke_common.o -OBJS_h += ../src/eap_server/eap_server_eke.o endif CONFIG_IEEE8021X_EAPOL=y NEED_DH_GROUPS=y @@ -682,7 +700,6 @@ OBJS += ../src/wps/wps_attr_process.o OBJS += ../src/wps/wps_dev_attr.o OBJS += ../src/wps/wps_enrollee.o OBJS += ../src/wps/wps_registrar.o -OBJS_h += ../src/eap_server/eap_server_wsc.o CONFIG_IEEE8021X_EAPOL=y NEED_DH_GROUPS=y NEED_SHA256=y @@ -745,8 +762,6 @@ else CFLAGS += -DEAP_IKEV2 OBJS += ../src/eap_peer/eap_ikev2.o ../src/eap_peer/ikev2.o OBJS += ../src/eap_common/eap_ikev2_common.o ../src/eap_common/ikev2_common.o -OBJS_h += ../src/eap_server/eap_server_ikev2.o -OBJS_h += ../src/eap_server/ikev2.o endif CONFIG_IEEE8021X_EAPOL=y NEED_DH_GROUPS=y @@ -762,7 +777,6 @@ EAPDYN += ../src/eap_peer/eap_vendor_test.so else CFLAGS += -DEAP_VENDOR_TEST OBJS += ../src/eap_peer/eap_vendor_test.o -OBJS_h += ../src/eap_server/eap_server_vendor_test.o endif CONFIG_IEEE8021X_EAPOL=y endif @@ -772,8 +786,6 @@ ifdef CONFIG_EAP_TNC CFLAGS += -DEAP_TNC OBJS += ../src/eap_peer/eap_tnc.o OBJS += ../src/eap_peer/tncc.o -OBJS_h += ../src/eap_server/eap_server_tnc.o -OBJS_h += ../src/eap_server/tncs.o NEED_BASE64=y ifndef CONFIG_NATIVE_WINDOWS ifndef CONFIG_DRIVER_BSD @@ -833,6 +845,8 @@ OBJS += ../src/ap/ap_drv_ops.o OBJS += ../src/ap/beacon.o OBJS += ../src/ap/bss_load.o OBJS += ../src/ap/eap_user_db.o +OBJS += ../src/ap/neighbor_db.o +OBJS += ../src/ap/rrm.o ifdef CONFIG_IEEE80211N OBJS += ../src/ap/ieee802_11_ht.o ifdef CONFIG_IEEE80211AC @@ -842,6 +856,9 @@ endif ifdef CONFIG_WNM OBJS += ../src/ap/wnm_ap.o endif +ifdef CONFIG_MBO +OBJS += ../src/ap/mbo_ap.o +endif ifdef CONFIG_CTRL_IFACE OBJS += ../src/ap/ctrl_iface_ap.o endif @@ -858,6 +875,11 @@ CFLAGS += -DCONFIG_IEEE80211AC endif endif +ifdef CONFIG_MBO +OBJS += mbo.o +CFLAGS += -DCONFIG_MBO +endif + ifdef NEED_AP_MLME OBJS += ../src/ap/wmm.o OBJS += ../src/ap/ap_list.o @@ -893,34 +915,10 @@ OBJS += ../src/ap/peerkey_auth.o endif endif -ifdef CONFIG_EAP_SERVER -CFLAGS += -DEAP_SERVER -OBJS_h += ../src/eap_server/eap_server.o -OBJS_h += ../src/eap_server/eap_server_identity.o -OBJS_h += ../src/eap_server/eap_server_methods.o -endif - -ifdef CONFIG_RADIUS_CLIENT -OBJS_h += ../src/utils/ip_addr.o -OBJS_h += ../src/radius/radius.o -OBJS_h += ../src/radius/radius_client.o -endif - -ifdef CONFIG_AUTHENTICATOR -OBJS_h += ../src/eapol_auth/eapol_auth_sm.o -OBJS_h += ../src/ap/ieee802_1x.o -endif - -ifdef CONFIG_WPA_AUTHENTICATOR -OBJS_h += ../src/ap/wpa_auth.o -OBJS_h += ../src/ap/wpa_auth_ie.o -OBJS_h += ../src/ap/pmksa_cache_auth.o -ifdef CONFIG_IEEE80211R -OBJS_h += ../src/ap/wpa_auth_ft.o -endif -ifdef CONFIG_PEERKEY -OBJS_h += ../src/ap/peerkey_auth.o -endif +ifdef CONFIG_ACS +CFLAGS += -DCONFIG_ACS +OBJS += ../src/ap/acs.o +LIBS += -lm endif ifdef CONFIG_PCSC @@ -933,9 +931,13 @@ ifdef CONFIG_NATIVE_WINDOWS #dynamic symbol loading that is now used in pcsc_funcs.c #LIBS += -lwinscard else +ifdef CONFIG_OSX +LIBS += -framework PCSC +else LIBS += -lpcsclite -lpthread endif endif +endif ifdef CONFIG_SIM_SIMULATOR CFLAGS += -DCONFIG_SIM_SIMULATOR @@ -974,7 +976,6 @@ ifdef TLS_FUNCS NEED_DES=y # Shared TLS functions (needed for EAP_TLS, EAP_PEAP, EAP_TTLS, and EAP_FAST) OBJS += ../src/eap_peer/eap_tls_common.o -OBJS_h += ../src/eap_server/eap_server_tls_common.o ifndef CONFIG_FIPS NEED_TLS_PRF=y NEED_SHA1=y @@ -999,6 +1000,7 @@ ifeq ($(CONFIG_TLS), openssl) ifdef TLS_FUNCS CFLAGS += -DEAP_TLS_OPENSSL OBJS += ../src/crypto/tls_openssl.o +OBJS += ../src/crypto/tls_openssl_ocsp.o LIBS += -lssl endif OBJS += ../src/crypto/crypto_openssl.o @@ -1049,6 +1051,7 @@ OBJS += ../src/tls/tlsv1_cred.o OBJS += ../src/tls/tlsv1_client.o OBJS += ../src/tls/tlsv1_client_write.o OBJS += ../src/tls/tlsv1_client_read.o +OBJS += ../src/tls/tlsv1_client_ocsp.o OBJS += ../src/tls/asn1.o OBJS += ../src/tls/rsa.o OBJS += ../src/tls/x509v3.o @@ -1102,6 +1105,8 @@ CONFIG_INTERNAL_SHA1=y CONFIG_INTERNAL_MD4=y CONFIG_INTERNAL_MD5=y CONFIG_INTERNAL_SHA256=y +CONFIG_INTERNAL_SHA384=y +CONFIG_INTERNAL_SHA512=y CONFIG_INTERNAL_RC4=y CONFIG_INTERNAL_DH_GROUP5=y endif @@ -1287,10 +1292,19 @@ SHA256OBJS += ../src/crypto/sha256-prf.o ifdef CONFIG_INTERNAL_SHA256 SHA256OBJS += ../src/crypto/sha256-internal.o endif +ifdef CONFIG_INTERNAL_SHA384 +CFLAGS += -DCONFIG_INTERNAL_SHA384 +SHA256OBJS += ../src/crypto/sha384-internal.o +endif +ifdef CONFIG_INTERNAL_SHA512 +CFLAGS += -DCONFIG_INTERNAL_SHA512 +SHA256OBJS += ../src/crypto/sha512-internal.o +endif ifdef NEED_TLS_PRF_SHA256 SHA256OBJS += ../src/crypto/sha256-tlsprf.o endif ifdef NEED_HMAC_SHA256_KDF +CFLAGS += -DCONFIG_HMAC_SHA256_KDF OBJS += ../src/crypto/sha256-kdf.o endif OBJS += $(SHA256OBJS) @@ -1333,6 +1347,7 @@ endif CFLAGS += -DCONFIG_CTRL_IFACE ifeq ($(CONFIG_CTRL_IFACE), unix) CFLAGS += -DCONFIG_CTRL_IFACE_UNIX +OBJS += ../src/common/ctrl_iface_common.o endif ifeq ($(CONFIG_CTRL_IFACE), udp) CFLAGS += -DCONFIG_CTRL_IFACE_UDP @@ -1374,6 +1389,7 @@ ifndef DBUS_INCLUDE DBUS_INCLUDE := $(shell $(PKG_CONFIG) --cflags dbus-1) endif DBUS_CFLAGS += $(DBUS_INCLUDE) +DBUS_INTERFACE=fi.epitest.hostap.WPASupplicant endif ifdef CONFIG_CTRL_IFACE_DBUS_NEW @@ -1399,6 +1415,7 @@ DBUS_OBJS += dbus/dbus_new_introspect.o DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_INTRO endif DBUS_CFLAGS += $(DBUS_INCLUDE) +DBUS_INTERFACE=fi.w1.wpa_supplicant1 endif ifdef DBUS @@ -1412,7 +1429,7 @@ LIBS += $(DBUS_LIBS) ifdef CONFIG_READLINE OBJS_c += ../src/utils/edit_readline.o -LIBS_c += -lncurses -lreadline +LIBS_c += -lreadline -lncurses else ifdef CONFIG_WPA_CLI_EDIT OBJS_c += ../src/utils/edit.o @@ -1443,6 +1460,10 @@ ifdef CONFIG_IPV6 CFLAGS += -DCONFIG_IPV6 endif +ifdef CONFIG_NO_LINUX_PACKET_SOCKET_WAR +CFLAGS += -DCONFIG_NO_LINUX_PACKET_SOCKET_WAR +endif + ifdef NEED_BASE64 OBJS += ../src/utils/base64.o endif @@ -1569,12 +1590,6 @@ endif OBJS += ../src/drivers/driver_common.o OBJS_priv += ../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 -endif -OBJS_wpa += $(OBJS_l2) OBJS += wpa_supplicant.o events.o blacklist.o wpas_glue.o scan.o OBJS_t := $(OBJS) $(OBJS_l2) eapol_test.o OBJS_t += ../src/radius/radius_client.o @@ -1636,6 +1651,7 @@ endif OBJS += $(FST_OBJS) OBJS_t += $(FST_OBJS) OBJS_t2 += $(FST_OBJS) +OBJS_nfc += $(FST_OBJS) endif ifndef LDO @@ -1691,9 +1707,11 @@ wpa_cli: $(OBJS_c) LIBCTRL += ../src/common/wpa_ctrl.o LIBCTRL += ../src/utils/os_$(CONFIG_OS).o +LIBCTRL += ../src/utils/common.o LIBCTRL += ../src/utils/wpa_debug.o LIBCTRLSO += ../src/common/wpa_ctrl.c LIBCTRLSO += ../src/utils/os_$(CONFIG_OS).c +LIBCTRLSO += ../src/utils/common.c LIBCTRLSO += ../src/utils/wpa_debug.c libwpa_client.a: $(LIBCTRL) @@ -1705,12 +1723,12 @@ libwpa_client.so: $(LIBCTRLSO) @$(E) " CC $@ ($^)" $(Q)$(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -fPIC $^ -link_test: $(OBJS) $(OBJS_h) tests/link_test.o - $(Q)$(LDO) $(LDFLAGS) -o link_test $(OBJS) $(OBJS_h) tests/link_test.o $(LIBS) +libwpa_test1: libwpa_test.o libwpa_client.a + $(Q)$(LDO) $(LDFLAGS) -o libwpa_test1 libwpa_test.o libwpa_client.a $(LIBS_c) @$(E) " LD " $@ -test_wpa: $(OBJS_wpa) $(OBJS_h) - $(Q)$(LDO) $(LDFLAGS) -o test_wpa $(OBJS_wpa) $(LIBS) +libwpa_test2: libwpa_test.o libwpa_client.so + $(Q)$(LDO) $(LDFLAGS) -o libwpa_test2 libwpa_test.o -L. -lwpa_client $(LIBS_c) @$(E) " LD " $@ nfc_pw_token: $(OBJS_nfc) @@ -1760,11 +1778,13 @@ else endif %.service: %.service.in - $(Q)sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@ + $(Q)sed -e 's|\@BINDIR\@|$(BINDIR)|g' \ + -e 's|\@DBUS_INTERFACE\@|$(DBUS_INTERFACE)|g' $< >$@ @$(E) " sed" $< %@.service: %.service.arg.in - $(Q)sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@ + $(Q)sed -e 's|\@BINDIR\@|$(BINDIR)|g' \ + -e 's|\@DBUS_INTERFACE\@|$(DBUS_INTERFACE)|g' $< >$@ @$(E) " sed" $< wpa_supplicant.exe: wpa_supplicant @@ -1795,17 +1815,6 @@ wpa_gui-qt4/lang/wpa_gui_de.qm: wpa_gui-qt4/lang/wpa_gui_de.ts wpa_gui-qt4: wpa_gui-qt4/Makefile wpa_gui-qt4/lang/wpa_gui_de.qm $(MAKE) -C wpa_gui-qt4 -TEST_EAP_SIM_COMMON_OBJS = $(SHA1OBJS) $(MD5OBJS) \ - ../src/utils/common.o ../src/utils/os_unix.o \ - ../src/utils/wpa_debug.o $(AESOBJS) \ - tests/test_eap_sim_common.o -test-eap_sim_common: $(TEST_EAP_SIM_COMMON_OBJS) - $(LDO) $(LDFLAGS) -o $@ $(TEST_EAP_SIM_COMMON_OBJS) $(LIBS) - ./test-eap_sim_common - rm test-eap_sim_common - -tests: test-eap_sim_common - FIPSDIR=/usr/local/ssl/fips-2.0 FIPSLD=$(FIPSDIR)/bin/fipsld fips: @@ -1826,5 +1835,6 @@ clean: rm -rf lcov-html rm -f libwpa_client.a rm -f libwpa_client.so + rm -f libwpa_test1 libwpa_test2 -include $(OBJS:%.o=%.d) diff --git a/wpa_supplicant/README b/wpa_supplicant/README index f9c65d2e0ff5e..11ab01a9c1710 100644 --- a/wpa_supplicant/README +++ b/wpa_supplicant/README @@ -1,7 +1,7 @@ WPA Supplicant ============== -Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> and contributors +Copyright (c) 2003-2016, Jouni Malinen <j@w1.fi> and contributors All Rights Reserved. This program is licensed under the BSD license (the one with @@ -72,11 +72,13 @@ Supported WPA/IEEE 802.11i features: * EAP-TTLS/CHAP * EAP-SIM * EAP-AKA + * EAP-AKA' * EAP-PSK * EAP-PAX * EAP-SAKE * EAP-IKEv2 * EAP-GPSK + * EAP-pwd * LEAP (note: requires special support from the driver for IEEE 802.11 authentication) (following methods are supported, but since they do not generate keying @@ -163,18 +165,12 @@ systems. In case of Windows builds, WinPcap is used by default Optional libraries for EAP-TLS, EAP-PEAP, and EAP-TTLS: -- OpenSSL (tested with 0.9.7c and 0.9.7d, and 0.9.8 versions; assumed to +- OpenSSL (tested with 1.0.1 and 1.0.2 versions; assumed to work with most relatively recent versions; this is likely to be available with most distributions, http://www.openssl.org/) - GnuTLS - internal TLSv1 implementation -TLS options for EAP-FAST: -- OpenSSL 0.9.8d _with_ openssl-0.9.8d-tls-extensions.patch applied - (i.e., the default OpenSSL package does not include support for - extensions needed for EAP-FAST) -- internal TLSv1 implementation - One of these libraries is needed when EAP-TLS, EAP-PEAP, EAP-TTLS, or EAP-FAST support is enabled. WPA-PSK mode does not require this or EAPOL/EAP implementation. A configuration file, .config, for compilation is @@ -308,7 +304,7 @@ Following build time configuration options are used to control IEEE 802.1X/EAPOL and EAP state machines and all EAP methods. Including TLS, PEAP, or TTLS will require linking wpa_supplicant with OpenSSL library for TLS implementation. Alternatively, GnuTLS or the internal -TLSv1 implementation can be used for TLS functionaly. +TLSv1 implementation can be used for TLS functionality. CONFIG_IEEE8021X_EAPOL=y CONFIG_EAP_MD5=y @@ -320,15 +316,17 @@ CONFIG_EAP_GTC=y CONFIG_EAP_OTP=y CONFIG_EAP_SIM=y CONFIG_EAP_AKA=y +CONFIG_EAP_AKA_PRIME=y CONFIG_EAP_PSK=y CONFIG_EAP_SAKE=y CONFIG_EAP_GPSK=y CONFIG_EAP_PAX=y CONFIG_EAP_LEAP=y CONFIG_EAP_IKEV2=y +CONFIG_EAP_PWD=y Following option can be used to include GSM SIM/USIM interface for GSM/UMTS -authentication algorithm (for EAP-SIM/EAP-AKA). This requires pcsc-lite +authentication algorithm (for EAP-SIM/EAP-AKA/EAP-AKA'). This requires pcsc-lite (http://www.linuxnet.com/) for smart card access. CONFIG_PCSC=y @@ -409,10 +407,10 @@ Command line options -------------------- usage: - wpa_supplicant [-BddfhKLqqtuvwW] [-P<pid file>] [-g<global ctrl>] \ + wpa_supplicant [-BddfhKLqqtuvW] [-P<pid file>] [-g<global ctrl>] \ [-G<group>] \ -i<ifname> -c<config file> [-C<ctrl>] [-D<driver>] [-p<driver_param>] \ - [-b<br_ifname> [-N -i<ifname> -c<conf> [-C<ctrl>] [-D<driver>] \ + [-b<br_ifname> [-MN -i<ifname> -c<conf> [-C<ctrl>] [-D<driver>] \ [-p<driver_param>] [-b<br_ifname>] [-m<P2P Device config file>] ... options: @@ -435,8 +433,8 @@ options: -q = decrease debugging verbosity (-qq even less) -u = enable DBus control interface -v = show version - -w = wait for interface to be added, if needed -W = wait for a control interface monitor before starting + -M = start describing matching interface -N = start describing new interface -m = Configuration file for the P2P Device @@ -479,6 +477,22 @@ wpa_supplicant \ -c wpa2.conf -i wlan1 -D wext +If the interfaces on which wpa_supplicant is to run are not known or do +not exist, wpa_supplicant can match an interface when it arrives. Each +matched interface is separated with -M argument and the -i argument now +allows for pattern matching. + +As an example, the following command would start wpa_supplicant for a +specific wired interface called lan0, any interface starting with wlan +and lastly any other interface. Each match has its own configuration +file, and for the wired interface a specific driver has also been given. + +wpa_supplicant \ + -M -c wpa_wired.conf -ilan0 -D wired \ + -M -c wpa1.conf -iwlan* \ + -M -c wpa2.conf + + 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: @@ -500,7 +514,7 @@ reloading can be triggered with 'wpa_cli reconfigure' command. Configuration file can include one or more network blocks, e.g., one for each used SSID. wpa_supplicant will automatically select the best -betwork based on the order of network blocks in the configuration +network based on the order of network blocks in the configuration file, network security level (WPA/WPA2 is preferred), and signal strength. @@ -792,7 +806,7 @@ addresses, etc. One wpa_cli process in "action" mode needs to be started for each interface. For example, the following command starts wpa_cli for the -default ingterface (-i can be used to select the interface in case of +default interface (-i can be used to select the interface in case of more than one interface being used at the same time): wpa_cli -a/sbin/wpa_action.sh -B @@ -1008,8 +1022,8 @@ event message is indicated that the external processing can start. Once the operation has been completed, "RADIO_WORK done <id>" is used to indicate that to wpa_supplicant. This allows other radio works to be performed. If this command is forgotten (e.g., due to the external -program terminating), wpa_supplicant will time out the radio owrk item -and send "EXT-RADIO-WORK-TIMEOUT <id>" event ot indicate that this has +program terminating), wpa_supplicant will time out the radio work item +and send "EXT-RADIO-WORK-TIMEOUT <id>" event to indicate that this has happened. "RADIO_WORK done <id>" can also be used to cancel items that have not yet been started. diff --git a/wpa_supplicant/README-HS20 b/wpa_supplicant/README-HS20 index 161dc06a2ddd6..e4eed2074f915 100644 --- a/wpa_supplicant/README-HS20 +++ b/wpa_supplicant/README-HS20 @@ -229,7 +229,7 @@ Credentials can be pre-configured for automatic network selection: # # sp_priority: Credential priority within a provisioning SP # This is the priority of the credential among all credentials -# provisionined by the same SP (i.e., for entries that have identical +# provisioned by the same SP (i.e., for entries that have identical # provisioning_sp value). The range of this priority is 0-255 with 0 # being the highest and 255 the lower priority. # @@ -564,3 +564,68 @@ OK <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 + + +Hotspot 2.0 Rel 2 online signup and OSEN +---------------------------------------- + +Following parameters can be used to create a network profile for +link-layer protected Hotspot 2.0 online signup connection with +OSEN. Note that ssid and identify (NAI) values need to be set based on +the information for the selected provider in the OSU Providers list +ANQP-element. + +network={ + ssid="HS 2.0 OSU" + proto=OSEN + key_mgmt=OSEN + pairwise=CCMP + group=GTK_NOT_USED + eap=WFA-UNAUTH-TLS + identity="anonymous@example.com" + ca_cert="osu-ca.pem" + ocsp=2 +} + + +Hotspot 2.0 connection with external network selection +------------------------------------------------------ + +When an component controlling wpa_supplicant takes care of Interworking +network selection, following configuration and network profile +parameters can be used to configure a temporary network profile for a +Hotspot 2.0 connection (e.g., with SET, ADD_NETWORK, SET_NETWORK, and +SELECT_NETWORK control interface commands): + +interworking=1 +hs20=1 +auto_interworking=0 + +network={ + ssid="test-hs20" + proto=RSN + key_mgmt=WPA-EAP + pairwise=CCMP + anonymous_identity="anonymous@example.com" + identity="hs20-test@example.com" + password="password" + ca_cert="ca.pem" + eap=TTLS + phase2="auth=MSCHAPV2" + update_identifier=54321 + #ocsp=2 +} + + +These parameters are set based on the PPS MO credential and/or NAI Realm +list ANQP-element: + +anonymous_identity: Credential/UsernamePassword/Username with username part + replaced with "anonymous" +identity: Credential/UsernamePassword/Username +password: Credential/UsernamePassword/Password +update_identifier: PPS/UpdateIdentifier +ca_cert: from the downloaded trust root based on PPS information +eap: Credential/UsernamePassword/EAPMethod or NAI Realm list +phase2: Credential/UsernamePassword/EAPMethod or NAI Realm list +ocsp: Credential/CheckAAAServerCertStatus diff --git a/wpa_supplicant/README-P2P b/wpa_supplicant/README-P2P index 6a5b032124a99..23ac7fa056a46 100644 --- a/wpa_supplicant/README-P2P +++ b/wpa_supplicant/README-P2P @@ -151,6 +151,7 @@ join-a-group style PD instead of GO Negotiation style PD. p2p_connect <peer device address> <pbc|pin|PIN#|p2ps> [display|keypad|p2ps] [persistent|persistent=<network id>] [join|auth] [go_intent=<0..15>] [freq=<in MHz>] [ht40] [vht] [provdisc] [auto] + [ssid=<hexdump>] Start P2P group formation with a discovered P2P peer. This includes optional group owner negotiation, group interface setup, provisioning, @@ -195,11 +196,17 @@ connection. out whether the peer device is operating as a GO and if so, use join-a-group operation rather than GO Negotiation. +"ssid=<hexdump>" can be used to specify the Group SSID for join +operations. This allows the P2P Client interface to filter scan results +based on SSID to avoid selecting an incorrect BSS entry in case the same +P2P Device or Interface address have been used in multiple groups +recently. + P2PS attribute changes to p2p_connect command: P2PS supports two WPS provisioning methods namely PIN method and P2PS default. -The remaining paramters hold same role as in legacy P2P. In case of P2PS default -config method "p2ps" keyword is added in p2p_connect command. +The remaining parameters hold same role as in legacy P2P. In case of P2PS +default config method "p2ps" keyword is added in p2p_connect command. For example: p2p_connect 02:0a:f5:85:11:00 12345670 p2ps persistent join @@ -281,7 +288,7 @@ 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 +operation. This operation 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 @@ -633,12 +640,17 @@ p2p_set managed <0/1> Disable/enable managed P2P Device operations. This is disabled by default. -p2p_set listen_channel <1/6/11> +p2p_set listen_channel <channel> [<op_class>] Set P2P Listen channel. This is mainly meant for testing purposes and changing the Listen channel during normal operations can result in protocol failures. +When specifying a social channel on the 2.4 GHz band (1/6/11) there is +no need to specify the operating class since it defaults to 81. When +specifying a social channel on the 60 GHz band (2), specify the 60 GHz +operating class (180). + p2p_set ssid_postfix <postfix> Set postfix string to be added to the automatically generated P2P SSID @@ -650,7 +662,7 @@ p2p_set per_sta_psk <0/1> Disabled(default)/enables use of per-client PSK in the P2P groups. This can be used to request GO to assign a unique PSK for each client during WPS provisioning. When enabled, this allow clients to be removed from -the group securily with p2p_remove_client command since that client's +the group securely with p2p_remove_client command since that client's PSK is removed at the same time to prevent it from connecting back using the old PSK. When per-client PSK is not used, the client can still be disconnected, but it will be able to re-join the group since the PSK it diff --git a/wpa_supplicant/README-Windows.txt b/wpa_supplicant/README-Windows.txt new file mode 100644 index 0000000000000..7288abd9a161c --- /dev/null +++ b/wpa_supplicant/README-Windows.txt @@ -0,0 +1,299 @@ +wpa_supplicant for Windows +========================== + +Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi> and contributors +All Rights Reserved. + +This program is licensed under the BSD license (the one with +advertisement clause removed). + + +wpa_supplicant has support for being used as a WPA/WPA2/IEEE 802.1X +Supplicant on Windows. The current port requires that WinPcap +(http://winpcap.polito.it/) is installed for accessing packets and the +driver interface. Both release versions 3.0 and 3.1 are supported. + +The current port is still somewhat experimental. It has been tested +mainly on Windows XP (SP2) with limited set of NDIS drivers. In +addition, the current version has been reported to work with Windows +2000. + +All security modes have been verified to work (at least complete +authentication and successfully ping a wired host): +- plaintext +- static WEP / open system authentication +- static WEP / shared key authentication +- IEEE 802.1X with dynamic WEP keys +- WPA-PSK, TKIP, CCMP, TKIP+CCMP +- WPA-EAP, TKIP, CCMP, TKIP+CCMP +- WPA2-PSK, TKIP, CCMP, TKIP+CCMP +- WPA2-EAP, TKIP, CCMP, TKIP+CCMP + + +Building wpa_supplicant with mingw +---------------------------------- + +The default build setup for wpa_supplicant is to use MinGW and +cross-compiling from Linux to MinGW/Windows. It should also be +possible to build this under Windows using the MinGW tools, but that +is not tested nor supported and is likely to require some changes to +the Makefile unless cygwin is used. + + +Building wpa_supplicant with MSVC +--------------------------------- + +wpa_supplicant can be built with Microsoft Visual C++ compiler. This +has been tested with Microsoft Visual C++ Toolkit 2003 and Visual +Studio 2005 using the included nmake.mak as a Makefile for nmake. IDE +can also be used by creating a project that includes the files and +defines mentioned in nmake.mak. Example VS2005 solution and project +files are included in vs2005 subdirectory. This can be used as a +starting point for building the programs with VS2005 IDE. Visual Studio +2008 Express Edition is also able to use these project files. + +WinPcap development package is needed for the build and this can be +downloaded from http://www.winpcap.org/install/bin/WpdPack_4_0_2.zip. The +default nmake.mak expects this to be unpacked into C:\dev\WpdPack so +that Include and Lib directories are in this directory. The files can be +stored elsewhere as long as the WINPCAPDIR in nmake.mak is updated to +match with the selected directory. In case a project file in the IDE is +used, these Include and Lib directories need to be added to project +properties as additional include/library directories. + +OpenSSL source package can be downloaded from +http://www.openssl.org/source/openssl-0.9.8i.tar.gz and built and +installed following instructions in INSTALL.W32. Note that if EAP-FAST +support will be included in the wpa_supplicant, OpenSSL needs to be +patched to# support it openssl-0.9.8i-tls-extensions.patch. The example +nmake.mak file expects OpenSSL to be installed into C:\dev\openssl, but +this directory can be modified by changing OPENSSLDIR variable in +nmake.mak. + +If you do not need EAP-FAST support, you may also be able to use Win32 +binary installation package of OpenSSL from +http://www.slproweb.com/products/Win32OpenSSL.html instead of building +the library yourself. In this case, you will need to copy Include and +Lib directories in suitable directory, e.g., C:\dev\openssl for the +default nmake.mak. Copy {Win32OpenSSLRoot}\include into +C:\dev\openssl\include and make C:\dev\openssl\lib subdirectory with +files from {Win32OpenSSLRoot}\VC (i.e., libeay*.lib and ssleay*.lib). +This will end up using dynamically linked OpenSSL (i.e., .dll files are +needed) for it. Alternative, you can copy files from +{Win32OpenSSLRoot}\VC\static to create a static build (no OpenSSL .dll +files needed). + + +Building wpa_supplicant for cygwin +---------------------------------- + +wpa_supplicant can be built for cygwin by installing the needed +development packages for cygwin. This includes things like compiler, +make, openssl development package, etc. In addition, developer's pack +for WinPcap (WPdpack.zip) from +http://winpcap.polito.it/install/default.htm is needed. + +.config file should enable only one driver interface, +CONFIG_DRIVER_NDIS. In addition, include directories may need to be +added to match the system. An example configuration is available in +defconfig. The library and include files for WinPcap will either need +to be installed in compiler/linker default directories or their +location will need to be adding to .config when building +wpa_supplicant. + +Othen than this, the build should be more or less identical to Linux +version, i.e., just run make after having created .config file. An +additional tool, win_if_list.exe, can be built by running "make +win_if_list". + + +Building wpa_gui +---------------- + +wpa_gui uses Qt application framework from Trolltech. It can be built +with the open source version of Qt4 and MinGW. Following commands can +be used to build the binary in the Qt 4 Command Prompt: + +# go to the root directory of wpa_supplicant source code +cd wpa_gui-qt4 +qmake -o Makefile wpa_gui.pro +make +# the wpa_gui.exe binary is created into 'release' subdirectory + + +Using wpa_supplicant for Windows +-------------------------------- + +wpa_supplicant, wpa_cli, and wpa_gui behave more or less identically to +Linux version, so instructions in README and example wpa_supplicant.conf +should be applicable for most parts. In addition, there is another +version of wpa_supplicant, wpasvc.exe, which can be used as a Windows +service and which reads its configuration from registry instead of +text file. + +When using access points in "hidden SSID" mode, ap_scan=2 mode need to +be used (see wpa_supplicant.conf for more information). + +Windows NDIS/WinPcap uses quite long interface names, so some care +will be needed when starting wpa_supplicant. Alternatively, the +adapter description can be used as the interface name which may be +easier since it is usually in more human-readable +format. win_if_list.exe can be used to find out the proper interface +name. + +Example steps in starting up wpa_supplicant: + +# win_if_list.exe +ifname: \Device\NPF_GenericNdisWanAdapter +description: Generic NdisWan adapter + +ifname: \Device\NPF_{769E012B-FD17-4935-A5E3-8090C38E25D2} +description: Atheros Wireless Network Adapter (Microsoft's Packet Scheduler) + +ifname: \Device\NPF_{732546E7-E26C-48E3-9871-7537B020A211} +description: Intel 8255x-based Integrated Fast Ethernet (Microsoft's Packet Scheduler) + + +Since the example configuration used Atheros WLAN card, the middle one +is the correct interface in this case. The interface name for -i +command line option is the full string following "ifname:" (the +"\Device\NPF_" prefix can be removed). In other words, wpa_supplicant +would be started with the following command: + +# wpa_supplicant.exe -i'{769E012B-FD17-4935-A5E3-8090C38E25D2}' -c wpa_supplicant.conf -d + +-d optional enables some more debugging (use -dd for even more, if +needed). It can be left out if debugging information is not needed. + +With the alternative mechanism for selecting the interface, this +command has identical results in this case: + +# wpa_supplicant.exe -iAtheros -c wpa_supplicant.conf -d + + +Simple configuration example for WPA-PSK: + +#ap_scan=2 +ctrl_interface= +network={ + ssid="test" + key_mgmt=WPA-PSK + proto=WPA + pairwise=TKIP + psk="secret passphrase" +} + +(remove '#' from the comment out ap_scan line to enable mode in which +wpa_supplicant tries to associate with the SSID without doing +scanning; this allows APs with hidden SSIDs to be used) + + +wpa_cli.exe and wpa_gui.exe can be used to interact with the +wpa_supplicant.exe program in the same way as with Linux. Note that +ctrl_interface is using UNIX domain sockets when built for cygwin, but +the native build for Windows uses named pipes and the contents of the +ctrl_interface configuration item is used to control access to the +interface. Anyway, this variable has to be included in the configuration +to enable the control interface. + + +Example SDDL string formats: + +(local admins group has permission, but nobody else): + +ctrl_interface=SDDL=D:(A;;GA;;;BA) + +("A" == "access allowed", "GA" == GENERIC_ALL == all permissions, and +"BA" == "builtin administrators" == the local admins. The empty fields +are for flags and object GUIDs, none of which should be required in this +case.) + +(local admins and the local "power users" group have permissions, +but nobody else): + +ctrl_interface=SDDL=D:(A;;GA;;;BA)(A;;GA;;;PU) + +(One ACCESS_ALLOWED ACE for GENERIC_ALL for builtin administrators, and +one ACCESS_ALLOWED ACE for GENERIC_ALL for power users.) + +(close to wide open, but you have to be a valid user on +the machine): + +ctrl_interface=SDDL=D:(A;;GA;;;AU) + +(One ACCESS_ALLOWED ACE for GENERIC_ALL for the "authenticated users" +group.) + +This one would allow absolutely everyone (including anonymous +users) -- this is *not* recommended, since named pipes can be attached +to from anywhere on the network (i.e. there's no "this machine only" +like there is with 127.0.0.1 sockets): + +ctrl_interface=SDDL=D:(A;;GA;;;BU)(A;;GA;;;AN) + +(BU == "builtin users", "AN" == "anonymous") + +See also [1] for the format of ACEs, and [2] for the possible strings +that can be used for principal names. + +[1] +http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthz/security/ace_strings.asp +[2] +http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthz/security/sid_strings.asp + + +Starting wpa_supplicant as a Windows service (wpasvc.exe) +--------------------------------------------------------- + +wpa_supplicant can be started as a Windows service by using wpasvc.exe +program that is alternative build of wpa_supplicant.exe. Most of the +core functionality of wpasvc.exe is identical to wpa_supplicant.exe, +but it is using Windows registry for configuration information instead +of a text file and command line parameters. In addition, it can be +registered as a service that can be started automatically or manually +like any other Windows service. + +The root of wpa_supplicant configuration in registry is +HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant. This level includes global +parameters and a 'interfaces' subkey with all the interface configuration +(adapter to confname mapping). Each such mapping is a subkey that has +'adapter', 'config', and 'ctrl_interface' values. + +This program can be run either as a normal command line application, +e.g., for debugging, with 'wpasvc.exe app' or as a Windows service. +Service need to be registered with 'wpasvc.exe reg <full path to +wpasvc.exe>'. Alternatively, 'wpasvc.exe reg' can be used to register +the service with the current location of wpasvc.exe. After this, wpasvc +can be started like any other Windows service (e.g., 'net start wpasvc') +or it can be configured to start automatically through the Services tool +in administrative tasks. The service can be unregistered with +'wpasvc.exe unreg'. + +If the service is set to start during system bootup to make the +network connection available before any user has logged in, there may +be a long (half a minute or so) delay in starting up wpa_supplicant +due to WinPcap needing a driver called "Network Monitor Driver" which +is started by default on demand. + +To speed up wpa_supplicant start during system bootup, "Network +Monitor Driver" can be configured to be started sooner by setting its +startup type to System instead of the default Demand. To do this, open +up Device Manager, select Show Hidden Devices, expand the "Non +Plug-and-Play devices" branch, double click "Network Monitor Driver", +go to the Driver tab, and change the Demand setting to System instead. + +Configuration data is in HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant\configs +key. Each configuration profile has its own key under this. In terms of text +files, each profile would map to a separate text file with possibly multiple +networks. Under each profile, there is a networks key that lists all +networks as a subkey. Each network has set of values in the same way as +network block in the configuration file. In addition, blobs subkey has +possible blobs as values. + +HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant\configs\test\networks\0000 + ssid="example" + key_mgmt=WPA-PSK + +See win_example.reg for an example on how to setup wpasvc.exe +parameters in registry. It can also be imported to registry as a +starting point for the configuration. diff --git a/wpa_supplicant/android.config b/wpa_supplicant/android.config new file mode 100644 index 0000000000000..02505bb991aa4 --- /dev/null +++ b/wpa_supplicant/android.config @@ -0,0 +1,492 @@ +# Example wpa_supplicant build time configuration +# +# This file lists the configuration options that are used when building the +# hostapd binary. All lines starting with # are ignored. Configuration option +# lines must be commented out complete, if they are not to be included, i.e., +# just setting VARIABLE=n is not disabling that variable. +# +# This file is included in Makefile, so variables like CFLAGS and LIBS can also +# be modified from here. In most cases, these lines should use += in order not +# to override previous values of the variables. + + +# Uncomment following two lines and fix the paths if you have installed OpenSSL +# or GnuTLS in non-default location +#CFLAGS += -I/usr/local/openssl/include +#LIBS += -L/usr/local/openssl/lib + +# Some Red Hat versions seem to include kerberos header files from OpenSSL, but +# the kerberos files are not in the default include path. Following line can be +# used to fix build issues on such systems (krb5.h not found). +#CFLAGS += -I/usr/include/kerberos + +# Driver interface for generic Linux wireless extensions +# Note: WEXT is deprecated in the current Linux kernel version and no new +# functionality is added to it. nl80211-based interface is the new +# replacement for WEXT and its use allows wpa_supplicant to properly control +# the driver to improve existing functionality like roaming and to support new +# functionality. +#CONFIG_DRIVER_WEXT=y + +# Driver interface for Linux drivers using the nl80211 kernel interface +#CONFIG_DRIVER_NL80211=y +CONFIG_LIBNL20=y + +# QCA vendor extensions to nl80211 +CONFIG_DRIVER_NL80211_QCA=y + +# Driver interface for FreeBSD net80211 layer (e.g., Atheros driver) +#CONFIG_DRIVER_BSD=y +#CFLAGS += -I/usr/local/include +#LIBS += -L/usr/local/lib +#LIBS_p += -L/usr/local/lib +#LIBS_c += -L/usr/local/lib + +# Driver interface for Windows NDIS +#CONFIG_DRIVER_NDIS=y +#CFLAGS += -I/usr/include/w32api/ddk +#LIBS += -L/usr/local/lib +# For native build using mingw +#CONFIG_NATIVE_WINDOWS=y +# Additional directories for cross-compilation on Linux host for mingw target +#CFLAGS += -I/opt/mingw/mingw32/include/ddk +#LIBS += -L/opt/mingw/mingw32/lib +#CC=mingw32-gcc +# By default, driver_ndis uses WinPcap for low-level operations. This can be +# replaced with the following option which replaces WinPcap calls with NDISUIO. +# However, this requires that WZC is disabled (net stop wzcsvc) before starting +# wpa_supplicant. +# CONFIG_USE_NDISUIO=y + +# Driver interface for wired Ethernet drivers +#CONFIG_DRIVER_WIRED=y + +# Driver interface for the Broadcom RoboSwitch family +#CONFIG_DRIVER_ROBOSWITCH=y + +# Driver interface for no driver (e.g., WPS ER only) +#CONFIG_DRIVER_NONE=y + +# Solaris libraries +#LIBS += -lsocket -ldlpi -lnsl +#LIBS_c += -lsocket + +# Enable IEEE 802.1X Supplicant (automatically included if any EAP method is +# included) +CONFIG_IEEE8021X_EAPOL=y + +# EAP-MD5 +CONFIG_EAP_MD5=y + +# EAP-MSCHAPv2 +CONFIG_EAP_MSCHAPV2=y + +# EAP-TLS +CONFIG_EAP_TLS=y + +# EAL-PEAP +CONFIG_EAP_PEAP=y + +# EAP-TTLS +CONFIG_EAP_TTLS=y + +# EAP-FAST +# Note: Default OpenSSL package does not include support for all the +# functionality needed for EAP-FAST. If EAP-FAST is enabled with OpenSSL, +# the OpenSSL library must be patched (openssl-0.9.8d-tls-extensions.patch) +# to add the needed functions. +#CONFIG_EAP_FAST=y + +# EAP-GTC +CONFIG_EAP_GTC=y + +# EAP-OTP +CONFIG_EAP_OTP=y + +# EAP-SIM (enable CONFIG_PCSC, if EAP-SIM is used) +CONFIG_EAP_SIM=y + +# EAP-PSK (experimental; this is _not_ needed for WPA-PSK) +#CONFIG_EAP_PSK=y + +# EAP-pwd (secure authentication using only a password) +CONFIG_EAP_PWD=y + +# EAP-PAX +#CONFIG_EAP_PAX=y + +# LEAP +CONFIG_EAP_LEAP=y + +# EAP-AKA (enable CONFIG_PCSC, if EAP-AKA is used) +CONFIG_EAP_AKA=y + +# EAP-AKA' (enable CONFIG_PCSC, if EAP-AKA' is used). +# This requires CONFIG_EAP_AKA to be enabled, too. +CONFIG_EAP_AKA_PRIME=y + +# Enable USIM simulator (Milenage) for EAP-AKA +#CONFIG_USIM_SIMULATOR=y + +# EAP-SAKE +#CONFIG_EAP_SAKE=y + +# EAP-GPSK +#CONFIG_EAP_GPSK=y +# Include support for optional SHA256 cipher suite in EAP-GPSK +#CONFIG_EAP_GPSK_SHA256=y + +# EAP-TNC and related Trusted Network Connect support (experimental) +#CONFIG_EAP_TNC=y + +# Wi-Fi Protected Setup (WPS) +CONFIG_WPS=y +# Enable WPS external registrar functionality +CONFIG_WPS_ER=y +# Disable credentials for an open network by default when acting as a WPS +# registrar. +#CONFIG_WPS_REG_DISABLE_OPEN=y +# Enable WPS support with NFC config method +CONFIG_WPS_NFC=y + +# EAP-IKEv2 +#CONFIG_EAP_IKEV2=y + +# PKCS#12 (PFX) support (used to read private key and certificate file from +# a file that usually has extension .p12 or .pfx) +CONFIG_PKCS12=y + +# Smartcard support (i.e., private key on a smartcard), e.g., with openssl +# engine. +CONFIG_SMARTCARD=y + +# PC/SC interface for smartcards (USIM, GSM SIM) +# Enable this if EAP-SIM or EAP-AKA is included +#CONFIG_PCSC=y + +# Support HT overrides (disable HT/HT40, mask MCS rates, etc.) +#CONFIG_HT_OVERRIDES=y + +# Support VHT overrides (disable VHT, mask MCS rates, etc.) +#CONFIG_VHT_OVERRIDES=y + +# Development testing +#CONFIG_EAPOL_TEST=y + +# Select control interface backend for external programs, e.g, wpa_cli: +# unix = UNIX domain sockets (default for Linux/*BSD) +# udp = UDP sockets using localhost (127.0.0.1) +# 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. +CONFIG_CTRL_IFACE=y + +# Include support for GNU Readline and History Libraries in wpa_cli. +# When building a wpa_cli binary for distribution, please note that these +# libraries are licensed under GPL and as such, BSD license may not apply for +# the resulting binary. +#CONFIG_READLINE=y + +# Include internal line edit mode in wpa_cli. This can be used as a replacement +# for GNU Readline to provide limited command line editing and history support. +CONFIG_WPA_CLI_EDIT=y + +# Remove debugging code that is printing out debug message to stdout. +# This can be used to reduce the size of the wpa_supplicant considerably +# if debugging code is not needed. The size reduction can be around 35% +# (e.g., 90 kB). +#CONFIG_NO_STDOUT_DEBUG=y + +# Remove WPA support, e.g., for wired-only IEEE 802.1X supplicant, to save +# 35-50 kB in code size. +#CONFIG_NO_WPA=y + +# Remove IEEE 802.11i/WPA-Personal ASCII passphrase support +# This option can be used to reduce code size by removing support for +# converting ASCII passphrases into PSK. If this functionality is removed, the +# PSK can only be configured as the 64-octet hexstring (e.g., from +# wpa_passphrase). This saves about 0.5 kB in code size. +#CONFIG_NO_WPA_PASSPHRASE=y + +# Disable scan result processing (ap_mode=1) to save code size by about 1 kB. +# This can be used if ap_scan=1 mode is never enabled. +#CONFIG_NO_SCAN_PROCESSING=y + +# Select configuration backend: +# file = text file (e.g., wpa_supplicant.conf; note: the configuration file +# path is given on command line, not here; this option is just used to +# select the backend that allows configuration files to be used) +# winreg = Windows registry (see win_example.reg for an example) +CONFIG_BACKEND=file + +# Remove configuration write functionality (i.e., to allow the configuration +# file to be updated based on runtime configuration changes). The runtime +# configuration can still be changed, the changes are just not going to be +# persistent over restarts. This option can be used to reduce code size by +# about 3.5 kB. +#CONFIG_NO_CONFIG_WRITE=y + +# Remove support for configuration blobs to reduce code size by about 1.5 kB. +#CONFIG_NO_CONFIG_BLOBS=y + +# Select program entry point implementation: +# main = UNIX/POSIX like main() function (default) +# main_winsvc = Windows service (read parameters from registry) +# main_none = Very basic example (development use only) +#CONFIG_MAIN=main + +# Select wrapper for operating system and C library specific functions +# unix = UNIX/POSIX like systems (default) +# win32 = Windows systems +# none = Empty template +CONFIG_OS=unix + +# Select event loop implementation +# eloop = select() loop (default) +# eloop_win = Windows events and WaitForMultipleObject() loop +CONFIG_ELOOP=eloop + +# Should we use poll instead of select? Select is used by default. +#CONFIG_ELOOP_POLL=y + +# Should we use epoll instead of select? Select is used by default. +#CONFIG_ELOOP_EPOLL=y + +# Select layer 2 packet implementation +# linux = Linux packet socket (default) +# pcap = libpcap/libdnet/WinPcap +# freebsd = FreeBSD libpcap +# winpcap = WinPcap with receive thread +# ndis = Windows NDISUIO (note: requires CONFIG_USE_NDISUIO=y) +# none = Empty template +CONFIG_L2_PACKET=linux + +# PeerKey handshake for Station to Station Link (IEEE 802.11e DLS) +CONFIG_PEERKEY=y + +# IEEE 802.11w (management frame protection), also known as PMF +# Driver support is also needed for IEEE 802.11w. +CONFIG_IEEE80211W=y + +# Select TLS implementation +# openssl = OpenSSL (default) +# gnutls = GnuTLS +# internal = Internal TLSv1 implementation (experimental) +# none = Empty template +#CONFIG_TLS=openssl + +# TLS-based EAP methods require at least TLS v1.0. Newer version of TLS (v1.1) +# can be enabled to get a stronger construction of messages when block ciphers +# are used. It should be noted that some existing TLS v1.0 -based +# implementation may not be compatible with TLS v1.1 message (ClientHello is +# sent prior to negotiating which version will be used) +#CONFIG_TLSV11=y + +# TLS-based EAP methods require at least TLS v1.0. Newer version of TLS (v1.2) +# can be enabled to enable use of stronger crypto algorithms. It should be +# noted that some existing TLS v1.0 -based implementation may not be compatible +# with TLS v1.2 message (ClientHello is sent prior to negotiating which version +# will be used) +#CONFIG_TLSV12=y + +# If CONFIG_TLS=internal is used, additional library and include paths are +# needed for LibTomMath. Alternatively, an integrated, minimal version of +# LibTomMath can be used. See beginning of libtommath.c for details on benefits +# and drawbacks of this option. +#CONFIG_INTERNAL_LIBTOMMATH=y +#ifndef CONFIG_INTERNAL_LIBTOMMATH +#LTM_PATH=/usr/src/libtommath-0.39 +#CFLAGS += -I$(LTM_PATH) +#LIBS += -L$(LTM_PATH) +#LIBS_p += -L$(LTM_PATH) +#endif +# At the cost of about 4 kB of additional binary size, the internal LibTomMath +# can be configured to include faster routines for exptmod, sqr, and div to +# speed up DH and RSA calculation considerably +#CONFIG_INTERNAL_LIBTOMMATH_FAST=y + +# Include NDIS event processing through WMI into wpa_supplicant/wpasvc. +# This is only for Windows builds and requires WMI-related header files and +# WbemUuid.Lib from Platform SDK even when building with MinGW. +#CONFIG_NDIS_EVENTS_INTEGRATED=y +#PLATFORMSDKLIB="/opt/Program Files/Microsoft Platform SDK/Lib" + +# Add support for old DBus control interface +# (fi.epitest.hostap.WPASupplicant) +#CONFIG_CTRL_IFACE_DBUS=y + +# Add support for new DBus control interface +# (fi.w1.hostap.wpa_supplicant1) +#CONFIG_CTRL_IFACE_DBUS_NEW=y + +# Add introspection support for new DBus control interface +#CONFIG_CTRL_IFACE_DBUS_INTRO=y + +# Add support for Binder control interface +# Only applicable for Android platforms. +#CONFIG_CTRL_IFACE_BINDER=y + +# Add support for loading EAP methods dynamically as shared libraries. +# When this option is enabled, each EAP method can be either included +# statically (CONFIG_EAP_<method>=y) or dynamically (CONFIG_EAP_<method>=dyn). +# Dynamic EAP methods are build as shared objects (eap_*.so) and they need to +# be loaded in the beginning of the wpa_supplicant configuration file +# (see load_dynamic_eap parameter in the example file) before being used in +# the network blocks. +# +# Note that some shared parts of EAP methods are included in the main program +# and in order to be able to use dynamic EAP methods using these parts, the +# main program must have been build with the EAP method enabled (=y or =dyn). +# This means that EAP-TLS/PEAP/TTLS/FAST cannot be added as dynamic libraries +# unless at least one of them was included in the main build to force inclusion +# of the shared code. Similarly, at least one of EAP-SIM/AKA must be included +# in the main build to be able to load these methods dynamically. +# +# Please also note that using dynamic libraries will increase the total binary +# size. Thus, it may not be the best option for targets that have limited +# amount of memory/flash. +#CONFIG_DYNAMIC_EAP_METHODS=y + +# IEEE Std 802.11r-2008 (Fast BSS Transition) +CONFIG_IEEE80211R=y + +# Add support for writing debug log to a file (/tmp/wpa_supplicant-log-#.txt) +#CONFIG_DEBUG_FILE=y + +# Send debug messages to syslog instead of stdout +#CONFIG_DEBUG_SYSLOG=y +# Set syslog facility for debug messages +#CONFIG_DEBUG_SYSLOG_FACILITY=LOG_DAEMON + +# Add support for sending all debug messages (regardless of debug verbosity) +# to the Linux kernel tracing facility. This helps debug the entire stack by +# making it easy to record everything happening from the driver up into the +# same file, e.g., using trace-cmd. +#CONFIG_DEBUG_LINUX_TRACING=y + +# Add support for writing debug log to Android logcat instead of standard +# output +CONFIG_ANDROID_LOG=y + +# Enable privilege separation (see README 'Privilege separation' for details) +#CONFIG_PRIVSEP=y + +# Enable mitigation against certain attacks against TKIP by delaying Michael +# MIC error reports by a random amount of time between 0 and 60 seconds +#CONFIG_DELAYED_MIC_ERROR_REPORT=y + +# Enable tracing code for developer debugging +# This tracks use of memory allocations and other registrations and reports +# incorrect use with a backtrace of call (or allocation) location. +#CONFIG_WPA_TRACE=y +# For BSD, uncomment these. +#LIBS += -lexecinfo +#LIBS_p += -lexecinfo +#LIBS_c += -lexecinfo + +# Use libbfd to get more details for developer debugging +# This enables use of libbfd to get more detailed symbols for the backtraces +# generated by CONFIG_WPA_TRACE=y. +#CONFIG_WPA_TRACE_BFD=y +# For BSD, uncomment these. +#LIBS += -lbfd -liberty -lz +#LIBS_p += -lbfd -liberty -lz +#LIBS_c += -lbfd -liberty -lz + +# wpa_supplicant depends on strong random number generation being available +# from the operating system. os_get_random() function is used to fetch random +# data when needed, e.g., for key generation. On Linux and BSD systems, this +# works by reading /dev/urandom. It should be noted that the OS entropy pool +# needs to be properly initialized before wpa_supplicant is started. This is +# important especially on embedded devices that do not have a hardware random +# number generator and may by default start up with minimal entropy available +# for random number generation. +# +# As a safety net, wpa_supplicant is by default trying to internally collect +# additional entropy for generating random data to mix in with the data fetched +# from the OS. This by itself is not considered to be very strong, but it may +# help in cases where the system pool is not initialized properly. However, it +# is very strongly recommended that the system pool is initialized with enough +# entropy either by using hardware assisted random number generator or by +# storing state over device reboots. +# +# wpa_supplicant can be configured to maintain its own entropy store over +# restarts to enhance random number generation. This is not perfect, but it is +# much more secure than using the same sequence of random numbers after every +# reboot. This can be enabled with -e<entropy file> command line option. The +# specified file needs to be readable and writable by wpa_supplicant. +# +# If the os_get_random() is known to provide strong random data (e.g., on +# Linux/BSD, the board in question is known to have reliable source of random +# data from /dev/urandom), the internal wpa_supplicant random pool can be +# disabled. This will save some in binary size and CPU use. However, this +# should only be considered for builds that are known to be used on devices +# that meet the requirements described above. +#CONFIG_NO_RANDOM_POOL=y + +# IEEE 802.11n (High Throughput) support (mainly for AP mode) +CONFIG_IEEE80211N=y + +# Wireless Network Management (IEEE Std 802.11v-2011) +# Note: This is experimental and not complete implementation. +CONFIG_WNM=y + +# Interworking (IEEE 802.11u) +# This can be used to enable functionality to improve interworking with +# external networks (GAS/ANQP to learn more about the networks and network +# selection based on available credentials). +CONFIG_INTERWORKING=y + +# Hotspot 2.0 +CONFIG_HS20=y + +# Disable roaming in wpa_supplicant +CONFIG_NO_ROAMING=y + +# AP mode operations with wpa_supplicant +# This can be used for controlling AP mode operations with wpa_supplicant. It +# should be noted that this is mainly aimed at simple cases like +# WPA2-Personal while more complex configurations like WPA2-Enterprise with an +# external RADIUS server can be supported with hostapd. +CONFIG_AP=y + +# P2P (Wi-Fi Direct) +# This can be used to enable P2P support in wpa_supplicant. See README-P2P for +# more information on P2P operations. +CONFIG_P2P=y + +# Enable TDLS support +CONFIG_TDLS=y + +# Wi-Fi Direct +# This can be used to enable Wi-Fi Direct extensions for P2P using an external +# program to control the additional information exchanges in the messages. +CONFIG_WIFI_DISPLAY=y + +# Autoscan +# This can be used to enable automatic scan support in wpa_supplicant. +# See wpa_supplicant.conf for more information on autoscan usage. +# +# Enabling directly a module will enable autoscan support. +# For exponential module: +#CONFIG_AUTOSCAN_EXPONENTIAL=y +# For periodic module: +#CONFIG_AUTOSCAN_PERIODIC=y + +# Password (and passphrase, etc.) backend for external storage +# These optional mechanisms can be used to add support for storing passwords +# and other secrets in external (to wpa_supplicant) location. This allows, for +# example, operating system specific key storage to be used +# +# External password backend for testing purposes (developer use) +#CONFIG_EXT_PASSWORD_TEST=y + +# Enable Fast Session Transfer (FST) +#CONFIG_FST=y + +# Support Multi Band Operation +#CONFIG_MBO=y + +include $(wildcard $(LOCAL_PATH)/android_config_*.inc) diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c index 7a4f4cf4fbe90..5afb772ba192e 100644 --- a/wpa_supplicant/ap.c +++ b/wpa_supplicant/ap.c @@ -56,12 +56,32 @@ static void wpas_conf_ap_vht(struct wpa_supplicant *wpa_s, if (!conf->secondary_channel) goto no_vht; - center_chan = wpas_p2p_get_vht80_center(wpa_s, mode, channel); + switch (conf->vht_oper_chwidth) { + case VHT_CHANWIDTH_80MHZ: + case VHT_CHANWIDTH_80P80MHZ: + center_chan = wpas_p2p_get_vht80_center(wpa_s, mode, channel); + break; + case VHT_CHANWIDTH_160MHZ: + center_chan = wpas_p2p_get_vht160_center(wpa_s, mode, channel); + break; + default: + /* + * conf->vht_oper_chwidth might not be set for non-P2P GO cases, + * try oper_cwidth 160 MHz first then VHT 80 MHz, if 160 MHz is + * not supported. + */ + conf->vht_oper_chwidth = VHT_CHANWIDTH_160MHZ; + center_chan = wpas_p2p_get_vht160_center(wpa_s, mode, channel); + if (!center_chan) { + conf->vht_oper_chwidth = VHT_CHANWIDTH_80MHZ; + center_chan = wpas_p2p_get_vht80_center(wpa_s, mode, + channel); + } + break; + } if (!center_chan) goto no_vht; - /* Use 80 MHz channel */ - conf->vht_oper_chwidth = 1; conf->vht_oper_centr_freq_seg0_idx = center_chan; return; @@ -72,14 +92,24 @@ no_vht: conf->vht_oper_centr_freq_seg0_idx = conf->channel + conf->secondary_channel * 2; #endif /* CONFIG_P2P */ + conf->vht_oper_chwidth = VHT_CHANWIDTH_USE_HT; } #endif /* CONFIG_IEEE80211N */ -void wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s, - struct wpa_ssid *ssid, - struct hostapd_config *conf) +int wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + struct hostapd_config *conf) { + conf->hw_mode = ieee80211_freq_to_chan(ssid->frequency, + &conf->channel); + + if (conf->hw_mode == NUM_HOSTAPD_MODES) { + wpa_printf(MSG_ERROR, "Unsupported AP mode frequency: %d MHz", + ssid->frequency); + return -1; + } + /* TODO: enable HT40 if driver supports it; * drop to 11b if driver does not support 11g */ @@ -166,6 +196,8 @@ void wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s, } } #endif /* CONFIG_IEEE80211N */ + + return 0; } @@ -179,15 +211,23 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, os_strlcpy(bss->iface, wpa_s->ifname, sizeof(bss->iface)); - conf->hw_mode = ieee80211_freq_to_chan(ssid->frequency, - &conf->channel); - if (conf->hw_mode == NUM_HOSTAPD_MODES) { - wpa_printf(MSG_ERROR, "Unsupported AP mode frequency: %d MHz", - ssid->frequency); + if (wpa_supplicant_conf_ap_ht(wpa_s, ssid, conf)) + return -1; + + if (ssid->pbss > 1) { + wpa_printf(MSG_ERROR, "Invalid pbss value(%d) for AP mode", + ssid->pbss); return -1; } + bss->pbss = ssid->pbss; - wpa_supplicant_conf_ap_ht(wpa_s, ssid, conf); +#ifdef CONFIG_ACS + if (ssid->acs) { + /* Setting channel to 0 in order to enable ACS */ + conf->channel = 0; + wpa_printf(MSG_DEBUG, "Use automatic channel selection"); + } +#endif /* CONFIG_ACS */ if (ieee80211_is_dfs(ssid->frequency) && wpa_s->conf->country[0]) { conf->ieee80211h = 1; @@ -229,12 +269,12 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, bss->force_per_enrollee_psk = wpa_s->global->p2p_per_sta_psk; if (ssid->p2p_group) { - os_memcpy(bss->ip_addr_go, wpa_s->parent->conf->ip_addr_go, 4); - os_memcpy(bss->ip_addr_mask, wpa_s->parent->conf->ip_addr_mask, + os_memcpy(bss->ip_addr_go, wpa_s->p2pdev->conf->ip_addr_go, 4); + os_memcpy(bss->ip_addr_mask, wpa_s->p2pdev->conf->ip_addr_mask, 4); os_memcpy(bss->ip_addr_start, - wpa_s->parent->conf->ip_addr_start, 4); - os_memcpy(bss->ip_addr_end, wpa_s->parent->conf->ip_addr_end, + wpa_s->p2pdev->conf->ip_addr_start, 4); + os_memcpy(bss->ip_addr_end, wpa_s->p2pdev->conf->ip_addr_end, 4); } #endif /* CONFIG_P2P */ @@ -254,7 +294,10 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt)) bss->wpa = ssid->proto; - bss->wpa_key_mgmt = ssid->key_mgmt; + if (ssid->key_mgmt == DEFAULT_KEY_MGMT) + bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK; + else + bss->wpa_key_mgmt = ssid->key_mgmt; bss->wpa_pairwise = ssid->pairwise_cipher; if (ssid->psk_set) { bin_clear_free(bss->ssid.wpa_psk, sizeof(*bss->ssid.wpa_psk)); @@ -263,6 +306,7 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, return -1; os_memcpy(bss->ssid.wpa_psk->psk, ssid->psk, PMK_LEN); bss->ssid.wpa_psk->group = 1; + bss->ssid.wpa_psk_set = 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] || @@ -297,13 +341,17 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, conf->beacon_int = wpa_s->conf->beacon_int; #ifdef CONFIG_P2P - if (wpa_s->conf->p2p_go_ctwindow > conf->beacon_int) { - wpa_printf(MSG_INFO, - "CTWindow (%d) is bigger than beacon interval (%d) - avoid configuring it", - wpa_s->conf->p2p_go_ctwindow, conf->beacon_int); - conf->p2p_go_ctwindow = 0; - } else { - conf->p2p_go_ctwindow = wpa_s->conf->p2p_go_ctwindow; + if (ssid->mode == WPAS_MODE_P2P_GO || + ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) { + if (wpa_s->conf->p2p_go_ctwindow > conf->beacon_int) { + wpa_printf(MSG_INFO, + "CTWindow (%d) is bigger than beacon interval (%d) - avoid configuring it", + wpa_s->conf->p2p_go_ctwindow, + conf->beacon_int); + conf->p2p_go_ctwindow = 0; + } else { + conf->p2p_go_ctwindow = wpa_s->conf->p2p_go_ctwindow; + } } #endif /* CONFIG_P2P */ @@ -372,6 +420,8 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, !(bss->wpa & 2))) goto no_wps; /* WPS2 does not allow WPA/TKIP-only * configuration */ + if (ssid->wps_disabled) + goto no_wps; bss->eap_server = 1; if (!ssid->ignore_broadcast_ssid) @@ -400,6 +450,8 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, 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; + if (ssid->eap.fragment_size != DEFAULT_FRAGMENT_SIZE) + bss->fragment_size = ssid->eap.fragment_size; no_wps: #endif /* CONFIG_WPS */ @@ -416,6 +468,9 @@ no_wps: wpabuf_dup(wpa_s->conf->ap_vendor_elements); } + bss->ftm_responder = wpa_s->conf->ftm_responder; + bss->ftm_initiator = wpa_s->conf->ftm_initiator; + return 0; } @@ -448,14 +503,14 @@ static void ap_wps_event_cb(void *ctx, enum wps_event event, if (event == WPS_EV_FAIL) { struct wps_event_fail *fail = &data->fail; - if (wpa_s->parent && wpa_s->parent != wpa_s && + if (wpa_s->p2pdev && wpa_s->p2pdev != 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 + wpa_msg(wpa_s->p2pdev, MSG_INFO, WPS_EVENT_FAIL "msg=%d config_error=%d", fail->msg, fail->config_error); } @@ -530,6 +585,11 @@ static void wpas_ap_configured_cb(void *ctx) { struct wpa_supplicant *wpa_s = ctx; +#ifdef CONFIG_ACS + if (wpa_s->current_ssid && wpa_s->current_ssid->acs) + wpa_s->assoc_freq = wpa_s->ap_iface->freq; +#endif /* CONFIG_ACS */ + wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); if (wpa_s->ap_configured_cb) @@ -595,8 +655,8 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s, params.p2p = 1; #endif /* CONFIG_P2P */ - if (wpa_s->parent->set_ap_uapsd) - params.uapsd = wpa_s->parent->ap_uapsd; + if (wpa_s->p2pdev->set_ap_uapsd) + params.uapsd = wpa_s->p2pdev->ap_uapsd; else if (params.p2p && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_AP_UAPSD)) params.uapsd = 1; /* mandatory for P2P GO */ else @@ -605,12 +665,17 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s, if (ieee80211_is_dfs(params.freq.freq)) params.freq.freq = 0; /* set channel after CAC */ + if (params.p2p) + wpa_drv_get_ext_capa(wpa_s, WPA_IF_P2P_GO); + else + wpa_drv_get_ext_capa(wpa_s, WPA_IF_AP_BSS); + if (wpa_drv_associate(wpa_s, ¶ms) < 0) { wpa_msg(wpa_s, MSG_INFO, "Failed to start AP functionality"); return -1; } - wpa_s->ap_iface = hapd_iface = os_zalloc(sizeof(*wpa_s->ap_iface)); + wpa_s->ap_iface = hapd_iface = hostapd_alloc_iface(); if (hapd_iface == NULL) return -1; hapd_iface->owner = wpa_s; @@ -627,6 +692,13 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s, return -1; } + /* Use the maximum oper channel width if it's given. */ + if (ssid->max_oper_chwidth) + conf->vht_oper_chwidth = ssid->max_oper_chwidth; + + ieee80211_freq_to_chan(ssid->vht_center_freq2, + &conf->vht_oper_centr_freq_seg1_idx); + os_memcpy(wpa_s->ap_iface->conf->wmm_ac_params, wpa_s->conf->wmm_ac_params, sizeof(wpa_s->conf->wmm_ac_params)); @@ -668,7 +740,7 @@ 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]->msg_ctx_parent = wpa_s->p2pdev; 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; @@ -864,7 +936,10 @@ int wpa_supplicant_ap_wps_pin(struct wpa_supplicant *wpa_s, const u8 *bssid, return -1; if (pin == NULL) { - unsigned int rpin = wps_generate_pin(); + unsigned int rpin; + + if (wps_generate_pin(&rpin) < 0) + return -1; ret_len = os_snprintf(buf, buflen, "%08d", rpin); if (os_snprintf_error(buflen, ret_len)) return -1; @@ -930,7 +1005,8 @@ const char * wpas_wps_ap_pin_random(struct wpa_supplicant *wpa_s, int timeout) if (wpa_s->ap_iface == NULL) return NULL; hapd = wpa_s->ap_iface->bss[0]; - pin = wps_generate_pin(); + if (wps_generate_pin(&pin) < 0) + return NULL; os_snprintf(pin_txt, sizeof(pin_txt), "%08u", pin); os_free(hapd->conf->ap_pin); hapd->conf->ap_pin = os_strdup(pin_txt); @@ -1265,8 +1341,8 @@ int wpas_ap_wps_add_nfc_pw(struct wpa_supplicant *wpa_s, u16 pw_id, hapd = wpa_s->ap_iface->bss[0]; wps = hapd->wps; - if (wpa_s->parent->conf->wps_nfc_dh_pubkey == NULL || - wpa_s->parent->conf->wps_nfc_dh_privkey == NULL) { + if (wpa_s->p2pdev->conf->wps_nfc_dh_pubkey == NULL || + wpa_s->p2pdev->conf->wps_nfc_dh_privkey == NULL) { wpa_printf(MSG_DEBUG, "P2P: No NFC DH key known"); return -1; } @@ -1275,9 +1351,9 @@ int wpas_ap_wps_add_nfc_pw(struct wpa_supplicant *wpa_s, u16 pw_id, wpabuf_free(wps->dh_pubkey); wpabuf_free(wps->dh_privkey); wps->dh_privkey = wpabuf_dup( - wpa_s->parent->conf->wps_nfc_dh_privkey); + wpa_s->p2pdev->conf->wps_nfc_dh_privkey); wps->dh_pubkey = wpabuf_dup( - wpa_s->parent->conf->wps_nfc_dh_pubkey); + wpa_s->p2pdev->conf->wps_nfc_dh_pubkey); if (wps->dh_privkey == NULL || wps->dh_pubkey == NULL) { wps->dh_ctx = NULL; wpabuf_free(wps->dh_pubkey); @@ -1308,6 +1384,58 @@ int wpas_ap_stop_ap(struct wpa_supplicant *wpa_s) hapd = wpa_s->ap_iface->bss[0]; return hostapd_ctrl_iface_stop_ap(hapd); } + + +int wpas_ap_pmksa_cache_list(struct wpa_supplicant *wpa_s, char *buf, + size_t len) +{ + size_t reply_len = 0, i; + char ap_delimiter[] = "---- AP ----\n"; + char mesh_delimiter[] = "---- mesh ----\n"; + size_t dlen; + + if (wpa_s->ap_iface) { + dlen = os_strlen(ap_delimiter); + if (dlen > len - reply_len) + return reply_len; + os_memcpy(&buf[reply_len], ap_delimiter, dlen); + reply_len += dlen; + + for (i = 0; i < wpa_s->ap_iface->num_bss; i++) { + reply_len += hostapd_ctrl_iface_pmksa_list( + wpa_s->ap_iface->bss[i], + &buf[reply_len], len - reply_len); + } + } + + if (wpa_s->ifmsh) { + dlen = os_strlen(mesh_delimiter); + if (dlen > len - reply_len) + return reply_len; + os_memcpy(&buf[reply_len], mesh_delimiter, dlen); + reply_len += dlen; + + reply_len += hostapd_ctrl_iface_pmksa_list( + wpa_s->ifmsh->bss[0], &buf[reply_len], + len - reply_len); + } + + return reply_len; +} + + +void wpas_ap_pmksa_cache_flush(struct wpa_supplicant *wpa_s) +{ + size_t i; + + if (wpa_s->ap_iface) { + for (i = 0; i < wpa_s->ap_iface->num_bss; i++) + hostapd_ctrl_iface_pmksa_flush(wpa_s->ap_iface->bss[i]); + } + + if (wpa_s->ifmsh) + hostapd_ctrl_iface_pmksa_flush(wpa_s->ifmsh->bss[0]); +} #endif /* CONFIG_CTRL_IFACE */ diff --git a/wpa_supplicant/ap.h b/wpa_supplicant/ap.h index 594168cf1e03b..5a59ddcc1c93e 100644 --- a/wpa_supplicant/ap.h +++ b/wpa_supplicant/ap.h @@ -76,12 +76,16 @@ int wpas_ap_wps_add_nfc_pw(struct wpa_supplicant *wpa_s, u16 pw_id, const struct wpabuf *pw, const u8 *pubkey_hash); struct hostapd_config; -void wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s, - struct wpa_ssid *ssid, - struct hostapd_config *conf); +int wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + struct hostapd_config *conf); int wpas_ap_stop_ap(struct wpa_supplicant *wpa_s); +int wpas_ap_pmksa_cache_list(struct wpa_supplicant *wpa_s, char *buf, + size_t len); +void wpas_ap_pmksa_cache_flush(struct wpa_supplicant *wpa_s); + void wpas_event_dfs_radar_detected(struct wpa_supplicant *wpa_s, struct dfs_event *radar); void wpas_event_dfs_cac_started(struct wpa_supplicant *wpa_s, diff --git a/wpa_supplicant/autoscan.c b/wpa_supplicant/autoscan.c index a2cf7a5ef2328..072a1d5414aea 100644 --- a/wpa_supplicant/autoscan.c +++ b/wpa_supplicant/autoscan.c @@ -1,6 +1,7 @@ /* * WPA Supplicant - auto scan * Copyright (c) 2012, Intel Corporation. All rights reserved. + * Copyright 2015 Intel Deutschland GmbH * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -15,13 +16,6 @@ #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 @@ -50,6 +44,11 @@ int autoscan_init(struct wpa_supplicant *wpa_s, int req_scan) size_t nlen; int i; const struct autoscan_ops *ops = NULL; + struct sched_scan_plan *scan_plans; + + /* Give preference to scheduled scan plans if supported/configured */ + if (wpa_s->sched_scan_plans) + return 0; if (wpa_s->autoscan && wpa_s->autoscan_priv) return 0; @@ -79,11 +78,23 @@ int autoscan_init(struct wpa_supplicant *wpa_s, int req_scan) return -1; } + scan_plans = os_malloc(sizeof(*wpa_s->sched_scan_plans)); + if (!scan_plans) + return -1; + wpa_s->autoscan_params = NULL; wpa_s->autoscan_priv = ops->init(wpa_s, params); - if (wpa_s->autoscan_priv == NULL) + if (!wpa_s->autoscan_priv) { + os_free(scan_plans); return -1; + } + + scan_plans[0].interval = 5; + scan_plans[0].iterations = 0; + os_free(wpa_s->sched_scan_plans); + wpa_s->sched_scan_plans = scan_plans; + wpa_s->sched_scan_plans_num = 1; wpa_s->autoscan = ops; wpa_printf(MSG_DEBUG, "autoscan: Initialized module '%s' with " @@ -116,7 +127,10 @@ void autoscan_deinit(struct wpa_supplicant *wpa_s) wpa_s->autoscan_priv = NULL; wpa_s->scan_interval = 5; - wpa_s->sched_scan_interval = 0; + + os_free(wpa_s->sched_scan_plans); + wpa_s->sched_scan_plans = NULL; + wpa_s->sched_scan_plans_num = 0; } } @@ -134,7 +148,7 @@ int autoscan_notify_scan(struct wpa_supplicant *wpa_s, return -1; wpa_s->scan_interval = interval; - wpa_s->sched_scan_interval = interval; + wpa_s->sched_scan_plans[0].interval = interval; request_scan(wpa_s); } diff --git a/wpa_supplicant/autoscan.h b/wpa_supplicant/autoscan.h index e2a7652213be7..560684fcbf770 100644 --- a/wpa_supplicant/autoscan.h +++ b/wpa_supplicant/autoscan.h @@ -27,6 +27,16 @@ void autoscan_deinit(struct wpa_supplicant *wpa_s); int autoscan_notify_scan(struct wpa_supplicant *wpa_s, struct wpa_scan_results *scan_res); +/* Available autoscan modules */ + +#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 */ + #else /* CONFIG_AUTOSCAN */ static inline int autoscan_init(struct wpa_supplicant *wpa_s, int req_scan) diff --git a/wpa_supplicant/bgscan.c b/wpa_supplicant/bgscan.c index f74cdbf24a450..798b43c3fdf76 100644 --- a/wpa_supplicant/bgscan.c +++ b/wpa_supplicant/bgscan.c @@ -13,12 +13,6 @@ #include "config_ssid.h" #include "bgscan.h" -#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 diff --git a/wpa_supplicant/bgscan.h b/wpa_supplicant/bgscan.h index 9131e4ecddc05..3df1550a97dda 100644 --- a/wpa_supplicant/bgscan.h +++ b/wpa_supplicant/bgscan.h @@ -39,6 +39,15 @@ void bgscan_notify_signal_change(struct wpa_supplicant *wpa_s, int above, int current_signal, int current_noise, int current_txrate); +/* Available bgscan modules */ + +#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 */ + #else /* CONFIG_BGSCAN */ static inline int bgscan_init(struct wpa_supplicant *wpa_s, diff --git a/wpa_supplicant/binder/.clang-format b/wpa_supplicant/binder/.clang-format new file mode 100644 index 0000000000000..dbfdabfc07fd6 --- /dev/null +++ b/wpa_supplicant/binder/.clang-format @@ -0,0 +1,9 @@ +BasedOnStyle: LLVM +IndentWidth: 8 +UseTab: Always +BreakBeforeBraces: Mozilla +AllowShortIfStatementsOnASingleLine: false +IndentCaseLabels: false +AccessModifierOffset: -8 +AlignAfterOpenBracket: AlwaysBreak +SortIncludes: false diff --git a/wpa_supplicant/binder/binder.cpp b/wpa_supplicant/binder/binder.cpp new file mode 100644 index 0000000000000..750e87818b208 --- /dev/null +++ b/wpa_supplicant/binder/binder.cpp @@ -0,0 +1,104 @@ +/* + * binder interface for wpa_supplicant daemon + * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <binder/ProcessState.h> + +#include "binder_manager.h" + +extern "C" { +#include "binder.h" +#include "binder_i.h" +#include "utils/common.h" +#include "utils/eloop.h" +#include "utils/includes.h" +} + +void wpas_binder_sock_handler(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct wpa_global *global = (wpa_global *)eloop_ctx; + struct wpas_binder_priv *priv = (wpas_binder_priv *)sock_ctx; + + wpa_printf( + MSG_DEBUG, "Processing binder events on FD %d", priv->binder_fd); + android::IPCThreadState::self()->handlePolledCommands(); +} + +struct wpas_binder_priv *wpas_binder_init(struct wpa_global *global) +{ + struct wpas_binder_priv *priv; + wpa_supplicant_binder::BinderManager *binder_manager; + + priv = (wpas_binder_priv *)os_zalloc(sizeof(*priv)); + if (!priv) + return NULL; + priv->global = global; + + android::ProcessState::self()->setThreadPoolMaxThreadCount(0); + android::IPCThreadState::self()->disableBackgroundScheduling(true); + android::IPCThreadState::self()->setupPolling(&priv->binder_fd); + wpa_printf(MSG_INFO, "Process binder events on FD %d", priv->binder_fd); + if (priv->binder_fd < 0) + goto err; + /* Look for read events from the binder socket in the eloop. */ + if (eloop_register_read_sock( + priv->binder_fd, wpas_binder_sock_handler, global, priv) < 0) + goto err; + + binder_manager = wpa_supplicant_binder::BinderManager::getInstance(); + if (!binder_manager) + goto err; + binder_manager->registerBinderService(global); + /* We may not need to store this binder manager reference in the + * global data strucure because we've made it a singleton class. */ + priv->binder_manager = (void *)binder_manager; + + return priv; + +err: + wpas_binder_deinit(priv); + return NULL; +} + +void wpas_binder_deinit(struct wpas_binder_priv *priv) +{ + if (!priv) + return; + + wpa_supplicant_binder::BinderManager::destroyInstance(); + eloop_unregister_read_sock(priv->binder_fd); + android::IPCThreadState::shutdown(); +} + +int wpas_binder_register_interface(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s->global->binder) + return 1; + + wpa_supplicant_binder::BinderManager *binder_manager = + wpa_supplicant_binder::BinderManager::getInstance(); + if (!binder_manager) + return 1; + + return binder_manager->registerInterface(wpa_s); +} + +int wpas_binder_unregister_interface(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s->global->binder) + return 1; + + wpa_supplicant_binder::BinderManager *binder_manager = + wpa_supplicant_binder::BinderManager::getInstance(); + if (!binder_manager) + return 1; + + return binder_manager->unregisterInterface(wpa_s); +} diff --git a/wpa_supplicant/binder/binder.h b/wpa_supplicant/binder/binder.h new file mode 100644 index 0000000000000..019e3275c5e29 --- /dev/null +++ b/wpa_supplicant/binder/binder.h @@ -0,0 +1,46 @@ +/* + * binder interface for wpa_supplicant daemon + * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WPA_SUPPLICANT_BINDER_BINDER_H +#define WPA_SUPPLICANT_BINDER_BINDER_H + +#ifdef _cplusplus +extern "C" { +#endif /* _cplusplus */ + +/** + * This is the binder RPC interface entry point to the wpa_supplicant core. + * This initializes the binder driver & BinderManager instance and then forwards + * all the notifcations from the supplicant core to the BinderManager. + */ +struct wpas_binder_priv; +struct wpa_global; + +struct wpas_binder_priv *wpas_binder_init(struct wpa_global *global); +void wpas_binder_deinit(struct wpas_binder_priv *priv); + +#ifdef CONFIG_CTRL_IFACE_BINDER +int wpas_binder_register_interface(struct wpa_supplicant *wpa_s); +int wpas_binder_unregister_interface(struct wpa_supplicant *wpa_s); +#else /* CONFIG_CTRL_IFACE_BINDER */ +static inline int wpas_binder_register_interface(struct wpa_supplicant *wpa_s) +{ + return 0; +} +static inline int wpas_binder_unregister_interface(struct wpa_supplicant *wpa_s) +{ + return 0; +} +#endif /* CONFIG_CTRL_IFACE_BINDER */ + +#ifdef _cplusplus +} +#endif /* _cplusplus */ + +#endif /* WPA_SUPPLICANT_BINDER_BINDER_H */ diff --git a/wpa_supplicant/binder/binder_constants.cpp b/wpa_supplicant/binder/binder_constants.cpp new file mode 100644 index 0000000000000..0d452b11baecb --- /dev/null +++ b/wpa_supplicant/binder/binder_constants.cpp @@ -0,0 +1,18 @@ +/* + * binder interface for wpa_supplicant daemon + * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "binder_constants.h" + +namespace wpa_supplicant_binder { +namespace binder_constants { + +const char kServiceName[] = "wpa_supplicant"; + +} /* namespace binder_constants */ +} /* namespace wpa_supplicant_binder */ diff --git a/wpa_supplicant/binder/binder_constants.h b/wpa_supplicant/binder/binder_constants.h new file mode 100644 index 0000000000000..a4d9b558edc04 --- /dev/null +++ b/wpa_supplicant/binder/binder_constants.h @@ -0,0 +1,21 @@ +/* + * binder interface for wpa_supplicant daemon + * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WPA_SUPPLICANT_BINDER_BINDER_CONSTANTS_H +#define WPA_SUPPLICANT_BINDER_BINDER_CONSTANTS_H + +namespace wpa_supplicant_binder { +namespace binder_constants { + +extern const char kServiceName[]; + +} /* namespace binder_constants */ +} /* namespace wpa_supplicant_binder */ + +#endif /* WPA_SUPPLICANT_BINDER_BINDER_CONSTANTS_H */ diff --git a/wpa_supplicant/binder/binder_i.h b/wpa_supplicant/binder/binder_i.h new file mode 100644 index 0000000000000..5140d6d6c01d1 --- /dev/null +++ b/wpa_supplicant/binder/binder_i.h @@ -0,0 +1,28 @@ +/* + * binder interface for wpa_supplicant daemon + * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef BINDER_I_H +#define BINDER_I_H + +#ifdef _cplusplus +extern "C" { +#endif // _cplusplus + +struct wpas_binder_priv +{ + int binder_fd; + struct wpa_global *global; + void *binder_manager; +}; + +#ifdef _cplusplus +} +#endif /* _cplusplus */ + +#endif /* BINDER_I_H */ diff --git a/wpa_supplicant/binder/binder_manager.cpp b/wpa_supplicant/binder/binder_manager.cpp new file mode 100644 index 0000000000000..27e8dedca44a1 --- /dev/null +++ b/wpa_supplicant/binder/binder_manager.cpp @@ -0,0 +1,100 @@ +/* + * binder interface for wpa_supplicant daemon + * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include <binder/IServiceManager.h> + +#include "binder_constants.h" +#include "binder_manager.h" + +extern "C" { +#include "utils/common.h" +#include "utils/includes.h" +} + +namespace wpa_supplicant_binder { + +BinderManager *BinderManager::instance_ = NULL; + +BinderManager *BinderManager::getInstance() +{ + if (!instance_) + instance_ = new BinderManager(); + return instance_; +} + +void BinderManager::destroyInstance() +{ + if (instance_) + delete instance_; + instance_ = NULL; +} + +int BinderManager::registerBinderService(struct wpa_global *global) +{ + /* Create the main binder service object and register with + * system service manager. */ + supplicant_object_ = new Supplicant(global); + android::String16 service_name(binder_constants::kServiceName); + android::defaultServiceManager()->addService( + service_name, android::IInterface::asBinder(supplicant_object_)); + return 0; +} + +int BinderManager::registerInterface(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s) + return 1; + + /* Using the corresponding wpa_supplicant pointer as key to our + * object map. */ + const void *iface_key = wpa_s; + + /* Return failure if we already have an object for that iface_key. */ + if (iface_object_map_.find(iface_key) != iface_object_map_.end()) + return 1; + + iface_object_map_[iface_key] = new Iface(wpa_s); + if (!iface_object_map_[iface_key].get()) + return 1; + + wpa_s->binder_object_key = iface_key; + + return 0; +} + +int BinderManager::unregisterInterface(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s || !wpa_s->binder_object_key) + return 1; + + const void *iface_key = wpa_s; + if (iface_object_map_.find(iface_key) == iface_object_map_.end()) + return 1; + + /* Delete the corresponding iface object from our map. */ + iface_object_map_.erase(iface_key); + wpa_s->binder_object_key = NULL; + return 0; +} + +int BinderManager::getIfaceBinderObjectByKey( + const void *iface_object_key, + android::sp<fi::w1::wpa_supplicant::IIface> *iface_object) +{ + if (!iface_object_key || !iface_object) + return 1; + + if (iface_object_map_.find(iface_object_key) == iface_object_map_.end()) + return 1; + + *iface_object = iface_object_map_[iface_object_key]; + return 0; +} + +} /* namespace wpa_supplicant_binder */ diff --git a/wpa_supplicant/binder/binder_manager.h b/wpa_supplicant/binder/binder_manager.h new file mode 100644 index 0000000000000..d8b7dd0f87263 --- /dev/null +++ b/wpa_supplicant/binder/binder_manager.h @@ -0,0 +1,58 @@ +/* + * binder interface for wpa_supplicant daemon + * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WPA_SUPPLICANT_BINDER_BINDER_MANAGER_H +#define WPA_SUPPLICANT_BINDER_BINDER_MANAGER_H + +#include <map> +#include <string> + +#include "iface.h" +#include "supplicant.h" + +struct wpa_global; +struct wpa_supplicant; + +namespace wpa_supplicant_binder { + +/** + * BinderManager is responsible for managing the lifetime of all + * binder objects created by wpa_supplicant. This is a singleton + * class which is created by the supplicant core and can be used + * to get references to the binder objects. + */ +class BinderManager +{ +public: + static BinderManager *getInstance(); + static void destroyInstance(); + int registerBinderService(struct wpa_global *global); + int registerInterface(struct wpa_supplicant *wpa_s); + int unregisterInterface(struct wpa_supplicant *wpa_s); + int getIfaceBinderObjectByKey( + const void *iface_object_key, + android::sp<fi::w1::wpa_supplicant::IIface> *iface_object); + +private: + BinderManager() = default; + ~BinderManager() = default; + + /* Singleton instance of this class. */ + static BinderManager *instance_; + /* The main binder service object. */ + android::sp<Supplicant> supplicant_object_; + /* Map of all the interface specific binder objects controlled by + * wpa_supplicant. This map is keyed in by the corresponding + * wpa_supplicant structure pointer. */ + std::map<const void *, android::sp<Iface>> iface_object_map_; +}; + +} /* namespace wpa_supplicant_binder */ + +#endif /* WPA_SUPPLICANT_BINDER_BINDER_MANAGER_H */ diff --git a/wpa_supplicant/binder/fi/w1/wpa_supplicant/IIface.aidl b/wpa_supplicant/binder/fi/w1/wpa_supplicant/IIface.aidl new file mode 100644 index 0000000000000..ea11d426df1ff --- /dev/null +++ b/wpa_supplicant/binder/fi/w1/wpa_supplicant/IIface.aidl @@ -0,0 +1,16 @@ +/* + * binder interface for wpa_supplicant daemon + * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +package fi.w1.wpa_supplicant; + +/** + * Interface exposed by wpa_supplicant for each network interface it controls. + */ +interface IIface { +} diff --git a/wpa_supplicant/binder/fi/w1/wpa_supplicant/ISupplicant.aidl b/wpa_supplicant/binder/fi/w1/wpa_supplicant/ISupplicant.aidl new file mode 100644 index 0000000000000..1cbee20a620f3 --- /dev/null +++ b/wpa_supplicant/binder/fi/w1/wpa_supplicant/ISupplicant.aidl @@ -0,0 +1,59 @@ +/* + * WPA Supplicant - binder interface for wpa_supplicant daemon + * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +package fi.w1.wpa_supplicant; + +import android.os.PersistableBundle; +import fi.w1.wpa_supplicant.IIface; + +/** + * Interface exposed by the wpa_supplicant binder service registered + * with the service manager with name: fi.w1.wpa_supplicant. + */ +interface ISupplicant { + /* Error values returned by the service to RPC method calls. */ + const int ERROR_INVALID_ARGS = 1; + const int ERROR_UNKNOWN = 2; + const int ERROR_IFACE_EXISTS = 3; + const int ERROR_IFACE_UNKNOWN = 4; + + /** + * Registers a wireless interface in wpa_supplicant. + * + * @param args A dictionary with arguments used to add the interface to + * wpa_supplicant. + * The dictionary may contain the following entries: + * Ifname(String) Name of the network interface to control, e.g., + * wlan0. + * BridgeIfname(String) Name of the bridge interface to control, e.g., + * br0. + * Driver(String) Driver name which the interface uses, e.g., nl80211. + * ConfigFile(String) Configuration file path. + * + * @return Binder object representing the interface. + */ + IIface CreateInterface(in PersistableBundle args); + + /** + * Deregisters a wireless interface from wpa_supplicant. + * + * @param ifname Name of the network interface, e.g., wlan0 + */ + void RemoveInterface(in @utf8InCpp String ifname); + + /** + * Gets a binder object for the interface corresponding to ifname + * which wpa_supplicant already controls. + * + * @param ifname Name of the network interface, e.g., wlan0 + * + * @return Binder object representing the interface. + */ + IIface GetInterface(in @utf8InCpp String ifname); +} diff --git a/wpa_supplicant/binder/fi/w1/wpa_supplicant/ISupplicantCallbacks.aidl b/wpa_supplicant/binder/fi/w1/wpa_supplicant/ISupplicantCallbacks.aidl new file mode 100644 index 0000000000000..d624d91336039 --- /dev/null +++ b/wpa_supplicant/binder/fi/w1/wpa_supplicant/ISupplicantCallbacks.aidl @@ -0,0 +1,20 @@ +/* + * binder interface for wpa_supplicant daemon + * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +package fi.w1.wpa_supplicant; + +import android.os.PersistableBundle; + +/** + * Callback Interface exposed by the wpa_supplicant service. Clients need + * to host an instance of this binder object and pass a reference of the object + * to wpa_supplicant via the registerCallbacksObject method. + */ +interface ISupplicantCallbacks { +} diff --git a/wpa_supplicant/binder/iface.cpp b/wpa_supplicant/binder/iface.cpp new file mode 100644 index 0000000000000..c61b3b0064275 --- /dev/null +++ b/wpa_supplicant/binder/iface.cpp @@ -0,0 +1,16 @@ +/* + * binder interface for wpa_supplicant daemon + * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "iface.h" + +namespace wpa_supplicant_binder { + +Iface::Iface(struct wpa_supplicant *wpa_s) : wpa_s_(wpa_s) {} + +} /* namespace wpa_supplicant_binder */ diff --git a/wpa_supplicant/binder/iface.h b/wpa_supplicant/binder/iface.h new file mode 100644 index 0000000000000..c0ee12c65fa5d --- /dev/null +++ b/wpa_supplicant/binder/iface.h @@ -0,0 +1,42 @@ +/* + * binder interface for wpa_supplicant daemon + * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WPA_SUPPLICANT_BINDER_IFACE_H +#define WPA_SUPPLICANT_BINDER_IFACE_H + +#include "fi/w1/wpa_supplicant/BnIface.h" + +extern "C" { +#include "utils/common.h" +#include "utils/includes.h" +#include "../wpa_supplicant_i.h" +} + +namespace wpa_supplicant_binder { + +/** + * Implementation of Iface binder object. Each unique binder + * object is used for control operations on a specific interface + * controlled by wpa_supplicant. + */ +class Iface : public fi::w1::wpa_supplicant::BnIface +{ +public: + Iface(struct wpa_supplicant *wpa_s); + virtual ~Iface() = default; + +private: + /* Raw pointer to the structure maintained by the core for this + * interface. */ + struct wpa_supplicant *wpa_s_; +}; + +} /* namespace wpa_supplicant_binder */ + +#endif /* WPA_SUPPLICANT_BINDER_IFACE_H */ diff --git a/wpa_supplicant/binder/supplicant.cpp b/wpa_supplicant/binder/supplicant.cpp new file mode 100644 index 0000000000000..76569b1471fbb --- /dev/null +++ b/wpa_supplicant/binder/supplicant.cpp @@ -0,0 +1,127 @@ +/* + * binder interface for wpa_supplicant daemon + * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "supplicant.h" +#include "binder_manager.h" + +namespace wpa_supplicant_binder { + +Supplicant::Supplicant(struct wpa_global *global) : wpa_global_(global) {} + +android::binder::Status Supplicant::CreateInterface( + const android::os::PersistableBundle ¶ms, + android::sp<fi::w1::wpa_supplicant::IIface> *aidl_return) +{ + android::String16 driver, ifname, confname, bridge_ifname; + + /* Check if required Ifname argument is missing */ + if (!params.getString(android::String16("Ifname"), &ifname)) + return android::binder::Status::fromServiceSpecificError( + ERROR_INVALID_ARGS, + android::String8("Ifname missing in params.")); + /* Retrieve the remaining params from the dictionary */ + params.getString(android::String16("Driver"), &driver); + params.getString(android::String16("ConfigFile"), &confname); + params.getString(android::String16("BridgeIfname"), &bridge_ifname); + + /* + * Try to get the wpa_supplicant record for this iface, return + * an error if we already control it. + */ + if (wpa_supplicant_get_iface( + wpa_global_, android::String8(ifname).string()) != NULL) + return android::binder::Status::fromServiceSpecificError( + ERROR_IFACE_EXISTS, + android::String8("wpa_supplicant already controls this " + "interface.")); + + android::binder::Status status; + struct wpa_supplicant *wpa_s = NULL; + struct wpa_interface iface; + + os_memset(&iface, 0, sizeof(iface)); + iface.driver = os_strdup(android::String8(driver).string()); + iface.ifname = os_strdup(android::String8(ifname).string()); + iface.confname = os_strdup(android::String8(confname).string()); + iface.bridge_ifname = + os_strdup(android::String8(bridge_ifname).string()); + /* Otherwise, have wpa_supplicant attach to it. */ + wpa_s = wpa_supplicant_add_iface(wpa_global_, &iface, NULL); + /* The supplicant core creates a corresponding binder object via + * BinderManager when |wpa_supplicant_add_iface| is called. */ + if (!wpa_s || !wpa_s->binder_object_key) { + status = android::binder::Status::fromServiceSpecificError( + ERROR_UNKNOWN, + android::String8( + "wpa_supplicant couldn't grab this interface.")); + } else { + BinderManager *binder_manager = BinderManager::getInstance(); + + if (!binder_manager || + binder_manager->getIfaceBinderObjectByKey( + wpa_s->binder_object_key, aidl_return)) + status = + android::binder::Status::fromServiceSpecificError( + ERROR_UNKNOWN, + android::String8("wpa_supplicant encountered a " + "binder error.")); + else + status = android::binder::Status::ok(); + } + os_free((void *)iface.driver); + os_free((void *)iface.ifname); + os_free((void *)iface.confname); + os_free((void *)iface.bridge_ifname); + return status; +} + +android::binder::Status Supplicant::RemoveInterface(const std::string &ifname) +{ + struct wpa_supplicant *wpa_s; + + wpa_s = wpa_supplicant_get_iface(wpa_global_, ifname.c_str()); + if (!wpa_s || !wpa_s->binder_object_key) + return android::binder::Status::fromServiceSpecificError( + ERROR_IFACE_UNKNOWN, + android::String8("wpa_supplicant does not control this " + "interface.")); + if (wpa_supplicant_remove_iface(wpa_global_, wpa_s, 0)) + return android::binder::Status::fromServiceSpecificError( + ERROR_UNKNOWN, + android::String8( + "wpa_supplicant couldn't remove this interface.")); + return android::binder::Status::ok(); +} + +android::binder::Status Supplicant::GetInterface( + const std::string &ifname, + android::sp<fi::w1::wpa_supplicant::IIface> *aidl_return) +{ + struct wpa_supplicant *wpa_s; + + wpa_s = wpa_supplicant_get_iface(wpa_global_, ifname.c_str()); + if (!wpa_s || !wpa_s->binder_object_key) + return android::binder::Status::fromServiceSpecificError( + ERROR_IFACE_UNKNOWN, + android::String8( + "wpa_supplicant does not control this interface.")); + + BinderManager *binder_manager = BinderManager::getInstance(); + if (!binder_manager || + binder_manager->getIfaceBinderObjectByKey( + wpa_s->binder_object_key, aidl_return)) + return android::binder::Status::fromServiceSpecificError( + ERROR_UNKNOWN, + android::String8( + "wpa_supplicant encountered a binder error.")); + + return android::binder::Status::ok(); +} + +} /* namespace wpa_supplicant_binder */ diff --git a/wpa_supplicant/binder/supplicant.h b/wpa_supplicant/binder/supplicant.h new file mode 100644 index 0000000000000..136b99b143274 --- /dev/null +++ b/wpa_supplicant/binder/supplicant.h @@ -0,0 +1,55 @@ +/* + * binder interface for wpa_supplicant daemon + * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WPA_SUPPLICANT_BINDER_SUPPLICANT_H +#define WPA_SUPPLICANT_BINDER_SUPPLICANT_H + +#include "fi/w1/wpa_supplicant/BnSupplicant.h" +#include "fi/w1/wpa_supplicant/IIface.h" +#include "fi/w1/wpa_supplicant/ISupplicantCallbacks.h" + +extern "C" { +#include "utils/common.h" +#include "utils/includes.h" +#include "../wpa_supplicant_i.h" +} + +namespace wpa_supplicant_binder { + +/** + * Implementation of the supplicant binder object. This binder + * object is used core for global control operations on + * wpa_supplicant. + */ +class Supplicant : public fi::w1::wpa_supplicant::BnSupplicant +{ +public: + Supplicant(struct wpa_global *global); + virtual ~Supplicant() = default; + + android::binder::Status CreateInterface( + const android::os::PersistableBundle ¶ms, + android::sp<fi::w1::wpa_supplicant::IIface> *aidl_return) override; + android::binder::Status + RemoveInterface(const std::string &ifname) override; + android::binder::Status GetInterface( + const std::string &ifname, + android::sp<fi::w1::wpa_supplicant::IIface> *aidl_return) override; + +private: + /* Raw pointer to the global structure maintained by the core. */ + struct wpa_global *wpa_global_; + /* All the callback objects registered by the clients. */ + std::vector<android::sp<fi::w1::wpa_supplicant::ISupplicantCallbacks>> + callbacks_; +}; + +} /* namespace wpa_supplicant_binder */ + +#endif /* WPA_SUPPLICANT_BINDER_SUPPLICANT_H */ diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c index 1051ee3a4c550..3a8778db9058d 100644 --- a/wpa_supplicant/bss.c +++ b/wpa_supplicant/bss.c @@ -12,6 +12,7 @@ #include "utils/eloop.h" #include "common/ieee802_11_defs.h" #include "drivers/driver.h" +#include "eap_peer/eap.h" #include "wpa_supplicant_i.h" #include "config.h" #include "notify.h" @@ -60,6 +61,9 @@ struct wpa_bss_anqp * wpa_bss_anqp_alloc(void) anqp = os_zalloc(sizeof(*anqp)); if (anqp == NULL) return NULL; +#ifdef CONFIG_INTERWORKING + dl_list_init(&anqp->anqp_elems); +#endif /* CONFIG_INTERWORKING */ anqp->users = 1; return anqp; } @@ -80,6 +84,7 @@ static struct wpa_bss_anqp * wpa_bss_anqp_clone(struct wpa_bss_anqp *anqp) #define ANQP_DUP(f) if (anqp->f) n->f = wpabuf_dup(anqp->f) #ifdef CONFIG_INTERWORKING + dl_list_init(&n->anqp_elems); ANQP_DUP(capability_list); ANQP_DUP(venue_name); ANQP_DUP(network_auth_type); @@ -141,6 +146,10 @@ int wpa_bss_anqp_unshare_alloc(struct wpa_bss *bss) */ static void wpa_bss_anqp_free(struct wpa_bss_anqp *anqp) { +#ifdef CONFIG_INTERWORKING + struct wpa_bss_anqp_elem *elem; +#endif /* CONFIG_INTERWORKING */ + if (anqp == NULL) return; @@ -159,6 +168,13 @@ static void wpa_bss_anqp_free(struct wpa_bss_anqp *anqp) wpabuf_free(anqp->nai_realm); wpabuf_free(anqp->anqp_3gpp); wpabuf_free(anqp->domain_name); + + while ((elem = dl_list_first(&anqp->anqp_elems, + struct wpa_bss_anqp_elem, list))) { + dl_list_del(&elem->list); + wpabuf_free(elem->payload); + os_free(elem); + } #endif /* CONFIG_INTERWORKING */ #ifdef CONFIG_HS20 wpabuf_free(anqp->hs20_capability_list); @@ -198,8 +214,8 @@ static void wpa_bss_update_pending_connect(struct wpa_supplicant *wpa_s, } -static void wpa_bss_remove(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, - const char *reason) +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; @@ -288,6 +304,47 @@ static void wpa_bss_copy_res(struct wpa_bss *dst, struct wpa_scan_res *src, } +static int wpa_bss_is_wps_candidate(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss) +{ +#ifdef CONFIG_WPS + struct wpa_ssid *ssid; + struct wpabuf *wps_ie; + int pbc = 0, ret; + + wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); + if (!wps_ie) + return 0; + + if (wps_is_selected_pbc_registrar(wps_ie)) { + pbc = 1; + } else if (!wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1)) { + wpabuf_free(wps_ie); + return 0; + } + + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { + if (!(ssid->key_mgmt & WPA_KEY_MGMT_WPS)) + continue; + if (ssid->ssid_len && + (ssid->ssid_len != bss->ssid_len || + os_memcmp(ssid->ssid, bss->ssid, ssid->ssid_len) != 0)) + continue; + + if (pbc) + ret = eap_is_wps_pbc_enrollee(&ssid->eap); + else + ret = eap_is_wps_pin_enrollee(&ssid->eap); + wpabuf_free(wps_ie); + return ret; + } + wpabuf_free(wps_ie); +#endif /* CONFIG_WPS */ + + return 0; +} + + static int wpa_bss_known(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) { struct wpa_ssid *ssid; @@ -326,7 +383,8 @@ 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)) { + if (!wpa_bss_known(wpa_s, bss) && + !wpa_bss_is_wps_candidate(wpa_s, bss)) { wpa_bss_remove(wpa_s, bss, __func__); return 0; } @@ -784,7 +842,7 @@ void wpa_bss_update_end(struct wpa_supplicant *wpa_s, struct scan_info *info, struct wpa_bss *bss, *n; os_get_reltime(&wpa_s->last_scan); - if (!new_scan) + if ((info && info->aborted) || !new_scan) return; /* do not expire entries without new scan */ dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) { @@ -1004,20 +1062,7 @@ struct wpa_bss * wpa_bss_get_id_range(struct wpa_supplicant *wpa_s, */ const u8 * wpa_bss_get_ie(const struct wpa_bss *bss, u8 ie) { - const u8 *end, *pos; - - pos = (const u8 *) (bss + 1); - end = pos + bss->ie_len; - - while (pos + 1 < end) { - if (pos + 2 + pos[1] > end) - break; - if (pos[0] == ie) - return pos; - pos += 2 + pos[1]; - } - - return NULL; + return get_ie((const u8 *) (bss + 1), bss->ie_len, ie); } @@ -1037,8 +1082,8 @@ const u8 * wpa_bss_get_vendor_ie(const struct wpa_bss *bss, u32 vendor_type) pos = (const u8 *) (bss + 1); end = pos + bss->ie_len; - while (pos + 1 < end) { - if (pos + 2 + pos[1] > end) + while (end - pos > 1) { + if (2 + pos[1] > end - pos) break; if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && vendor_type == WPA_GET_BE32(&pos[2])) @@ -1074,8 +1119,8 @@ const u8 * wpa_bss_get_vendor_ie_beacon(const struct wpa_bss *bss, pos += bss->ie_len; end = pos + bss->beacon_ie_len; - while (pos + 1 < end) { - if (pos + 2 + pos[1] > end) + while (end - pos > 1) { + if (2 + pos[1] > end - pos) break; if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && vendor_type == WPA_GET_BE32(&pos[2])) @@ -1110,8 +1155,8 @@ struct wpabuf * wpa_bss_get_vendor_ie_multi(const struct wpa_bss *bss, pos = (const u8 *) (bss + 1); end = pos + bss->ie_len; - while (pos + 1 < end) { - if (pos + 2 + pos[1] > end) + while (end - pos > 1) { + if (2 + pos[1] > end - pos) break; if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && vendor_type == WPA_GET_BE32(&pos[2])) @@ -1155,8 +1200,8 @@ struct wpabuf * wpa_bss_get_vendor_ie_multi_beacon(const struct wpa_bss *bss, pos += bss->ie_len; end = pos + bss->beacon_ie_len; - while (pos + 1 < end) { - if (pos + 2 + pos[1] > end) + while (end - pos > 1) { + if (2 + pos[1] > end - pos) break; if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && vendor_type == WPA_GET_BE32(&pos[2])) diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h index b215380eeb152..84e8fb07461e4 100644 --- a/wpa_supplicant/bss.h +++ b/wpa_supplicant/bss.h @@ -19,6 +19,12 @@ struct wpa_scan_res; #define WPA_BSS_ASSOCIATED BIT(5) #define WPA_BSS_ANQP_FETCH_TRIED BIT(6) +struct wpa_bss_anqp_elem { + struct dl_list list; + u16 infoid; + struct wpabuf *payload; +}; + /** * struct wpa_bss_anqp - ANQP data for a BSS entry (struct wpa_bss) */ @@ -34,6 +40,7 @@ struct wpa_bss_anqp { struct wpabuf *nai_realm; struct wpabuf *anqp_3gpp; struct wpabuf *domain_name; + struct dl_list anqp_elems; /* list of struct wpa_bss_anqp_elem */ #endif /* CONFIG_INTERWORKING */ #ifdef CONFIG_HS20 struct wpabuf *hs20_capability_list; @@ -106,6 +113,8 @@ void wpa_bss_update_start(struct wpa_supplicant *wpa_s); void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s, struct wpa_scan_res *res, struct os_reltime *fetch_time); +void wpa_bss_remove(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, + const char *reason); 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); @@ -141,6 +150,17 @@ static inline int bss_is_dmg(const struct wpa_bss *bss) return bss->freq > 45000; } +/** + * Test whether a BSS is a PBSS. + * This checks whether a BSS is a DMG-band PBSS. PBSS is used for P2P DMG + * network. + */ +static inline int bss_is_pbss(struct wpa_bss *bss) +{ + return bss_is_dmg(bss) && + (bss->caps & IEEE80211_CAP_DMG_MASK) == IEEE80211_CAP_DMG_PBSS; +} + static inline void wpa_bss_update_level(struct wpa_bss *bss, int new_level) { if (bss != NULL && new_level < 0) diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c index b1adab77bbe0c..dd922caf80af9 100644 --- a/wpa_supplicant/config.c +++ b/wpa_supplicant/config.c @@ -32,7 +32,11 @@ struct parse_data { /* Configuration variable name */ char *name; - /* Parser function for this variable */ + /* Parser function for this variable. The parser functions return 0 or 1 + * to indicate success. Value 0 indicates that the parameter value may + * have changed while value 1 means that the value did not change. + * Error cases (failure to parse the string) are indicated by returning + * -1. */ int (*parser)(const struct parse_data *data, struct wpa_ssid *ssid, int line, const char *value); @@ -59,7 +63,7 @@ static int wpa_config_parse_str(const struct parse_data *data, struct wpa_ssid *ssid, int line, const char *value) { - size_t res_len, *dst_len; + size_t res_len, *dst_len, prev_len; char **dst, *tmp; if (os_strcmp(value, "NULL") == 0) { @@ -105,6 +109,21 @@ static int wpa_config_parse_str(const struct parse_data *data, set: dst = (char **) (((u8 *) ssid) + (long) data->param1); dst_len = (size_t *) (((u8 *) ssid) + (long) data->param2); + + if (data->param2) + prev_len = *dst_len; + else if (*dst) + prev_len = os_strlen(*dst); + else + prev_len = 0; + if ((*dst == NULL && tmp == NULL) || + (*dst && tmp && prev_len == res_len && + os_memcmp(*dst, tmp, res_len) == 0)) { + /* No change to the previously configured value */ + os_free(tmp); + return 1; + } + os_free(*dst); *dst = tmp; if (data->param2) @@ -190,6 +209,9 @@ static int wpa_config_parse_int(const struct parse_data *data, line, value); return -1; } + + if (*dst == val) + return 1; *dst = val; wpa_printf(MSG_MSGDUMP, "%s=%d (0x%x)", data->name, *dst, *dst); @@ -456,9 +478,17 @@ static int wpa_config_parse_psk(const struct parse_data *data, } wpa_hexdump_ascii_key(MSG_MSGDUMP, "PSK (ASCII passphrase)", (u8 *) value, len); + if (has_ctrl_char((u8 *) value, len)) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid passphrase character", + line); + return -1; + } if (ssid->passphrase && os_strlen(ssid->passphrase) == len && - os_memcmp(ssid->passphrase, value, len) == 0) - return 0; + os_memcmp(ssid->passphrase, value, len) == 0) { + /* No change to the previously configured value */ + return 1; + } ssid->psk_set = 0; str_clear_free(ssid->passphrase); ssid->passphrase = dup_binstr(value, len); @@ -569,6 +599,8 @@ static int wpa_config_parse_proto(const struct parse_data *data, errors++; } + if (!errors && ssid->proto == val) + return 1; wpa_printf(MSG_MSGDUMP, "proto: 0x%x", val); ssid->proto = val; return errors ? -1 : 0; @@ -705,6 +737,8 @@ static int wpa_config_parse_key_mgmt(const struct parse_data *data, errors++; } + if (!errors && ssid->key_mgmt == val) + return 1; wpa_printf(MSG_MSGDUMP, "key_mgmt: 0x%x", val); ssid->key_mgmt = val; return errors ? -1 : 0; @@ -899,6 +933,9 @@ static char * wpa_config_write_key_mgmt(const struct parse_data *data, static int wpa_config_parse_cipher(int line, const char *value) { +#ifdef CONFIG_NO_WPA + return -1; +#else /* CONFIG_NO_WPA */ int val = wpa_parse_cipher(value); if (val < 0) { wpa_printf(MSG_ERROR, "Line %d: invalid cipher '%s'.", @@ -911,12 +948,16 @@ static int wpa_config_parse_cipher(int line, const char *value) return -1; } return val; +#endif /* CONFIG_NO_WPA */ } #ifndef NO_CONFIG_WRITE static char * wpa_config_write_cipher(int cipher) { +#ifdef CONFIG_NO_WPA + return NULL; +#else /* CONFIG_NO_WPA */ char *buf = os_zalloc(50); if (buf == NULL) return NULL; @@ -927,6 +968,7 @@ static char * wpa_config_write_cipher(int cipher) } return buf; +#endif /* CONFIG_NO_WPA */ } #endif /* NO_CONFIG_WRITE */ @@ -945,6 +987,8 @@ static int wpa_config_parse_pairwise(const struct parse_data *data, return -1; } + if (ssid->pairwise_cipher == val) + return 1; wpa_printf(MSG_MSGDUMP, "pairwise: 0x%x", val); ssid->pairwise_cipher = val; return 0; @@ -981,6 +1025,8 @@ static int wpa_config_parse_group(const struct parse_data *data, return -1; } + if (ssid->group_cipher == val) + return 1; wpa_printf(MSG_MSGDUMP, "group: 0x%x", val); ssid->group_cipher = val; return 0; @@ -1042,6 +1088,8 @@ static int wpa_config_parse_auth_alg(const struct parse_data *data, errors++; } + if (!errors && ssid->auth_alg == val) + return 1; wpa_printf(MSG_MSGDUMP, "auth_alg: 0x%x", val); ssid->auth_alg = val; return errors ? -1 : 0; @@ -1296,6 +1344,32 @@ static int wpa_config_parse_eap(const struct parse_data *data, methods[num_methods].method = EAP_TYPE_NONE; num_methods++; + if (!errors && ssid->eap.eap_methods) { + struct eap_method_type *prev_m; + size_t i, j, prev_methods, match = 0; + + prev_m = ssid->eap.eap_methods; + for (i = 0; prev_m[i].vendor != EAP_VENDOR_IETF || + prev_m[i].method != EAP_TYPE_NONE; i++) { + /* Count the methods */ + } + prev_methods = i + 1; + + for (i = 0; prev_methods == num_methods && i < prev_methods; + i++) { + for (j = 0; j < num_methods; j++) { + if (prev_m[i].vendor == methods[j].vendor && + prev_m[i].method == methods[j].method) { + match++; + break; + } + } + } + if (match == num_methods) { + os_free(methods); + return 1; + } + } wpa_hexdump(MSG_MSGDUMP, "eap methods", (u8 *) methods, num_methods * sizeof(*methods)); os_free(ssid->eap.eap_methods); @@ -1348,6 +1422,8 @@ static int wpa_config_parse_password(const struct parse_data *data, u8 *hash; if (os_strcmp(value, "NULL") == 0) { + if (!ssid->eap.password) + return 1; /* Already unset */ wpa_printf(MSG_DEBUG, "Unset configuration string 'password'"); bin_clear_free(ssid->eap.password, ssid->eap.password_len); ssid->eap.password = NULL; @@ -1411,6 +1487,12 @@ static int wpa_config_parse_password(const struct parse_data *data, wpa_hexdump_key(MSG_MSGDUMP, data->name, hash, 16); + if (ssid->eap.password && ssid->eap.password_len == 16 && + os_memcmp(ssid->eap.password, hash, 16) == 0 && + (ssid->eap.flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH)) { + bin_clear_free(hash, 16); + return 1; + } bin_clear_free(ssid->eap.password, ssid->eap.password_len); ssid->eap.password = hash; ssid->eap.password_len = 16; @@ -1837,6 +1919,8 @@ static const struct parse_data ssid_fields[] = { { FUNC(auth_alg) }, { FUNC(scan_freq) }, { FUNC(freq_list) }, + { INT_RANGE(max_oper_chwidth, VHT_CHANWIDTH_USE_HT, + VHT_CHANWIDTH_80P80MHZ) }, #ifdef IEEE8021X_EAPOL { FUNC(eap) }, { STR_LENe(identity) }, @@ -1910,6 +1994,9 @@ static const struct parse_data ssid_fields[] = { { INT_RANGE(mixed_cell, 0, 1) }, { INT_RANGE(frequency, 0, 65000) }, { INT_RANGE(fixed_freq, 0, 1) }, +#ifdef CONFIG_ACS + { INT_RANGE(acs, 0, 1) }, +#endif /* CONFIG_ACS */ #ifdef CONFIG_MESH { FUNC(mesh_basic_rates) }, { INT(dot11MeshMaxRetries) }, @@ -1918,6 +2005,7 @@ static const struct parse_data ssid_fields[] = { { INT(dot11MeshHoldingTimeout) }, #endif /* CONFIG_MESH */ { INT(wpa_ptk_rekey) }, + { INT(group_rekey) }, { STR(bgscan) }, { INT_RANGE(ignore_broadcast_ssid, 0, 2) }, #ifdef CONFIG_P2P @@ -1967,6 +2055,8 @@ static const struct parse_data ssid_fields[] = { { INT(update_identifier) }, #endif /* CONFIG_HS20 */ { INT_RANGE(mac_addr, 0, 2) }, + { INT_RANGE(pbss, 0, 2) }, + { INT_RANGE(wps_disabled, 0, 1) }, }; #undef OFFSET @@ -2271,6 +2361,11 @@ void wpa_config_free(struct wpa_config *config) os_free(config->bgscan); os_free(config->wowlan_triggers); os_free(config->fst_group_id); + os_free(config->sched_scan_plans); +#ifdef CONFIG_MBO + os_free(config->non_pref_chan); +#endif /* CONFIG_MBO */ + os_free(config); } @@ -2453,7 +2548,8 @@ void wpa_config_set_network_defaults(struct wpa_ssid *ssid) * @var: Variable name, e.g., "ssid" * @value: Variable value * @line: Line number in configuration file or 0 if not used - * Returns: 0 on success, -1 on failure + * Returns: 0 on success with possible change in the value, 1 on success with + * no change to previously configured value, or -1 on failure * * This function can be used to set network configuration variables based on * both the configuration file and management interface input. The value @@ -2474,7 +2570,8 @@ int wpa_config_set(struct wpa_ssid *ssid, const char *var, const char *value, if (os_strcmp(var, field->name) != 0) continue; - if (field->parser(field, ssid, line, value)) { + ret = field->parser(field, ssid, line, value); + if (ret < 0) { if (line) { wpa_printf(MSG_ERROR, "Line %d: failed to " "parse %s '%s'.", line, var, value); @@ -2573,9 +2670,8 @@ char ** wpa_config_get_all(struct wpa_ssid *ssid, int get_keys) return props; err: - value = *props; - while (value) - os_free(value++); + for (i = 0; props[i]; i++) + os_free(props[i]); os_free(props); return NULL; #endif /* NO_CONFIG_WRITE */ @@ -2604,8 +2700,19 @@ char * wpa_config_get(struct wpa_ssid *ssid, const char *var) for (i = 0; i < NUM_SSID_FIELDS; i++) { const struct parse_data *field = &ssid_fields[i]; - if (os_strcmp(var, field->name) == 0) - return field->writer(field, ssid); + if (os_strcmp(var, field->name) == 0) { + char *ret = field->writer(field, ssid); + + if (ret && has_newline(ret)) { + wpa_printf(MSG_ERROR, + "Found newline in value for %s; not returning it", + var); + os_free(ret); + ret = NULL; + } + + return ret; + } } return NULL; @@ -2790,6 +2897,8 @@ int wpa_config_set_cred(struct wpa_cred *cred, const char *var, if (os_strcmp(var, "password") == 0 && os_strncmp(value, "ext:", 4) == 0) { + if (has_newline(value)) + return -1; str_clear_free(cred->password); cred->password = os_strdup(value); cred->ext_password = 1; @@ -2840,9 +2949,14 @@ int wpa_config_set_cred(struct wpa_cred *cred, const char *var, } val = wpa_config_parse_string(value, &len); - if (val == NULL) { + if (val == NULL || + (os_strcmp(var, "excluded_ssid") != 0 && + os_strcmp(var, "roaming_consortium") != 0 && + os_strcmp(var, "required_roaming_consortium") != 0 && + has_newline(val))) { wpa_printf(MSG_ERROR, "Line %d: invalid field '%s' string " "value '%s'.", line, var, value); + os_free(val); return -1; } @@ -3540,6 +3654,11 @@ struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface, config->rand_addr_lifetime = DEFAULT_RAND_ADDR_LIFETIME; config->key_mgmt_offload = DEFAULT_KEY_MGMT_OFFLOAD; config->cert_in_cb = DEFAULT_CERT_IN_CB; + config->wpa_rsc_relaxation = DEFAULT_WPA_RSC_RELAXATION; + +#ifdef CONFIG_MBO + config->mbo_cell_capa = DEFAULT_MBO_CELL_CAPA; +#endif /* CONFIG_MBO */ if (ctrl_interface) config->ctrl_interface = os_strdup(ctrl_interface); @@ -3646,6 +3765,12 @@ static int wpa_global_config_parse_str(const struct global_parse_data *data, return -1; } + if (has_newline(pos)) { + wpa_printf(MSG_ERROR, "Line %d: invalid %s value with newline", + line, data->name); + return -1; + } + tmp = os_strdup(pos); if (tmp == NULL) return -1; @@ -3684,22 +3809,12 @@ 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) + tmp = wpabuf_parse_bin(pos); + if (!tmp) 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; @@ -4246,6 +4361,16 @@ static const struct global_parse_data global_fields[] = { { INT_RANGE(fst_priority, 1, FST_MAX_PRIO_VALUE), 0 }, { INT_RANGE(fst_llt, 1, FST_MAX_LLT_MS), 0 }, #endif /* CONFIG_FST */ + { INT_RANGE(wpa_rsc_relaxation, 0, 1), 0 }, + { STR(sched_scan_plans), CFG_CHANGED_SCHED_SCAN_PLANS }, +#ifdef CONFIG_MBO + { STR(non_pref_chan), 0 }, + { INT_RANGE(mbo_cell_capa, MBO_CELL_CAPA_AVAILABLE, + MBO_CELL_CAPA_NOT_SUPPORTED), 0 }, +#endif /*CONFIG_MBO */ + { INT(gas_address3), 0 }, + { INT_RANGE(ftm_responder, 0, 1), 0 }, + { INT_RANGE(ftm_initiator, 0, 1), 0 }, }; #undef FUNC @@ -4304,6 +4429,23 @@ int wpa_config_get_value(const char *name, struct wpa_config *config, } +int wpa_config_get_num_global_field_names(void) +{ + return NUM_GLOBAL_FIELDS; +} + + +const char * wpa_config_get_global_field_name(unsigned int i, int *no_var) +{ + if (i >= NUM_GLOBAL_FIELDS) + return NULL; + + if (no_var) + *no_var = !global_fields[i].param1; + return global_fields[i].name; +} + + int wpa_config_process_global(struct wpa_config *config, char *pos, int line) { size_t i; diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h index 627f38b6e0056..48e64be5da1a1 100644 --- a/wpa_supplicant/config.h +++ b/wpa_supplicant/config.h @@ -39,6 +39,8 @@ #define DEFAULT_KEY_MGMT_OFFLOAD 1 #define DEFAULT_CERT_IN_CB 1 #define DEFAULT_P2P_GO_CTWINDOW 0 +#define DEFAULT_WPA_RSC_RELAXATION 1 +#define DEFAULT_MBO_CELL_CAPA MBO_CELL_CAPA_NOT_SUPPORTED #include "config_ssid.h" #include "wps/wps.h" @@ -331,6 +333,7 @@ struct wpa_cred { #define CFG_CHANGED_EXT_PW_BACKEND BIT(14) #define CFG_CHANGED_NFC_PASSWORD_TOKEN BIT(15) #define CFG_CHANGED_P2P_PASSPHRASE_LEN BIT(16) +#define CFG_CHANGED_SCHED_SCAN_PLANS BIT(17) /** * struct wpa_config - wpa_supplicant configuration data @@ -761,12 +764,17 @@ struct wpa_config { * frequency list of the local device and the peer device. * * @P2P_GO_FREQ_MOVE_STAY: Prefer to stay on the current frequency. + * + * @P2P_GO_FREQ_MOVE_SCM_ECSA: Same as + * P2P_GO_FREQ_MOVE_SCM_PEER_SUPPORTS but a transition is possible only + * if all the group members advertise eCSA support. */ enum { P2P_GO_FREQ_MOVE_SCM = 0, P2P_GO_FREQ_MOVE_SCM_PEER_SUPPORTS = 1, P2P_GO_FREQ_MOVE_STAY = 2, - P2P_GO_FREQ_MOVE_MAX = P2P_GO_FREQ_MOVE_STAY, + P2P_GO_FREQ_MOVE_SCM_ECSA = 3, + P2P_GO_FREQ_MOVE_MAX = P2P_GO_FREQ_MOVE_SCM_ECSA, } p2p_go_freq_change_policy; #define DEFAULT_P2P_GO_FREQ_MOVE P2P_GO_FREQ_MOVE_STAY @@ -1031,7 +1039,8 @@ struct wpa_config { * * 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. + * this default behavior for RSN network (this is not applicable for + * non-RSN cases). */ enum mfp_options pmf; @@ -1247,6 +1256,78 @@ struct wpa_config { * interface. */ int fst_llt; + + /** + * wpa_rsc_relaxation - RSC relaxation on GTK installation + * + * Values: + * 0 - use the EAPOL-Key RSC value on GTK installation + * 1 - use the null RSC if a bogus RSC value is detected in message 3 + * of 4-Way Handshake or message 1 of Group Key Handshake. + */ + int wpa_rsc_relaxation; + + /** + * sched_scan_plans - Scan plans for scheduled scan + * + * Each scan plan specifies the interval between scans and the number of + * iterations. The last scan plan only specifies the scan interval and + * will be run infinitely. + * + * format: <interval:iterations> <interval2:iterations2> ... <interval> + */ + char *sched_scan_plans; + +#ifdef CONFIG_MBO + /** + * non_pref_chan - Non-preferred channels list, separated by spaces. + * + * format: op_class:chan:preference:reason<:detail> + * Detail is optional. + */ + char *non_pref_chan; + + /** + * mbo_cell_capa - Cellular capabilities for MBO + */ + enum mbo_cellular_capa mbo_cell_capa; +#endif /* CONFIG_MBO */ + + /** + * gas_address3 - GAS Address3 field behavior + * + * Values: + * 0 - P2P specification (Address3 = AP BSSID) + * 1 = IEEE 802.11 standard compliant (Address3 = Wildcard BSSID when + * sent to not-associated AP; if associated, AP BSSID) + */ + int gas_address3; + + /** + * ftm_responder - Publish FTM (fine timing measurement) + * responder functionality + * + * Values: + * 0 - do not publish FTM responder functionality (Default) + * 1 - publish FTM responder functionality in + * bit 70 of Extended Capabilities element + * Note, actual FTM responder operation is managed outside + * wpa_supplicant. + */ + int ftm_responder; + + /** + * ftm_initiator - Publish FTM (fine timing measurement) + * initiator functionality + * + * Values: + * 0 - do not publish FTM initiator functionality (Default) + * 1 - publish FTM initiator functionality in + * bit 71 of Extended Capabilities element + * Note, actual FTM initiator operation is managed outside + * wpa_supplicant. + */ + int ftm_initiator; }; @@ -1305,6 +1386,9 @@ void wpa_config_debug_dump_networks(struct wpa_config *config); /* Prototypes for common functions from config.c */ int wpa_config_process_global(struct wpa_config *config, char *pos, int line); +int wpa_config_get_num_global_field_names(void); + +const char * wpa_config_get_global_field_name(unsigned int i, int *no_var); /* Prototypes for backend specific functions from the selected config_*.c */ diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c index fb438ea43e13f..7ae16545bebca 100644 --- a/wpa_supplicant/config_file.c +++ b/wpa_supplicant/config_file.c @@ -747,10 +747,16 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) INT(no_auto_peer); INT(frequency); INT(fixed_freq); +#ifdef CONFIG_ACS + INT(acs); +#endif /* CONFIG_ACS */ write_int(f, "proactive_key_caching", ssid->proactive_key_caching, -1); INT(disabled); INT(peerkey); INT(mixed_cell); + INT(max_oper_chwidth); + INT(pbss); + INT(wps_disabled); #ifdef CONFIG_IEEE80211W write_int(f, "ieee80211w", ssid->ieee80211w, MGMT_FRAME_PROTECTION_DEFAULT); @@ -779,6 +785,7 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) INT_DEF(dot11MeshHoldingTimeout, DEFAULT_MESH_HOLDING_TIMEOUT); #endif /* CONFIG_MESH */ INT(wpa_ptk_rekey); + INT(group_rekey); INT(ignore_broadcast_ssid); #ifdef CONFIG_HT_OVERRIDES INT_DEF(disable_ht, DEFAULT_DISABLE_HT); @@ -1136,6 +1143,22 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) if (config->p2p_go_freq_change_policy != DEFAULT_P2P_GO_FREQ_MOVE) fprintf(f, "p2p_go_freq_change_policy=%u\n", config->p2p_go_freq_change_policy); + if (WPA_GET_BE32(config->ip_addr_go)) + fprintf(f, "ip_addr_go=%u.%u.%u.%u\n", + config->ip_addr_go[0], config->ip_addr_go[1], + config->ip_addr_go[2], config->ip_addr_go[3]); + if (WPA_GET_BE32(config->ip_addr_mask)) + fprintf(f, "ip_addr_mask=%u.%u.%u.%u\n", + config->ip_addr_mask[0], config->ip_addr_mask[1], + config->ip_addr_mask[2], config->ip_addr_mask[3]); + if (WPA_GET_BE32(config->ip_addr_start)) + fprintf(f, "ip_addr_start=%u.%u.%u.%u\n", + config->ip_addr_start[0], config->ip_addr_start[1], + config->ip_addr_start[2], config->ip_addr_start[3]); + if (WPA_GET_BE32(config->ip_addr_end)) + fprintf(f, "ip_addr_end=%u.%u.%u.%u\n", + config->ip_addr_end[0], config->ip_addr_end[1], + config->ip_addr_end[2], config->ip_addr_end[3]); #endif /* CONFIG_P2P */ if (config->country[0] && config->country[1]) { fprintf(f, "country=%c%c\n", @@ -1299,6 +1322,28 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) if (config->wps_priority) fprintf(f, "wps_priority=%d\n", config->wps_priority); + + if (config->wpa_rsc_relaxation != DEFAULT_WPA_RSC_RELAXATION) + fprintf(f, "wpa_rsc_relaxation=%d\n", + config->wpa_rsc_relaxation); + + if (config->sched_scan_plans) + fprintf(f, "sched_scan_plans=%s\n", config->sched_scan_plans); + +#ifdef CONFIG_MBO + if (config->non_pref_chan) + fprintf(f, "non_pref_chan=%s\n", config->non_pref_chan); + if (config->mbo_cell_capa != DEFAULT_MBO_CELL_CAPA) + fprintf(f, "mbo_cell_capa=%u\n", config->mbo_cell_capa); +#endif /* CONFIG_MBO */ + + if (config->gas_address3) + fprintf(f, "gas_address3=%d\n", config->gas_address3); + + if (config->ftm_responder) + fprintf(f, "ftm_responder=%d\n", config->ftm_responder); + if (config->ftm_initiator) + fprintf(f, "ftm_initiator=%d\n", config->ftm_initiator); } #endif /* CONFIG_NO_CONFIG_WRITE */ diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h index 7ef326cfbed63..010b594af85e8 100644 --- a/wpa_supplicant/config_ssid.h +++ b/wpa_supplicant/config_ssid.h @@ -360,6 +360,19 @@ struct wpa_ssid { } mode; /** + * pbss - Whether to use PBSS. Relevant to DMG networks only. + * 0 = do not use PBSS + * 1 = use PBSS + * 2 = don't care (not allowed in AP mode) + * Used together with mode configuration. When mode is AP, it + * means to start a PCP instead of a regular AP. When mode is INFRA it + * means connect to a PCP instead of AP. In this mode you can also + * specify 2 (don't care) meaning connect to either AP or PCP. + * P2P_GO and P2P_GROUP_FORMATION modes must use PBSS in DMG network. + */ + int pbss; + + /** * disabled - Whether this network is currently disabled * * 0 = this network can be used (default). @@ -431,6 +444,18 @@ struct wpa_ssid { */ int fixed_freq; +#ifdef CONFIG_ACS + /** + * ACS - Automatic Channel Selection for AP mode + * + * If present, it will be handled together with frequency. + * frequency will be used to determine hardware mode only, when it is + * used for both hardware mode and channel when used alone. This will + * force the channel to be set to 0, thus enabling ACS. + */ + int acs; +#endif /* CONFIG_ACS */ + /** * mesh_basic_rates - BSS Basic rate set for mesh network * @@ -449,6 +474,10 @@ struct wpa_ssid { int vht; + u8 max_oper_chwidth; + + unsigned int vht_center_freq2; + /** * wpa_ptk_rekey - Maximum lifetime for PTK in seconds * @@ -458,6 +487,14 @@ struct wpa_ssid { int wpa_ptk_rekey; /** + * group_rekey - Group rekeying time in seconds + * + * This value, if non-zero, is used as the dot11RSNAConfigGroupRekeyTime + * parameter when operating in Authenticator role in IBSS. + */ + int group_rekey; + + /** * scan_freq - Array of frequencies to scan or %NULL for all * * This is an optional zero-terminated array of frequencies in @@ -719,6 +756,14 @@ struct wpa_ssid { * this MBSS will trigger a peering attempt. */ int no_auto_peer; + + /** + * wps_disabled - WPS disabled in AP mode + * + * 0 = WPS enabled and configured (default) + * 1 = WPS disabled + */ + int wps_disabled; }; #endif /* CONFIG_SSID_H */ diff --git a/wpa_supplicant/config_winreg.c b/wpa_supplicant/config_winreg.c index 199f04fbbf13e..82ba3b015dc9a 100644 --- a/wpa_supplicant/config_winreg.c +++ b/wpa_supplicant/config_winreg.c @@ -933,6 +933,7 @@ static int wpa_config_write_network(HKEY hk, struct wpa_ssid *ssid, int id) #ifdef CONFIG_HS20 INT(update_identifier); #endif /* CONFIG_HS20 */ + INT(group_rekey); #undef STR #undef INT diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index 3b97806d871df..d814fdf7fd2d4 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -15,6 +15,7 @@ #include "utils/common.h" #include "utils/eloop.h" #include "utils/uuid.h" +#include "utils/module_tests.h" #include "common/version.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" @@ -55,6 +56,7 @@ static int wpa_supplicant_global_iface_list(struct wpa_global *global, char *buf, int len); static int wpa_supplicant_global_iface_interfaces(struct wpa_global *global, + const char *input, char *buf, int len); static int * freq_range_to_channel_list(struct wpa_supplicant *wpa_s, char *val); @@ -310,6 +312,33 @@ static int wpas_ctrl_set_band(struct wpa_supplicant *wpa_s, char *band) } +static int wpas_ctrl_iface_set_lci(struct wpa_supplicant *wpa_s, + const char *cmd) +{ + struct wpabuf *lci; + + if (*cmd == '\0' || os_strcmp(cmd, "\"\"") == 0) { + wpabuf_free(wpa_s->lci); + wpa_s->lci = NULL; + return 0; + } + + lci = wpabuf_parse_bin(cmd); + if (!lci) + return -1; + + if (os_get_reltime(&wpa_s->lci_time)) { + wpabuf_free(lci); + return -1; + } + + wpabuf_free(wpa_s->lci); + wpa_s->lci = lci; + + return 0; +} + + static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, char *cmd) { @@ -371,6 +400,20 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, wps_corrupt_pkhash = atoi(value); wpa_printf(MSG_DEBUG, "WPS: Testing - wps_corrupt_pkhash=%d", wps_corrupt_pkhash); + } else if (os_strcasecmp(cmd, "wps_force_auth_types") == 0) { + if (value[0] == '\0') { + wps_force_auth_types_in_use = 0; + } else { + wps_force_auth_types = strtol(value, NULL, 0); + wps_force_auth_types_in_use = 1; + } + } else if (os_strcasecmp(cmd, "wps_force_encr_types") == 0) { + if (value[0] == '\0') { + wps_force_encr_types_in_use = 0; + } else { + wps_force_encr_types = strtol(value, NULL, 0); + wps_force_encr_types_in_use = 1; + } #endif /* CONFIG_WPS_TESTING */ } else if (os_strcasecmp(cmd, "ampdu") == 0) { if (wpa_drv_ampdu(wpa_s, atoi(value)) < 0) @@ -378,7 +421,6 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, #ifdef CONFIG_TDLS #ifdef CONFIG_TDLS_TESTING } else if (os_strcasecmp(cmd, "tdls_testing") == 0) { - extern unsigned int tdls_testing; tdls_testing = strtol(value, NULL, 0); wpa_printf(MSG_DEBUG, "TDLS: tdls_testing=0x%x", tdls_testing); #endif /* CONFIG_TDLS_TESTING */ @@ -467,6 +509,14 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, wpa_s->extra_roc_dur = atoi(value); } else if (os_strcasecmp(cmd, "test_failure") == 0) { wpa_s->test_failure = atoi(value); + } else if (os_strcasecmp(cmd, "p2p_go_csa_on_inv") == 0) { + wpa_s->p2p_go_csa_on_inv = !!atoi(value); + } else if (os_strcasecmp(cmd, "ignore_auth_resp") == 0) { + wpa_s->ignore_auth_resp = !!atoi(value); + } else if (os_strcasecmp(cmd, "ignore_assoc_disallow") == 0) { + wpa_s->ignore_assoc_disallow = !!atoi(value); + } else if (os_strcasecmp(cmd, "reject_btm_req_reason") == 0) { + wpa_s->reject_btm_req_reason = atoi(value); #endif /* CONFIG_TESTING_OPTIONS */ #ifndef CONFIG_NO_CONFIG_BLOBS } else if (os_strcmp(cmd, "blob") == 0) { @@ -474,6 +524,14 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, #endif /* CONFIG_NO_CONFIG_BLOBS */ } else if (os_strcasecmp(cmd, "setband") == 0) { ret = wpas_ctrl_set_band(wpa_s, value); +#ifdef CONFIG_MBO + } else if (os_strcasecmp(cmd, "non_pref_chan") == 0) { + ret = wpas_mbo_update_non_pref_chan(wpa_s, value); + } else if (os_strcasecmp(cmd, "mbo_cell_capa") == 0) { + wpas_mbo_update_cell_capa(wpa_s, atoi(value)); +#endif /* CONFIG_MBO */ + } else if (os_strcasecmp(cmd, "lci") == 0) { + ret = wpas_ctrl_iface_set_lci(wpa_s, value); } else { value[-1] = '='; ret = wpa_config_process_global(wpa_s->conf, cmd, -1); @@ -940,7 +998,8 @@ static int wpa_supplicant_ctrl_iface_wps_pin(struct wpa_supplicant *wpa_s, if (os_strcmp(cmd, "any") == 0) _bssid = NULL; else if (os_strcmp(cmd, "get") == 0) { - ret = wps_generate_pin(); + if (wps_generate_pin((unsigned int *) &ret) < 0) + return -1; goto done; } else if (hwaddr_aton(cmd, bssid)) { wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_PIN: invalid BSSID '%s'", @@ -1833,6 +1892,10 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, "mode=P2P GO - group " "formation\n"); break; + case WPAS_MODE_MESH: + ret = os_snprintf(pos, end - pos, + "mode=mesh\n"); + break; default: ret = 0; break; @@ -2703,6 +2766,40 @@ static int wpa_supplicant_ctrl_iface_mesh_group_remove( return 0; } + +static int wpa_supplicant_ctrl_iface_mesh_peer_remove( + struct wpa_supplicant *wpa_s, char *cmd) +{ + u8 addr[ETH_ALEN]; + + if (hwaddr_aton(cmd, addr) < 0) + return -1; + + return wpas_mesh_peer_remove(wpa_s, addr); +} + + +static int wpa_supplicant_ctrl_iface_mesh_peer_add( + struct wpa_supplicant *wpa_s, char *cmd) +{ + u8 addr[ETH_ALEN]; + int duration; + char *pos; + + pos = os_strstr(cmd, " duration="); + if (pos) { + *pos = '\0'; + duration = atoi(pos + 10); + } else { + duration = -1; + } + + if (hwaddr_aton(cmd, addr)) + return -1; + + return wpas_mesh_peer_add(wpa_s, addr, duration); +} + #endif /* CONFIG_MESH */ @@ -2832,15 +2929,10 @@ static int wpa_supplicant_ctrl_iface_add_network( wpa_printf(MSG_DEBUG, "CTRL_IFACE: ADD_NETWORK"); - ssid = wpa_config_add_network(wpa_s->conf); + ssid = wpa_supplicant_add_network(wpa_s); if (ssid == NULL) return -1; - wpas_notify_network_added(wpa_s, ssid); - - ssid->disabled = 1; - wpa_config_set_network_defaults(ssid); - ret = os_snprintf(buf, buflen, "%d\n", ssid->id); if (os_snprintf_error(buflen, ret)) return -1; @@ -2853,7 +2945,7 @@ static int wpa_supplicant_ctrl_iface_remove_network( { int id; struct wpa_ssid *ssid; - int was_disabled; + int result; /* cmd: "<network id>" or "all" */ if (os_strcmp(cmd, "all") == 0) { @@ -2889,54 +2981,17 @@ static int wpa_supplicant_ctrl_iface_remove_network( id = atoi(cmd); wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_NETWORK id=%d", id); - ssid = wpa_config_get_network(wpa_s->conf, id); - if (ssid) - wpas_notify_network_removed(wpa_s, ssid); - if (ssid == NULL) { + result = wpa_supplicant_remove_network(wpa_s, id); + if (result == -1) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find network " "id=%d", id); return -1; } - - if (wpa_s->last_ssid == ssid) - wpa_s->last_ssid = NULL; - - if (ssid == wpa_s->current_ssid || wpa_s->current_ssid == NULL) { -#ifdef CONFIG_SME - wpa_s->sme.prev_bssid_set = 0; -#endif /* CONFIG_SME */ - /* - * Invalidate the EAP session cache if the current or - * previously used network is removed. - */ - eapol_sm_invalidate_cached_session(wpa_s->eapol); - } - - if (ssid == wpa_s->current_ssid) { - wpa_sm_set_config(wpa_s->wpa, NULL); - eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); - - if (wpa_s->wpa_state >= WPA_AUTHENTICATING) - wpa_s->own_disconnect_req = 1; - wpa_supplicant_deauthenticate(wpa_s, - WLAN_REASON_DEAUTH_LEAVING); - } - - was_disabled = ssid->disabled; - - if (wpa_config_remove_network(wpa_s->conf, id) < 0) { + if (result == -2) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Not able to remove the " "network id=%d", id); return -1; } - - if (!was_disabled && wpa_s->sched_scanning) { - wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan to remove " - "network from filters"); - wpa_supplicant_cancel_sched_scan(wpa_s); - wpa_supplicant_req_scan(wpa_s, 0, 0); - } - return 0; } @@ -2945,22 +3000,29 @@ static int wpa_supplicant_ctrl_iface_update_network( struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, char *name, char *value) { - if (wpa_config_set(ssid, name, value, 0) < 0) { + int ret; + + ret = wpa_config_set(ssid, name, value, 0); + if (ret < 0) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to set network " "variable '%s'", name); return -1; } + if (ret == 1) + return 0; /* No change to the previously configured value */ if (os_strcmp(name, "bssid") != 0 && - os_strcmp(name, "priority") != 0) + os_strcmp(name, "priority") != 0) { wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid); - if (wpa_s->current_ssid == ssid || wpa_s->current_ssid == NULL) { - /* - * Invalidate the EAP session cache if anything in the current - * or previously used configuration changes. - */ - eapol_sm_invalidate_cached_session(wpa_s->eapol); + if (wpa_s->current_ssid == ssid || + wpa_s->current_ssid == NULL) { + /* + * Invalidate the EAP session cache if anything in the + * current or previously used configuration changes. + */ + eapol_sm_invalidate_cached_session(wpa_s->eapol); + } } if ((os_strcmp(name, "psk") == 0 && @@ -3935,6 +3997,15 @@ static int wpa_supplicant_ctrl_iface_get_capability( } #endif /* CONFIG_FIPS */ +#ifdef CONFIG_ACS + if (os_strcmp(field, "acs") == 0) { + res = os_snprintf(buf, buflen, "ACS"); + if (os_snprintf_error(buflen, res)) + return -1; + return res; + } +#endif /* CONFIG_ACS */ + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'", field); @@ -4195,9 +4266,10 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, 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) + if (ret >= end - pos) return 0; - pos += ret; + if (ret > 0) + pos += ret; } #endif /* CONFIG_P2P */ @@ -4231,6 +4303,8 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, #ifdef CONFIG_INTERWORKING if ((mask & WPA_BSS_MASK_INTERNETW) && bss->anqp) { struct wpa_bss_anqp *anqp = bss->anqp; + struct wpa_bss_anqp_elem *elem; + pos = anqp_add_hex(pos, end, "anqp_capability_list", anqp->capability_list); pos = anqp_add_hex(pos, end, "anqp_venue_name", @@ -4260,6 +4334,15 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, pos = anqp_add_hex(pos, end, "hs20_osu_providers_list", anqp->hs20_osu_providers_list); #endif /* CONFIG_HS20 */ + + dl_list_for_each(elem, &anqp->anqp_elems, + struct wpa_bss_anqp_elem, list) { + char title[20]; + + os_snprintf(title, sizeof(title), "anqp[%u]", + elem->infoid); + pos = anqp_add_hex(pos, end, title, elem->payload); + } } #endif /* CONFIG_INTERWORKING */ @@ -4267,9 +4350,10 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, if (mask & WPA_BSS_MASK_MESH_SCAN) { ie = (const u8 *) (bss + 1); ret = wpas_mesh_scan_result_text(ie, bss->ie_len, pos, end); - if (ret < 0 || ret >= end - pos) + if (ret >= end - pos) return 0; - pos += ret; + if (ret > 0) + pos += ret; } #endif /* CONFIG_MESH */ @@ -4676,7 +4760,7 @@ static int p2ps_ctrl_parse_cpt_priority(const char *pos, u8 *cpt) return -1; } - if (isblank(*last)) { + if (isblank((unsigned char) *last)) { i++; break; } @@ -4848,6 +4932,30 @@ static int p2p_ctrl_asp_provision(struct wpa_supplicant *wpa_s, char *cmd) } +static int parse_freq(int chwidth, int freq2) +{ + if (freq2 < 0) + return -1; + if (freq2) + return VHT_CHANWIDTH_80P80MHZ; + + switch (chwidth) { + case 0: + case 20: + case 40: + return VHT_CHANWIDTH_USE_HT; + case 80: + return VHT_CHANWIDTH_80MHZ; + case 160: + return VHT_CHANWIDTH_160MHZ; + default: + wpa_printf(MSG_DEBUG, "Unknown max oper bandwidth: %d", + chwidth); + return -1; + } +} + + static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { @@ -4864,7 +4972,9 @@ static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd, int go_intent = -1; int freq = 0; int pd; - int ht40, vht; + int ht40, vht, max_oper_chwidth, chwidth = 0, freq2 = 0; + u8 _group_ssid[SSID_MAX_LEN], *group_ssid = NULL; + size_t group_ssid_len = 0; if (!wpa_s->global->p2p_init_wpa_s) return -1; @@ -4877,7 +4987,7 @@ static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd, /* <addr> <"pbc" | "pin" | PIN> [label|display|keypad|p2ps] * [persistent|persistent=<network id>] * [join] [auth] [go_intent=<0..15>] [freq=<in MHz>] [provdisc] - * [ht40] [vht] [auto] */ + * [ht40] [vht] [auto] [ssid=<hexdump>] */ if (hwaddr_aton(cmd, addr)) return -1; @@ -4925,11 +5035,41 @@ static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd, return -1; } + pos2 = os_strstr(pos, " freq2="); + if (pos2) + freq2 = atoi(pos2 + 7); + + pos2 = os_strstr(pos, " max_oper_chwidth="); + if (pos2) + chwidth = atoi(pos2 + 18); + + max_oper_chwidth = parse_freq(chwidth, freq2); + if (max_oper_chwidth < 0) + return -1; + + pos2 = os_strstr(pos, " ssid="); + if (pos2) { + char *end; + + pos2 += 6; + end = os_strchr(pos2, ' '); + if (!end) + group_ssid_len = os_strlen(pos2) / 2; + else + group_ssid_len = (end - pos2) / 2; + if (group_ssid_len == 0 || group_ssid_len > SSID_MAX_LEN || + hexstr2bin(pos2, _group_ssid, group_ssid_len) < 0) + return -1; + group_ssid = _group_ssid; + } + 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 if (os_strstr(pos, "p2ps") != NULL) { + wps_method = WPS_P2PS; } else { pin = pos; pos = os_strchr(pin, ' '); @@ -4938,8 +5078,6 @@ static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd, *pos++ = '\0'; if (os_strncmp(pos, "display", 7) == 0) wps_method = WPS_PIN_DISPLAY; - else if (os_strncmp(pos, "p2ps", 4) == 0) - wps_method = WPS_P2PS; } if (!wps_pin_str_valid(pin)) { os_memcpy(buf, "FAIL-INVALID-PIN\n", 17); @@ -4949,8 +5087,9 @@ static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd, new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method, persistent_group, automatic, join, - auth, go_intent, freq, persistent_id, pd, - ht40, vht); + auth, go_intent, freq, freq2, persistent_id, + pd, ht40, vht, max_oper_chwidth, + group_ssid, group_ssid_len); if (new_pin == -2) { os_memcpy(buf, "FAIL-CHANNEL-UNAVAILABLE\n", 25); return 25; @@ -5505,7 +5644,7 @@ static int p2p_ctrl_invite_persistent(struct wpa_supplicant *wpa_s, char *cmd) struct wpa_ssid *ssid; u8 *_peer = NULL, peer[ETH_ALEN]; int freq = 0, pref_freq = 0; - int ht40, vht; + int ht40, vht, max_oper_chwidth, chwidth = 0, freq2 = 0; id = atoi(cmd); pos = os_strstr(cmd, " peer="); @@ -5543,8 +5682,20 @@ static int p2p_ctrl_invite_persistent(struct wpa_supplicant *wpa_s, char *cmd) ht40 = (os_strstr(cmd, " ht40") != NULL) || wpa_s->conf->p2p_go_ht40 || vht; - return wpas_p2p_invite(wpa_s, _peer, ssid, NULL, freq, ht40, vht, - pref_freq); + pos = os_strstr(cmd, "freq2="); + if (pos) + freq2 = atoi(pos + 6); + + pos = os_strstr(cmd, " max_oper_chwidth="); + if (pos) + chwidth = atoi(pos + 18); + + max_oper_chwidth = parse_freq(chwidth, freq2); + if (max_oper_chwidth < 0) + return -1; + + return wpas_p2p_invite(wpa_s, _peer, ssid, NULL, freq, freq2, ht40, vht, + max_oper_chwidth, pref_freq); } @@ -5591,7 +5742,8 @@ static int p2p_ctrl_invite(struct wpa_supplicant *wpa_s, char *cmd) static int p2p_ctrl_group_add_persistent(struct wpa_supplicant *wpa_s, - int id, int freq, int ht40, int vht) + int id, int freq, int vht_center_freq2, + int ht40, int vht, int vht_chwidth) { struct wpa_ssid *ssid; @@ -5603,8 +5755,9 @@ static int p2p_ctrl_group_add_persistent(struct wpa_supplicant *wpa_s, return -1; } - return wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0, ht40, vht, - NULL, 0, 0); + return wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, + vht_center_freq2, 0, ht40, vht, + vht_chwidth, NULL, 0, 0); } @@ -5613,11 +5766,14 @@ static int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd) int freq = 0, persistent = 0, group_id = -1; int vht = wpa_s->conf->p2p_go_vht; int ht40 = wpa_s->conf->p2p_go_ht40 || vht; + int max_oper_chwidth, chwidth = 0, freq2 = 0; char *token, *context = NULL; while ((token = str_token(cmd, " ", &context))) { if (sscanf(token, "freq=%d", &freq) == 1 || - sscanf(token, "persistent=%d", &group_id) == 1) { + sscanf(token, "freq2=%d", &freq2) == 1 || + sscanf(token, "persistent=%d", &group_id) == 1 || + sscanf(token, "max_oper_chwidth=%d", &chwidth) == 1) { continue; } else if (os_strcmp(token, "ht40") == 0) { ht40 = 1; @@ -5634,11 +5790,40 @@ static int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd) } } + max_oper_chwidth = parse_freq(chwidth, freq2); + if (max_oper_chwidth < 0) + return -1; + if (group_id >= 0) return p2p_ctrl_group_add_persistent(wpa_s, group_id, - freq, ht40, vht); + freq, freq2, ht40, vht, + max_oper_chwidth); + + return wpas_p2p_group_add(wpa_s, persistent, freq, freq2, ht40, vht, + max_oper_chwidth); +} + + +static int p2p_ctrl_group_member(struct wpa_supplicant *wpa_s, const char *cmd, + char *buf, size_t buflen) +{ + u8 dev_addr[ETH_ALEN]; + struct wpa_ssid *ssid; + int res; + const u8 *iaddr; + + ssid = wpa_s->current_ssid; + if (!wpa_s->global->p2p || !ssid || ssid->mode != WPAS_MODE_P2P_GO || + hwaddr_aton(cmd, dev_addr)) + return -1; - return wpas_p2p_group_add(wpa_s, persistent, freq, ht40, vht); + iaddr = p2p_group_get_client_interface_addr(wpa_s->p2p_group, dev_addr); + if (!iaddr) + return -1; + res = os_snprintf(buf, buflen, MACSTR, MAC2STR(iaddr)); + if (os_snprintf_error(buflen, res)) + return -1; + return res; } @@ -5797,8 +5982,15 @@ static int p2p_ctrl_set(struct wpa_supplicant *wpa_s, char *cmd) } if (os_strcmp(cmd, "listen_channel") == 0) { - return p2p_set_listen_channel(wpa_s->global->p2p, 81, - atoi(param), 1); + char *pos; + u8 channel, op_class; + + channel = atoi(param); + pos = os_strchr(param, ' '); + op_class = pos ? atoi(pos) : 81; + + return p2p_set_listen_channel(wpa_s->global->p2p, op_class, + channel, 1); } if (os_strcmp(cmd, "ssid_postfix") == 0) { @@ -6059,6 +6251,21 @@ static int p2p_ctrl_remove_client(struct wpa_supplicant *wpa_s, const char *cmd) return 0; } + +static int p2p_ctrl_iface_p2p_lo_start(struct wpa_supplicant *wpa_s, char *cmd) +{ + int freq = 0, period = 0, interval = 0, count = 0; + + if (sscanf(cmd, "%d %d %d %d", &freq, &period, &interval, &count) != 4) + { + wpa_printf(MSG_DEBUG, + "CTRL: Invalid P2P LO Start parameter: '%s'", cmd); + return -1; + } + + return wpas_p2p_lo_start(wpa_s, freq, period, interval, count); +} + #endif /* CONFIG_P2P */ @@ -6176,6 +6383,7 @@ static int get_anqp(struct wpa_supplicant *wpa_s, char *dst) u16 id[MAX_ANQP_INFO_ID]; size_t num_id = 0; u32 subtypes = 0; + int get_cell_pref = 0; used = hwaddr_aton2(dst, dst_addr); if (used < 0) @@ -6193,6 +6401,15 @@ static int get_anqp(struct wpa_supplicant *wpa_s, char *dst) #else /* CONFIG_HS20 */ return -1; #endif /* CONFIG_HS20 */ + } else if (os_strncmp(pos, "mbo:", 4) == 0) { +#ifdef CONFIG_MBO + int num = atoi(pos + 4); + if (num != MBO_ANQP_SUBTYPE_CELL_CONN_PREF) + return -1; + get_cell_pref = 1; +#else /* CONFIG_MBO */ + return -1; +#endif /* CONFIG_MBO */ } else { id[num_id] = atoi(pos); if (id[num_id]) @@ -6207,7 +6424,8 @@ static int get_anqp(struct wpa_supplicant *wpa_s, char *dst) if (num_id == 0) return -1; - return anqp_send_req(wpa_s, dst_addr, id, num_id, subtypes); + return anqp_send_req(wpa_s, dst_addr, id, num_id, subtypes, + get_cell_pref); } @@ -6378,7 +6596,7 @@ static int get_hs20_anqp(struct wpa_supplicant *wpa_s, char *dst) if (subtypes == 0) return -1; - return hs20_anqp_send_req(wpa_s, dst_addr, subtypes, NULL, 0); + return hs20_anqp_send_req(wpa_s, dst_addr, subtypes, NULL, 0, 0); } @@ -6401,7 +6619,7 @@ static int hs20_nai_home_realm_list(struct wpa_supplicant *wpa_s, ret = hs20_anqp_send_req(wpa_s, addr, BIT(HS20_STYPE_NAI_HOME_REALM_QUERY), - buf, len); + buf, len, 0); os_free(buf); @@ -6447,14 +6665,59 @@ static int hs20_get_nai_home_realm_list(struct wpa_supplicant *wpa_s, ret = hs20_anqp_send_req(wpa_s, dst_addr, BIT(HS20_STYPE_NAI_HOME_REALM_QUERY), - buf, len); + buf, len, 0); os_free(buf); return ret; } -static int hs20_icon_request(struct wpa_supplicant *wpa_s, char *cmd) +static int get_hs20_icon(struct wpa_supplicant *wpa_s, char *cmd, char *reply, + int buflen) +{ + u8 dst_addr[ETH_ALEN]; + int used; + char *ctx = NULL, *icon, *poffset, *psize; + + used = hwaddr_aton2(cmd, dst_addr); + if (used < 0) + return -1; + cmd += used; + + icon = str_token(cmd, " ", &ctx); + poffset = str_token(cmd, " ", &ctx); + psize = str_token(cmd, " ", &ctx); + if (!icon || !poffset || !psize) + return -1; + + wpa_s->fetch_osu_icon_in_progress = 0; + return hs20_get_icon(wpa_s, dst_addr, icon, atoi(poffset), atoi(psize), + reply, buflen); +} + + +static int del_hs20_icon(struct wpa_supplicant *wpa_s, char *cmd) +{ + u8 dst_addr[ETH_ALEN]; + int used; + char *icon; + + if (!cmd[0]) + return hs20_del_icon(wpa_s, NULL, NULL); + + used = hwaddr_aton2(cmd, dst_addr); + if (used < 0) + return -1; + + while (cmd[used] == ' ') + used++; + icon = cmd[used] ? &cmd[used] : NULL; + + return hs20_del_icon(wpa_s, dst_addr, icon); +} + + +static int hs20_icon_request(struct wpa_supplicant *wpa_s, char *cmd, int inmem) { u8 dst_addr[ETH_ALEN]; int used; @@ -6470,7 +6733,7 @@ static int hs20_icon_request(struct wpa_supplicant *wpa_s, char *cmd) wpa_s->fetch_osu_icon_in_progress = 0; return hs20_anqp_send_req(wpa_s, dst_addr, BIT(HS20_STYPE_ICON_REQUEST), - (u8 *) icon, os_strlen(icon)); + (u8 *) icon, os_strlen(icon), inmem); } #endif /* CONFIG_HS20 */ @@ -6560,14 +6823,27 @@ static int wpas_ctrl_iface_wnm_sleep(struct wpa_supplicant *wpa_s, char *cmd) static int wpas_ctrl_iface_wnm_bss_query(struct wpa_supplicant *wpa_s, char *cmd) { - int query_reason; + int query_reason, list = 0; query_reason = atoi(cmd); - wpa_printf(MSG_DEBUG, "CTRL_IFACE: WNM_BSS_QUERY query_reason=%d", - query_reason); + cmd = os_strchr(cmd, ' '); + if (cmd) { + cmd++; + if (os_strncmp(cmd, "list", 4) == 0) { + list = 1; + } else { + wpa_printf(MSG_DEBUG, "WNM Query: Invalid option %s", + cmd); + return -1; + } + } - return wnm_send_bss_transition_mgmt_query(wpa_s, query_reason); + wpa_printf(MSG_DEBUG, + "CTRL_IFACE: WNM_BSS_QUERY query_reason=%d%s", + query_reason, list ? " candidate list" : ""); + + return wnm_send_bss_transition_mgmt_query(wpa_s, query_reason, list); } #endif /* CONFIG_WNM */ @@ -6632,6 +6908,28 @@ static int wpa_supplicant_signal_poll(struct wpa_supplicant *wpa_s, char *buf, } +static int wpas_ctrl_iface_signal_monitor(struct wpa_supplicant *wpa_s, + const char *cmd) +{ + const char *pos; + int threshold = 0; + int hysteresis = 0; + + if (wpa_s->bgscan && wpa_s->bgscan_priv) { + wpa_printf(MSG_DEBUG, + "Reject SIGNAL_MONITOR command - bgscan is active"); + return -1; + } + pos = os_strstr(cmd, "THRESHOLD="); + if (pos) + threshold = atoi(pos + 10); + pos = os_strstr(cmd, "HYSTERESIS="); + if (pos) + hysteresis = atoi(pos + 11); + return wpa_drv_signal_monitor(wpa_s, threshold, hysteresis); +} + + static int wpas_ctrl_iface_get_pref_freq_list( struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { @@ -6679,6 +6977,34 @@ static int wpas_ctrl_iface_get_pref_freq_list( } +static int wpas_ctrl_iface_driver_flags(struct wpa_supplicant *wpa_s, + char *buf, size_t buflen) +{ + int ret, i; + char *pos, *end; + + ret = os_snprintf(buf, buflen, "%016llX:\n", + (long long unsigned) wpa_s->drv_flags); + if (os_snprintf_error(buflen, ret)) + return -1; + + pos = buf + ret; + end = buf + buflen; + + for (i = 0; i < 64; i++) { + if (wpa_s->drv_flags & (1LLU << i)) { + ret = os_snprintf(pos, end - pos, "%s\n", + driver_flag_to_string(1LLU << i)); + if (os_snprintf_error(end - pos, ret)) + return -1; + pos += ret; + } + } + + return pos - buf; +} + + static int wpa_supplicant_pktcnt_poll(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) { @@ -6736,13 +7062,13 @@ static int wpa_supplicant_vendor_cmd(struct wpa_supplicant *wpa_s, char *cmd, /* cmd: <vendor id> <subcommand id> [<hex formatted data>] */ vendor_id = strtoul(cmd, &pos, 16); - if (!isblank(*pos)) + if (!isblank((unsigned char) *pos)) return -EINVAL; subcmd = strtoul(pos, &pos, 10); if (*pos != '\0') { - if (!isblank(*pos++)) + if (!isblank((unsigned char) *pos++)) return -EINVAL; data_len = os_strlen(pos); } @@ -6790,10 +7116,20 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s) wpa_dbg(wpa_s, MSG_DEBUG, "Flush all wpa_supplicant state"); + wpas_abort_ongoing_scan(wpa_s); + + if (wpa_s->wpa_state >= WPA_AUTHENTICATING) { + /* + * Avoid possible auto connect re-connection on getting + * disconnected due to state flush. + */ + wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); + } + #ifdef CONFIG_P2P + wpas_p2p_group_remove(p2p_wpa_s, "*"); wpas_p2p_cancel(p2p_wpa_s); p2p_ctrl_flush(p2p_wpa_s); - wpas_p2p_group_remove(p2p_wpa_s, "*"); wpas_p2p_service_flush(p2p_wpa_s); p2p_wpa_s->global->p2p_disabled = 0; p2p_wpa_s->global->p2p_per_sta_psk = 0; @@ -6803,12 +7139,15 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s) p2p_wpa_s->global->p2p_go_avoid_freq.range = NULL; p2p_wpa_s->global->p2p_go_avoid_freq.num = 0; p2p_wpa_s->global->pending_p2ps_group = 0; + p2p_wpa_s->global->pending_p2ps_group_freq = 0; #endif /* CONFIG_P2P */ #ifdef CONFIG_WPS_TESTING wps_version_number = 0x20; wps_testing_dummy_cred = 0; wps_corrupt_pkhash = 0; + wps_force_auth_types_in_use = 0; + wps_force_encr_types_in_use = 0; #endif /* CONFIG_WPS_TESTING */ #ifdef CONFIG_WPS wpa_s->wps_fragment_size = 0; @@ -6820,7 +7159,6 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s) #ifdef CONFIG_TDLS #ifdef CONFIG_TDLS_TESTING - extern unsigned int tdls_testing; tdls_testing = 0; #endif /* CONFIG_TDLS_TESTING */ wpa_drv_tdls_oper(wpa_s, TDLS_ENABLE, NULL); @@ -6866,7 +7204,10 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s) wpa_s->next_ssid = NULL; #ifdef CONFIG_INTERWORKING +#ifdef CONFIG_HS20 hs20_cancel_fetch_osu(wpa_s); + hs20_del_icon(wpa_s, NULL, NULL); +#endif /* CONFIG_HS20 */ #endif /* CONFIG_INTERWORKING */ wpa_s->ext_mgmt_frame_handling = 0; @@ -6874,6 +7215,11 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s) #ifdef CONFIG_TESTING_OPTIONS wpa_s->extra_roc_dur = 0; wpa_s->test_failure = WPAS_TEST_FAILURE_NONE; + wpa_s->p2p_go_csa_on_inv = 0; + wpa_s->ignore_auth_resp = 0; + wpa_s->ignore_assoc_disallow = 0; + wpa_s->reject_btm_req_reason = 0; + wpa_sm_set_test_assoc_ie(wpa_s->wpa, NULL); #endif /* CONFIG_TESTING_OPTIONS */ wpa_s->disconnected = 0; @@ -6891,6 +7237,11 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s) } eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL); + wpa_s->wnmsleep_used = 0; + +#ifdef CONFIG_SME + wpa_s->sme.last_unprot_disconnect.sec = 0; +#endif /* CONFIG_SME */ } @@ -6947,6 +7298,13 @@ static void wpas_ctrl_radio_work_cb(struct wpa_radio_work *work, int deinit) eloop_cancel_timeout(wpas_ctrl_radio_work_timeout, work, NULL); + /* + * work->type points to a buffer in ework, so need to replace + * that here with a fixed string to avoid use of freed memory + * in debug prints. + */ + work->type = "freed-ext-work"; + work->ctx = NULL; os_free(ework); return; } @@ -7396,6 +7754,76 @@ static void wpas_ctrl_iface_mgmt_tx_done(struct wpa_supplicant *wpa_s) } +static int wpas_ctrl_iface_mgmt_rx_process(struct wpa_supplicant *wpa_s, + char *cmd) +{ + char *pos, *param; + size_t len; + u8 *buf; + int freq = 0, datarate = 0, ssi_signal = 0; + union wpa_event_data event; + + if (!wpa_s->ext_mgmt_frame_handling) + return -1; + + /* freq=<MHz> datarate=<val> ssi_signal=<val> frame=<frame hexdump> */ + + wpa_printf(MSG_DEBUG, "External MGMT RX process: %s", cmd); + + pos = cmd; + param = os_strstr(pos, "freq="); + if (param) { + param += 5; + freq = atoi(param); + } + + param = os_strstr(pos, " datarate="); + if (param) { + param += 10; + datarate = atoi(param); + } + + param = os_strstr(pos, " ssi_signal="); + if (param) { + param += 12; + ssi_signal = atoi(param); + } + + param = os_strstr(pos, " frame="); + if (param == NULL) + return -1; + param += 7; + + len = os_strlen(param); + if (len & 1) + return -1; + len /= 2; + + buf = os_malloc(len); + if (buf == NULL) + return -1; + + if (hexstr2bin(param, buf, len) < 0) { + os_free(buf); + return -1; + } + + os_memset(&event, 0, sizeof(event)); + event.rx_mgmt.freq = freq; + event.rx_mgmt.frame = buf; + event.rx_mgmt.frame_len = len; + event.rx_mgmt.ssi_signal = ssi_signal; + event.rx_mgmt.datarate = datarate; + wpa_s->ext_mgmt_frame_handling = 0; + wpa_supplicant_event(wpa_s, EVENT_RX_MGMT, &event); + wpa_s->ext_mgmt_frame_handling = 1; + + os_free(buf); + + return 0; +} + + static int wpas_ctrl_iface_driver_event(struct wpa_supplicant *wpa_s, char *cmd) { char *pos, *param; @@ -7495,7 +7923,8 @@ static u16 ipv4_hdr_checksum(const void *buf, size_t len) #define HWSIM_PACKETLEN 1500 #define HWSIM_IP_LEN (HWSIM_PACKETLEN - sizeof(struct ether_header)) -void wpas_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) +static void wpas_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len) { struct wpa_supplicant *wpa_s = ctx; const struct ether_header *eth; @@ -7529,6 +7958,8 @@ static int wpas_ctrl_iface_data_test_config(struct wpa_supplicant *wpa_s, char *cmd) { int enabled = atoi(cmd); + char *pos; + const char *ifname; if (!enabled) { if (wpa_s->l2_test) { @@ -7542,7 +7973,13 @@ static int wpas_ctrl_iface_data_test_config(struct wpa_supplicant *wpa_s, if (wpa_s->l2_test) return 0; - wpa_s->l2_test = l2_packet_init(wpa_s->ifname, wpa_s->own_addr, + pos = os_strstr(cmd, " ifname="); + if (pos) + ifname = pos + 8; + else + ifname = wpa_s->ifname; + + wpa_s->l2_test = l2_packet_init(ifname, wpa_s->own_addr, ETHERTYPE_IP, wpas_data_test_rx, wpa_s, 1); if (wpa_s->l2_test == NULL) @@ -7663,8 +8100,6 @@ done: static int wpas_ctrl_test_alloc_fail(struct wpa_supplicant *wpa_s, char *cmd) { #ifdef WPA_TRACE_BFD - extern char wpa_trace_fail_func[256]; - extern unsigned int wpa_trace_fail_after; char *pos; wpa_trace_fail_after = atoi(cmd); @@ -7687,9 +8122,6 @@ static int wpas_ctrl_get_alloc_fail(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) { #ifdef WPA_TRACE_BFD - extern char wpa_trace_fail_func[256]; - extern unsigned int wpa_trace_fail_after; - return os_snprintf(buf, buflen, "%u:%s", wpa_trace_fail_after, wpa_trace_fail_func); #else /* WPA_TRACE_BFD */ @@ -7701,8 +8133,6 @@ static int wpas_ctrl_get_alloc_fail(struct wpa_supplicant *wpa_s, static int wpas_ctrl_test_fail(struct wpa_supplicant *wpa_s, char *cmd) { #ifdef WPA_TRACE_BFD - extern char wpa_trace_test_fail_func[256]; - extern unsigned int wpa_trace_test_fail_after; char *pos; wpa_trace_test_fail_after = atoi(cmd); @@ -7725,9 +8155,6 @@ static int wpas_ctrl_get_fail(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) { #ifdef WPA_TRACE_BFD - extern char wpa_trace_test_fail_func[256]; - extern unsigned int wpa_trace_test_fail_after; - return os_snprintf(buf, buflen, "%u:%s", wpa_trace_test_fail_after, wpa_trace_test_fail_func); #else /* WPA_TRACE_BFD */ @@ -7735,62 +8162,64 @@ static int wpas_ctrl_get_fail(struct wpa_supplicant *wpa_s, #endif /* WPA_TRACE_BFD */ } -#endif /* CONFIG_TESTING_OPTIONS */ - -static void wpas_ctrl_vendor_elem_update(struct wpa_supplicant *wpa_s) +static void wpas_ctrl_event_test_cb(void *eloop_ctx, void *timeout_ctx) { - unsigned int i; - char buf[30]; + struct wpa_supplicant *wpa_s = eloop_ctx; + int i, count = (intptr_t) timeout_ctx; - wpa_printf(MSG_DEBUG, "Update vendor elements"); + wpa_printf(MSG_DEBUG, "TEST: Send %d control interface event messages", + count); + for (i = 0; i < count; i++) { + wpa_msg_ctrl(wpa_s, MSG_INFO, "TEST-EVENT-MESSAGE %d/%d", + i + 1, count); + } +} - for (i = 0; i < NUM_VENDOR_ELEM_FRAMES; i++) { - if (wpa_s->vendor_elem[i]) { - int res; - res = os_snprintf(buf, sizeof(buf), "frame[%u]", i); - if (!os_snprintf_error(sizeof(buf), res)) { - wpa_hexdump_buf(MSG_DEBUG, buf, - wpa_s->vendor_elem[i]); - } - } - } +static int wpas_ctrl_event_test(struct wpa_supplicant *wpa_s, const char *cmd) +{ + int count; -#ifdef CONFIG_P2P - if (wpa_s->parent == wpa_s && - wpa_s->global->p2p && - !wpa_s->global->p2p_disabled) - p2p_set_vendor_elems(wpa_s->global->p2p, wpa_s->vendor_elem); -#endif /* CONFIG_P2P */ + count = atoi(cmd); + if (count <= 0) + return -1; + + return eloop_register_timeout(0, 0, wpas_ctrl_event_test_cb, wpa_s, + (void *) (intptr_t) count); } -static struct wpa_supplicant * -wpas_ctrl_vendor_elem_iface(struct wpa_supplicant *wpa_s, - enum wpa_vendor_elem_frame frame) +static int wpas_ctrl_test_assoc_ie(struct wpa_supplicant *wpa_s, + const char *cmd) { - switch (frame) { -#ifdef CONFIG_P2P - case VENDOR_ELEM_PROBE_REQ_P2P: - case VENDOR_ELEM_PROBE_RESP_P2P: - case VENDOR_ELEM_PROBE_RESP_P2P_GO: - case VENDOR_ELEM_BEACON_P2P_GO: - case VENDOR_ELEM_P2P_PD_REQ: - case VENDOR_ELEM_P2P_PD_RESP: - case VENDOR_ELEM_P2P_GO_NEG_REQ: - case VENDOR_ELEM_P2P_GO_NEG_RESP: - case VENDOR_ELEM_P2P_GO_NEG_CONF: - case VENDOR_ELEM_P2P_INV_REQ: - case VENDOR_ELEM_P2P_INV_RESP: - case VENDOR_ELEM_P2P_ASSOC_REQ: - return wpa_s->parent; -#endif /* CONFIG_P2P */ - default: - return wpa_s; + struct wpabuf *buf; + size_t len; + + len = os_strlen(cmd); + if (len & 1) + return -1; + len /= 2; + + if (len == 0) { + buf = NULL; + } else { + buf = wpabuf_alloc(len); + if (buf == NULL) + return -1; + + if (hexstr2bin(cmd, wpabuf_put(buf, len), len) < 0) { + wpabuf_free(buf); + return -1; + } } + + wpa_sm_set_test_assoc_ie(wpa_s->wpa, buf); + return 0; } +#endif /* CONFIG_TESTING_OPTIONS */ + static int wpas_ctrl_vendor_elem_add(struct wpa_supplicant *wpa_s, char *cmd) { @@ -7803,7 +8232,7 @@ static int wpas_ctrl_vendor_elem_add(struct wpa_supplicant *wpa_s, char *cmd) frame = atoi(pos); if (frame < 0 || frame >= NUM_VENDOR_ELEM_FRAMES) return -1; - wpa_s = wpas_ctrl_vendor_elem_iface(wpa_s, frame); + wpa_s = wpas_vendor_elem(wpa_s, frame); pos = os_strchr(pos, ' '); if (pos == NULL) @@ -7834,7 +8263,7 @@ static int wpas_ctrl_vendor_elem_add(struct wpa_supplicant *wpa_s, char *cmd) if (wpa_s->vendor_elem[frame] == NULL) { wpa_s->vendor_elem[frame] = buf; - wpas_ctrl_vendor_elem_update(wpa_s); + wpas_vendor_elem_update(wpa_s); return 0; } @@ -7845,7 +8274,7 @@ static int wpas_ctrl_vendor_elem_add(struct wpa_supplicant *wpa_s, char *cmd) wpabuf_put_buf(wpa_s->vendor_elem[frame], buf); wpabuf_free(buf); - wpas_ctrl_vendor_elem_update(wpa_s); + wpas_vendor_elem_update(wpa_s); return 0; } @@ -7858,7 +8287,7 @@ static int wpas_ctrl_vendor_elem_get(struct wpa_supplicant *wpa_s, char *cmd, if (frame < 0 || frame >= NUM_VENDOR_ELEM_FRAMES) return -1; - wpa_s = wpas_ctrl_vendor_elem_iface(wpa_s, frame); + wpa_s = wpas_vendor_elem(wpa_s, frame); if (wpa_s->vendor_elem[frame] == NULL) return 0; @@ -7876,12 +8305,12 @@ static int wpas_ctrl_vendor_elem_remove(struct wpa_supplicant *wpa_s, char *cmd) size_t len; u8 *buf; struct ieee802_11_elems elems; - u8 *ie, *end; + int res; frame = atoi(pos); if (frame < 0 || frame >= NUM_VENDOR_ELEM_FRAMES) return -1; - wpa_s = wpas_ctrl_vendor_elem_iface(wpa_s, frame); + wpa_s = wpas_vendor_elem(wpa_s, frame); pos = os_strchr(pos, ' '); if (pos == NULL) @@ -7891,7 +8320,7 @@ static int wpas_ctrl_vendor_elem_remove(struct wpa_supplicant *wpa_s, char *cmd) if (*pos == '*') { wpabuf_free(wpa_s->vendor_elem[frame]); wpa_s->vendor_elem[frame] = NULL; - wpas_ctrl_vendor_elem_update(wpa_s); + wpas_vendor_elem_update(wpa_s); return 0; } @@ -7919,65 +8348,149 @@ static int wpas_ctrl_vendor_elem_remove(struct wpa_supplicant *wpa_s, char *cmd) return -1; } - ie = wpabuf_mhead_u8(wpa_s->vendor_elem[frame]); - end = ie + wpabuf_len(wpa_s->vendor_elem[frame]); - - for (; ie + 1 < end; ie += 2 + ie[1]) { - if (ie + len > end) - break; - if (os_memcmp(ie, buf, len) != 0) - continue; - - if (wpabuf_len(wpa_s->vendor_elem[frame]) == len) { - wpabuf_free(wpa_s->vendor_elem[frame]); - wpa_s->vendor_elem[frame] = NULL; - } else { - os_memmove(ie, ie + len, - end - (ie + len)); - wpa_s->vendor_elem[frame]->used -= len; - } - os_free(buf); - wpas_ctrl_vendor_elem_update(wpa_s); - return 0; - } - + res = wpas_vendor_elem_remove(wpa_s, frame, buf, len); os_free(buf); - - return -1; + return res; } static void wpas_ctrl_neighbor_rep_cb(void *ctx, struct wpabuf *neighbor_rep) { struct wpa_supplicant *wpa_s = ctx; + size_t len; + const u8 *data; - if (neighbor_rep) { - wpa_msg_ctrl(wpa_s, MSG_INFO, RRM_EVENT_NEIGHBOR_REP_RXED - "length=%u", - (unsigned int) wpabuf_len(neighbor_rep)); - wpabuf_free(neighbor_rep); - } else { + /* + * Neighbor Report element (IEEE P802.11-REVmc/D5.0) + * BSSID[6] + * BSSID Information[4] + * Operating Class[1] + * Channel Number[1] + * PHY Type[1] + * Optional Subelements[variable] + */ +#define NR_IE_MIN_LEN (ETH_ALEN + 4 + 1 + 1 + 1) + + if (!neighbor_rep || wpabuf_len(neighbor_rep) == 0) { wpa_msg_ctrl(wpa_s, MSG_INFO, RRM_EVENT_NEIGHBOR_REP_FAILED); + goto out; + } + + data = wpabuf_head_u8(neighbor_rep); + len = wpabuf_len(neighbor_rep); + + while (len >= 2 + NR_IE_MIN_LEN) { + const u8 *nr; + char lci[256 * 2 + 1]; + char civic[256 * 2 + 1]; + u8 nr_len = data[1]; + const u8 *pos = data, *end; + + if (pos[0] != WLAN_EID_NEIGHBOR_REPORT || + nr_len < NR_IE_MIN_LEN) { + wpa_printf(MSG_DEBUG, + "CTRL: Invalid Neighbor Report element: id=%u len=%u", + data[0], nr_len); + goto out; + } + + if (2U + nr_len > len) { + wpa_printf(MSG_DEBUG, + "CTRL: Invalid Neighbor Report element: id=%u len=%zu nr_len=%u", + data[0], len, nr_len); + goto out; + } + pos += 2; + end = pos + nr_len; + + nr = pos; + pos += NR_IE_MIN_LEN; + + lci[0] = '\0'; + civic[0] = '\0'; + while (end - pos > 2) { + u8 s_id, s_len; + + s_id = *pos++; + s_len = *pos++; + if (s_len > end - pos) + goto out; + if (s_id == WLAN_EID_MEASURE_REPORT && s_len > 3) { + /* Measurement Token[1] */ + /* Measurement Report Mode[1] */ + /* Measurement Type[1] */ + /* Measurement Report[variable] */ + switch (pos[2]) { + case MEASURE_TYPE_LCI: + if (lci[0]) + break; + wpa_snprintf_hex(lci, sizeof(lci), + pos, s_len); + break; + case MEASURE_TYPE_LOCATION_CIVIC: + if (civic[0]) + break; + wpa_snprintf_hex(civic, sizeof(civic), + pos, s_len); + break; + } + } + + pos += s_len; + } + + wpa_msg(wpa_s, MSG_INFO, RRM_EVENT_NEIGHBOR_REP_RXED + "bssid=" MACSTR + " info=0x%x op_class=%u chan=%u phy_type=%u%s%s%s%s", + MAC2STR(nr), WPA_GET_LE32(nr + ETH_ALEN), + nr[ETH_ALEN + 4], nr[ETH_ALEN + 5], + nr[ETH_ALEN + 6], + lci[0] ? " lci=" : "", lci, + civic[0] ? " civic=" : "", civic); + + data = end; + len -= 2 + nr_len; } + +out: + wpabuf_free(neighbor_rep); } -static int wpas_ctrl_iface_send_neigbor_rep(struct wpa_supplicant *wpa_s, - char *cmd) +static int wpas_ctrl_iface_send_neighbor_rep(struct wpa_supplicant *wpa_s, + char *cmd) { - struct wpa_ssid ssid; - struct wpa_ssid *ssid_p = NULL; - int ret = 0; + struct wpa_ssid_value ssid, *ssid_p = NULL; + int ret, lci = 0, civic = 0; + char *ssid_s; - if (os_strncmp(cmd, " ssid=", 6) == 0) { - ssid.ssid_len = os_strlen(cmd + 6); - if (ssid.ssid_len > SSID_MAX_LEN) + ssid_s = os_strstr(cmd, "ssid="); + if (ssid_s) { + if (ssid_parse(ssid_s + 5, &ssid)) { + wpa_printf(MSG_ERROR, + "CTRL: Send Neighbor Report: bad SSID"); return -1; - ssid.ssid = (u8 *) (cmd + 6); + } + ssid_p = &ssid; + + /* + * Move cmd after the SSID text that may include "lci" or + * "civic". + */ + cmd = os_strchr(ssid_s + 6, ssid_s[5] == '"' ? '"' : ' '); + if (cmd) + cmd++; + } - ret = wpas_rrm_send_neighbor_rep_request(wpa_s, ssid_p, + if (cmd && os_strstr(cmd, "lci")) + lci = 1; + + if (cmd && os_strstr(cmd, "civic")) + civic = 1; + + ret = wpas_rrm_send_neighbor_rep_request(wpa_s, ssid_p, lci, civic, wpas_ctrl_neighbor_rep_cb, wpa_s); @@ -8062,10 +8575,7 @@ static int wpas_ctrl_iface_mac_rand_scan(struct wpa_supplicant *wpa_s, } } else if (wpa_s->sched_scanning && (type & MAC_ADDR_RAND_SCHED_SCAN)) { - /* simulate timeout to restart the sched scan */ - wpa_s->sched_scan_timed_out = 1; - wpa_s->prev_sched_ssid = NULL; - wpa_supplicant_cancel_sched_scan(wpa_s); + wpas_scan_restart_sched_scan(wpa_s); } return 0; } @@ -8091,12 +8601,8 @@ static int wpas_ctrl_iface_mac_rand_scan(struct wpa_supplicant *wpa_s, wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_SCHED_SCAN, addr, mask); - if (wpa_s->sched_scanning && !wpa_s->pno) { - /* simulate timeout to restart the sched scan */ - wpa_s->sched_scan_timed_out = 1; - wpa_s->prev_sched_ssid = NULL; - wpa_supplicant_cancel_sched_scan(wpa_s); - } + if (wpa_s->sched_scanning && !wpa_s->pno) + wpas_scan_restart_sched_scan(wpa_s); } if (type & MAC_ADDR_RAND_PNO) { @@ -8112,6 +8618,29 @@ static int wpas_ctrl_iface_mac_rand_scan(struct wpa_supplicant *wpa_s, } +static int wpas_ctrl_iface_pmksa(struct wpa_supplicant *wpa_s, + char *buf, size_t buflen) +{ + size_t reply_len; + + reply_len = wpa_sm_pmksa_cache_list(wpa_s->wpa, buf, buflen); +#ifdef CONFIG_AP + reply_len += wpas_ap_pmksa_cache_list(wpa_s, &buf[reply_len], + buflen - reply_len); +#endif /* CONFIG_AP */ + return reply_len; +} + + +static void wpas_ctrl_iface_pmksa_flush(struct wpa_supplicant *wpa_s) +{ + wpa_sm_pmksa_cache_flush(wpa_s->wpa, NULL); +#ifdef CONFIG_AP + wpas_ap_pmksa_cache_flush(wpa_s); +#endif /* CONFIG_AP */ +} + + static int wpas_ctrl_cmd_debug_level(const char *cmd) { if (os_strcmp(cmd, "PING") == 0 || @@ -8183,10 +8712,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, reply_len = wpa_supplicant_ctrl_iface_status( wpa_s, buf + 6, reply, reply_size); } else if (os_strcmp(buf, "PMKSA") == 0) { - reply_len = wpa_sm_pmksa_cache_list(wpa_s->wpa, reply, - reply_size); + reply_len = wpas_ctrl_iface_pmksa(wpa_s, reply, reply_size); } else if (os_strcmp(buf, "PMKSA_FLUSH") == 0) { - wpa_sm_pmksa_cache_flush(wpa_s->wpa, NULL); + wpas_ctrl_iface_pmksa_flush(wpa_s); } else if (os_strncmp(buf, "SET ", 4) == 0) { if (wpa_supplicant_ctrl_iface_set(wpa_s, buf + 4)) reply_len = -1; @@ -8354,6 +8882,12 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, if (wpa_supplicant_ctrl_iface_mesh_group_remove(wpa_s, buf + 18)) reply_len = -1; + } else if (os_strncmp(buf, "MESH_PEER_REMOVE ", 17) == 0) { + if (wpa_supplicant_ctrl_iface_mesh_peer_remove(wpa_s, buf + 17)) + reply_len = -1; + } else if (os_strncmp(buf, "MESH_PEER_ADD ", 14) == 0) { + if (wpa_supplicant_ctrl_iface_mesh_peer_add(wpa_s, buf + 14)) + reply_len = -1; #endif /* CONFIG_MESH */ #ifdef CONFIG_P2P } else if (os_strncmp(buf, "P2P_FIND ", 9) == 0) { @@ -8388,6 +8922,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } 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_GROUP_MEMBER ", 17) == 0) { + reply_len = p2p_ctrl_group_member(wpa_s, buf + 17, reply, + reply_size); } else if (os_strncmp(buf, "P2P_PROV_DISC ", 14) == 0) { if (p2p_ctrl_prov_disc(wpa_s, buf + 14)) reply_len = -1; @@ -8453,6 +8990,12 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strncmp(buf, "P2P_REMOVE_CLIENT ", 18) == 0) { if (p2p_ctrl_remove_client(wpa_s, buf + 18) < 0) reply_len = -1; + } else if (os_strncmp(buf, "P2P_LO_START ", 13) == 0) { + if (p2p_ctrl_iface_p2p_lo_start(wpa_s, buf + 13)) + reply_len = -1; + } else if (os_strcmp(buf, "P2P_LO_STOP") == 0) { + if (wpas_p2p_lo_stop(wpa_s)) + reply_len = -1; #endif /* CONFIG_P2P */ #ifdef CONFIG_WIFI_DISPLAY } else if (os_strncmp(buf, "WFD_SUBELEM_SET ", 16) == 0) { @@ -8506,10 +9049,21 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, if (hs20_get_nai_home_realm_list(wpa_s, buf + 29) < 0) reply_len = -1; } else if (os_strncmp(buf, "HS20_ICON_REQUEST ", 18) == 0) { - if (hs20_icon_request(wpa_s, buf + 18) < 0) + if (hs20_icon_request(wpa_s, buf + 18, 0) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "REQ_HS20_ICON ", 14) == 0) { + if (hs20_icon_request(wpa_s, buf + 14, 1) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "GET_HS20_ICON ", 14) == 0) { + reply_len = get_hs20_icon(wpa_s, buf + 14, reply, reply_size); + } else if (os_strncmp(buf, "DEL_HS20_ICON ", 14) == 0) { + if (del_hs20_icon(wpa_s, buf + 14) < 0) reply_len = -1; } else if (os_strcmp(buf, "FETCH_OSU") == 0) { - if (hs20_fetch_osu(wpa_s) < 0) + if (hs20_fetch_osu(wpa_s, 0) < 0) + reply_len = -1; + } else if (os_strcmp(buf, "FETCH_OSU no-scan") == 0) { + if (hs20_fetch_osu(wpa_s, 1) < 0) reply_len = -1; } else if (os_strcmp(buf, "CANCEL_FETCH_OSU") == 0) { hs20_cancel_fetch_osu(wpa_s); @@ -8548,16 +9102,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, reply_len = wpa_supplicant_ctrl_iface_list_networks( wpa_s, NULL, reply, reply_size); } else if (os_strcmp(buf, "DISCONNECT") == 0) { -#ifdef CONFIG_SME - wpa_s->sme.prev_bssid_set = 0; -#endif /* CONFIG_SME */ - wpa_s->reassociate = 0; - wpa_s->disconnected = 1; - wpa_supplicant_cancel_sched_scan(wpa_s); - wpa_supplicant_cancel_scan(wpa_s); - wpa_supplicant_deauthenticate(wpa_s, - WLAN_REASON_DEAUTH_LEAVING); - eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL); + wpas_request_disconnection(wpa_s); } else if (os_strcmp(buf, "SCAN") == 0) { wpas_ctrl_scan(wpa_s, NULL, reply, reply_size, &reply_len); } else if (os_strncmp(buf, "SCAN ", 5) == 0) { @@ -8565,6 +9110,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strcmp(buf, "SCAN_RESULTS") == 0) { reply_len = wpa_supplicant_ctrl_iface_scan_results( wpa_s, reply, reply_size); + } else if (os_strcmp(buf, "ABORT_SCAN") == 0) { + if (wpas_abort_ongoing_scan(wpa_s) < 0) + reply_len = -1; } else if (os_strncmp(buf, "SELECT_NETWORK ", 15) == 0) { if (wpa_supplicant_ctrl_iface_select_network(wpa_s, buf + 15)) reply_len = -1; @@ -8623,9 +9171,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strcmp(buf, "INTERFACE_LIST") == 0) { reply_len = wpa_supplicant_global_iface_list( wpa_s->global, reply, reply_size); - } else if (os_strcmp(buf, "INTERFACES") == 0) { + } else if (os_strncmp(buf, "INTERFACES", 10) == 0) { reply_len = wpa_supplicant_global_iface_interfaces( - wpa_s->global, reply, reply_size); + wpa_s->global, buf + 10, reply, reply_size); } else if (os_strncmp(buf, "BSS ", 4) == 0) { reply_len = wpa_supplicant_ctrl_iface_bss( wpa_s, buf + 4, reply, reply_size); @@ -8706,6 +9254,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } 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, "SIGNAL_MONITOR", 14) == 0) { + if (wpas_ctrl_iface_signal_monitor(wpa_s, buf + 14)) + reply_len = -1; } else if (os_strncmp(buf, "PKTCNT_POLL", 11) == 0) { reply_len = wpa_supplicant_pktcnt_poll(wpa_s, reply, reply_size); @@ -8714,6 +9265,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, if (wpa_supplicant_ctrl_iface_autoscan(wpa_s, buf + 9)) reply_len = -1; #endif /* CONFIG_AUTOSCAN */ + } else if (os_strcmp(buf, "DRIVER_FLAGS") == 0) { + reply_len = wpas_ctrl_iface_driver_flags(wpa_s, reply, + reply_size); #ifdef ANDROID } else if (os_strncmp(buf, "DRIVER ", 7) == 0) { reply_len = wpa_supplicant_driver_cmd(wpa_s, buf + 7, reply, @@ -8744,6 +9298,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, reply_len = -1; } else if (os_strcmp(buf, "MGMT_TX_DONE") == 0) { wpas_ctrl_iface_mgmt_tx_done(wpa_s); + } else if (os_strncmp(buf, "MGMT_RX_PROCESS ", 16) == 0) { + if (wpas_ctrl_iface_mgmt_rx_process(wpa_s, buf + 16) < 0) + reply_len = -1; } else if (os_strncmp(buf, "DRIVER_EVENT ", 13) == 0) { if (wpas_ctrl_iface_driver_event(wpa_s, buf + 13) < 0) reply_len = -1; @@ -8769,6 +9326,12 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, reply_len = -1; } else if (os_strcmp(buf, "GET_FAIL") == 0) { reply_len = wpas_ctrl_get_fail(wpa_s, reply, reply_size); + } else if (os_strncmp(buf, "EVENT_TEST ", 11) == 0) { + if (wpas_ctrl_event_test(wpa_s, buf + 11) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "TEST_ASSOC_IE ", 14) == 0) { + if (wpas_ctrl_test_assoc_ie(wpa_s, buf + 14) < 0) + reply_len = -1; #endif /* CONFIG_TESTING_OPTIONS */ } else if (os_strncmp(buf, "VENDOR_ELEM_ADD ", 16) == 0) { if (wpas_ctrl_vendor_elem_add(wpa_s, buf + 16) < 0) @@ -8780,7 +9343,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, if (wpas_ctrl_vendor_elem_remove(wpa_s, buf + 19) < 0) reply_len = -1; } else if (os_strncmp(buf, "NEIGHBOR_REP_REQUEST", 20) == 0) { - if (wpas_ctrl_iface_send_neigbor_rep(wpa_s, buf + 20)) + if (wpas_ctrl_iface_send_neighbor_rep(wpa_s, buf + 20)) reply_len = -1; } else if (os_strcmp(buf, "ERP_FLUSH") == 0) { wpas_ctrl_iface_erp_flush(wpa_s); @@ -8813,10 +9376,11 @@ static int wpa_supplicant_global_iface_add(struct wpa_global *global, struct wpa_supplicant *wpa_s; unsigned int create_iface = 0; u8 mac_addr[ETH_ALEN]; + enum wpa_driver_if_type type = WPA_IF_STATION; /* * <ifname>TAB<confname>TAB<driver>TAB<ctrl_interface>TAB<driver_param> - * TAB<bridge_ifname>[TAB<create>] + * TAB<bridge_ifname>[TAB<create>[TAB<interface_type>]] */ wpa_printf(MSG_DEBUG, "CTRL_IFACE GLOBAL INTERFACE_ADD '%s'", cmd); @@ -8884,9 +9448,22 @@ static int wpa_supplicant_global_iface_add(struct wpa_global *global, if (!extra[0]) break; - if (os_strcmp(extra, "create") == 0) + if (os_strcmp(extra, "create") == 0) { create_iface = 1; - else { + if (!pos) + break; + + if (os_strcmp(pos, "sta") == 0) { + type = WPA_IF_STATION; + } else if (os_strcmp(pos, "ap") == 0) { + type = WPA_IF_AP_BSS; + } else { + wpa_printf(MSG_DEBUG, + "INTERFACE_ADD unsupported interface type: '%s'", + pos); + return -1; + } + } else { wpa_printf(MSG_DEBUG, "INTERFACE_ADD unsupported extra parameter: '%s'", extra); @@ -8899,7 +9476,7 @@ static int wpa_supplicant_global_iface_add(struct wpa_global *global, iface.ifname); if (!global->ifaces) return -1; - if (wpa_drv_if_add(global->ifaces, WPA_IF_STATION, iface.ifname, + if (wpa_drv_if_add(global->ifaces, type, iface.ifname, NULL, NULL, NULL, mac_addr, NULL) < 0) { wpa_printf(MSG_ERROR, "CTRL_IFACE interface creation failed"); @@ -9008,18 +9585,31 @@ static int wpa_supplicant_global_iface_list(struct wpa_global *global, static int wpa_supplicant_global_iface_interfaces(struct wpa_global *global, + const char *input, char *buf, int len) { int res; char *pos, *end; struct wpa_supplicant *wpa_s; + int show_ctrl = 0; + + if (input) + show_ctrl = !!os_strstr(input, "ctrl"); wpa_s = global->ifaces; pos = buf; end = buf + len; while (wpa_s) { - res = os_snprintf(pos, end - pos, "%s\n", wpa_s->ifname); + if (show_ctrl) + res = os_snprintf(pos, end - pos, "%s ctrl_iface=%s\n", + wpa_s->ifname, + wpa_s->conf->ctrl_interface ? + wpa_s->conf->ctrl_interface : "N/A"); + else + res = os_snprintf(pos, end - pos, "%s\n", + wpa_s->ifname); + if (os_snprintf_error(end - pos, res)) { *pos = '\0'; break; @@ -9085,6 +9675,7 @@ static char * wpas_global_ctrl_iface_redir_p2p(struct wpa_global *global, "P2P_LISTEN ", "P2P_GROUP_REMOVE ", "P2P_GROUP_ADD ", + "P2P_GROUP_MEMBER ", "P2P_PROV_DISC ", "P2P_SERV_DISC_REQ ", "P2P_SERV_DISC_CANCEL_REQ ", @@ -9408,9 +9999,9 @@ char * wpa_supplicant_global_ctrl_iface_process(struct wpa_global *global, } else if (os_strcmp(buf, "INTERFACE_LIST") == 0) { reply_len = wpa_supplicant_global_iface_list( global, reply, reply_size); - } else if (os_strcmp(buf, "INTERFACES") == 0) { + } else if (os_strncmp(buf, "INTERFACES", 10) == 0) { reply_len = wpa_supplicant_global_iface_interfaces( - global, reply, reply_size); + global, buf + 10, reply, reply_size); #ifdef CONFIG_FST } else if (os_strncmp(buf, "FST-ATTACH ", 11) == 0) { reply_len = wpas_global_ctrl_iface_fst_attach(global, buf + 11, @@ -9456,7 +10047,6 @@ char * wpa_supplicant_global_ctrl_iface_process(struct wpa_global *global, reply_size); #ifdef CONFIG_MODULE_TESTS } else if (os_strcmp(buf, "MODULE_TESTS") == 0) { - int wpas_module_tests(void); if (wpas_module_tests() < 0) reply_len = -1; #endif /* CONFIG_MODULE_TESTS */ diff --git a/wpa_supplicant/ctrl_iface_udp.c b/wpa_supplicant/ctrl_iface_udp.c index 76f69f2b57bb2..0dc0937ff0aa6 100644 --- a/wpa_supplicant/ctrl_iface_udp.c +++ b/wpa_supplicant/ctrl_iface_udp.c @@ -1,6 +1,6 @@ /* * WPA Supplicant / UDP socket -based control interface - * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -48,13 +48,33 @@ struct ctrl_iface_priv { u8 cookie[COOKIE_LEN]; }; +struct ctrl_iface_global_priv { + int sock; + struct wpa_ctrl_dst *ctrl_dst; + u8 cookie[COOKIE_LEN]; +}; -static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, + +static void wpa_supplicant_ctrl_iface_send(struct wpa_supplicant *wpa_s, + const char *ifname, int sock, + struct wpa_ctrl_dst **head, int level, const char *buf, size_t len); -static int wpa_supplicant_ctrl_iface_attach(struct ctrl_iface_priv *priv, +static void wpas_ctrl_iface_free_dst(struct wpa_ctrl_dst *dst) +{ + struct wpa_ctrl_dst *prev; + + while (dst) { + prev = dst; + dst = dst->next; + os_free(prev); + } +} + + +static int wpa_supplicant_ctrl_iface_attach(struct wpa_ctrl_dst **head, #ifdef CONFIG_CTRL_IFACE_UDP_IPV6 struct sockaddr_in6 *from, #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ @@ -73,8 +93,8 @@ static int wpa_supplicant_ctrl_iface_attach(struct ctrl_iface_priv *priv, os_memcpy(&dst->addr, from, sizeof(*from)); dst->addrlen = fromlen; dst->debug_level = MSG_INFO; - dst->next = priv->ctrl_dst; - priv->ctrl_dst = dst; + dst->next = *head; + *head = dst; #ifdef CONFIG_CTRL_IFACE_UDP_IPV6 wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor attached %s:%d", inet_ntop(AF_INET6, &from->sin6_addr, addr, sizeof(*from)), @@ -87,7 +107,7 @@ static int wpa_supplicant_ctrl_iface_attach(struct ctrl_iface_priv *priv, } -static int wpa_supplicant_ctrl_iface_detach(struct ctrl_iface_priv *priv, +static int wpa_supplicant_ctrl_iface_detach(struct wpa_ctrl_dst **head, #ifdef CONFIG_CTRL_IFACE_UDP_IPV6 struct sockaddr_in6 *from, #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ @@ -100,7 +120,7 @@ static int wpa_supplicant_ctrl_iface_detach(struct ctrl_iface_priv *priv, char addr[INET6_ADDRSTRLEN]; #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ - dst = priv->ctrl_dst; + dst = *head; while (dst) { #ifdef CONFIG_CTRL_IFACE_UDP_IPV6 if (from->sin6_port == dst->addr.sin6_port && @@ -118,7 +138,7 @@ static int wpa_supplicant_ctrl_iface_detach(struct ctrl_iface_priv *priv, ntohs(from->sin_port)); #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ if (prev == NULL) - priv->ctrl_dst = dst->next; + *head = dst->next; else prev->next = dst->next; os_free(dst); @@ -282,14 +302,16 @@ static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx, pos++; if (os_strcmp(pos, "ATTACH") == 0) { - if (wpa_supplicant_ctrl_iface_attach(priv, &from, fromlen)) + if (wpa_supplicant_ctrl_iface_attach(&priv->ctrl_dst, + &from, fromlen)) reply_len = 1; else { new_attached = 1; reply_len = 2; } } else if (os_strcmp(pos, "DETACH") == 0) { - if (wpa_supplicant_ctrl_iface_detach(priv, &from, fromlen)) + if (wpa_supplicant_ctrl_iface_detach(&priv->ctrl_dst, + &from, fromlen)) reply_len = 1; else reply_len = 2; @@ -327,9 +349,28 @@ static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level, const char *txt, size_t len) { struct wpa_supplicant *wpa_s = ctx; - if (wpa_s == NULL || wpa_s->ctrl_iface == NULL) + + if (!wpa_s) return; - wpa_supplicant_ctrl_iface_send(wpa_s->ctrl_iface, level, txt, len); + + if (type != WPA_MSG_NO_GLOBAL && wpa_s->global->ctrl_iface) { + struct ctrl_iface_global_priv *priv = wpa_s->global->ctrl_iface; + + if (priv->ctrl_dst) { + wpa_supplicant_ctrl_iface_send( + wpa_s, + type != WPA_MSG_PER_INTERFACE ? + NULL : wpa_s->ifname, + priv->sock, &priv->ctrl_dst, level, txt, len); + } + } + + if (type == WPA_MSG_ONLY_GLOBAL || !wpa_s->ctrl_iface) + return; + + wpa_supplicant_ctrl_iface_send(wpa_s, NULL, wpa_s->ctrl_iface->sock, + &wpa_s->ctrl_iface->ctrl_dst, + level, txt, len); } @@ -337,7 +378,9 @@ struct ctrl_iface_priv * wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) { struct ctrl_iface_priv *priv; + char port_str[40]; int port = WPA_CTRL_IFACE_PORT; + char *pos; #ifdef CONFIG_CTRL_IFACE_UDP_IPV6 struct sockaddr_in6 addr; int domain = PF_INET6; @@ -356,6 +399,17 @@ wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) if (wpa_s->conf->ctrl_interface == NULL) return priv; + pos = os_strstr(wpa_s->conf->ctrl_interface, "udp:"); + if (pos) { + pos += 4; + port = atoi(pos); + if (port <= 0) { + wpa_printf(MSG_ERROR, "Invalid ctrl_iface UDP port: %s", + wpa_s->conf->ctrl_interface); + goto fail; + } + } + priv->sock = socket(domain, SOCK_DGRAM, 0); if (priv->sock < 0) { wpa_printf(MSG_ERROR, "socket(PF_INET): %s", strerror(errno)); @@ -392,6 +446,15 @@ try_again: goto fail; } + /* Update the ctrl_interface value to match the selected port */ + os_snprintf(port_str, sizeof(port_str), "udp:%d", port); + os_free(wpa_s->conf->ctrl_interface); + wpa_s->conf->ctrl_interface = os_strdup(port_str); + if (!wpa_s->conf->ctrl_interface) { + wpa_msg(wpa_s, MSG_ERROR, "Failed to malloc ctrl_interface"); + 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 */ @@ -412,8 +475,6 @@ fail: void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv) { - struct wpa_ctrl_dst *dst, *prev; - if (priv->sock > -1) { eloop_unregister_read_sock(priv->sock); if (priv->ctrl_dst) { @@ -430,22 +491,19 @@ void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv) priv->sock = -1; } - dst = priv->ctrl_dst; - while (dst) { - prev = dst; - dst = dst->next; - os_free(prev); - } + wpas_ctrl_iface_free_dst(priv->ctrl_dst); os_free(priv); } -static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, +static void wpa_supplicant_ctrl_iface_send(struct wpa_supplicant *wpa_s, + const char *ifname, int sock, + struct wpa_ctrl_dst **head, int level, const char *buf, size_t len) { struct wpa_ctrl_dst *dst, *next; - char levelstr[10]; + char levelstr[64]; int idx; char *sbuf; int llen; @@ -453,11 +511,15 @@ static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, char addr[INET6_ADDRSTRLEN]; #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ - dst = priv->ctrl_dst; - if (priv->sock < 0 || dst == NULL) + dst = *head; + if (sock < 0 || dst == NULL) return; - os_snprintf(levelstr, sizeof(levelstr), "<%d>", level); + if (ifname) + os_snprintf(levelstr, sizeof(levelstr), "IFACE=%s <%d>", + ifname, level); + else + os_snprintf(levelstr, sizeof(levelstr), "<%d>", level); llen = os_strlen(levelstr); sbuf = os_malloc(llen + len); @@ -481,7 +543,7 @@ static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, inet_ntoa(dst->addr.sin_addr), ntohs(dst->addr.sin_port)); #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ - if (sendto(priv->sock, sbuf, llen + len, 0, + if (sendto(sock, sbuf, llen + len, 0, (struct sockaddr *) &dst->addr, sizeof(dst->addr)) < 0) { wpa_printf(MSG_ERROR, @@ -490,7 +552,7 @@ static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, dst->errors++; if (dst->errors > 10) { wpa_supplicant_ctrl_iface_detach( - priv, &dst->addr, + head, &dst->addr, dst->addrlen); } } else @@ -513,12 +575,6 @@ void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv) /* Global ctrl_iface */ -struct ctrl_iface_global_priv { - int sock; - u8 cookie[COOKIE_LEN]; -}; - - static char * wpa_supplicant_global_get_cookie(struct ctrl_iface_global_priv *priv, size_t *reply_len) @@ -546,9 +602,13 @@ static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx, struct ctrl_iface_global_priv *priv = sock_ctx; char buf[256], *pos; int res; +#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 + struct sockaddr_in6 from; +#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ struct sockaddr_in from; +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ socklen_t fromlen = sizeof(from); - char *reply; + char *reply = NULL; size_t reply_len; u8 cookie[COOKIE_LEN]; @@ -561,6 +621,7 @@ static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx, } #ifndef CONFIG_CTRL_IFACE_UDP_REMOTE +#ifndef CONFIG_CTRL_IFACE_UDP_IPV6 if (from.sin_addr.s_addr != htonl((127 << 24) | 1)) { /* * The OS networking stack is expected to drop this kind of @@ -572,6 +633,7 @@ 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_IPV6 */ #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ buf[res] = '\0'; @@ -603,17 +665,34 @@ static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx, while (*pos == ' ') pos++; - reply = wpa_supplicant_global_ctrl_iface_process(global, pos, - &reply_len); + if (os_strcmp(pos, "ATTACH") == 0) { + if (wpa_supplicant_ctrl_iface_attach(&priv->ctrl_dst, + &from, fromlen)) + reply_len = 1; + else + reply_len = 2; + } else if (os_strcmp(pos, "DETACH") == 0) { + if (wpa_supplicant_ctrl_iface_detach(&priv->ctrl_dst, + &from, fromlen)) + reply_len = 1; + else + reply_len = 2; + } else { + reply = wpa_supplicant_global_ctrl_iface_process(global, pos, + &reply_len); + } done: if (reply) { sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, fromlen); os_free(reply); - } else if (reply_len) { + } else if (reply_len == 1) { sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from, fromlen); + } else if (reply_len == 2) { + sendto(sock, "OK\n", 3, 0, (struct sockaddr *) &from, + fromlen); } } @@ -623,6 +702,7 @@ wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global) { struct ctrl_iface_global_priv *priv; struct sockaddr_in addr; + char *pos; int port = WPA_GLOBAL_CTRL_IFACE_PORT; priv = os_zalloc(sizeof(*priv)); @@ -637,6 +717,17 @@ wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global) wpa_printf(MSG_DEBUG, "Global control interface '%s'", global->params.ctrl_interface); + pos = os_strstr(global->params.ctrl_interface, "udp:"); + if (pos) { + pos += 4; + port = atoi(pos); + if (port <= 0) { + wpa_printf(MSG_ERROR, "Invalid global ctrl UDP port %s", + global->params.ctrl_interface); + goto fail; + } + } + priv->sock = socket(PF_INET, SOCK_DGRAM, 0); if (priv->sock < 0) { wpa_printf(MSG_ERROR, "socket(PF_INET): %s", strerror(errno)); @@ -655,7 +746,7 @@ try_again: if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { port++; if ((port - WPA_GLOBAL_CTRL_IFACE_PORT) < - WPA_GLOBAL_CTRL_IFACE_PORT_LIMIT) + WPA_GLOBAL_CTRL_IFACE_PORT_LIMIT && !pos) goto try_again; wpa_printf(MSG_ERROR, "bind(AF_INET): %s", strerror(errno)); goto fail; @@ -668,6 +759,7 @@ try_again: eloop_register_read_sock(priv->sock, wpa_supplicant_global_ctrl_iface_receive, global, priv); + wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb); return priv; @@ -686,5 +778,7 @@ wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv) eloop_unregister_read_sock(priv->sock); close(priv->sock); } + + wpas_ctrl_iface_free_dst(priv->ctrl_dst); os_free(priv); } diff --git a/wpa_supplicant/ctrl_iface_unix.c b/wpa_supplicant/ctrl_iface_unix.c index 11f2814731308..4db712fff7bbb 100644 --- a/wpa_supplicant/ctrl_iface_unix.c +++ b/wpa_supplicant/ctrl_iface_unix.c @@ -15,7 +15,6 @@ #include <fcntl.h> #ifdef __linux__ #include <sys/ioctl.h> -#include <linux/sockios.h> #endif /* __linux__ */ #ifdef ANDROID #include <cutils/sockets.h> @@ -24,6 +23,7 @@ #include "utils/common.h" #include "utils/eloop.h" #include "utils/list.h" +#include "common/ctrl_iface_common.h" #include "eapol_supp/eapol_supp_sm.h" #include "config.h" #include "wpa_supplicant_i.h" @@ -31,27 +31,13 @@ /* Per-interface ctrl_iface */ -/** - * struct wpa_ctrl_dst - Internal data structure of control interface monitors - * - * This structure is used to store information about registered control - * interface monitors into struct wpa_supplicant. This data is private to - * ctrl_iface_unix.c and should not be touched directly from other files. - */ -struct wpa_ctrl_dst { - struct dl_list list; - struct sockaddr_un addr; - socklen_t addrlen; - int debug_level; - int errors; -}; - - struct ctrl_iface_priv { struct wpa_supplicant *wpa_s; int sock; struct dl_list ctrl_dst; int android_control_socket; + struct dl_list msg_queue; + unsigned int throttle_count; }; @@ -60,6 +46,17 @@ struct ctrl_iface_global_priv { int sock; struct dl_list ctrl_dst; int android_control_socket; + struct dl_list msg_queue; + unsigned int throttle_count; +}; + +struct ctrl_iface_msg { + struct dl_list list; + struct wpa_supplicant *wpa_s; + int level; + enum wpa_msg_type type; + const char *txt; + size_t len; }; @@ -92,7 +89,7 @@ static void wpas_ctrl_sock_debug(const char *title, int sock, const char *buf, if (getsockopt(sock, SOL_SOCKET, SO_SNDBUF, &sndbuf, &optlen) < 0) sndbuf = -1; - if (ioctl(sock, SIOCOUTQ, &outq) < 0) + if (ioctl(sock, TIOCOUTQ, &outq) < 0) outq = -1; wpa_printf(level, @@ -103,81 +100,29 @@ static void wpas_ctrl_sock_debug(const char *title, int sock, const char *buf, static int wpa_supplicant_ctrl_iface_attach(struct dl_list *ctrl_dst, - struct sockaddr_un *from, + struct sockaddr_storage *from, socklen_t fromlen, int global) { - struct wpa_ctrl_dst *dst; - char addr_txt[200]; - - dst = os_zalloc(sizeof(*dst)); - if (dst == NULL) - return -1; - os_memcpy(&dst->addr, from, sizeof(struct sockaddr_un)); - dst->addrlen = fromlen; - dst->debug_level = MSG_INFO; - dl_list_add(ctrl_dst, &dst->list); - printf_encode(addr_txt, sizeof(addr_txt), - (u8 *) from->sun_path, - fromlen - offsetof(struct sockaddr_un, sun_path)); - wpa_printf(MSG_DEBUG, "CTRL_IFACE %smonitor attached %s", - global ? "global " : "", addr_txt); - return 0; + return ctrl_iface_attach(ctrl_dst, from, fromlen); } static int wpa_supplicant_ctrl_iface_detach(struct dl_list *ctrl_dst, - struct sockaddr_un *from, + struct sockaddr_storage *from, socklen_t fromlen) { - struct wpa_ctrl_dst *dst; - - dl_list_for_each(dst, ctrl_dst, struct wpa_ctrl_dst, list) { - if (fromlen == dst->addrlen && - os_memcmp(from->sun_path, dst->addr.sun_path, - fromlen - offsetof(struct sockaddr_un, sun_path)) - == 0) { - char addr_txt[200]; - printf_encode(addr_txt, sizeof(addr_txt), - (u8 *) from->sun_path, - fromlen - - offsetof(struct sockaddr_un, sun_path)); - wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached %s", - addr_txt); - dl_list_del(&dst->list); - os_free(dst); - return 0; - } - } - return -1; + return ctrl_iface_detach(ctrl_dst, from, fromlen); } static int wpa_supplicant_ctrl_iface_level(struct ctrl_iface_priv *priv, - struct sockaddr_un *from, + struct sockaddr_storage *from, socklen_t fromlen, char *level) { - struct wpa_ctrl_dst *dst; - wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level); - dl_list_for_each(dst, &priv->ctrl_dst, struct wpa_ctrl_dst, list) { - if (fromlen == dst->addrlen && - os_memcmp(from->sun_path, dst->addr.sun_path, - fromlen - offsetof(struct sockaddr_un, sun_path)) - == 0) { - char addr_txt[200]; - dst->debug_level = atoi(level); - printf_encode(addr_txt, sizeof(addr_txt), - (u8 *) from->sun_path, fromlen - - offsetof(struct sockaddr_un, sun_path)); - wpa_printf(MSG_DEBUG, "CTRL_IFACE changed monitor level to %d for %s", - dst->debug_level, addr_txt); - return 0; - } - } - - return -1; + return ctrl_iface_level(&priv->ctrl_dst, from, fromlen, level); } @@ -188,7 +133,7 @@ static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx, struct ctrl_iface_priv *priv = sock_ctx; char buf[4096]; int res; - struct sockaddr_un from; + struct sockaddr_storage from; socklen_t fromlen = sizeof(from); char *reply = NULL, *reply_buf = NULL; size_t reply_len = 0; @@ -334,33 +279,209 @@ static char * wpa_supplicant_ctrl_iface_path(struct wpa_supplicant *wpa_s) } +static int wpas_ctrl_iface_throttle(int sock) +{ +#ifdef __linux__ + socklen_t optlen; + int sndbuf, outq; + + optlen = sizeof(sndbuf); + sndbuf = 0; + if (getsockopt(sock, SOL_SOCKET, SO_SNDBUF, &sndbuf, &optlen) < 0 || + ioctl(sock, TIOCOUTQ, &outq) < 0 || + sndbuf <= 0 || outq < 0) + return 0; + return outq > sndbuf / 2; +#else /* __linux__ */ + return 0; +#endif /* __linux__ */ +} + + +static void wpas_ctrl_msg_send_pending_global(struct wpa_global *global) +{ + struct ctrl_iface_global_priv *gpriv; + struct ctrl_iface_msg *msg; + + gpriv = global->ctrl_iface; + while (gpriv && !dl_list_empty(&gpriv->msg_queue) && + !wpas_ctrl_iface_throttle(gpriv->sock)) { + msg = dl_list_first(&gpriv->msg_queue, struct ctrl_iface_msg, + list); + if (!msg) + break; + dl_list_del(&msg->list); + wpa_supplicant_ctrl_iface_send( + msg->wpa_s, + msg->type != WPA_MSG_PER_INTERFACE ? + NULL : msg->wpa_s->ifname, + gpriv->sock, &gpriv->ctrl_dst, msg->level, + msg->txt, msg->len, NULL, gpriv); + os_free(msg); + } +} + + +static void wpas_ctrl_msg_send_pending_iface(struct wpa_supplicant *wpa_s) +{ + struct ctrl_iface_priv *priv; + struct ctrl_iface_msg *msg; + + priv = wpa_s->ctrl_iface; + while (priv && !dl_list_empty(&priv->msg_queue) && + !wpas_ctrl_iface_throttle(priv->sock)) { + msg = dl_list_first(&priv->msg_queue, struct ctrl_iface_msg, + list); + if (!msg) + break; + dl_list_del(&msg->list); + wpa_supplicant_ctrl_iface_send(wpa_s, NULL, priv->sock, + &priv->ctrl_dst, msg->level, + msg->txt, msg->len, priv, NULL); + os_free(msg); + } +} + + +static void wpas_ctrl_msg_queue_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct ctrl_iface_priv *priv; + struct ctrl_iface_global_priv *gpriv; + int sock = -1, gsock = -1; + + wpas_ctrl_msg_send_pending_global(wpa_s->global); + wpas_ctrl_msg_send_pending_iface(wpa_s); + + priv = wpa_s->ctrl_iface; + if (priv && !dl_list_empty(&priv->msg_queue)) + sock = priv->sock; + + gpriv = wpa_s->global->ctrl_iface; + if (gpriv && !dl_list_empty(&gpriv->msg_queue)) + gsock = gpriv->sock; + + if (sock > -1 || gsock > -1) { + /* Continue pending message transmission from a timeout */ + wpa_printf(MSG_MSGDUMP, + "CTRL: Had to throttle pending event message transmission for (sock %d gsock %d)", + sock, gsock); + eloop_register_timeout(0, 20000, wpas_ctrl_msg_queue_timeout, + wpa_s, NULL); + } +} + + +static void wpas_ctrl_msg_queue(struct dl_list *queue, + struct wpa_supplicant *wpa_s, int level, + enum wpa_msg_type type, + const char *txt, size_t len) +{ + struct ctrl_iface_msg *msg; + + msg = os_zalloc(sizeof(*msg) + len); + if (!msg) + return; + + msg->wpa_s = wpa_s; + msg->level = level; + msg->type = type; + os_memcpy(msg + 1, txt, len); + msg->txt = (const char *) (msg + 1); + msg->len = len; + dl_list_add_tail(queue, &msg->list); + eloop_cancel_timeout(wpas_ctrl_msg_queue_timeout, wpa_s, NULL); + eloop_register_timeout(0, 0, wpas_ctrl_msg_queue_timeout, wpa_s, NULL); +} + + +static void wpas_ctrl_msg_queue_limit(unsigned int throttle_count, + struct dl_list *queue) +{ + struct ctrl_iface_msg *msg; + + if (throttle_count < 2000) + return; + + msg = dl_list_first(queue, struct ctrl_iface_msg, list); + if (msg) { + wpa_printf(MSG_DEBUG, "CTRL: Dropped oldest pending message"); + dl_list_del(&msg->list); + os_free(msg); + } +} + + static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level, enum wpa_msg_type type, const char *txt, size_t len) { struct wpa_supplicant *wpa_s = ctx; + struct ctrl_iface_priv *priv; + struct ctrl_iface_global_priv *gpriv; if (wpa_s == NULL) return; - if (type != WPA_MSG_NO_GLOBAL && wpa_s->global->ctrl_iface) { - struct ctrl_iface_global_priv *priv = wpa_s->global->ctrl_iface; - if (!dl_list_empty(&priv->ctrl_dst)) { + gpriv = wpa_s->global->ctrl_iface; + + if (type != WPA_MSG_NO_GLOBAL && gpriv && + !dl_list_empty(&gpriv->ctrl_dst)) { + if (!dl_list_empty(&gpriv->msg_queue) || + wpas_ctrl_iface_throttle(gpriv->sock)) { + if (gpriv->throttle_count == 0) { + wpa_printf(MSG_MSGDUMP, + "CTRL: Had to throttle global event message for sock %d", + gpriv->sock); + } + gpriv->throttle_count++; + wpas_ctrl_msg_queue_limit(gpriv->throttle_count, + &gpriv->msg_queue); + wpas_ctrl_msg_queue(&gpriv->msg_queue, wpa_s, level, + type, txt, len); + } else { + if (gpriv->throttle_count) { + wpa_printf(MSG_MSGDUMP, + "CTRL: Had to throttle %u global event message(s) for sock %d", + gpriv->throttle_count, gpriv->sock); + } + gpriv->throttle_count = 0; wpa_supplicant_ctrl_iface_send( wpa_s, type != WPA_MSG_PER_INTERFACE ? NULL : wpa_s->ifname, - priv->sock, &priv->ctrl_dst, level, txt, len, - NULL, priv); + gpriv->sock, &gpriv->ctrl_dst, level, + txt, len, NULL, gpriv); } } - if (type == WPA_MSG_ONLY_GLOBAL || wpa_s->ctrl_iface == NULL) - return; - wpa_supplicant_ctrl_iface_send(wpa_s, NULL, wpa_s->ctrl_iface->sock, - &wpa_s->ctrl_iface->ctrl_dst, - level, txt, len, wpa_s->ctrl_iface, - NULL); + priv = wpa_s->ctrl_iface; + + if (type != WPA_MSG_ONLY_GLOBAL && priv) { + if (!dl_list_empty(&priv->msg_queue) || + wpas_ctrl_iface_throttle(priv->sock)) { + if (priv->throttle_count == 0) { + wpa_printf(MSG_MSGDUMP, + "CTRL: Had to throttle event message for sock %d", + priv->sock); + } + priv->throttle_count++; + wpas_ctrl_msg_queue_limit(priv->throttle_count, + &priv->msg_queue); + wpas_ctrl_msg_queue(&priv->msg_queue, wpa_s, level, + type, txt, len); + } else { + if (priv->throttle_count) { + wpa_printf(MSG_MSGDUMP, + "CTRL: Had to throttle %u event message(s) for sock %d", + priv->throttle_count, priv->sock); + } + priv->throttle_count = 0; + wpa_supplicant_ctrl_iface_send(wpa_s, NULL, priv->sock, + &priv->ctrl_dst, level, + txt, len, priv, NULL); + } + } } @@ -578,6 +699,7 @@ wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) if (priv == NULL) return NULL; dl_list_init(&priv->ctrl_dst); + dl_list_init(&priv->msg_queue); priv->wpa_s = wpa_s; priv->sock = -1; @@ -671,6 +793,8 @@ static int wpas_ctrl_iface_reinit(struct wpa_supplicant *wpa_s, void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv) { struct wpa_ctrl_dst *dst, *prev; + struct ctrl_iface_msg *msg, *prev_msg; + struct ctrl_iface_global_priv *gpriv; if (priv->sock > -1) { char *fname; @@ -724,8 +848,26 @@ void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv) free_dst: dl_list_for_each_safe(dst, prev, &priv->ctrl_dst, struct wpa_ctrl_dst, - list) + list) { + dl_list_del(&dst->list); os_free(dst); + } + dl_list_for_each_safe(msg, prev_msg, &priv->msg_queue, + struct ctrl_iface_msg, list) { + dl_list_del(&msg->list); + os_free(msg); + } + gpriv = priv->wpa_s->global->ctrl_iface; + if (gpriv) { + dl_list_for_each_safe(msg, prev_msg, &gpriv->msg_queue, + struct ctrl_iface_msg, list) { + if (msg->wpa_s == priv->wpa_s) { + dl_list_del(&msg->list); + os_free(msg); + } + } + } + eloop_cancel_timeout(wpas_ctrl_msg_queue_timeout, priv->wpa_s, NULL); os_free(priv); } @@ -785,33 +927,31 @@ static void wpa_supplicant_ctrl_iface_send(struct wpa_supplicant *wpa_s, dl_list_for_each_safe(dst, next, ctrl_dst, struct wpa_ctrl_dst, list) { int _errno; - char addr_txt[200]; + char txt[200]; if (level < dst->debug_level) continue; - printf_encode(addr_txt, sizeof(addr_txt), - (u8 *) dst->addr.sun_path, dst->addrlen - - offsetof(struct sockaddr_un, sun_path)); msg.msg_name = (void *) &dst->addr; msg.msg_namelen = dst->addrlen; wpas_ctrl_sock_debug("ctrl_sock-sendmsg", sock, buf, len); if (sendmsg(sock, &msg, MSG_DONTWAIT) >= 0) { - wpa_printf(MSG_MSGDUMP, - "CTRL_IFACE monitor sent successfully to %s", - addr_txt); + sockaddr_print(MSG_MSGDUMP, + "CTRL_IFACE monitor sent successfully to", + &dst->addr, dst->addrlen); dst->errors = 0; continue; } _errno = errno; - wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor[%s]: %d - %s", - addr_txt, errno, strerror(errno)); + os_snprintf(txt, sizeof(txt), "CTRL_IFACE monitor: %d (%s) for", + _errno, strerror(_errno)); + sockaddr_print(MSG_DEBUG, txt, &dst->addr, dst->addrlen); dst->errors++; if (dst->errors > 10 || _errno == ENOENT || _errno == EPERM) { - wpa_printf(MSG_INFO, "CTRL_IFACE: Detach monitor %s that cannot receive messages", - addr_txt); + sockaddr_print(MSG_INFO, "CTRL_IFACE: Detach monitor that cannot receive messages:", + &dst->addr, dst->addrlen); wpa_supplicant_ctrl_iface_detach(ctrl_dst, &dst->addr, dst->addrlen); } @@ -845,9 +985,12 @@ void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv) { char buf[256]; int res; - struct sockaddr_un from; + struct sockaddr_storage from; socklen_t fromlen = sizeof(from); + if (priv->sock == -1) + return; + for (;;) { wpa_printf(MSG_DEBUG, "CTRL_IFACE - %s - wait for monitor to " "attach", priv->wpa_s->ifname); @@ -905,7 +1048,7 @@ static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx, struct ctrl_iface_global_priv *priv = sock_ctx; char buf[4096]; int res; - struct sockaddr_un from; + struct sockaddr_storage from; socklen_t fromlen = sizeof(from); char *reply = NULL, *reply_buf = NULL; size_t reply_len; @@ -1155,6 +1298,7 @@ wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global) if (priv == NULL) return NULL; dl_list_init(&priv->ctrl_dst); + dl_list_init(&priv->msg_queue); priv->global = global; priv->sock = -1; @@ -1204,6 +1348,7 @@ void wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv) { struct wpa_ctrl_dst *dst, *prev; + struct ctrl_iface_msg *msg, *prev_msg; if (priv->sock >= 0) { eloop_unregister_read_sock(priv->sock); @@ -1212,7 +1357,14 @@ wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv) if (priv->global->params.ctrl_interface) unlink(priv->global->params.ctrl_interface); dl_list_for_each_safe(dst, prev, &priv->ctrl_dst, struct wpa_ctrl_dst, - list) + list) { + dl_list_del(&dst->list); os_free(dst); + } + dl_list_for_each_safe(msg, prev_msg, &priv->msg_queue, + struct ctrl_iface_msg, list) { + dl_list_del(&msg->list); + os_free(msg); + } os_free(priv); } diff --git a/wpa_supplicant/dbus/dbus-wpa_supplicant.conf b/wpa_supplicant/dbus/dbus-wpa_supplicant.conf index c091234f7691d..382dcb34318ca 100644 --- a/wpa_supplicant/dbus/dbus-wpa_supplicant.conf +++ b/wpa_supplicant/dbus/dbus-wpa_supplicant.conf @@ -17,11 +17,9 @@ <policy context="default"> <deny own="fi.epitest.hostap.WPASupplicant"/> <deny send_destination="fi.epitest.hostap.WPASupplicant"/> - <deny send_interface="fi.epitest.hostap.WPASupplicant"/> <deny own="fi.w1.wpa_supplicant1"/> <deny send_destination="fi.w1.wpa_supplicant1"/> - <deny send_interface="fi.w1.wpa_supplicant1"/> <deny receive_sender="fi.w1.wpa_supplicant1" receive_type="signal"/> </policy> </busconfig> diff --git a/wpa_supplicant/dbus/dbus_common_i.h b/wpa_supplicant/dbus/dbus_common_i.h index a551ccd554b18..95eb4bcb50ca9 100644 --- a/wpa_supplicant/dbus/dbus_common_i.h +++ b/wpa_supplicant/dbus/dbus_common_i.h @@ -13,6 +13,8 @@ #include <dbus/dbus.h> +struct wpa_dbus_property_desc; + struct wpas_dbus_priv { DBusConnection *con; int should_dispatch; @@ -20,9 +22,13 @@ struct wpas_dbus_priv { u32 next_objid; int dbus_new_initialized; -#if defined(CONFIG_CTRL_IFACE_DBUS_NEW) && defined(CONFIG_AP) +#if defined(CONFIG_CTRL_IFACE_DBUS_NEW) + struct wpa_dbus_property_desc *all_interface_properties; + int globals_start; +#if defined(CONFIG_AP) int dbus_noc_refcnt; -#endif /* CONFIG_CTRL_IFACE_DBUS_NEW && CONFIG_AP */ +#endif /* CONFIG_AP */ +#endif /* CONFIG_CTRL_IFACE_DBUS_NEW */ }; #endif /* DBUS_COMMON_I_H */ diff --git a/wpa_supplicant/dbus/dbus_dict_helpers.c b/wpa_supplicant/dbus/dbus_dict_helpers.c index a0c44ebfa41d9..e4e9b8da96b7f 100644 --- a/wpa_supplicant/dbus/dbus_dict_helpers.c +++ b/wpa_supplicant/dbus/dbus_dict_helpers.c @@ -205,24 +205,6 @@ dbus_bool_t wpa_dbus_dict_append_string(DBusMessageIter *iter_dict, /** - * Add a byte entry 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 value The byte value - * @return TRUE on success, FALSE on failure - * - */ -dbus_bool_t wpa_dbus_dict_append_byte(DBusMessageIter *iter_dict, - const char *key, const char value) -{ - return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_BYTE, - &value); -} - - -/** * Add a boolean entry to the dict. * * @param iter_dict A valid DBusMessageIter returned from @@ -317,62 +299,6 @@ dbus_bool_t wpa_dbus_dict_append_uint32(DBusMessageIter *iter_dict, /** - * Add a 64-bit integer entry 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 value The 64-bit integer value - * @return TRUE on success, FALSE on failure - * - */ -dbus_bool_t wpa_dbus_dict_append_int64(DBusMessageIter *iter_dict, - const char *key, - const dbus_int64_t value) -{ - return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_INT64, - &value); -} - - -/** - * Add a 64-bit unsigned integer entry 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 value The 64-bit unsigned integer value - * @return TRUE on success, FALSE on failure - * - */ -dbus_bool_t wpa_dbus_dict_append_uint64(DBusMessageIter *iter_dict, - const char *key, - const dbus_uint64_t value) -{ - return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_UINT64, - &value); -} - - -/** - * Add a double-precision floating point entry 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 value The double-precision floating point value - * @return TRUE on success, FALSE on failure - * - */ -dbus_bool_t wpa_dbus_dict_append_double(DBusMessageIter *iter_dict, - const char *key, const double value) -{ - return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_DOUBLE, - &value); -} - - -/** * Add a DBus object path entry to the dict. * * @param iter_dict A valid DBusMessageIter returned from diff --git a/wpa_supplicant/dbus/dbus_dict_helpers.h b/wpa_supplicant/dbus/dbus_dict_helpers.h index b068431a74cc8..94a0efdbeb1fd 100644 --- a/wpa_supplicant/dbus/dbus_dict_helpers.h +++ b/wpa_supplicant/dbus/dbus_dict_helpers.h @@ -26,9 +26,6 @@ const char * wpa_dbus_type_as_string(const int type); dbus_bool_t wpa_dbus_dict_append_string(DBusMessageIter *iter_dict, const char *key, const char *value); -dbus_bool_t wpa_dbus_dict_append_byte(DBusMessageIter *iter_dict, - const char *key, const char value); - dbus_bool_t wpa_dbus_dict_append_bool(DBusMessageIter *iter_dict, const char *key, const dbus_bool_t value); @@ -49,18 +46,6 @@ dbus_bool_t wpa_dbus_dict_append_uint32(DBusMessageIter *iter_dict, const char *key, const dbus_uint32_t value); -dbus_bool_t wpa_dbus_dict_append_int64(DBusMessageIter *iter_dict, - const char *key, - const dbus_int64_t value); - -dbus_bool_t wpa_dbus_dict_append_uint64(DBusMessageIter *iter_dict, - const char *key, - const dbus_uint64_t value); - -dbus_bool_t wpa_dbus_dict_append_double(DBusMessageIter *iter_dict, - const char *key, - const double value); - dbus_bool_t wpa_dbus_dict_append_object_path(DBusMessageIter *iter_dict, const char *key, const char *value); diff --git a/wpa_supplicant/dbus/dbus_new.c b/wpa_supplicant/dbus/dbus_new.c index 67d0e2877a478..27b3012aede8e 100644 --- a/wpa_supplicant/dbus/dbus_new.c +++ b/wpa_supplicant/dbus/dbus_new.c @@ -1207,7 +1207,7 @@ static int match_group_where_peer_is_client(struct p2p_group *group, cfg->ssid_len); if (wpa_s_go != NULL && wpa_s_go == data->wpa_s) { wpas_dbus_signal_peer_groups_changed( - data->wpa_s->parent, data->info->p2p_device_addr); + data->wpa_s->p2pdev, data->info->p2p_device_addr); return 0; } @@ -1224,7 +1224,7 @@ static void signal_peer_groups_changed(struct p2p_peer_info *info, wpa_s_go = wpas_get_p2p_client_iface(data->wpa_s, info->p2p_device_addr); if (wpa_s_go != NULL && wpa_s_go == data->wpa_s) { - wpas_dbus_signal_peer_groups_changed(data->wpa_s->parent, + wpas_dbus_signal_peer_groups_changed(data->wpa_s->p2pdev, info->p2p_device_addr); return; } @@ -1254,14 +1254,11 @@ static void peer_groups_changed(struct wpa_supplicant *wpa_s) * 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 + * @persistent: 0 - non persistent group, 1 - persistent group */ void wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s, - const struct wpa_ssid *ssid, - int client, int network_id) + int client, int persistent) { DBusMessage *msg; DBusMessageIter iter, dict_iter; @@ -1300,6 +1297,7 @@ void wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s, wpa_s->dbus_new_path) || !wpa_dbus_dict_append_string(&dict_iter, "role", client ? "client" : "GO") || + !wpa_dbus_dict_append_bool(&dict_iter, "persistent", persistent) || !wpa_dbus_dict_append_object_path(&dict_iter, "group_object", wpa_s->dbus_groupobj_path) || !wpa_dbus_dict_close_write(&iter, &dict_iter)) { @@ -1950,6 +1948,7 @@ void wpas_dbus_signal_p2p_invitation_received(struct wpa_supplicant *wpa_s, } dbus_connection_send(iface->con, msg, NULL); + dbus_message_unref(msg); } @@ -2000,6 +1999,10 @@ void wpas_dbus_signal_prop_changed(struct wpa_supplicant *wpa_s, prop = "DisconnectReason"; flush = TRUE; break; + case WPAS_DBUS_PROP_ASSOC_STATUS_CODE: + prop = "AssocStatusCode"; + flush = TRUE; + break; default: wpa_printf(MSG_ERROR, "dbus: %s: Unknown Property value %d", __func__, property); @@ -2172,41 +2175,54 @@ static const struct wpa_dbus_method_desc wpas_dbus_global_methods[] = { END_ARGS } }, + { "ExpectDisconnect", WPAS_DBUS_NEW_INTERFACE, + (WPADBusMethodHandler) wpas_dbus_handler_expect_disconnect, + { + END_ARGS + } + }, { NULL, NULL, NULL, { END_ARGS } } }; static const struct wpa_dbus_property_desc wpas_dbus_global_properties[] = { { "DebugLevel", WPAS_DBUS_NEW_INTERFACE, "s", wpas_dbus_getter_debug_level, - wpas_dbus_setter_debug_level + wpas_dbus_setter_debug_level, + NULL }, { "DebugTimestamp", WPAS_DBUS_NEW_INTERFACE, "b", wpas_dbus_getter_debug_timestamp, - wpas_dbus_setter_debug_timestamp + wpas_dbus_setter_debug_timestamp, + NULL }, { "DebugShowKeys", WPAS_DBUS_NEW_INTERFACE, "b", wpas_dbus_getter_debug_show_keys, - wpas_dbus_setter_debug_show_keys + wpas_dbus_setter_debug_show_keys, + NULL }, { "Interfaces", WPAS_DBUS_NEW_INTERFACE, "ao", wpas_dbus_getter_interfaces, + NULL, NULL }, { "EapMethods", WPAS_DBUS_NEW_INTERFACE, "as", wpas_dbus_getter_eap_methods, + NULL, NULL }, { "Capabilities", WPAS_DBUS_NEW_INTERFACE, "as", wpas_dbus_getter_global_capabilities, + NULL, NULL }, #ifdef CONFIG_WIFI_DISPLAY { "WFDIEs", WPAS_DBUS_NEW_INTERFACE, "ay", wpas_dbus_getter_global_wfd_ies, - wpas_dbus_setter_global_wfd_ies + wpas_dbus_setter_global_wfd_ies, + NULL }, #endif /* CONFIG_WIFI_DISPLAY */ - { NULL, NULL, NULL, NULL, NULL } + { NULL, NULL, NULL, NULL, NULL, NULL } }; static const struct wpa_dbus_signal_desc wpas_dbus_global_signals[] = { @@ -2234,12 +2250,50 @@ static const struct wpa_dbus_signal_desc wpas_dbus_global_signals[] = { }; +static char * uscore_to_dbus(const char *uscore) +{ + const char *p = uscore; + char *str, *s; + dbus_bool_t last_was_uscore = TRUE; + + s = str = os_zalloc(os_strlen(uscore) + 1); + if (!str) + return NULL; + while (p && *p) { + if (*p == '_') { + last_was_uscore = TRUE; + } else { + *s++ = last_was_uscore ? toupper(*p) : *p; + last_was_uscore = FALSE; + } + p++; + } + + return str; +} + + +static int wpa_dbus_ctrl_iface_props_init(struct wpas_dbus_priv *priv); + + +static void wpa_dbus_ctrl_iface_props_deinit(struct wpas_dbus_priv *priv) +{ + int idx = priv->globals_start; + + /* Free all allocated property values */ + while (priv->all_interface_properties[idx].dbus_property) + os_free((char *) + priv->all_interface_properties[idx++].dbus_property); + os_free((char *) priv->all_interface_properties); +} + + /** * wpas_dbus_ctrl_iface_init - Initialize dbus control interface * @global: Pointer to global data from wpa_supplicant_init() * Returns: 0 on success or -1 on failure * - * Initialize the dbus control interface for wpa_supplicantand and start + * Initialize the dbus control interface for wpa_supplicant and start * receiving commands from external programs over the bus. */ int wpas_dbus_ctrl_iface_init(struct wpas_dbus_priv *priv) @@ -2247,11 +2301,18 @@ int wpas_dbus_ctrl_iface_init(struct wpas_dbus_priv *priv) struct wpa_dbus_object_desc *obj_desc; int ret; + ret = wpa_dbus_ctrl_iface_props_init(priv); + if (ret < 0) { + wpa_printf(MSG_ERROR, + "dbus: Not enough memory to init interface properties"); + return -1; + } + obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc)); if (!obj_desc) { wpa_printf(MSG_ERROR, "Not enough memory to create object description"); - return -1; + goto error; } wpas_dbus_register(obj_desc, priv->global, NULL, @@ -2264,31 +2325,36 @@ int wpas_dbus_ctrl_iface_init(struct wpas_dbus_priv *priv) ret = wpa_dbus_ctrl_iface_init(priv, WPAS_DBUS_NEW_PATH, WPAS_DBUS_NEW_SERVICE, obj_desc); - if (ret < 0) + if (ret < 0) { free_dbus_object_desc(obj_desc); - else - priv->dbus_new_initialized = 1; + goto error; + } - return ret; + priv->dbus_new_initialized = 1; + return 0; + +error: + wpa_dbus_ctrl_iface_props_deinit(priv); + return -1; } /** * wpas_dbus_ctrl_iface_deinit - Deinitialize dbus ctrl interface for * wpa_supplicant - * @iface: Pointer to dbus private data from wpas_dbus_init() + * @priv: Pointer to dbus private data from wpas_dbus_init() * * Deinitialize the dbus control interface that was initialized with * wpas_dbus_ctrl_iface_init(). */ -void wpas_dbus_ctrl_iface_deinit(struct wpas_dbus_priv *iface) +void wpas_dbus_ctrl_iface_deinit(struct wpas_dbus_priv *priv) { - if (!iface->dbus_new_initialized) + if (!priv->dbus_new_initialized) return; wpa_printf(MSG_DEBUG, "dbus: Unregister D-Bus object '%s'", WPAS_DBUS_NEW_PATH); - dbus_connection_unregister_object_path(iface->con, - WPAS_DBUS_NEW_PATH); + dbus_connection_unregister_object_path(priv->con, WPAS_DBUS_NEW_PATH); + wpa_dbus_ctrl_iface_props_deinit(priv); } @@ -2301,13 +2367,15 @@ 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}", wpas_dbus_getter_network_properties, - wpas_dbus_setter_network_properties + wpas_dbus_setter_network_properties, + NULL }, { "Enabled", WPAS_DBUS_NEW_IFACE_NETWORK, "b", wpas_dbus_getter_enabled, - wpas_dbus_setter_enabled + wpas_dbus_setter_enabled, + NULL }, - { NULL, NULL, NULL, NULL, NULL } + { NULL, NULL, NULL, NULL, NULL, NULL } }; @@ -2446,53 +2514,65 @@ 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", wpas_dbus_getter_bss_ssid, + NULL, NULL }, { "BSSID", WPAS_DBUS_NEW_IFACE_BSS, "ay", wpas_dbus_getter_bss_bssid, + NULL, NULL }, { "Privacy", WPAS_DBUS_NEW_IFACE_BSS, "b", wpas_dbus_getter_bss_privacy, + NULL, NULL }, { "Mode", WPAS_DBUS_NEW_IFACE_BSS, "s", wpas_dbus_getter_bss_mode, + NULL, NULL }, { "Signal", WPAS_DBUS_NEW_IFACE_BSS, "n", wpas_dbus_getter_bss_signal, + NULL, NULL }, { "Frequency", WPAS_DBUS_NEW_IFACE_BSS, "q", wpas_dbus_getter_bss_frequency, + NULL, NULL }, { "Rates", WPAS_DBUS_NEW_IFACE_BSS, "au", wpas_dbus_getter_bss_rates, + NULL, NULL }, { "WPA", WPAS_DBUS_NEW_IFACE_BSS, "a{sv}", wpas_dbus_getter_bss_wpa, + NULL, NULL }, { "RSN", WPAS_DBUS_NEW_IFACE_BSS, "a{sv}", wpas_dbus_getter_bss_rsn, + NULL, NULL }, { "WPS", WPAS_DBUS_NEW_IFACE_BSS, "a{sv}", wpas_dbus_getter_bss_wps, + NULL, NULL }, { "IEs", WPAS_DBUS_NEW_IFACE_BSS, "ay", wpas_dbus_getter_bss_ies, + NULL, NULL }, { "Age", WPAS_DBUS_NEW_IFACE_BSS, "u", wpas_dbus_getter_bss_age, + NULL, NULL }, - { NULL, NULL, NULL, NULL, NULL } + { NULL, NULL, NULL, NULL, NULL, NULL } }; @@ -2992,131 +3072,202 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { } }, #endif /* CONFIG_TDLS */ + { "VendorElemAdd", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) wpas_dbus_handler_vendor_elem_add, + { + { "frame_id", "i", ARG_IN }, + { "ielems", "ay", ARG_IN }, + END_ARGS + } + }, + { "VendorElemGet", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) wpas_dbus_handler_vendor_elem_get, + { + { "frame_id", "i", ARG_IN }, + { "ielems", "ay", ARG_OUT }, + END_ARGS + } + }, + { "VendorElemRem", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) wpas_dbus_handler_vendor_elem_remove, + { + { "frame_id", "i", ARG_IN }, + { "ielems", "ay", ARG_IN }, + END_ARGS + } + }, +#ifndef CONFIG_NO_CONFIG_WRITE + { "SaveConfig", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) wpas_dbus_handler_save_config, + { + END_ARGS + } + }, +#endif /* CONFIG_NO_CONFIG_WRITE */ { NULL, NULL, NULL, { END_ARGS } } }; static const struct wpa_dbus_property_desc wpas_dbus_interface_properties[] = { { "Capabilities", WPAS_DBUS_NEW_IFACE_INTERFACE, "a{sv}", wpas_dbus_getter_capabilities, + NULL, NULL }, { "State", WPAS_DBUS_NEW_IFACE_INTERFACE, "s", wpas_dbus_getter_state, + NULL, NULL }, { "Scanning", WPAS_DBUS_NEW_IFACE_INTERFACE, "b", wpas_dbus_getter_scanning, + NULL, NULL }, { "ApScan", WPAS_DBUS_NEW_IFACE_INTERFACE, "u", wpas_dbus_getter_ap_scan, - wpas_dbus_setter_ap_scan + wpas_dbus_setter_ap_scan, + NULL }, { "BSSExpireAge", WPAS_DBUS_NEW_IFACE_INTERFACE, "u", wpas_dbus_getter_bss_expire_age, - wpas_dbus_setter_bss_expire_age + wpas_dbus_setter_bss_expire_age, + NULL }, { "BSSExpireCount", WPAS_DBUS_NEW_IFACE_INTERFACE, "u", wpas_dbus_getter_bss_expire_count, - wpas_dbus_setter_bss_expire_count + wpas_dbus_setter_bss_expire_count, + NULL }, { "Country", WPAS_DBUS_NEW_IFACE_INTERFACE, "s", wpas_dbus_getter_country, - wpas_dbus_setter_country + wpas_dbus_setter_country, + NULL }, { "Ifname", WPAS_DBUS_NEW_IFACE_INTERFACE, "s", wpas_dbus_getter_ifname, + NULL, NULL }, { "Driver", WPAS_DBUS_NEW_IFACE_INTERFACE, "s", wpas_dbus_getter_driver, + NULL, NULL }, { "BridgeIfname", WPAS_DBUS_NEW_IFACE_INTERFACE, "s", wpas_dbus_getter_bridge_ifname, + NULL, + NULL + }, + { "ConfigFile", WPAS_DBUS_NEW_IFACE_INTERFACE, "s", + wpas_dbus_getter_config_file, + NULL, NULL }, { "CurrentBSS", WPAS_DBUS_NEW_IFACE_INTERFACE, "o", wpas_dbus_getter_current_bss, + NULL, NULL }, { "CurrentNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE, "o", wpas_dbus_getter_current_network, + NULL, NULL }, { "CurrentAuthMode", WPAS_DBUS_NEW_IFACE_INTERFACE, "s", wpas_dbus_getter_current_auth_mode, + NULL, NULL }, { "Blobs", WPAS_DBUS_NEW_IFACE_INTERFACE, "a{say}", wpas_dbus_getter_blobs, + NULL, NULL }, { "BSSs", WPAS_DBUS_NEW_IFACE_INTERFACE, "ao", wpas_dbus_getter_bsss, + NULL, NULL }, { "Networks", WPAS_DBUS_NEW_IFACE_INTERFACE, "ao", wpas_dbus_getter_networks, + NULL, NULL }, { "FastReauth", WPAS_DBUS_NEW_IFACE_INTERFACE, "b", wpas_dbus_getter_fast_reauth, - wpas_dbus_setter_fast_reauth + wpas_dbus_setter_fast_reauth, + NULL }, { "ScanInterval", WPAS_DBUS_NEW_IFACE_INTERFACE, "i", wpas_dbus_getter_scan_interval, - wpas_dbus_setter_scan_interval + wpas_dbus_setter_scan_interval, + NULL }, { "PKCS11EnginePath", WPAS_DBUS_NEW_IFACE_INTERFACE, "s", wpas_dbus_getter_pkcs11_engine_path, + NULL, NULL }, { "PKCS11ModulePath", WPAS_DBUS_NEW_IFACE_INTERFACE, "s", wpas_dbus_getter_pkcs11_module_path, + NULL, NULL }, #ifdef CONFIG_WPS { "ProcessCredentials", WPAS_DBUS_NEW_IFACE_WPS, "b", wpas_dbus_getter_process_credentials, - wpas_dbus_setter_process_credentials + wpas_dbus_setter_process_credentials, + NULL }, { "ConfigMethods", WPAS_DBUS_NEW_IFACE_WPS, "s", wpas_dbus_getter_config_methods, - wpas_dbus_setter_config_methods + wpas_dbus_setter_config_methods, + NULL }, #endif /* CONFIG_WPS */ #ifdef CONFIG_P2P { "P2PDeviceConfig", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "a{sv}", wpas_dbus_getter_p2p_device_config, - wpas_dbus_setter_p2p_device_config + wpas_dbus_setter_p2p_device_config, + NULL }, { "Peers", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "ao", wpas_dbus_getter_p2p_peers, + NULL, NULL }, { "Role", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "s", wpas_dbus_getter_p2p_role, + NULL, NULL }, { "Group", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "o", wpas_dbus_getter_p2p_group, + NULL, NULL }, { "PeerGO", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "o", wpas_dbus_getter_p2p_peergo, + NULL, NULL }, { "PersistentGroups", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "ao", wpas_dbus_getter_persistent_groups, + NULL, NULL }, #endif /* CONFIG_P2P */ { "DisconnectReason", WPAS_DBUS_NEW_IFACE_INTERFACE, "i", wpas_dbus_getter_disconnect_reason, + NULL, + NULL + }, + { "AssocStatusCode", WPAS_DBUS_NEW_IFACE_INTERFACE, "i", + wpas_dbus_getter_assoc_status_code, + NULL, NULL }, - { NULL, NULL, NULL, NULL, NULL } + { NULL, NULL, NULL, NULL, NULL, NULL } }; static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = { @@ -3206,6 +3357,13 @@ static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = { END_ARGS } }, + { "DeviceFoundProperties", 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 }, @@ -3390,6 +3548,77 @@ static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = { }; +static int wpa_dbus_ctrl_iface_props_init(struct wpas_dbus_priv *priv) +{ + size_t all_size; + unsigned int i, j, count, num_const, num_globals; + const char *global_name; + static const char * const ignored_globals[] = { + "bss_expiration_age", "bss_expiration_scan_count", + "ap_scan", "country", "fast_reauth", + "pkcs11_engine_path", "pkcs11_module_path" + }; + + /* wpas_dbus_interface_properties terminates with a NULL element */ + num_const = ARRAY_SIZE(wpas_dbus_interface_properties) - 1; + + num_globals = wpa_config_get_num_global_field_names(); + priv->globals_start = num_const; + + /* allocate enough for all properties + terminating NULL element */ + all_size = (num_globals + num_const + 1) * + sizeof(wpas_dbus_interface_properties[0]); + priv->all_interface_properties = os_zalloc(all_size); + if (!priv->all_interface_properties) { + wpa_printf(MSG_ERROR, + "dbus: Not enough memory for interface properties"); + return -1; + } + + /* Copy constant interface properties to the start of the array */ + os_memcpy(priv->all_interface_properties, + wpas_dbus_interface_properties, + sizeof(wpas_dbus_interface_properties)); + + /* Dynamically construct interface global properties */ + for (i = 0, count = num_const; i < num_globals; i++) { + struct wpa_dbus_property_desc *desc; + int no_var = 0; + + /* ignore globals that are actually just methods */ + global_name = wpa_config_get_global_field_name(i, &no_var); + if (no_var) + continue; + /* Ignore fields already explicitly exposed */ + for (j = 0; j < ARRAY_SIZE(ignored_globals); j++) { + if (os_strcmp(global_name, ignored_globals[j]) == 0) + break; + } + if (j < ARRAY_SIZE(ignored_globals)) + continue; + + desc = &priv->all_interface_properties[count++]; + desc->dbus_property = uscore_to_dbus(global_name); + if (!desc->dbus_property) { + wpa_printf(MSG_ERROR, + "dbus: Not enough memory for D-Bus property name"); + goto error; + } + desc->dbus_interface = WPAS_DBUS_NEW_IFACE_INTERFACE; + desc->type = "s"; + desc->getter = wpas_dbus_getter_iface_global; + desc->setter = wpas_dbus_setter_iface_global; + desc->data = global_name; + } + + return 0; + +error: + wpa_dbus_ctrl_iface_props_deinit(priv); + return -1; +} + + /** * wpas_dbus_register_interface - Register an interface with D-Bus * @wpa_s: wpa_supplicant interface structure @@ -3397,7 +3626,6 @@ static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = { */ int wpas_dbus_register_interface(struct wpa_supplicant *wpa_s) { - struct wpa_dbus_object_desc *obj_desc = NULL; struct wpas_dbus_priv *ctrl_iface = wpa_s->global->dbus; int next; @@ -3423,7 +3651,7 @@ int wpas_dbus_register_interface(struct wpa_supplicant *wpa_s) } wpas_dbus_register(obj_desc, wpa_s, NULL, wpas_dbus_interface_methods, - wpas_dbus_interface_properties, + ctrl_iface->all_interface_properties, wpas_dbus_interface_signals); wpa_printf(MSG_DEBUG, "dbus: Register interface object '%s'", @@ -3489,65 +3717,80 @@ int wpas_dbus_unregister_interface(struct wpa_supplicant *wpa_s) 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, NULL }, { "Manufacturer", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s", wpas_dbus_getter_p2p_peer_manufacturer, + NULL, NULL }, { "ModelName", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s", wpas_dbus_getter_p2p_peer_modelname, + NULL, NULL }, { "ModelNumber", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s", wpas_dbus_getter_p2p_peer_modelnumber, + NULL, NULL }, { "SerialNumber", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s", wpas_dbus_getter_p2p_peer_serialnumber, + NULL, NULL }, { "PrimaryDeviceType", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ay", wpas_dbus_getter_p2p_peer_primary_device_type, + NULL, NULL }, { "config_method", WPAS_DBUS_NEW_IFACE_P2P_PEER, "q", wpas_dbus_getter_p2p_peer_config_method, + NULL, NULL }, { "level", WPAS_DBUS_NEW_IFACE_P2P_PEER, "i", wpas_dbus_getter_p2p_peer_level, + NULL, NULL }, { "devicecapability", WPAS_DBUS_NEW_IFACE_P2P_PEER, "y", wpas_dbus_getter_p2p_peer_device_capability, + NULL, NULL }, { "groupcapability", WPAS_DBUS_NEW_IFACE_P2P_PEER, "y", wpas_dbus_getter_p2p_peer_group_capability, + NULL, NULL }, { "SecondaryDeviceTypes", WPAS_DBUS_NEW_IFACE_P2P_PEER, "aay", wpas_dbus_getter_p2p_peer_secondary_device_types, + NULL, NULL }, { "VendorExtension", WPAS_DBUS_NEW_IFACE_P2P_PEER, "aay", wpas_dbus_getter_p2p_peer_vendor_extension, + NULL, NULL }, { "IEs", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ay", wpas_dbus_getter_p2p_peer_ies, + NULL, NULL }, { "DeviceAddress", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ay", wpas_dbus_getter_p2p_peer_device_address, + NULL, NULL }, { "Groups", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ao", wpas_dbus_getter_p2p_peer_groups, + NULL, NULL }, - { NULL, NULL, NULL, NULL, NULL } + { NULL, NULL, NULL, NULL, NULL, NULL } }; static const struct wpa_dbus_signal_desc wpas_dbus_p2p_peer_signals[] = { @@ -3569,12 +3812,13 @@ static const struct wpa_dbus_signal_desc wpas_dbus_p2p_peer_signals[] = { * In case of peer objects, it would be emitted by either * the "interface object" or by "peer objects" * @sig_name: signal name - DeviceFound + * @properties: Whether to add a second argument with object properties * - * Notify listeners about event related with newly found p2p peer device + * Notify listeners about event related with 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) + const char *sig_name, int properties) { struct wpas_dbus_priv *iface; DBusMessage *msg; @@ -3602,7 +3846,10 @@ static void wpas_dbus_signal_peer(struct wpa_supplicant *wpa_s, dbus_message_iter_init_append(msg, &iter); path = peer_obj_path; if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, - &path)) + &path) || + (properties && !wpa_dbus_get_object_properties( + iface, peer_obj_path, WPAS_DBUS_NEW_IFACE_P2P_PEER, + &iter))) wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); else dbus_connection_send(iface->con, msg, NULL); @@ -3623,7 +3870,11 @@ void wpas_dbus_signal_peer_device_found(struct wpa_supplicant *wpa_s, { wpas_dbus_signal_peer(wpa_s, dev_addr, WPAS_DBUS_NEW_IFACE_P2PDEVICE, - "DeviceFound"); + "DeviceFound", FALSE); + + wpas_dbus_signal_peer(wpa_s, dev_addr, + WPAS_DBUS_NEW_IFACE_P2PDEVICE, + "DeviceFoundProperties", TRUE); } /** @@ -3638,7 +3889,7 @@ void wpas_dbus_signal_peer_device_lost(struct wpa_supplicant *wpa_s, { wpas_dbus_signal_peer(wpa_s, dev_addr, WPAS_DBUS_NEW_IFACE_P2PDEVICE, - "DeviceLost"); + "DeviceLost", FALSE); } /** @@ -3805,41 +4056,50 @@ void wpas_dbus_signal_peer_groups_changed(struct wpa_supplicant *wpa_s, 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, NULL }, { "Group", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "o", wpas_dbus_getter_p2p_group, + NULL, NULL }, { "Role", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "s", wpas_dbus_getter_p2p_role, + NULL, NULL }, { "SSID", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ay", wpas_dbus_getter_p2p_group_ssid, + NULL, NULL }, { "BSSID", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ay", wpas_dbus_getter_p2p_group_bssid, + NULL, NULL }, { "Frequency", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "q", wpas_dbus_getter_p2p_group_frequency, + NULL, NULL }, { "Passphrase", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "s", wpas_dbus_getter_p2p_group_passphrase, + NULL, NULL }, { "PSK", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ay", wpas_dbus_getter_p2p_group_psk, + NULL, NULL }, { "WPSVendorExtensions", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "aay", wpas_dbus_getter_p2p_group_vendor_ext, - wpas_dbus_setter_p2p_group_vendor_ext + wpas_dbus_setter_p2p_group_vendor_ext, + NULL }, - { NULL, NULL, NULL, NULL, NULL } + { NULL, NULL, NULL, NULL, NULL, NULL } }; static const struct wpa_dbus_signal_desc wpas_dbus_p2p_group_signals[] = { @@ -3966,9 +4226,10 @@ 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 + wpas_dbus_setter_persistent_group_properties, + NULL }, - { NULL, NULL, NULL, NULL, NULL } + { NULL, NULL, NULL, NULL, NULL, NULL } }; /* No signals intended for persistent group objects */ diff --git a/wpa_supplicant/dbus/dbus_new.h b/wpa_supplicant/dbus/dbus_new.h index 6d240fffce786..d64fceef718c9 100644 --- a/wpa_supplicant/dbus/dbus_new.h +++ b/wpa_supplicant/dbus/dbus_new.h @@ -29,6 +29,7 @@ enum wpas_dbus_prop { WPAS_DBUS_PROP_CURRENT_AUTH_MODE, WPAS_DBUS_PROP_BSSS, WPAS_DBUS_PROP_DISCONNECT_REASON, + WPAS_DBUS_PROP_ASSOC_STATUS_CODE, }; enum wpas_dbus_bss_prop { @@ -189,8 +190,7 @@ void wpas_dbus_signal_p2p_go_neg_req(struct wpa_supplicant *wpa_s, const u8 *src, u16 dev_passwd_id, u8 go_intent); void wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s, - const struct wpa_ssid *ssid, - int client, int network_id); + int client, int persistent); void wpas_dbus_signal_p2p_group_formation_failure(struct wpa_supplicant *wpa_s, const char *reason); void wpas_dbus_register_p2p_group(struct wpa_supplicant *wpa_s, @@ -400,8 +400,7 @@ static inline void wpas_dbus_signal_p2p_go_neg_req(struct wpa_supplicant *wpa_s, static inline void wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s, - const struct wpa_ssid *ssid, - int client, int network_id) + int client, int persistent) { } diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c index 67562a547172f..e11dd36ca23c7 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers.c +++ b/wpa_supplicant/dbus/dbus_new_handlers.c @@ -435,7 +435,8 @@ dbus_bool_t wpas_dbus_simple_array_property_getter(DBusMessageIter *iter, for (i = 0; i < array_len; i++) { if (!dbus_message_iter_append_basic(&array_iter, type, - array + i * element_size)) { + (const char *) array + + i * element_size)) { dbus_set_error(error, DBUS_ERROR_FAILED, "%s: failed to construct message 2.5", __func__); @@ -711,9 +712,9 @@ DBusMessage * wpas_dbus_handler_get_interface(DBusMessage *message, * * Getter for "DebugLevel" property. */ -dbus_bool_t wpas_dbus_getter_debug_level(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_debug_level( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { const char *str; int idx = wpa_debug_level; @@ -737,9 +738,9 @@ dbus_bool_t wpas_dbus_getter_debug_level(DBusMessageIter *iter, * * Getter for "DebugTimestamp" property. */ -dbus_bool_t wpas_dbus_getter_debug_timestamp(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_debug_timestamp( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN, &wpa_debug_timestamp, error); @@ -756,9 +757,9 @@ dbus_bool_t wpas_dbus_getter_debug_timestamp(DBusMessageIter *iter, * * Getter for "DebugShowKeys" property. */ -dbus_bool_t wpas_dbus_getter_debug_show_keys(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_debug_show_keys( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN, &wpa_debug_show_keys, error); @@ -774,8 +775,9 @@ dbus_bool_t wpas_dbus_getter_debug_show_keys(DBusMessageIter *iter, * * Setter for "DebugLevel" property. */ -dbus_bool_t wpas_dbus_setter_debug_level(DBusMessageIter *iter, - DBusError *error, void *user_data) +dbus_bool_t wpas_dbus_setter_debug_level( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_global *global = user_data; const char *str = NULL; @@ -812,9 +814,9 @@ dbus_bool_t wpas_dbus_setter_debug_level(DBusMessageIter *iter, * * Setter for "DebugTimestamp" property. */ -dbus_bool_t wpas_dbus_setter_debug_timestamp(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_setter_debug_timestamp( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_global *global = user_data; dbus_bool_t val; @@ -838,9 +840,9 @@ dbus_bool_t wpas_dbus_setter_debug_timestamp(DBusMessageIter *iter, * * Setter for "DebugShowKeys" property. */ -dbus_bool_t wpas_dbus_setter_debug_show_keys(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_setter_debug_show_keys( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_global *global = user_data; dbus_bool_t val; @@ -867,9 +869,9 @@ dbus_bool_t wpas_dbus_setter_debug_show_keys(DBusMessageIter *iter, * by dbus clients to return list of registered interfaces objects * paths */ -dbus_bool_t wpas_dbus_getter_interfaces(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_interfaces( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_global *global = user_data; struct wpa_supplicant *wpa_s; @@ -912,8 +914,9 @@ dbus_bool_t wpas_dbus_getter_interfaces(DBusMessageIter *iter, * Getter for "EapMethods" property. Handles requests * by dbus clients to return list of strings with supported EAP methods */ -dbus_bool_t wpas_dbus_getter_eap_methods(DBusMessageIter *iter, - DBusError *error, void *user_data) +dbus_bool_t wpas_dbus_getter_eap_methods( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { char **eap_methods; size_t num_items = 0; @@ -948,9 +951,9 @@ dbus_bool_t wpas_dbus_getter_eap_methods(DBusMessageIter *iter, * 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) +dbus_bool_t wpas_dbus_getter_global_capabilities( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { const char *capabilities[5] = { NULL, NULL, NULL, NULL, NULL }; size_t num_items = 0; @@ -1472,10 +1475,7 @@ DBusMessage * wpas_dbus_handler_disconnect(DBusMessage *message, struct wpa_supplicant *wpa_s) { if (wpa_s->current_ssid != NULL) { - wpa_s->disconnected = 1; - wpa_supplicant_deauthenticate(wpa_s, - WLAN_REASON_DEAUTH_LEAVING); - + wpas_request_disconnection(wpa_s); return NULL; } @@ -1504,7 +1504,7 @@ DBusMessage * wpas_dbus_handler_add_network(DBusMessage *message, dbus_message_iter_init(message, &iter); if (wpa_s->dbus_new_path) - ssid = wpa_config_add_network(wpa_s->conf); + ssid = wpa_supplicant_add_network(wpa_s); if (ssid == NULL) { wpa_printf(MSG_ERROR, "%s[dbus]: can't add new interface.", __func__); @@ -1513,9 +1513,6 @@ DBusMessage * wpas_dbus_handler_add_network(DBusMessage *message, "wpa_supplicant could not add a network on this interface."); goto err; } - wpas_notify_network_added(wpa_s, ssid); - ssid->disabled = 1; - wpa_config_set_network_defaults(ssid); dbus_error_init(&error); if (!set_network_properties(wpa_s, ssid, &iter, &error)) { @@ -1580,6 +1577,27 @@ DBusMessage * wpas_dbus_handler_reassociate(DBusMessage *message, /** + * wpas_dbus_handler_expect_disconnect - ExpectDisconnect + * @message: Pointer to incoming dbus message + * @global: %wpa_supplicant global data structure + * Returns: NULL + * + * Handler function for notifying system there will be a expected disconnect. + * This will prevent wpa_supplicant from adding blacklists upon next disconnect.. + */ +DBusMessage * wpas_dbus_handler_expect_disconnect(DBusMessage *message, + struct wpa_global *global) +{ + struct wpa_supplicant *wpa_s = global->ifaces; + + for (; wpa_s; wpa_s = wpa_s->next) + if (wpa_s->wpa_state >= WPA_ASSOCIATED) + wpa_s->own_disconnect_req = 1; + return NULL; +} + + +/** * wpas_dbus_handler_reattach - Reattach to current AP * @message: Pointer to incoming dbus message * @wpa_s: wpa_supplicant structure for a network interface @@ -1641,8 +1659,7 @@ DBusMessage * wpas_dbus_handler_remove_network(DBusMessage *message, const char *op; char *iface, *net_id; int id; - struct wpa_ssid *ssid; - int was_disabled; + int result; dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &op, DBUS_TYPE_INVALID); @@ -1665,27 +1682,12 @@ DBusMessage * wpas_dbus_handler_remove_network(DBusMessage *message, goto out; } - ssid = wpa_config_get_network(wpa_s->conf, id); - if (ssid == NULL) { + result = wpa_supplicant_remove_network(wpa_s, id); + if (result == -1) { reply = wpas_dbus_error_network_unknown(message); goto out; } - - was_disabled = ssid->disabled; - - wpas_notify_network_removed(wpa_s, ssid); - - if (ssid == wpa_s->current_ssid) - wpa_supplicant_deauthenticate(wpa_s, - WLAN_REASON_DEAUTH_LEAVING); - else if (!was_disabled && wpa_s->sched_scanning) { - wpa_printf(MSG_DEBUG, - "Stop ongoing sched_scan to remove network from filters"); - wpa_supplicant_cancel_sched_scan(wpa_s); - wpa_supplicant_req_scan(wpa_s, 0, 0); - } - - if (wpa_config_remove_network(wpa_s->conf, id) < 0) { + if (result == -2) { wpa_printf(MSG_ERROR, "%s[dbus]: error occurred when removing network %d", __func__, id); @@ -1854,7 +1856,7 @@ out: os_free(iface); return reply; #else /* IEEE8021X_EAPOL */ - wpa_printf(MSG_DEBUG, "CTRL_IFACE: 802.1X not included"); + wpa_printf(MSG_DEBUG, "dbus: 802.1X not included"); return wpas_dbus_error_unknown_error(message, "802.1X not included"); #endif /* IEEE8021X_EAPOL */ } @@ -2271,6 +2273,35 @@ DBusMessage * wpas_dbus_handler_tdls_teardown(DBusMessage *message, #endif /* CONFIG_TDLS */ +#ifndef CONFIG_NO_CONFIG_WRITE +/** + * wpas_dbus_handler_save_config - Save configuration to configuration file + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: NULL on Success, Otherwise errror message + * + * Handler function for "SaveConfig" method call of network interface. + */ +DBusMessage * wpas_dbus_handler_save_config(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + int ret; + + if (!wpa_s->conf->update_config) { + return wpas_dbus_error_unknown_error( + message, + "Not allowed to update configuration (update_config=0)"); + } + + ret = wpa_config_write(wpa_s->confname, wpa_s->conf); + if (ret) + return wpas_dbus_error_unknown_error( + message, "Failed to update configuration"); + return NULL; +} +#endif /* CONFIG_NO_CONFIG_WRITE */ + + /** * wpas_dbus_handler_set_pkcs11_engine_and_module_path - Set PKCS #11 engine and module path * @message: Pointer to incoming dbus message @@ -2338,8 +2369,9 @@ DBusMessage * wpas_dbus_handler_set_pkcs11_engine_and_module_path( * * Getter for "Capabilities" property of an interface. */ -dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter, - DBusError *error, void *user_data) +dbus_bool_t wpas_dbus_getter_capabilities( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; struct wpa_driver_capa capa; @@ -2585,12 +2617,14 @@ dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter, &iter_array) || !wpa_dbus_dict_string_array_add_element( &iter_array, "infrastructure") || - !wpa_dbus_dict_string_array_add_element( - &iter_array, "ad-hoc") || + (res >= 0 && (capa.flags & WPA_DRIVER_FLAGS_IBSS) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "ad-hoc")) || (res >= 0 && (capa.flags & WPA_DRIVER_FLAGS_AP) && !wpa_dbus_dict_string_array_add_element( &iter_array, "ap")) || (res >= 0 && (capa.flags & WPA_DRIVER_FLAGS_P2P_CAPABLE) && + !wpa_s->conf->p2p_disabled && !wpa_dbus_dict_string_array_add_element( &iter_array, "p2p")) || !wpa_dbus_dict_end_string_array(&iter_dict, @@ -2629,8 +2663,9 @@ nomem: * * Getter for "State" property. */ -dbus_bool_t wpas_dbus_getter_state(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_state( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; const char *str_state; @@ -2669,8 +2704,9 @@ dbus_bool_t wpas_dbus_getter_state(DBusMessageIter *iter, DBusError *error, * * Getter for "scanning" property. */ -dbus_bool_t wpas_dbus_getter_scanning(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_scanning( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; dbus_bool_t scanning = wpa_s->scanning ? TRUE : FALSE; @@ -2689,8 +2725,9 @@ dbus_bool_t wpas_dbus_getter_scanning(DBusMessageIter *iter, DBusError *error, * * Getter function for "ApScan" property. */ -dbus_bool_t wpas_dbus_getter_ap_scan(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_ap_scan( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; dbus_uint32_t ap_scan = wpa_s->conf->ap_scan; @@ -2709,8 +2746,9 @@ dbus_bool_t wpas_dbus_getter_ap_scan(DBusMessageIter *iter, DBusError *error, * * Setter function for "ApScan" property. */ -dbus_bool_t wpas_dbus_setter_ap_scan(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_setter_ap_scan( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; dbus_uint32_t ap_scan; @@ -2738,9 +2776,9 @@ dbus_bool_t wpas_dbus_setter_ap_scan(DBusMessageIter *iter, DBusError *error, * * Getter function for "FastReauth" property. */ -dbus_bool_t wpas_dbus_getter_fast_reauth(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_fast_reauth( + const struct wpa_dbus_property_desc *property_desc, + 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; @@ -2760,9 +2798,9 @@ dbus_bool_t wpas_dbus_getter_fast_reauth(DBusMessageIter *iter, * * Setter function for "FastReauth" property. */ -dbus_bool_t wpas_dbus_setter_fast_reauth(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_setter_fast_reauth( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; dbus_bool_t fast_reauth; @@ -2786,9 +2824,9 @@ dbus_bool_t wpas_dbus_setter_fast_reauth(DBusMessageIter *iter, * 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) +dbus_bool_t wpas_dbus_getter_disconnect_reason( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; dbus_int32_t reason = wpa_s->disconnect_reason; @@ -2799,6 +2837,27 @@ dbus_bool_t wpas_dbus_getter_disconnect_reason(DBusMessageIter *iter, /** + * wpas_dbus_getter_assoc_status_code - Get most recent failed assoc status 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 for "AssocStatusCode" property. + */ +dbus_bool_t wpas_dbus_getter_assoc_status_code( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + dbus_int32_t status_code = wpa_s->assoc_status_code; + + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_INT32, + &status_code, 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 @@ -2807,9 +2866,9 @@ dbus_bool_t wpas_dbus_getter_disconnect_reason(DBusMessageIter *iter, * * Getter function for "BSSExpireAge" property. */ -dbus_bool_t wpas_dbus_getter_bss_expire_age(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_bss_expire_age( + const struct wpa_dbus_property_desc *property_desc, + 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; @@ -2828,9 +2887,9 @@ dbus_bool_t wpas_dbus_getter_bss_expire_age(DBusMessageIter *iter, * * Setter function for "BSSExpireAge" property. */ -dbus_bool_t wpas_dbus_setter_bss_expire_age(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_setter_bss_expire_age( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; dbus_uint32_t expire_age; @@ -2857,9 +2916,9 @@ dbus_bool_t wpas_dbus_setter_bss_expire_age(DBusMessageIter *iter, * * Getter function for "BSSExpireCount" property. */ -dbus_bool_t wpas_dbus_getter_bss_expire_count(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_bss_expire_count( + const struct wpa_dbus_property_desc *property_desc, + 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; @@ -2878,9 +2937,9 @@ dbus_bool_t wpas_dbus_getter_bss_expire_count(DBusMessageIter *iter, * * Setter function for "BSSExpireCount" property. */ -dbus_bool_t wpas_dbus_setter_bss_expire_count(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_setter_bss_expire_count( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; dbus_uint32_t expire_count; @@ -2907,8 +2966,9 @@ dbus_bool_t wpas_dbus_setter_bss_expire_count(DBusMessageIter *iter, * * Getter function for "Country" property. */ -dbus_bool_t wpas_dbus_getter_country(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_country( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; char country[3]; @@ -2932,8 +2992,9 @@ dbus_bool_t wpas_dbus_getter_country(DBusMessageIter *iter, DBusError *error, * * Setter function for "Country" property. */ -dbus_bool_t wpas_dbus_setter_country(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_setter_country( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; const char *country; @@ -2970,9 +3031,9 @@ dbus_bool_t wpas_dbus_setter_country(DBusMessageIter *iter, DBusError *error, * * Getter function for "ScanInterval" property. */ -dbus_bool_t wpas_dbus_getter_scan_interval(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_scan_interval( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; dbus_int32_t scan_interval = wpa_s->scan_interval; @@ -2991,9 +3052,9 @@ dbus_bool_t wpas_dbus_getter_scan_interval(DBusMessageIter *iter, * * Setter function for "ScanInterval" property. */ -dbus_bool_t wpas_dbus_setter_scan_interval(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_setter_scan_interval( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; dbus_int32_t scan_interval; @@ -3020,8 +3081,9 @@ dbus_bool_t wpas_dbus_setter_scan_interval(DBusMessageIter *iter, * * Getter for "Ifname" property. */ -dbus_bool_t wpas_dbus_getter_ifname(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_ifname( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; const char *ifname = wpa_s->ifname; @@ -3040,8 +3102,9 @@ dbus_bool_t wpas_dbus_getter_ifname(DBusMessageIter *iter, DBusError *error, * * Getter for "Driver" property. */ -dbus_bool_t wpas_dbus_getter_driver(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_driver( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; const char *driver; @@ -3069,9 +3132,9 @@ dbus_bool_t wpas_dbus_getter_driver(DBusMessageIter *iter, DBusError *error, * * Getter for "CurrentBSS" property. */ -dbus_bool_t wpas_dbus_getter_current_bss(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_current_bss( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *bss_obj_path = path_buf; @@ -3097,9 +3160,9 @@ dbus_bool_t wpas_dbus_getter_current_bss(DBusMessageIter *iter, * * Getter for "CurrentNetwork" property. */ -dbus_bool_t wpas_dbus_getter_current_network(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_current_network( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *net_obj_path = path_buf; @@ -3125,9 +3188,9 @@ dbus_bool_t wpas_dbus_getter_current_network(DBusMessageIter *iter, * * Getter for "CurrentAuthMode" property. */ -dbus_bool_t wpas_dbus_getter_current_auth_mode(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_current_auth_mode( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; const char *eap_mode; @@ -3143,9 +3206,11 @@ dbus_bool_t wpas_dbus_getter_current_auth_mode(DBusMessageIter *iter, "EAP-%s", eap_mode); auth_mode = eap_mode_buf; - } else { + } else if (wpa_s->current_ssid) { auth_mode = wpa_key_mgmt_txt(wpa_s->key_mgmt, wpa_s->current_ssid->proto); + } else { + auth_mode = "UNKNOWN"; } return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, @@ -3162,9 +3227,9 @@ dbus_bool_t wpas_dbus_getter_current_auth_mode(DBusMessageIter *iter, * * Getter for "BridgeIfname" property. */ -dbus_bool_t wpas_dbus_getter_bridge_ifname(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_bridge_ifname( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; const char *bridge_ifname = wpa_s->bridge_ifname; @@ -3175,6 +3240,30 @@ dbus_bool_t wpas_dbus_getter_bridge_ifname(DBusMessageIter *iter, /** + * wpas_dbus_getter_config_file - Get interface configuration file path + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Getter for "ConfigFile" property. + */ +dbus_bool_t wpas_dbus_getter_config_file( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + char *confname = ""; + + if (wpa_s->confname) + confname = wpa_s->confname; + + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, + &confname, error); +} + + +/** * wpas_dbus_getter_bsss - Get array of BSSs objects * @iter: Pointer to incoming dbus message iter * @error: Location to store error on failure @@ -3183,8 +3272,9 @@ dbus_bool_t wpas_dbus_getter_bridge_ifname(DBusMessageIter *iter, * * Getter for "BSSs" property. */ -dbus_bool_t wpas_dbus_getter_bsss(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_bsss( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; struct wpa_bss *bss; @@ -3240,8 +3330,9 @@ out: * * Getter for "Networks" property. */ -dbus_bool_t wpas_dbus_getter_networks(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_networks( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; struct wpa_ssid *ssid; @@ -3303,9 +3394,9 @@ out: * * Getter for "PKCS11EnginePath" property. */ -dbus_bool_t wpas_dbus_getter_pkcs11_engine_path(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_pkcs11_engine_path( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; const char *pkcs11_engine_path; @@ -3328,9 +3419,9 @@ dbus_bool_t wpas_dbus_getter_pkcs11_engine_path(DBusMessageIter *iter, * * Getter for "PKCS11ModulePath" property. */ -dbus_bool_t wpas_dbus_getter_pkcs11_module_path(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_pkcs11_module_path( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; const char *pkcs11_module_path; @@ -3353,8 +3444,9 @@ dbus_bool_t wpas_dbus_getter_pkcs11_module_path(DBusMessageIter *iter, * * Getter for "Blobs" property. */ -dbus_bool_t wpas_dbus_getter_blobs(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_blobs( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; DBusMessageIter variant_iter, dict_iter, entry_iter, array_iter; @@ -3406,6 +3498,79 @@ dbus_bool_t wpas_dbus_getter_blobs(DBusMessageIter *iter, DBusError *error, } +dbus_bool_t wpas_dbus_getter_iface_global( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + int ret; + char buf[250]; + char *p = buf; + + if (!property_desc->data) { + dbus_set_error(error, DBUS_ERROR_INVALID_ARGS, + "Unhandled interface property %s", + property_desc->dbus_property); + return FALSE; + } + + ret = wpa_config_get_value(property_desc->data, wpa_s->conf, buf, + sizeof(buf)); + if (ret < 0) + *p = '\0'; + + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &p, + error); +} + + +dbus_bool_t wpas_dbus_setter_iface_global( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + const char *new_value = NULL; + char buf[250]; + size_t combined_len; + int ret; + + if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_STRING, + &new_value)) + return FALSE; + + combined_len = os_strlen(property_desc->data) + os_strlen(new_value) + + 3; + if (combined_len >= sizeof(buf)) { + dbus_set_error(error, DBUS_ERROR_INVALID_ARGS, + "Interface property %s value too large", + property_desc->dbus_property); + return FALSE; + } + + if (!new_value[0]) + new_value = "NULL"; + + ret = os_snprintf(buf, combined_len, "%s=%s", property_desc->data, + new_value); + if (os_snprintf_error(combined_len, ret)) { + dbus_set_error(error, WPAS_DBUS_ERROR_UNKNOWN_ERROR, + "Failed to construct new interface property %s", + property_desc->dbus_property); + return FALSE; + } + + if (wpa_config_process_global(wpa_s->conf, buf, -1)) { + dbus_set_error(error, DBUS_ERROR_INVALID_ARGS, + "Failed to set interface property %s", + property_desc->dbus_property); + return FALSE; + } + + wpa_supplicant_update_config(wpa_s); + return TRUE; +} + + static struct wpa_bss * get_bss_helper(struct bss_handler_args *args, DBusError *error, const char *func_name) { @@ -3432,8 +3597,9 @@ static struct wpa_bss * get_bss_helper(struct bss_handler_args *args, * * Getter for "BSSID" property. */ -dbus_bool_t wpas_dbus_getter_bss_bssid(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_bss_bssid( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct bss_handler_args *args = user_data; struct wpa_bss *res; @@ -3457,8 +3623,9 @@ dbus_bool_t wpas_dbus_getter_bss_bssid(DBusMessageIter *iter, DBusError *error, * * Getter for "SSID" property. */ -dbus_bool_t wpas_dbus_getter_bss_ssid(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_bss_ssid( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct bss_handler_args *args = user_data; struct wpa_bss *res; @@ -3482,8 +3649,9 @@ dbus_bool_t wpas_dbus_getter_bss_ssid(DBusMessageIter *iter, DBusError *error, * * Getter for "Privacy" property. */ -dbus_bool_t wpas_dbus_getter_bss_privacy(DBusMessageIter *iter, - DBusError *error, void *user_data) +dbus_bool_t wpas_dbus_getter_bss_privacy( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct bss_handler_args *args = user_data; struct wpa_bss *res; @@ -3508,8 +3676,9 @@ dbus_bool_t wpas_dbus_getter_bss_privacy(DBusMessageIter *iter, * * Getter for "Mode" property. */ -dbus_bool_t wpas_dbus_getter_bss_mode(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_bss_mode( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct bss_handler_args *args = user_data; struct wpa_bss *res; @@ -3549,8 +3718,9 @@ dbus_bool_t wpas_dbus_getter_bss_mode(DBusMessageIter *iter, DBusError *error, * * Getter for "Level" property. */ -dbus_bool_t wpas_dbus_getter_bss_signal(DBusMessageIter *iter, - DBusError *error, void *user_data) +dbus_bool_t wpas_dbus_getter_bss_signal( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct bss_handler_args *args = user_data; struct wpa_bss *res; @@ -3575,8 +3745,9 @@ dbus_bool_t wpas_dbus_getter_bss_signal(DBusMessageIter *iter, * * Getter for "Frequency" property. */ -dbus_bool_t wpas_dbus_getter_bss_frequency(DBusMessageIter *iter, - DBusError *error, void *user_data) +dbus_bool_t wpas_dbus_getter_bss_frequency( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct bss_handler_args *args = user_data; struct wpa_bss *res; @@ -3607,8 +3778,9 @@ static int cmp_u8s_desc(const void *a, const void *b) * * Getter for "Rates" property. */ -dbus_bool_t wpas_dbus_getter_bss_rates(DBusMessageIter *iter, - DBusError *error, void *user_data) +dbus_bool_t wpas_dbus_getter_bss_rates( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct bss_handler_args *args = user_data; struct wpa_bss *res; @@ -3647,9 +3819,9 @@ dbus_bool_t wpas_dbus_getter_bss_rates(DBusMessageIter *iter, } -static dbus_bool_t wpas_dbus_get_bss_security_prop(DBusMessageIter *iter, - struct wpa_ie_data *ie_data, - DBusError *error) +static dbus_bool_t wpas_dbus_get_bss_security_prop( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, struct wpa_ie_data *ie_data, DBusError *error) { DBusMessageIter iter_dict, variant_iter; const char *group; @@ -3780,8 +3952,9 @@ nomem: * * Getter for "WPA" property. */ -dbus_bool_t wpas_dbus_getter_bss_wpa(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_bss_wpa( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct bss_handler_args *args = user_data; struct wpa_bss *res; @@ -3800,7 +3973,7 @@ dbus_bool_t wpas_dbus_getter_bss_wpa(DBusMessageIter *iter, DBusError *error, return FALSE; } - return wpas_dbus_get_bss_security_prop(iter, &wpa_data, error); + return wpas_dbus_get_bss_security_prop(property_desc, iter, &wpa_data, error); } @@ -3813,8 +3986,9 @@ dbus_bool_t wpas_dbus_getter_bss_wpa(DBusMessageIter *iter, DBusError *error, * * Getter for "RSN" property. */ -dbus_bool_t wpas_dbus_getter_bss_rsn(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_bss_rsn( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct bss_handler_args *args = user_data; struct wpa_bss *res; @@ -3833,7 +4007,7 @@ dbus_bool_t wpas_dbus_getter_bss_rsn(DBusMessageIter *iter, DBusError *error, return FALSE; } - return wpas_dbus_get_bss_security_prop(iter, &wpa_data, error); + return wpas_dbus_get_bss_security_prop(property_desc, iter, &wpa_data, error); } @@ -3846,8 +4020,9 @@ dbus_bool_t wpas_dbus_getter_bss_rsn(DBusMessageIter *iter, DBusError *error, * * Getter for "WPS" property. */ -dbus_bool_t wpas_dbus_getter_bss_wps(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_bss_wps( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct bss_handler_args *args = user_data; struct wpa_bss *res; @@ -3902,8 +4077,9 @@ nomem: * * Getter for "IEs" property. */ -dbus_bool_t wpas_dbus_getter_bss_ies(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_bss_ies( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct bss_handler_args *args = user_data; struct wpa_bss *res; @@ -3927,8 +4103,9 @@ dbus_bool_t wpas_dbus_getter_bss_ies(DBusMessageIter *iter, DBusError *error, * * Getter for BSS age */ -dbus_bool_t wpas_dbus_getter_bss_age(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_bss_age( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct bss_handler_args *args = user_data; struct wpa_bss *res; @@ -3956,8 +4133,9 @@ dbus_bool_t wpas_dbus_getter_bss_age(DBusMessageIter *iter, DBusError *error, * * Getter for "enabled" property of a configured network. */ -dbus_bool_t wpas_dbus_getter_enabled(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_enabled( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct network_handler_args *net = user_data; dbus_bool_t enabled = net->ssid->disabled ? FALSE : TRUE; @@ -3976,8 +4154,9 @@ dbus_bool_t wpas_dbus_getter_enabled(DBusMessageIter *iter, DBusError *error, * * Setter for "Enabled" property of a configured network. */ -dbus_bool_t wpas_dbus_setter_enabled(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_setter_enabled( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct network_handler_args *net = user_data; struct wpa_supplicant *wpa_s; @@ -4009,9 +4188,9 @@ dbus_bool_t wpas_dbus_setter_enabled(DBusMessageIter *iter, DBusError *error, * * Getter for "Properties" property of a configured network. */ -dbus_bool_t wpas_dbus_getter_network_properties(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_network_properties( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct network_handler_args *net = user_data; DBusMessageIter variant_iter, dict_iter; @@ -4071,9 +4250,9 @@ out: * * Setter for "Properties" property of a configured network. */ -dbus_bool_t wpas_dbus_setter_network_properties(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_setter_network_properties( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct network_handler_args *net = user_data; struct wpa_ssid *ssid = net->ssid; @@ -4211,3 +4390,147 @@ out: } #endif /* CONFIG_AP */ + + +DBusMessage * wpas_dbus_handler_vendor_elem_add(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + u8 *ielems; + int len; + struct ieee802_11_elems elems; + dbus_int32_t frame_id; + DBusMessageIter iter, array; + + dbus_message_iter_init(message, &iter); + dbus_message_iter_get_basic(&iter, &frame_id); + if (frame_id < 0 || frame_id >= NUM_VENDOR_ELEM_FRAMES) { + return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, + "Invalid ID"); + } + + dbus_message_iter_next(&iter); + dbus_message_iter_recurse(&iter, &array); + dbus_message_iter_get_fixed_array(&array, &ielems, &len); + if (!ielems || len == 0) { + return dbus_message_new_error( + message, DBUS_ERROR_INVALID_ARGS, "Invalid value"); + } + + if (ieee802_11_parse_elems(ielems, len, &elems, 0) == ParseFailed) { + return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, + "Parse error"); + } + + wpa_s = wpas_vendor_elem(wpa_s, frame_id); + if (!wpa_s->vendor_elem[frame_id]) { + wpa_s->vendor_elem[frame_id] = wpabuf_alloc_copy(ielems, len); + wpas_vendor_elem_update(wpa_s); + return NULL; + } + + if (wpabuf_resize(&wpa_s->vendor_elem[frame_id], len) < 0) { + return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, + "Resize error"); + } + + wpabuf_put_data(wpa_s->vendor_elem[frame_id], ielems, len); + wpas_vendor_elem_update(wpa_s); + return NULL; +} + + +DBusMessage * wpas_dbus_handler_vendor_elem_get(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply; + DBusMessageIter iter, array_iter; + dbus_int32_t frame_id; + const u8 *elem; + size_t elem_len; + + dbus_message_iter_init(message, &iter); + dbus_message_iter_get_basic(&iter, &frame_id); + + if (frame_id < 0 || frame_id >= NUM_VENDOR_ELEM_FRAMES) { + return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, + "Invalid ID"); + } + + wpa_s = wpas_vendor_elem(wpa_s, frame_id); + if (!wpa_s->vendor_elem[frame_id]) { + return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, + "ID value does not exist"); + } + + reply = dbus_message_new_method_return(message); + if (!reply) + return wpas_dbus_error_no_memory(message); + + dbus_message_iter_init_append(reply, &iter); + + elem = wpabuf_head_u8(wpa_s->vendor_elem[frame_id]); + elem_len = wpabuf_len(wpa_s->vendor_elem[frame_id]); + + if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + DBUS_TYPE_BYTE_AS_STRING, + &array_iter) || + !dbus_message_iter_append_fixed_array(&array_iter, DBUS_TYPE_BYTE, + &elem, elem_len) || + !dbus_message_iter_close_container(&iter, &array_iter)) { + dbus_message_unref(reply); + reply = wpas_dbus_error_no_memory(message); + } + + return reply; +} + + +DBusMessage * wpas_dbus_handler_vendor_elem_remove(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + u8 *ielems; + int len; + struct ieee802_11_elems elems; + DBusMessageIter iter, array; + dbus_int32_t frame_id; + + dbus_message_iter_init(message, &iter); + dbus_message_iter_get_basic(&iter, &frame_id); + if (frame_id < 0 || frame_id >= NUM_VENDOR_ELEM_FRAMES) { + return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, + "Invalid ID"); + } + + dbus_message_iter_next(&iter); + dbus_message_iter_recurse(&iter, &array); + dbus_message_iter_get_fixed_array(&array, &ielems, &len); + if (!ielems || len == 0) { + return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, + "Invalid value"); + } + + wpa_s = wpas_vendor_elem(wpa_s, frame_id); + + if (len == 1 && *ielems == '*') { + wpabuf_free(wpa_s->vendor_elem[frame_id]); + wpa_s->vendor_elem[frame_id] = NULL; + wpas_vendor_elem_update(wpa_s); + return NULL; + } + + if (!wpa_s->vendor_elem[frame_id]) { + return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, + "ID value does not exist"); + } + + if (ieee802_11_parse_elems(ielems, len, &elems, 0) == ParseFailed) { + return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, + "Parse error"); + } + + if (wpas_vendor_elem_remove(wpa_s, frame_id, ielems, len) == 0) + return NULL; + + return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, + "Not found"); +} diff --git a/wpa_supplicant/dbus/dbus_new_handlers.h b/wpa_supplicant/dbus/dbus_new_handlers.h index 50f72ec507bf3..1d6235d6f3e4c 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers.h +++ b/wpa_supplicant/dbus/dbus_new_handlers.h @@ -10,6 +10,8 @@ #ifndef CTRL_IFACE_DBUS_NEW_HANDLERS_H #define CTRL_IFACE_DBUS_NEW_HANDLERS_H +#include "dbus_new_helpers.h" + struct network_handler_args { struct wpa_supplicant *wpa_s; struct wpa_ssid *ssid; @@ -50,39 +52,20 @@ DBusMessage * wpas_dbus_handler_remove_interface(DBusMessage *message, DBusMessage * wpas_dbus_handler_get_interface(DBusMessage *message, struct wpa_global *global); -dbus_bool_t wpas_dbus_getter_debug_level(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_debug_timestamp(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_debug_show_keys(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_setter_debug_level(DBusMessageIter *iter, - DBusError *error, void *user_data); - -dbus_bool_t wpas_dbus_setter_debug_timestamp(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_setter_debug_show_keys(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_interfaces(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_eap_methods(DBusMessageIter *iter, - DBusError *error, void *user_data); - -dbus_bool_t wpas_dbus_getter_global_capabilities(DBusMessageIter *iter, - DBusError *error, - void *user_data); +DBusMessage * wpas_dbus_handler_expect_disconnect(DBusMessage *message, + struct wpa_global *global); + +DECLARE_ACCESSOR(wpas_dbus_getter_debug_level); +DECLARE_ACCESSOR(wpas_dbus_getter_debug_timestamp); +DECLARE_ACCESSOR(wpas_dbus_getter_debug_show_keys); +DECLARE_ACCESSOR(wpas_dbus_setter_debug_level); +DECLARE_ACCESSOR(wpas_dbus_setter_debug_timestamp); +DECLARE_ACCESSOR(wpas_dbus_setter_debug_show_keys); +DECLARE_ACCESSOR(wpas_dbus_getter_interfaces); +DECLARE_ACCESSOR(wpas_dbus_getter_eap_methods); +DECLARE_ACCESSOR(wpas_dbus_getter_global_capabilities); +DECLARE_ACCESSOR(wpas_dbus_getter_iface_global); +DECLARE_ACCESSOR(wpas_dbus_setter_iface_global); DBusMessage * wpas_dbus_handler_scan(DBusMessage *message, struct wpa_supplicant *wpa_s); @@ -146,150 +129,52 @@ DBusMessage * wpas_dbus_handler_eap_logoff(DBusMessage *message, DBusMessage * wpas_dbus_handler_eap_logon(DBusMessage *message, struct wpa_supplicant *wpa_s); -dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter, - DBusError *error, void *user_data); - -dbus_bool_t wpas_dbus_getter_state(DBusMessageIter *iter, DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_scanning(DBusMessageIter *iter, DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_ap_scan(DBusMessageIter *iter, DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_setter_ap_scan(DBusMessageIter *iter, DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_fast_reauth(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_setter_fast_reauth(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_disconnect_reason(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_bss_expire_age(DBusMessageIter *iter, - DBusError *error, void *user_data); - -dbus_bool_t wpas_dbus_setter_bss_expire_age(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_bss_expire_count(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_setter_bss_expire_count(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_country(DBusMessageIter *iter, DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_setter_country(DBusMessageIter *iter, DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_scan_interval(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_setter_scan_interval(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_ifname(DBusMessageIter *iter, DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_driver(DBusMessageIter *iter, DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_bridge_ifname(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_current_bss(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_current_network(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_current_auth_mode(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_bsss(DBusMessageIter *iter, DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_networks(DBusMessageIter *iter, DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_pkcs11_engine_path(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_pkcs11_module_path(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_blobs(DBusMessageIter *iter, DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_bss_bssid(DBusMessageIter *iter, DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_bss_ssid(DBusMessageIter *iter, DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_bss_privacy(DBusMessageIter *iter, - DBusError *error, void *user_data); - -dbus_bool_t wpas_dbus_getter_bss_mode(DBusMessageIter *iter, DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_bss_signal(DBusMessageIter *iter, - DBusError *error, void *user_data); - -dbus_bool_t wpas_dbus_getter_bss_frequency(DBusMessageIter *iter, - DBusError *error, void *user_data); - -dbus_bool_t wpas_dbus_getter_bss_rates(DBusMessageIter *iter, - DBusError *error, void *user_data); - -dbus_bool_t wpas_dbus_getter_bss_wpa(DBusMessageIter *iter, DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_bss_rsn(DBusMessageIter *iter, DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_bss_wps(DBusMessageIter *iter, DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_bss_ies(DBusMessageIter *iter, DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_bss_age(DBusMessageIter *iter, DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_enabled(DBusMessageIter *iter, DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_setter_enabled(DBusMessageIter *iter, DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_network_properties(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_setter_network_properties(DBusMessageIter *iter, - DBusError *error, - void *user_data); +DECLARE_ACCESSOR(wpas_dbus_getter_capabilities); +DECLARE_ACCESSOR(wpas_dbus_getter_state); +DECLARE_ACCESSOR(wpas_dbus_getter_scanning); +DECLARE_ACCESSOR(wpas_dbus_getter_ap_scan); +DECLARE_ACCESSOR(wpas_dbus_setter_ap_scan); +DECLARE_ACCESSOR(wpas_dbus_getter_fast_reauth); +DECLARE_ACCESSOR(wpas_dbus_setter_fast_reauth); +DECLARE_ACCESSOR(wpas_dbus_getter_disconnect_reason); +DECLARE_ACCESSOR(wpas_dbus_getter_disassociate_reason); +DECLARE_ACCESSOR(wpas_dbus_getter_assoc_status_code); +DECLARE_ACCESSOR(wpas_dbus_getter_bss_expire_age); +DECLARE_ACCESSOR(wpas_dbus_setter_bss_expire_age); +DECLARE_ACCESSOR(wpas_dbus_getter_bss_expire_count); +DECLARE_ACCESSOR(wpas_dbus_setter_bss_expire_count); +DECLARE_ACCESSOR(wpas_dbus_getter_country); +DECLARE_ACCESSOR(wpas_dbus_setter_country); +DECLARE_ACCESSOR(wpas_dbus_getter_scan_interval); +DECLARE_ACCESSOR(wpas_dbus_setter_scan_interval); +DECLARE_ACCESSOR(wpas_dbus_getter_ifname); +DECLARE_ACCESSOR(wpas_dbus_getter_driver); +DECLARE_ACCESSOR(wpas_dbus_getter_bridge_ifname); +DECLARE_ACCESSOR(wpas_dbus_getter_config_file); +DECLARE_ACCESSOR(wpas_dbus_getter_current_bss); +DECLARE_ACCESSOR(wpas_dbus_getter_current_network); +DECLARE_ACCESSOR(wpas_dbus_getter_current_auth_mode); +DECLARE_ACCESSOR(wpas_dbus_getter_bsss); +DECLARE_ACCESSOR(wpas_dbus_getter_networks); +DECLARE_ACCESSOR(wpas_dbus_getter_pkcs11_engine_path); +DECLARE_ACCESSOR(wpas_dbus_getter_pkcs11_module_path); +DECLARE_ACCESSOR(wpas_dbus_getter_blobs); +DECLARE_ACCESSOR(wpas_dbus_getter_bss_bssid); +DECLARE_ACCESSOR(wpas_dbus_getter_bss_ssid); +DECLARE_ACCESSOR(wpas_dbus_getter_bss_privacy); +DECLARE_ACCESSOR(wpas_dbus_getter_bss_mode); +DECLARE_ACCESSOR(wpas_dbus_getter_bss_signal); +DECLARE_ACCESSOR(wpas_dbus_getter_bss_frequency); +DECLARE_ACCESSOR(wpas_dbus_getter_bss_rates); +DECLARE_ACCESSOR(wpas_dbus_getter_bss_wpa); +DECLARE_ACCESSOR(wpas_dbus_getter_bss_rsn); +DECLARE_ACCESSOR(wpas_dbus_getter_bss_wps); +DECLARE_ACCESSOR(wpas_dbus_getter_bss_ies); +DECLARE_ACCESSOR(wpas_dbus_getter_bss_age); +DECLARE_ACCESSOR(wpas_dbus_getter_enabled); +DECLARE_ACCESSOR(wpas_dbus_setter_enabled); +DECLARE_ACCESSOR(wpas_dbus_getter_network_properties); +DECLARE_ACCESSOR(wpas_dbus_setter_network_properties); DBusMessage * wpas_dbus_handler_wps_start(DBusMessage *message, struct wpa_supplicant *wpa_s); @@ -297,20 +182,10 @@ DBusMessage * wpas_dbus_handler_wps_start(DBusMessage *message, DBusMessage * wpas_dbus_handler_wps_cancel(DBusMessage *message, struct wpa_supplicant *wpa_s); -dbus_bool_t wpas_dbus_getter_process_credentials(DBusMessageIter *iter, - DBusError *error, void *user_data); - -dbus_bool_t wpas_dbus_setter_process_credentials(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_config_methods(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_setter_config_methods(DBusMessageIter *iter, - DBusError *error, - void *user_data); +DECLARE_ACCESSOR(wpas_dbus_getter_process_credentials); +DECLARE_ACCESSOR(wpas_dbus_setter_process_credentials); +DECLARE_ACCESSOR(wpas_dbus_getter_config_methods); +DECLARE_ACCESSOR(wpas_dbus_setter_config_methods); DBusMessage * wpas_dbus_handler_tdls_discover(DBusMessage *message, struct wpa_supplicant *wpa_s); @@ -321,6 +196,16 @@ DBusMessage * wpas_dbus_handler_tdls_status(DBusMessage *message, DBusMessage * wpas_dbus_handler_tdls_teardown(DBusMessage *message, struct wpa_supplicant *wpa_s); +DBusMessage * wpas_dbus_handler_vendor_elem_add(DBusMessage *message, + struct wpa_supplicant *wpa_s); +DBusMessage * wpas_dbus_handler_vendor_elem_get(DBusMessage *message, + struct wpa_supplicant *wpa_s); +DBusMessage * wpas_dbus_handler_vendor_elem_remove( + DBusMessage *message, struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_handler_save_config(DBusMessage *message, + struct wpa_supplicant *wpa_s); + DBusMessage * wpas_dbus_error_invalid_args(DBusMessage *message, const char *arg); DBusMessage * wpas_dbus_error_unknown_error(DBusMessage *message, diff --git a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c index 67c079e7506d7..73b9e20c20b04 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c +++ b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c @@ -364,13 +364,14 @@ DBusMessage * wpas_dbus_handler_p2p_group_add(DBusMessage *message, goto inv_args; if (wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0, 0, 0, - NULL, 0, 0)) { + 0, 0, NULL, 0, 0)) { reply = wpas_dbus_error_unknown_error( message, "Failed to reinvoke a persistent group"); goto out; } - } else if (wpas_p2p_group_add(wpa_s, persistent_group, freq, 0, 0)) + } else if (wpas_p2p_group_add(wpa_s, persistent_group, freq, 0, 0, 0, + 0)) goto inv_args; out: @@ -582,7 +583,7 @@ DBusMessage * wpas_dbus_handler_p2p_connect(DBusMessage *message, new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method, persistent_group, 0, join, authorize_only, - go_intent, freq, -1, 0, 0, 0); + go_intent, freq, 0, -1, 0, 0, 0, 0, NULL, 0); if (new_pin >= 0) { char npin[9]; @@ -733,8 +734,8 @@ DBusMessage * wpas_dbus_handler_p2p_invite(DBusMessage *message, if (ssid == NULL || ssid->disabled != 2) goto err; - if (wpas_p2p_invite(wpa_s, peer_addr, ssid, NULL, 0, 0, 0, 0) < - 0) { + if (wpas_p2p_invite(wpa_s, peer_addr, ssid, NULL, 0, 0, 0, 0, 0, + 0) < 0) { reply = wpas_dbus_error_unknown_error( message, "Failed to reinvoke a persistent group"); @@ -807,9 +808,9 @@ DBusMessage * wpas_dbus_handler_p2p_prov_disc_req(DBusMessage *message, * P2P Device property accessor methods. */ -dbus_bool_t wpas_dbus_getter_p2p_device_config(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_p2p_device_config( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; DBusMessageIter variant_iter, dict_iter; @@ -916,9 +917,9 @@ err_no_mem: } -dbus_bool_t wpas_dbus_setter_p2p_device_config(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_setter_p2p_device_config( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; DBusMessageIter variant_iter, iter_dict; @@ -944,7 +945,8 @@ dbus_bool_t wpas_dbus_setter_p2p_device_config(DBusMessageIter *iter, if (os_strcmp(entry.key, "DeviceName") == 0) { char *devname; - if (entry.type != DBUS_TYPE_STRING) + if (entry.type != DBUS_TYPE_STRING || + os_strlen(entry.str_value) > WPS_DEV_NAME_MAX_LEN) goto error; devname = os_strdup(entry.str_value); @@ -1087,8 +1089,9 @@ dbus_bool_t wpas_dbus_setter_p2p_device_config(DBusMessageIter *iter, } -dbus_bool_t wpas_dbus_getter_p2p_peers(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_p2p_peers( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; struct p2p_data *p2p = wpa_s->global->p2p; @@ -1201,8 +1204,9 @@ static enum wpas_p2p_role wpas_get_p2p_role(struct wpa_supplicant *wpa_s) } -dbus_bool_t wpas_dbus_getter_p2p_role(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_p2p_role( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; char *str; @@ -1224,8 +1228,9 @@ dbus_bool_t wpas_dbus_getter_p2p_role(DBusMessageIter *iter, DBusError *error, } -dbus_bool_t wpas_dbus_getter_p2p_group(DBusMessageIter *iter, DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_p2p_group( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; char path_buf[WPAS_DBUS_OBJECT_PATH_MAX]; @@ -1243,8 +1248,9 @@ dbus_bool_t wpas_dbus_getter_p2p_group(DBusMessageIter *iter, DBusError *error, } -dbus_bool_t wpas_dbus_getter_p2p_peergo(DBusMessageIter *iter, - DBusError *error, void *user_data) +dbus_bool_t wpas_dbus_getter_p2p_peergo( + const struct wpa_dbus_property_desc *property_desc, + 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; @@ -1271,9 +1277,9 @@ dbus_bool_t wpas_dbus_getter_p2p_peergo(DBusMessageIter *iter, * Peer object properties accessor methods */ -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_device_name( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; @@ -1309,9 +1315,9 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_device_name(DBusMessageIter *iter, } -dbus_bool_t wpas_dbus_getter_p2p_peer_manufacturer(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_p2p_peer_manufacturer( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; @@ -1346,9 +1352,9 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_manufacturer(DBusMessageIter *iter, } -dbus_bool_t wpas_dbus_getter_p2p_peer_modelname(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_p2p_peer_modelname( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; @@ -1383,9 +1389,9 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_modelname(DBusMessageIter *iter, } -dbus_bool_t wpas_dbus_getter_p2p_peer_modelnumber(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_p2p_peer_modelnumber( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; @@ -1420,9 +1426,9 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_modelnumber(DBusMessageIter *iter, } -dbus_bool_t wpas_dbus_getter_p2p_peer_serialnumber(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_p2p_peer_serialnumber( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; @@ -1458,6 +1464,7 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_serialnumber(DBusMessageIter *iter, dbus_bool_t wpas_dbus_getter_p2p_peer_primary_device_type( + const struct wpa_dbus_property_desc *property_desc, DBusMessageIter *iter, DBusError *error, void *user_data) { struct peer_handler_args *peer_args = user_data; @@ -1483,9 +1490,9 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_primary_device_type( } -dbus_bool_t wpas_dbus_getter_p2p_peer_config_method(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_p2p_peer_config_method( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; @@ -1508,9 +1515,9 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_config_method(DBusMessageIter *iter, } -dbus_bool_t wpas_dbus_getter_p2p_peer_level(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_p2p_peer_level( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; @@ -1533,9 +1540,9 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_level(DBusMessageIter *iter, } -dbus_bool_t wpas_dbus_getter_p2p_peer_device_capability(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_p2p_peer_device_capability( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; @@ -1558,9 +1565,9 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_device_capability(DBusMessageIter *iter, } -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_group_capability( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; @@ -1584,6 +1591,7 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_group_capability(DBusMessageIter *iter, dbus_bool_t wpas_dbus_getter_p2p_peer_secondary_device_types( + const struct wpa_dbus_property_desc *property_desc, DBusMessageIter *iter, DBusError *error, void *user_data) { struct peer_handler_args *peer_args = user_data; @@ -1649,9 +1657,9 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_secondary_device_types( } -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_vendor_extension( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpabuf *vendor_extension[P2P_MAX_WPS_VENDOR_EXT]; unsigned int i, num = 0; @@ -1684,8 +1692,9 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_vendor_extension(DBusMessageIter *iter, } -dbus_bool_t wpas_dbus_getter_p2p_peer_ies(DBusMessageIter *iter, - DBusError *error, void *user_data) +dbus_bool_t wpas_dbus_getter_p2p_peer_ies( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; @@ -1709,9 +1718,9 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_ies(DBusMessageIter *iter, } -dbus_bool_t wpas_dbus_getter_p2p_peer_device_address(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_p2p_peer_device_address( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; @@ -1774,9 +1783,9 @@ out_of_memory: } -dbus_bool_t wpas_dbus_getter_p2p_peer_groups(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_p2p_peer_groups( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; @@ -1842,9 +1851,9 @@ out: * * Getter for "PersistentGroups" property. */ -dbus_bool_t wpas_dbus_getter_persistent_groups(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_persistent_groups( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; struct wpa_ssid *ssid; @@ -1904,16 +1913,16 @@ out: * * Getter for "Properties" property of a persistent group. */ -dbus_bool_t wpas_dbus_getter_persistent_group_properties(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_persistent_group_properties( + const struct wpa_dbus_property_desc *property_desc, + 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); + return wpas_dbus_getter_network_properties(property_desc, iter, error, net); } @@ -1927,9 +1936,9 @@ dbus_bool_t wpas_dbus_getter_persistent_group_properties(DBusMessageIter *iter, * * Setter for "Properties" property of a persistent group. */ -dbus_bool_t wpas_dbus_setter_persistent_group_properties(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_setter_persistent_group_properties( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct network_handler_args *net = user_data; struct wpa_ssid *ssid = net->ssid; @@ -2142,9 +2151,9 @@ DBusMessage * wpas_dbus_handler_remove_all_persistent_groups( * Group object properties accessor methods */ -dbus_bool_t wpas_dbus_getter_p2p_group_members(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_p2p_group_members( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; struct wpa_ssid *ssid; @@ -2211,8 +2220,9 @@ out_of_memory: } -dbus_bool_t wpas_dbus_getter_p2p_group_ssid(DBusMessageIter *iter, - DBusError *error, void *user_data) +dbus_bool_t wpas_dbus_getter_p2p_group_ssid( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; @@ -2224,9 +2234,9 @@ dbus_bool_t wpas_dbus_getter_p2p_group_ssid(DBusMessageIter *iter, } -dbus_bool_t wpas_dbus_getter_p2p_group_bssid(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_p2p_group_bssid( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; u8 role = wpas_get_p2p_role(wpa_s); @@ -2248,9 +2258,9 @@ dbus_bool_t wpas_dbus_getter_p2p_group_bssid(DBusMessageIter *iter, } -dbus_bool_t wpas_dbus_getter_p2p_group_frequency(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_p2p_group_frequency( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; u16 op_freq; @@ -2271,9 +2281,9 @@ dbus_bool_t wpas_dbus_getter_p2p_group_frequency(DBusMessageIter *iter, } -dbus_bool_t wpas_dbus_getter_p2p_group_passphrase(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_p2p_group_passphrase( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; char *p_pass; @@ -2292,8 +2302,9 @@ dbus_bool_t wpas_dbus_getter_p2p_group_passphrase(DBusMessageIter *iter, } -dbus_bool_t wpas_dbus_getter_p2p_group_psk(DBusMessageIter *iter, - DBusError *error, void *user_data) +dbus_bool_t wpas_dbus_getter_p2p_group_psk( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; u8 *p_psk = NULL; @@ -2313,9 +2324,9 @@ dbus_bool_t wpas_dbus_getter_p2p_group_psk(DBusMessageIter *iter, } -dbus_bool_t wpas_dbus_getter_p2p_group_vendor_ext(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_p2p_group_vendor_ext( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; struct hostapd_data *hapd; @@ -2348,9 +2359,9 @@ dbus_bool_t wpas_dbus_getter_p2p_group_vendor_ext(DBusMessageIter *iter, } -dbus_bool_t wpas_dbus_setter_p2p_group_vendor_ext(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_setter_p2p_group_vendor_ext( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; DBusMessageIter variant_iter, iter_dict, array_iter, sub; @@ -2876,8 +2887,9 @@ DBusMessage * wpas_dbus_handler_p2p_serv_disc_external( #ifdef CONFIG_WIFI_DISPLAY -dbus_bool_t wpas_dbus_getter_global_wfd_ies(DBusMessageIter *iter, - DBusError *error, void *user_data) +dbus_bool_t wpas_dbus_getter_global_wfd_ies( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_global *global = user_data; struct wpabuf *ie; @@ -2898,8 +2910,9 @@ dbus_bool_t wpas_dbus_getter_global_wfd_ies(DBusMessageIter *iter, } -dbus_bool_t wpas_dbus_setter_global_wfd_ies(DBusMessageIter *iter, - DBusError *error, void *user_data) +dbus_bool_t wpas_dbus_setter_global_wfd_ies( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_global *global = user_data; DBusMessageIter variant, array; diff --git a/wpa_supplicant/dbus/dbus_new_handlers_p2p.h b/wpa_supplicant/dbus/dbus_new_handlers_p2p.h index 2aecbbe465070..c4c02615dbc3d 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers_p2p.h +++ b/wpa_supplicant/dbus/dbus_new_handlers_p2p.h @@ -89,139 +89,50 @@ DBusMessage *wpas_dbus_handler_p2p_serv_disc_external( /* * 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); +DECLARE_ACCESSOR(wpas_dbus_setter_p2p_device_config); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_device_config); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peers); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_role); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_group); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peergo); /* * P2P Peer properties. */ - -dbus_bool_t wpas_dbus_getter_p2p_peer_device_name(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_p2p_peer_manufacturer(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_p2p_peer_modelname(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_p2p_peer_modelnumber(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_p2p_peer_serialnumber(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_p2p_peer_primary_device_type( - DBusMessageIter *iter, DBusError *error, void *user_data); - -dbus_bool_t wpas_dbus_getter_p2p_peer_config_method(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_p2p_peer_level(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_p2p_peer_device_capability(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_p2p_peer_group_capability(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_p2p_peer_secondary_device_types( - DBusMessageIter *iter, DBusError *error, void *user_data); - -dbus_bool_t wpas_dbus_getter_p2p_peer_vendor_extension(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_p2p_peer_ies(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_p2p_peer_device_address(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_getter_p2p_peer_groups(DBusMessageIter *iter, - DBusError *error, - void *user_data); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_device_name); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_manufacturer); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_modelname); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_modelnumber); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_serialnumber); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_primary_device_type); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_config_method); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_level); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_device_capability); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_group_capability); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_secondary_device_types); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_vendor_extension); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_ies); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_device_address); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_groups); /* * 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); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_group_members); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_group_ssid); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_group_bssid); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_group_frequency); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_group_passphrase); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_group_psk); +DECLARE_ACCESSOR(wpas_dbus_getter_p2p_group_vendor_ext); +DECLARE_ACCESSOR(wpas_dbus_setter_p2p_group_vendor_ext); /* * 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); +DECLARE_ACCESSOR(wpas_dbus_getter_persistent_groups); +DECLARE_ACCESSOR(wpas_dbus_getter_persistent_group_properties); +DECLARE_ACCESSOR(wpas_dbus_setter_persistent_group_properties); DBusMessage * wpas_dbus_handler_add_persistent_group( DBusMessage *message, struct wpa_supplicant *wpa_s); @@ -233,15 +144,8 @@ DBusMessage * wpas_dbus_handler_remove_all_persistent_groups( DBusMessage *message, struct wpa_supplicant *wpa_s); #ifdef CONFIG_WIFI_DISPLAY - -dbus_bool_t wpas_dbus_getter_global_wfd_ies(DBusMessageIter *iter, - DBusError *error, - void *user_data); - -dbus_bool_t wpas_dbus_setter_global_wfd_ies(DBusMessageIter *iter, - DBusError *error, - void *user_data); - +DECLARE_ACCESSOR(wpas_dbus_getter_global_wfd_ies); +DECLARE_ACCESSOR(wpas_dbus_setter_global_wfd_ies); #endif /* CONFIG_WIFI_DISPLAY */ #endif /* DBUS_NEW_HANDLERS_P2P_H */ diff --git a/wpa_supplicant/dbus/dbus_new_handlers_wps.c b/wpa_supplicant/dbus/dbus_new_handlers_wps.c index b2251baa3fe5c..f16e2290c7ed4 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers_wps.c +++ b/wpa_supplicant/dbus/dbus_new_handlers_wps.c @@ -325,7 +325,7 @@ DBusMessage * wpas_dbus_handler_wps_start(DBusMessage *message, * @wpa_s: %wpa_supplicant data structure * Returns: NULL on success or DBus error on failure * - * Handler for "Cancel" method call. Returns NULL if WPS cancel successfull + * Handler for "Cancel" method call. Returns NULL if WPS cancel successful * or DBus error on WPS cancel failure */ DBusMessage * wpas_dbus_handler_wps_cancel(DBusMessage *message, @@ -349,9 +349,9 @@ DBusMessage * wpas_dbus_handler_wps_cancel(DBusMessage *message, * true if wps_cred_processing configuration field is not equal to 1 or false * if otherwise. */ -dbus_bool_t wpas_dbus_getter_process_credentials(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_process_credentials( + const struct wpa_dbus_property_desc *property_desc, + 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; @@ -371,9 +371,9 @@ dbus_bool_t wpas_dbus_getter_process_credentials(DBusMessageIter *iter, * Setter for "ProcessCredentials" property. Sets credentials_processed on 2 * if boolean argument is true or on 1 if otherwise. */ -dbus_bool_t wpas_dbus_setter_process_credentials(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_setter_process_credentials( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; dbus_bool_t process_credentials, old_pc; @@ -407,9 +407,9 @@ dbus_bool_t wpas_dbus_setter_process_credentials(DBusMessageIter *iter, * Getter for "ConfigMethods" property. Returned boolean will be true if * providing the relevant string worked, or false otherwise. */ -dbus_bool_t wpas_dbus_getter_config_methods(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_getter_config_methods( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; char *methods = wpa_s->conf->config_methods; @@ -431,9 +431,9 @@ dbus_bool_t wpas_dbus_getter_config_methods(DBusMessageIter *iter, * Setter for "ConfigMethods" property. Sets the methods string, apply such * change and returns true on success. Returns false otherwise. */ -dbus_bool_t wpas_dbus_setter_config_methods(DBusMessageIter *iter, - DBusError *error, - void *user_data) +dbus_bool_t wpas_dbus_setter_config_methods( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; char *methods, *new_methods; diff --git a/wpa_supplicant/dbus/dbus_new_helpers.c b/wpa_supplicant/dbus/dbus_new_helpers.c index 45623f3464655..0115e32a1d34b 100644 --- a/wpa_supplicant/dbus/dbus_new_helpers.c +++ b/wpa_supplicant/dbus/dbus_new_helpers.c @@ -46,7 +46,7 @@ static dbus_bool_t fill_dict_with_properties( goto error; /* An error getting a property fails the request entirely */ - if (!dsc->getter(&entry_iter, error, user_data)) { + if (!dsc->getter(dsc, &entry_iter, error, user_data)) { wpa_printf(MSG_INFO, "dbus: %s dbus_interface=%s dbus_property=%s getter failed", __func__, dsc->dbus_interface, @@ -176,7 +176,7 @@ static DBusMessage * properties_get(DBusMessage *message, dbus_message_iter_init_append(reply, &iter); dbus_error_init(&error); - if (dsc->getter(&iter, &error, user_data) == FALSE) { + if (dsc->getter(dsc, &iter, &error, user_data) == FALSE) { dbus_message_unref(reply); reply = wpas_dbus_reply_new_from_error( message, &error, DBUS_ERROR_FAILED, @@ -213,7 +213,7 @@ static DBusMessage * properties_set(DBusMessage *message, /* Iter will now point to the property's new value */ dbus_error_init(&error); - if (dsc->setter(&iter, &error, user_data) == TRUE) { + if (dsc->setter(dsc, &iter, &error, user_data) == TRUE) { /* Success */ reply = dbus_message_new_method_return(message); } else { @@ -627,7 +627,8 @@ static dbus_bool_t put_changed_properties( return FALSE; dbus_error_init(&error); - if (!dsc->getter(&entry_iter, &error, obj_dsc->user_data)) { + if (!dsc->getter(dsc, &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", diff --git a/wpa_supplicant/dbus/dbus_new_helpers.h b/wpa_supplicant/dbus/dbus_new_helpers.h index 6e2c1f1933f12..7b63b28d7707d 100644 --- a/wpa_supplicant/dbus/dbus_new_helpers.h +++ b/wpa_supplicant/dbus/dbus_new_helpers.h @@ -16,9 +16,13 @@ typedef DBusMessage * (*WPADBusMethodHandler)(DBusMessage *message, void *user_data); typedef void (*WPADBusArgumentFreeFunction)(void *handler_arg); -typedef dbus_bool_t (*WPADBusPropertyAccessor)(DBusMessageIter *iter, - DBusError *error, - void *user_data); +struct wpa_dbus_property_desc; +typedef dbus_bool_t (*WPADBusPropertyAccessor)( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data); +#define DECLARE_ACCESSOR(f) \ +dbus_bool_t f(const struct wpa_dbus_property_desc *property_desc, \ + DBusMessageIter *iter, DBusError *error, void *user_data) struct wpa_dbus_object_desc { DBusConnection *connection; @@ -89,6 +93,8 @@ struct wpa_dbus_property_desc { WPADBusPropertyAccessor getter; /* property setter function */ WPADBusPropertyAccessor setter; + /* other data */ + const char *data; }; diff --git a/wpa_supplicant/dbus/dbus_new_introspect.c b/wpa_supplicant/dbus/dbus_new_introspect.c index fba57e6361ae5..aee105b4b54c8 100644 --- a/wpa_supplicant/dbus/dbus_new_introspect.c +++ b/wpa_supplicant/dbus/dbus_new_introspect.c @@ -38,7 +38,7 @@ static struct interfaces * add_interface(struct dl_list *list, if (!iface) return NULL; iface->dbus_interface = os_strdup(dbus_interface); - iface->xml = wpabuf_alloc(6000); + iface->xml = wpabuf_alloc(15000); if (iface->dbus_interface == NULL || iface->xml == NULL) { os_free(iface->dbus_interface); wpabuf_free(iface->xml); @@ -257,7 +257,7 @@ DBusMessage * wpa_dbus_introspect(DBusMessage *message, DBusMessage *reply; struct wpabuf *xml; - xml = wpabuf_alloc(15000); + xml = wpabuf_alloc(20000); if (xml == NULL) return NULL; diff --git a/wpa_supplicant/dbus/dbus_old_handlers.c b/wpa_supplicant/dbus/dbus_old_handlers.c index e8f62ef6bdc3f..e540832f254bb 100644 --- a/wpa_supplicant/dbus/dbus_old_handlers.c +++ b/wpa_supplicant/dbus/dbus_old_handlers.c @@ -717,16 +717,13 @@ DBusMessage * wpas_dbus_iface_add_network(DBusMessage *message, char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *path = path_buf; if (wpa_s->dbus_path) - ssid = wpa_config_add_network(wpa_s->conf); + ssid = wpa_supplicant_add_network(wpa_s); if (ssid == NULL) { reply = dbus_message_new_error( message, WPAS_ERROR_ADD_NETWORK_ERROR, "wpa_supplicant could not add a network on this interface."); goto out; } - wpas_notify_network_added(wpa_s, ssid); - ssid->disabled = 1; - wpa_config_set_network_defaults(ssid); /* Construct the object path for this network. */ os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX, @@ -758,7 +755,7 @@ DBusMessage * wpas_dbus_iface_remove_network(DBusMessage *message, const char *op; char *iface = NULL, *net_id = NULL; int id; - struct wpa_ssid *ssid; + int result; if (!dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &op, @@ -781,19 +778,12 @@ DBusMessage * wpas_dbus_iface_remove_network(DBusMessage *message, } id = strtoul(net_id, NULL, 10); - ssid = wpa_config_get_network(wpa_s->conf, id); - if (ssid == NULL) { + result = wpa_supplicant_remove_network(wpa_s, id); + if (result == -1) { reply = wpas_dbus_new_invalid_network_error(message); goto out; } - - wpas_notify_network_removed(wpa_s, ssid); - - if (ssid == wpa_s->current_ssid) - wpa_supplicant_deauthenticate(wpa_s, - WLAN_REASON_DEAUTH_LEAVING); - - if (wpa_config_remove_network(wpa_s->conf, id) < 0) { + if (result == -2) { reply = dbus_message_new_error( message, WPAS_ERROR_REMOVE_NETWORK_ERROR, "error removing the specified on this interface."); @@ -1069,8 +1059,7 @@ out: DBusMessage * wpas_dbus_iface_disconnect(DBusMessage *message, struct wpa_supplicant *wpa_s) { - wpa_s->disconnected = 1; - wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); + wpas_request_disconnection(wpa_s); return wpas_dbus_new_success_reply(message); } diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig index 01a8c2ccb00b7..1d05198f849af 100644 --- a/wpa_supplicant/defconfig +++ b/wpa_supplicant/defconfig @@ -31,6 +31,9 @@ CONFIG_DRIVER_WEXT=y # Driver interface for Linux drivers using the nl80211 kernel interface CONFIG_DRIVER_NL80211=y +# QCA vendor extensions to nl80211 +#CONFIG_DRIVER_NL80211_QCA=y + # driver_nl80211.c requires libnl. If you are compiling it yourself # you may need to point hostapd to your version of libnl. # @@ -267,6 +270,9 @@ CONFIG_BACKEND=file # Should we use epoll instead of select? Select is used by default. #CONFIG_ELOOP_EPOLL=y +# Should we use kqueue instead of select? Select is used by default. +#CONFIG_ELOOP_KQUEUE=y + # Select layer 2 packet implementation # linux = Linux packet socket (default) # pcap = libpcap/libdnet/WinPcap @@ -276,6 +282,12 @@ CONFIG_BACKEND=file # none = Empty template #CONFIG_L2_PACKET=linux +# Disable Linux packet socket workaround applicable for station interface +# in a bridge for EAPOL frames. This should be uncommented only if the kernel +# is known to not have the regression issue in packet socket behavior with +# bridge interfaces (commit 'bridge: respect RFC2863 operational state')'). +#CONFIG_NO_LINUX_PACKET_SOCKET_WAR=y + # PeerKey handshake for Station to Station Link (IEEE 802.11e DLS) CONFIG_PEERKEY=y @@ -455,6 +467,9 @@ CONFIG_PEERKEY=y # Hotspot 2.0 #CONFIG_HS20=y +# Enable interface matching in wpa_supplicant +#CONFIG_MATCH_IFACE=y + # Disable roaming in wpa_supplicant #CONFIG_NO_ROAMING=y @@ -504,3 +519,32 @@ CONFIG_PEERKEY=y # OS X builds. This is only for building eapol_test. #CONFIG_OSX=y + +# Automatic Channel Selection +# This will allow wpa_supplicant to pick the channel automatically when channel +# is set to "0". +# +# TODO: Extend parser to be able to parse "channel=acs_survey" as an alternative +# to "channel=0". This would enable us to eventually add other ACS algorithms in +# similar way. +# +# Automatic selection is currently only done through initialization, later on +# we hope to do background checks to keep us moving to more ideal channels as +# time goes by. ACS is currently only supported through the nl80211 driver and +# your driver must have survey dump capability that is filled by the driver +# during scanning. +# +# TODO: In analogy to hostapd be able to customize the ACS survey algorithm with +# a newly to create wpa_supplicant.conf variable acs_num_scans. +# +# Supported ACS drivers: +# * ath9k +# * ath5k +# * ath10k +# +# For more details refer to: +# http://wireless.kernel.org/en/users/Documentation/acs +#CONFIG_ACS=y + +# Support Multi Band Operation +#CONFIG_MBO=y diff --git a/wpa_supplicant/doc/docbook/eapol_test.8 b/wpa_supplicant/doc/docbook/eapol_test.8 index 6361fc8eabb7d..af232826cb471 100644 --- a/wpa_supplicant/doc/docbook/eapol_test.8 +++ b/wpa_supplicant/doc/docbook/eapol_test.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 "EAPOL_TEST" "8" "27 September 2015" "" "" +.TH "EAPOL_TEST" "8" "02 October 2016" "" "" .SH NAME eapol_test \- EAP peer and RADIUS client testing @@ -115,7 +115,7 @@ Save configuration after authentication. \fBwpa_supplicant\fR(8) .SH "LEGAL" .PP -wpa_supplicant is copyright (c) 2003-2015, +wpa_supplicant is copyright (c) 2003-2016, Jouni Malinen <j@w1.fi> and contributors. All Rights Reserved. diff --git a/wpa_supplicant/doc/docbook/eapol_test.sgml b/wpa_supplicant/doc/docbook/eapol_test.sgml index e9af6d9617dfd..3f224133a3016 100644 --- a/wpa_supplicant/doc/docbook/eapol_test.sgml +++ b/wpa_supplicant/doc/docbook/eapol_test.sgml @@ -194,7 +194,7 @@ eapol_test -ctest.conf -a127.0.0.1 -p1812 -ssecret -r1 </refsect1> <refsect1> <title>Legal</title> - <para>wpa_supplicant is copyright (c) 2003-2015, + <para>wpa_supplicant is copyright (c) 2003-2016, Jouni Malinen <email>j@w1.fi</email> and contributors. All Rights Reserved.</para> diff --git a/wpa_supplicant/doc/docbook/wpa_background.8 b/wpa_supplicant/doc/docbook/wpa_background.8 index 8beced14a086c..9283266d405e1 100644 --- a/wpa_supplicant/doc/docbook/wpa_background.8 +++ b/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" "27 September 2015" "" "" +.TH "WPA_BACKGROUND" "8" "02 October 2016" "" "" .SH NAME wpa_background \- Background information on Wi-Fi Protected Access and IEEE 802.11i @@ -75,7 +75,7 @@ pre-authentication, and PMKSA caching). \fBwpa_supplicant\fR(8) .SH "LEGAL" .PP -wpa_supplicant is copyright (c) 2003-2015, +wpa_supplicant is copyright (c) 2003-2016, Jouni Malinen <j@w1.fi> and contributors. All Rights Reserved. diff --git a/wpa_supplicant/doc/docbook/wpa_background.sgml b/wpa_supplicant/doc/docbook/wpa_background.sgml index afb8c3b7d78c9..13c9f4514ff85 100644 --- a/wpa_supplicant/doc/docbook/wpa_background.sgml +++ b/wpa_supplicant/doc/docbook/wpa_background.sgml @@ -90,7 +90,7 @@ <refsect1> <title>Legal</title> - <para>wpa_supplicant is copyright (c) 2003-2015, + <para>wpa_supplicant is copyright (c) 2003-2016, Jouni Malinen <email>j@w1.fi</email> and contributors. All Rights Reserved.</para> diff --git a/wpa_supplicant/doc/docbook/wpa_cli.8 b/wpa_supplicant/doc/docbook/wpa_cli.8 index b00b266a54d79..440bfc5bdfccb 100644 --- a/wpa_supplicant/doc/docbook/wpa_cli.8 +++ b/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" "27 September 2015" "" "" +.TH "WPA_CLI" "8" "02 October 2016" "" "" .SH NAME wpa_cli \- WPA command line client @@ -210,7 +210,7 @@ exit wpa_cli \fBwpa_supplicant\fR(8) .SH "LEGAL" .PP -wpa_supplicant is copyright (c) 2003-2015, +wpa_supplicant is copyright (c) 2003-2016, Jouni Malinen <j@w1.fi> and contributors. All Rights Reserved. diff --git a/wpa_supplicant/doc/docbook/wpa_cli.sgml b/wpa_supplicant/doc/docbook/wpa_cli.sgml index 47947c1deef55..15400f04730a9 100644 --- a/wpa_supplicant/doc/docbook/wpa_cli.sgml +++ b/wpa_supplicant/doc/docbook/wpa_cli.sgml @@ -345,7 +345,7 @@ CTRL-REQ-OTP-2:Challenge 1235663 needed for SSID foobar </refsect1> <refsect1> <title>Legal</title> - <para>wpa_supplicant is copyright (c) 2003-2015, + <para>wpa_supplicant is copyright (c) 2003-2016, Jouni Malinen <email>j@w1.fi</email> and contributors. All Rights Reserved.</para> diff --git a/wpa_supplicant/doc/docbook/wpa_gui.8 b/wpa_supplicant/doc/docbook/wpa_gui.8 index 6284db29a1501..73bf362a8d46a 100644 --- a/wpa_supplicant/doc/docbook/wpa_gui.8 +++ b/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" "27 September 2015" "" "" +.TH "WPA_GUI" "8" "02 October 2016" "" "" .SH NAME wpa_gui \- WPA Graphical User Interface @@ -51,7 +51,7 @@ icon pop-up messages. \fBwpa_supplicant\fR(8) .SH "LEGAL" .PP -wpa_supplicant is copyright (c) 2003-2015, +wpa_supplicant is copyright (c) 2003-2016, Jouni Malinen <j@w1.fi> and contributors. All Rights Reserved. diff --git a/wpa_supplicant/doc/docbook/wpa_gui.sgml b/wpa_supplicant/doc/docbook/wpa_gui.sgml index 5f7b49da0bcc2..352d3d28cf5b7 100644 --- a/wpa_supplicant/doc/docbook/wpa_gui.sgml +++ b/wpa_supplicant/doc/docbook/wpa_gui.sgml @@ -91,7 +91,7 @@ </refsect1> <refsect1> <title>Legal</title> - <para>wpa_supplicant is copyright (c) 2003-2015, + <para>wpa_supplicant is copyright (c) 2003-2016, Jouni Malinen <email>j@w1.fi</email> and contributors. All Rights Reserved.</para> diff --git a/wpa_supplicant/doc/docbook/wpa_passphrase.8 b/wpa_supplicant/doc/docbook/wpa_passphrase.8 index 27cf1da94b650..ed0347af2c47a 100644 --- a/wpa_supplicant/doc/docbook/wpa_passphrase.8 +++ b/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" "27 September 2015" "" "" +.TH "WPA_PASSPHRASE" "8" "02 October 2016" "" "" .SH NAME wpa_passphrase \- Generate a WPA PSK from an ASCII passphrase for a SSID @@ -31,7 +31,7 @@ passphrase will be read from standard input. \fBwpa_supplicant\fR(8) .SH "LEGAL" .PP -wpa_supplicant is copyright (c) 2003-2015, +wpa_supplicant is copyright (c) 2003-2016, Jouni Malinen <j@w1.fi> and contributors. All Rights Reserved. diff --git a/wpa_supplicant/doc/docbook/wpa_passphrase.sgml b/wpa_supplicant/doc/docbook/wpa_passphrase.sgml index b381e4092e5b6..faf1f2799ea6a 100644 --- a/wpa_supplicant/doc/docbook/wpa_passphrase.sgml +++ b/wpa_supplicant/doc/docbook/wpa_passphrase.sgml @@ -62,7 +62,7 @@ </refsect1> <refsect1> <title>Legal</title> - <para>wpa_supplicant is copyright (c) 2003-2015, + <para>wpa_supplicant is copyright (c) 2003-2016, Jouni Malinen <email>j@w1.fi</email> and contributors. All Rights Reserved.</para> diff --git a/wpa_supplicant/doc/docbook/wpa_priv.8 b/wpa_supplicant/doc/docbook/wpa_priv.8 index a9c2b6a2ce72b..9a9fe82bbaeff 100644 --- a/wpa_supplicant/doc/docbook/wpa_priv.8 +++ b/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" "27 September 2015" "" "" +.TH "WPA_PRIV" "8" "02 October 2016" "" "" .SH NAME wpa_priv \- wpa_supplicant privilege separation helper @@ -111,7 +111,7 @@ processes at the same time, if desired. \fBwpa_supplicant\fR(8) .SH "LEGAL" .PP -wpa_supplicant is copyright (c) 2003-2015, +wpa_supplicant is copyright (c) 2003-2016, Jouni Malinen <j@w1.fi> and contributors. All Rights Reserved. diff --git a/wpa_supplicant/doc/docbook/wpa_priv.sgml b/wpa_supplicant/doc/docbook/wpa_priv.sgml index d13a5dbbfff5e..403c9b2d2f847 100644 --- a/wpa_supplicant/doc/docbook/wpa_priv.sgml +++ b/wpa_supplicant/doc/docbook/wpa_priv.sgml @@ -137,7 +137,7 @@ wpa_supplicant -i ath0 -c wpa_supplicant.conf </refsect1> <refsect1> <title>Legal</title> - <para>wpa_supplicant is copyright (c) 2003-2015, + <para>wpa_supplicant is copyright (c) 2003-2016, Jouni Malinen <email>j@w1.fi</email> and contributors. All Rights Reserved.</para> diff --git a/wpa_supplicant/doc/docbook/wpa_supplicant.8 b/wpa_supplicant/doc/docbook/wpa_supplicant.8 index ce973c5cebb1f..95ff5ff8c2a1c 100644 --- a/wpa_supplicant/doc/docbook/wpa_supplicant.8 +++ b/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" "27 September 2015" "" "" +.TH "WPA_SUPPLICANT" "8" "02 October 2016" "" "" .SH NAME wpa_supplicant \- Wi-Fi Protected Access client and IEEE 802.1X supplicant @@ -281,9 +281,6 @@ definitions may be omitted. \fB-K\fR Include keys (passwords, etc.) in debug output. .TP -\fB-t\fR -Include timestamp in debug messages. -.TP \fB-h\fR Help. Show a usage message. .TP @@ -535,7 +532,7 @@ in. \fBwpa_passphrase\fR(8) .SH "LEGAL" .PP -wpa_supplicant is copyright (c) 2003-2015, +wpa_supplicant is copyright (c) 2003-2016, Jouni Malinen <j@w1.fi> and contributors. All Rights Reserved. diff --git a/wpa_supplicant/doc/docbook/wpa_supplicant.conf.5 b/wpa_supplicant/doc/docbook/wpa_supplicant.conf.5 index 304ef238d72ea..f4c997a1077b8 100644 --- a/wpa_supplicant/doc/docbook/wpa_supplicant.conf.5 +++ b/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" "27 September 2015" "" "" +.TH "WPA_SUPPLICANT.CONF" "5" "02 October 2016" "" "" .SH NAME wpa_supplicant.conf \- configuration file for wpa_supplicant diff --git a/wpa_supplicant/doc/docbook/wpa_supplicant.sgml b/wpa_supplicant/doc/docbook/wpa_supplicant.sgml index 46c21b5c91a33..11e0e90d76527 100644 --- a/wpa_supplicant/doc/docbook/wpa_supplicant.sgml +++ b/wpa_supplicant/doc/docbook/wpa_supplicant.sgml @@ -377,13 +377,6 @@ </varlistentry> <varlistentry> - <term>-t</term> - <listitem> - <para>Include timestamp in debug messages.</para> - </listitem> - </varlistentry> - - <varlistentry> <term>-h</term> <listitem> <para>Help. Show a usage message.</para> @@ -736,7 +729,7 @@ fi </refsect1> <refsect1> <title>Legal</title> - <para>wpa_supplicant is copyright (c) 2003-2015, + <para>wpa_supplicant is copyright (c) 2003-2016, Jouni Malinen <email>j@w1.fi</email> and contributors. All Rights Reserved.</para> diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h index 73768c756f0a3..220b7ba3ddca6 100644 --- a/wpa_supplicant/driver_i.h +++ b/wpa_supplicant/driver_i.h @@ -100,12 +100,10 @@ static inline int wpa_drv_scan(struct wpa_supplicant *wpa_s, } static inline int wpa_drv_sched_scan(struct wpa_supplicant *wpa_s, - struct wpa_driver_scan_params *params, - u32 interval) + struct wpa_driver_scan_params *params) { if (wpa_s->driver->sched_scan) - return wpa_s->driver->sched_scan(wpa_s->drv_priv, - params, interval); + return wpa_s->driver->sched_scan(wpa_s->drv_priv, params); return -1; } @@ -160,6 +158,15 @@ static inline int wpa_drv_set_key(struct wpa_supplicant *wpa_s, return -1; } +static inline int wpa_drv_get_seqnum(struct wpa_supplicant *wpa_s, + const u8 *addr, int idx, u8 *seq) +{ + if (wpa_s->driver->get_seqnum) + return wpa_s->driver->get_seqnum(wpa_s->ifname, wpa_s->drv_priv, + addr, idx, seq); + return -1; +} + static inline int wpa_drv_sta_deauth(struct wpa_supplicant *wpa_s, const u8 *addr, int reason_code) { @@ -292,7 +299,7 @@ static inline int wpa_drv_send_mlme(struct wpa_supplicant *wpa_s, if (wpa_s->driver->send_mlme) return wpa_s->driver->send_mlme(wpa_s->drv_priv, data, data_len, noack, - freq); + freq, NULL, 0); return -1; } @@ -401,7 +408,7 @@ static inline int wpa_drv_if_add(struct wpa_supplicant *wpa_s, if (wpa_s->driver->if_add) return wpa_s->driver->if_add(wpa_s->drv_priv, type, ifname, addr, bss_ctx, NULL, force_ifname, - if_addr, bridge, 0); + if_addr, bridge, 0, 0); return -1; } @@ -726,12 +733,11 @@ static inline int wpa_drv_set_replay_protect(struct wpa_supplicant *wpa_s, } static inline int wpa_drv_set_current_cipher_suite(struct wpa_supplicant *wpa_s, - const u8 *cs, size_t cs_len) + u64 cs) { if (!wpa_s->driver->set_current_cipher_suite) return -1; - return wpa_s->driver->set_current_cipher_suite(wpa_s->drv_priv, cs, - cs_len); + return wpa_s->driver->set_current_cipher_suite(wpa_s->drv_priv, cs); } static inline int wpa_drv_enable_controlled_port(struct wpa_supplicant *wpa_s, @@ -912,4 +918,62 @@ static inline int wpa_drv_set_prob_oper_freq(struct wpa_supplicant *wpa_s, return wpa_s->driver->set_prob_oper_freq(wpa_s->drv_priv, freq); } +static inline int wpa_drv_abort_scan(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s->driver->abort_scan) + return -1; + return wpa_s->driver->abort_scan(wpa_s->drv_priv); +} + +static inline int wpa_drv_configure_frame_filters(struct wpa_supplicant *wpa_s, + u32 filters) +{ + if (!wpa_s->driver->configure_data_frame_filters) + return -1; + return wpa_s->driver->configure_data_frame_filters(wpa_s->drv_priv, + filters); +} + +static inline int wpa_drv_get_ext_capa(struct wpa_supplicant *wpa_s, + enum wpa_driver_if_type type) +{ + if (!wpa_s->driver->get_ext_capab) + return -1; + return wpa_s->driver->get_ext_capab(wpa_s->drv_priv, type, + &wpa_s->extended_capa, + &wpa_s->extended_capa_mask, + &wpa_s->extended_capa_len); +} + +static inline int wpa_drv_p2p_lo_start(struct wpa_supplicant *wpa_s, + unsigned int channel, + unsigned int period, + unsigned int interval, + unsigned int count, + const u8 *device_types, + size_t dev_types_len, + const u8 *ies, size_t ies_len) +{ + if (!wpa_s->driver->p2p_lo_start) + return -1; + return wpa_s->driver->p2p_lo_start(wpa_s->drv_priv, channel, period, + interval, count, device_types, + dev_types_len, ies, ies_len); +} + +static inline int wpa_drv_p2p_lo_stop(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s->driver->p2p_lo_stop) + return -1; + return wpa_s->driver->p2p_lo_stop(wpa_s->drv_priv); +} + +static inline int wpa_drv_set_default_scan_ies(struct wpa_supplicant *wpa_s, + const u8 *ies, size_t len) +{ + if (!wpa_s->driver->set_default_scan_ies) + return -1; + return wpa_s->driver->set_default_scan_ies(wpa_s->drv_priv, ies, len); +} + #endif /* DRIVER_I_H */ diff --git a/wpa_supplicant/eapol_test.c b/wpa_supplicant/eapol_test.c index dce7d1fadd44e..6548bd17b11f8 100644 --- a/wpa_supplicant/eapol_test.c +++ b/wpa_supplicant/eapol_test.c @@ -14,6 +14,7 @@ #include "common.h" #include "utils/ext_password.h" +#include "common/version.h" #include "config.h" #include "eapol_supp/eapol_supp_sm.h" #include "eap_peer/eap.h" @@ -192,7 +193,7 @@ static void ieee802_1x_encapsulate_radius(struct eapol_test_data *e, return; } - radius_msg_make_authenticator(msg, (u8 *) e, sizeof(*e)); + radius_msg_make_authenticator(msg); hdr = (const struct eap_hdr *) eap; pos = (const u8 *) (hdr + 1); @@ -257,6 +258,13 @@ static void ieee802_1x_encapsulate_radius(struct eapol_test_data *e, goto fail; } + if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_SERVICE_TYPE) && + !radius_msg_add_attr_int32(msg, RADIUS_ATTR_SERVICE_TYPE, + RADIUS_SERVICE_TYPE_FRAMED)) { + printf("Could not add Service-Type\n"); + goto fail; + } + os_snprintf(buf, sizeof(buf), "%s", e->connect_info); if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_CONNECT_INFO) && !radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, @@ -1239,7 +1247,7 @@ static void eapol_test_terminate(int sig, void *signal_ctx) static void usage(void) { printf("usage:\n" - "eapol_test [-enWS] -c<conf> [-a<AS IP>] [-p<AS port>] " + "eapol_test [-enWSv] -c<conf> [-a<AS IP>] [-p<AS port>] " "[-s<AS secret>]\\\n" " [-r<count>] [-t<timeout>] [-C<Connect-Info>] \\\n" " [-M<client MAC address>] [-o<server cert file] \\\n" @@ -1264,6 +1272,7 @@ static void usage(void) " -W = wait for a control interface monitor before starting\n" " -S = save configuration after authentication\n" " -n = no MPPE keys expected\n" + " -v = show version\n" " -t<timeout> = sets timeout in seconds (default: 30 s)\n" " -C<Connect-Info> = RADIUS Connect-Info (default: " "CONNECT 11Mbps 802.11b)\n" @@ -1317,7 +1326,7 @@ int main(int argc, char *argv[]) wpa_debug_show_keys = 1; for (;;) { - c = getopt(argc, argv, "a:A:c:C:ei:M:nN:o:p:P:r:R:s:St:T:W"); + c = getopt(argc, argv, "a:A:c:C:ei:M:nN:o:p:P:r:R:s:St:T:vW"); if (c < 0) break; switch (c) { @@ -1383,6 +1392,9 @@ int main(int argc, char *argv[]) ctrl_iface = optarg; eapol_test.ctrl_iface = 1; break; + case 'v': + printf("eapol_test v" VERSION_STR "\n"); + return 0; case 'W': wait_for_monitor++; break; diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index 3af1c7d89c649..abe3b476773d4 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -72,6 +72,7 @@ static int wpas_temp_disabled(struct wpa_supplicant *wpa_s, } +#ifndef CONFIG_NO_SCAN_PROCESSING /** * wpas_reenabled_network_time - Time until first network is re-enabled * @wpa_s: Pointer to wpa_supplicant data @@ -107,6 +108,7 @@ static int wpas_reenabled_network_time(struct wpa_supplicant *wpa_s) return res; } +#endif /* CONFIG_NO_SCAN_PROCESSING */ void wpas_network_reenabled(void *eloop_ctx, void *timeout_ctx) @@ -279,6 +281,11 @@ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s) wpa_supplicant_ap_deinit(wpa_s); #endif /* CONFIG_AP */ +#ifdef CONFIG_HS20 + /* Clear possibly configured frame filters */ + wpa_drv_configure_frame_filters(wpa_s, 0); +#endif /* CONFIG_HS20 */ + if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) return; @@ -303,6 +310,7 @@ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s) wpa_s->key_mgmt = 0; wpas_rrm_reset(wpa_s); + wpa_s->wnmsleep_used = 0; } @@ -564,11 +572,36 @@ static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s, break; } #endif /* CONFIG_IEEE80211W */ + if ((ie.capabilities & WPA_CAPABILITY_MFPR) && + wpas_get_ssid_pmf(wpa_s, ssid) == + NO_MGMT_FRAME_PROTECTION) { + wpa_dbg(wpa_s, MSG_DEBUG, + " skip RSN IE - no mgmt frame protection enabled but AP requires it"); + break; + } +#ifdef CONFIG_MBO + if (!(ie.capabilities & WPA_CAPABILITY_MFPC) && + wpas_mbo_get_bss_attr(bss, MBO_ATTR_ID_AP_CAPA_IND) && + wpas_get_ssid_pmf(wpa_s, ssid) != + NO_MGMT_FRAME_PROTECTION) { + wpa_dbg(wpa_s, MSG_DEBUG, + " skip RSN IE - no mgmt frame protection enabled on MBO AP"); + break; + } +#endif /* CONFIG_MBO */ wpa_dbg(wpa_s, MSG_DEBUG, " selected based on RSN IE"); return 1; } +#ifdef CONFIG_IEEE80211W + if (wpas_get_ssid_pmf(wpa_s, ssid) == MGMT_FRAME_PROTECTION_REQUIRED) { + wpa_dbg(wpa_s, MSG_DEBUG, + " skip - MFP Required but network not MFP Capable"); + return 0; + } +#endif /* CONFIG_IEEE80211W */ + wpa_ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); while ((ssid->proto & WPA_PROTO_WPA) && wpa_ie) { proto_match++; @@ -806,10 +839,10 @@ static int addr_in_list(const u8 *addr, const u8 *list, size_t num) } -static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, - int i, struct wpa_bss *bss, - struct wpa_ssid *group, - int only_first_ssid) +struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, + int i, struct wpa_bss *bss, + struct wpa_ssid *group, + int only_first_ssid) { u8 wpa_ie_len, rsn_ie_len; int wpa; @@ -817,6 +850,9 @@ static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, const u8 *ie; struct wpa_ssid *ssid; int osen; +#ifdef CONFIG_MBO + const u8 *assoc_disallow; +#endif /* CONFIG_MBO */ ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); wpa_ie_len = ie ? ie[1] : 0; @@ -978,8 +1014,16 @@ static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, continue; } - if (!bss_is_ess(bss)) { - wpa_dbg(wpa_s, MSG_DEBUG, " skip - not ESS network"); + if (ssid->mode != IEEE80211_MODE_MESH && !bss_is_ess(bss) && + !bss_is_pbss(bss)) { + wpa_dbg(wpa_s, MSG_DEBUG, + " skip - not ESS, PBSS, or MBSS"); + continue; + } + + if (ssid->pbss != 2 && ssid->pbss != bss_is_pbss(bss)) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - PBSS mismatch (ssid %d bss %d)", + ssid->pbss, bss_is_pbss(bss)); continue; } @@ -989,6 +1033,14 @@ static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, continue; } +#ifdef CONFIG_MESH + if (ssid->mode == IEEE80211_MODE_MESH && ssid->frequency > 0 && + ssid->frequency != bss->freq) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - frequency not allowed (mesh)"); + continue; + } +#endif /* CONFIG_MESH */ + if (!rate_match(wpa_s, bss)) { wpa_dbg(wpa_s, MSG_DEBUG, " skip - rate sets do " "not match"); @@ -1048,6 +1100,29 @@ static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, (unsigned int) diff.usec); continue; } +#ifdef CONFIG_MBO +#ifdef CONFIG_TESTING_OPTIONS + if (wpa_s->ignore_assoc_disallow) + goto skip_assoc_disallow; +#endif /* CONFIG_TESTING_OPTIONS */ + assoc_disallow = wpas_mbo_get_bss_attr( + bss, MBO_ATTR_ID_ASSOC_DISALLOW); + if (assoc_disallow && assoc_disallow[1] >= 1) { + wpa_dbg(wpa_s, MSG_DEBUG, + " skip - MBO association disallowed (reason %u)", + assoc_disallow[2]); + continue; + } + + if (wpa_is_bss_tmp_disallowed(wpa_s, bss->bssid)) { + wpa_dbg(wpa_s, MSG_DEBUG, + " skip - MBO retry delay has not passed yet"); + continue; + } +#ifdef CONFIG_TESTING_OPTIONS + skip_assoc_disallow: +#endif /* CONFIG_TESTING_OPTIONS */ +#endif /* CONFIG_MBO */ /* Matching configuration found */ return ssid; @@ -1301,6 +1376,7 @@ static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s, struct wpa_bss *current_bss = NULL; #ifndef CONFIG_NO_ROAMING int min_diff; + int to_5ghz; #endif /* CONFIG_NO_ROAMING */ if (wpa_s->reassociate) @@ -1356,7 +1432,10 @@ static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s, return 1; } - if (current_bss->level < 0 && current_bss->level > selected->level) { + to_5ghz = selected->freq > 4000 && current_bss->freq < 4000; + + if (current_bss->level < 0 && + current_bss->level > selected->level + to_5ghz * 2) { wpa_dbg(wpa_s, MSG_DEBUG, "Skip roam - Current BSS has better " "signal level"); return 0; @@ -1375,6 +1454,13 @@ static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s, else min_diff = 5; } + if (to_5ghz) { + /* Make it easier to move to 5 GHz band */ + if (min_diff > 2) + min_diff -= 2; + else + min_diff = 0; + } if (abs(current_bss->level - selected->level) < min_diff) { wpa_dbg(wpa_s, MSG_DEBUG, "Skip roam - too small difference " "in signal level"); @@ -1417,6 +1503,8 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, return -1; if (!own_request) return -1; + if (data && data->scan_info.external_scan) + 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); @@ -1441,7 +1529,7 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, #endif /* CONFIG_NO_RANDOM_POOL */ if (own_request && wpa_s->scan_res_handler && - (wpa_s->own_scan_running || !wpa_s->radio->external_scan_running)) { + !(data && data->scan_info.external_scan)) { void (*scan_res_handler)(struct wpa_supplicant *wpa_s, struct wpa_scan_results *scan_res); @@ -1462,9 +1550,11 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, } wpa_dbg(wpa_s, MSG_DEBUG, "New scan results available (own=%u ext=%u)", - wpa_s->own_scan_running, wpa_s->radio->external_scan_running); + wpa_s->own_scan_running, + data ? data->scan_info.external_scan : 0); if (wpa_s->last_scan_req == MANUAL_SCAN_REQ && - wpa_s->manual_scan_use_id && wpa_s->own_scan_running) { + wpa_s->manual_scan_use_id && wpa_s->own_scan_running && + own_request && !(data && data->scan_info.external_scan)) { wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS "id=%u", wpa_s->manual_scan_id); wpa_s->manual_scan_use_id = 0; @@ -1475,7 +1565,7 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, wpas_notify_scan_done(wpa_s, 1); - if (!wpa_s->own_scan_running && wpa_s->radio->external_scan_running) { + if (data && data->scan_info.external_scan) { wpa_dbg(wpa_s, MSG_DEBUG, "Do not use results from externally requested scan operation for network selection"); wpa_scan_results_free(scan_res); return 0; @@ -1504,9 +1594,13 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, wpas_wps_update_ap_info(wpa_s, scan_res); + if (wpa_s->wpa_state >= WPA_AUTHENTICATING && + wpa_s->wpa_state < WPA_COMPLETED) + goto scan_work_done; + wpa_scan_results_free(scan_res); - if (wpa_s->scan_work) { + if (own_request && wpa_s->scan_work) { struct wpa_radio_work *work = wpa_s->scan_work; wpa_s->scan_work = NULL; radio_work_done(work); @@ -1516,7 +1610,7 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, scan_work_done: wpa_scan_results_free(scan_res); - if (wpa_s->scan_work) { + if (own_request && wpa_s->scan_work) { struct wpa_radio_work *work = wpa_s->scan_work; wpa_s->scan_work = NULL; radio_work_done(work); @@ -1547,6 +1641,14 @@ static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s, selected = wpa_supplicant_pick_network(wpa_s, &ssid); +#ifdef CONFIG_MESH + if (wpa_s->ifmsh) { + wpa_msg(wpa_s, MSG_INFO, + "Avoiding join because we already joined a mesh group"); + return 0; + } +#endif /* CONFIG_MESH */ + if (selected) { int skip; skip = !wpa_supplicant_need_to_roam(wpa_s, selected, ssid); @@ -1556,6 +1658,13 @@ static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s, return 0; } + if (ssid != wpa_s->current_ssid && + wpa_s->wpa_state >= WPA_AUTHENTICATING) { + wpa_s->own_disconnect_req = 1; + wpa_supplicant_deauthenticate( + wpa_s, WLAN_REASON_DEAUTH_LEAVING); + } + if (wpa_supplicant_connect(wpa_s, selected, ssid) < 0) { wpa_dbg(wpa_s, MSG_DEBUG, "Connect failed"); return -1; @@ -1568,13 +1677,6 @@ static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s, */ return 1; } else { -#ifdef CONFIG_MESH - if (wpa_s->ifmsh) { - wpa_msg(wpa_s, MSG_INFO, - "Avoiding join because we already joined a mesh group"); - return 0; - } -#endif /* CONFIG_MESH */ wpa_dbg(wpa_s, MSG_DEBUG, "No suitable network found"); ssid = wpa_supplicant_pick_new_network(wpa_s); if (ssid) { @@ -1830,6 +1932,50 @@ static void interworking_process_assoc_resp(struct wpa_supplicant *wpa_s, #endif /* CONFIG_INTERWORKING */ +#ifdef CONFIG_FST +static int wpas_fst_update_mbie(struct wpa_supplicant *wpa_s, + const u8 *ie, size_t ie_len) +{ + struct mb_ies_info mb_ies; + + if (!ie || !ie_len || !wpa_s->fst) + return -ENOENT; + + os_memset(&mb_ies, 0, sizeof(mb_ies)); + + while (ie_len >= 2 && mb_ies.nof_ies < MAX_NOF_MB_IES_SUPPORTED) { + size_t len; + + len = 2 + ie[1]; + if (len > ie_len) { + wpa_hexdump(MSG_DEBUG, "FST: Truncated IE found", + ie, ie_len); + break; + } + + if (ie[0] == WLAN_EID_MULTI_BAND) { + wpa_printf(MSG_DEBUG, "MB IE of %u bytes found", + (unsigned int) len); + mb_ies.ies[mb_ies.nof_ies].ie = ie + 2; + mb_ies.ies[mb_ies.nof_ies].ie_len = len - 2; + mb_ies.nof_ies++; + } + + ie_len -= len; + ie += len; + } + + if (mb_ies.nof_ies > 0) { + wpabuf_free(wpa_s->received_mb_ies); + wpa_s->received_mb_ies = mb_ies_by_info(&mb_ies); + return 0; + } + + return -ENOENT; +} +#endif /* CONFIG_FST */ + + static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { @@ -1880,6 +2026,8 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, } if ((p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 6 && (os_memcmp(&p[2], "\x00\x50\xF2\x01\x01\x00", 6) == 0)) || + (p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 4 && + (os_memcmp(&p[2], "\x50\x6F\x9A\x12", 4) == 0)) || (p[0] == WLAN_EID_RSN && p[1] >= 2)) { if (wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, p, len)) break; @@ -2012,19 +2160,6 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, if (wpa_found || rsn_found) wpa_s->ap_ies_from_associnfo = 1; -#ifdef CONFIG_FST - wpabuf_free(wpa_s->received_mb_ies); - wpa_s->received_mb_ies = NULL; - if (wpa_s->fst) { - struct mb_ies_info mb_ies; - - wpa_printf(MSG_DEBUG, "Looking for MB IE"); - if (!mb_ies_info_by_ies(&mb_ies, data->assoc_info.resp_ies, - data->assoc_info.resp_ies_len)) - wpa_s->received_mb_ies = mb_ies_by_info(&mb_ies); - } -#endif /* CONFIG_FST */ - if (wpa_s->assoc_freq && data->assoc_info.freq && wpa_s->assoc_freq != data->assoc_info.freq) { wpa_printf(MSG_DEBUG, "Operating frequency changed from " @@ -2063,11 +2198,50 @@ static int wpa_supplicant_assoc_update_ie(struct wpa_supplicant *wpa_s) } +static void wpas_fst_update_mb_assoc(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ +#ifdef CONFIG_FST + struct assoc_info *ai = data ? &data->assoc_info : NULL; + struct wpa_bss *bss = wpa_s->current_bss; + const u8 *ieprb, *iebcn; + + wpabuf_free(wpa_s->received_mb_ies); + wpa_s->received_mb_ies = NULL; + + if (ai && + !wpas_fst_update_mbie(wpa_s, ai->resp_ies, ai->resp_ies_len)) { + wpa_printf(MSG_DEBUG, + "FST: MB IEs updated from Association Response frame"); + return; + } + + if (ai && + !wpas_fst_update_mbie(wpa_s, ai->beacon_ies, ai->beacon_ies_len)) { + wpa_printf(MSG_DEBUG, + "FST: MB IEs updated from association event Beacon IEs"); + return; + } + + if (!bss) + return; + + ieprb = (const u8 *) (bss + 1); + iebcn = ieprb + bss->ie_len; + + if (!wpas_fst_update_mbie(wpa_s, ieprb, bss->ie_len)) + wpa_printf(MSG_DEBUG, "FST: MB IEs updated from bss IE"); + else if (!wpas_fst_update_mbie(wpa_s, iebcn, bss->beacon_ie_len)) + wpa_printf(MSG_DEBUG, "FST: MB IEs updated from bss beacon IE"); +#endif /* CONFIG_FST */ +} + + static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { u8 bssid[ETH_ALEN]; - int ft_completed; + int ft_completed, already_authorized; int new_bss = 0; #ifdef CONFIG_AP @@ -2123,6 +2297,8 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, "WPA/RSN IEs not updated"); } + wpas_fst_update_mb_assoc(wpa_s, data); + #ifdef CONFIG_SME os_memcpy(wpa_s->sme.prev_bssid, bssid, ETH_ALEN); wpa_s->sme.prev_bssid_set = 1; @@ -2141,6 +2317,8 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, if (wpa_s->l2) l2_packet_notify_auth_start(wpa_s->l2); + already_authorized = data && data->assoc_info.authorized; + /* * Set portEnabled first to FALSE in order to get EAP state machine out * of the SUCCESS state and eapSuccess cleared. Without this, EAPOL PAE @@ -2149,11 +2327,12 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, * AUTHENTICATED without ever giving chance to EAP state machine to * reset the state. */ - if (!ft_completed) { + if (!ft_completed && !already_authorized) { eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE); eapol_sm_notify_portValid(wpa_s->eapol, FALSE); } - if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) || ft_completed) + if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) || ft_completed || + already_authorized) eapol_sm_notify_eap_success(wpa_s->eapol, FALSE); /* 802.1X::portControl = Auto */ eapol_sm_notify_portEnabled(wpa_s->eapol, TRUE); @@ -2245,7 +2424,7 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, 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); + wpa_s->ibss_rsn = ibss_rsn_init(wpa_s, wpa_s->current_ssid); if (!wpa_s->ibss_rsn) { wpa_msg(wpa_s, MSG_INFO, "Failed to init IBSS RSN"); wpa_supplicant_deauthenticate( @@ -2339,6 +2518,7 @@ static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s, struct wpa_bss *fast_reconnect = NULL; struct wpa_ssid *fast_reconnect_ssid = NULL; struct wpa_ssid *last_ssid; + struct wpa_bss *curr = NULL; authenticating = wpa_s->wpa_state == WPA_AUTHENTICATING; os_memcpy(prev_pending_bssid, wpa_s->pending_bssid, ETH_ALEN); @@ -2354,6 +2534,19 @@ static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s, return; } + if (!wpa_s->disconnected && wpa_s->wpa_state >= WPA_AUTHENTICATING && + reason_code == WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY && + locally_generated) + /* + * Remove the inactive AP (which is probably out of range) from + * the BSS list after marking disassociation. In particular + * mac80211-based drivers use the + * WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY reason code in + * locally generated disconnection events for cases where the + * AP does not reply anymore. + */ + curr = wpa_s->current_bss; + 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"); @@ -2364,7 +2557,8 @@ static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s, if (!wpa_s->disconnected && (!wpa_s->auto_reconnect_disabled || wpa_s->key_mgmt == WPA_KEY_MGMT_WPS || - wpas_wps_searching(wpa_s))) { + wpas_wps_searching(wpa_s) || + wpas_wps_reenable_networks_pending(wpa_s))) { wpa_dbg(wpa_s, MSG_DEBUG, "Auto connect enabled: try to " "reconnect (wps=%d/%d wpa_state=%d)", wpa_s->key_mgmt == WPA_KEY_MGMT_WPS, @@ -2414,6 +2608,9 @@ static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s, last_ssid = wpa_s->current_ssid; wpa_supplicant_mark_disassoc(wpa_s); + if (curr) + wpa_bss_remove(wpa_s, curr, "Connection to AP lost"); + 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; @@ -2424,7 +2621,8 @@ static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s, !disallowed_bssid(wpa_s, fast_reconnect->bssid) && !disallowed_ssid(wpa_s, fast_reconnect->ssid, fast_reconnect->ssid_len) && - !wpas_temp_disabled(wpa_s, fast_reconnect_ssid)) { + !wpas_temp_disabled(wpa_s, fast_reconnect_ssid) && + !wpa_is_bss_tmp_disallowed(wpa_s, fast_reconnect->bssid)) { #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, @@ -2622,6 +2820,13 @@ wpa_supplicant_event_interface_status(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_P2P */ +#ifdef CONFIG_MATCH_IFACE + if (wpa_s->matched) { + wpa_supplicant_remove_iface(wpa_s->global, wpa_s, 0); + break; + } +#endif /* CONFIG_MATCH_IFACE */ + #ifdef CONFIG_TERMINATE_ONLASTIF /* check if last interface */ if (!any_interfaces(wpa_s->global->ifaces)) @@ -3007,7 +3212,16 @@ static void wpa_supplicant_update_channel_list( { struct wpa_supplicant *ifs; - wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_REGDOM_CHANGE "init=%s type=%s%s%s", + /* + * To allow backwards compatibility with higher level layers that + * assumed the REGDOM_CHANGE event is sent over the initially added + * interface. Find the highest parent of this interface and use it to + * send the event. + */ + for (ifs = wpa_s; ifs->parent && ifs != ifs->parent; ifs = ifs->parent) + ; + + wpa_msg(ifs, MSG_INFO, WPA_EVENT_REGDOM_CHANGE "init=%s type=%s%s%s", reg_init_str(info->initiator), reg_type_str(info->type), info->alpha2[0] ? " alpha2=" : "", info->alpha2[0] ? info->alpha2 : ""); @@ -3022,14 +3236,16 @@ static void wpa_supplicant_update_channel_list( free_hw_features(ifs); ifs->hw.modes = wpa_drv_get_hw_feature_data( ifs, &ifs->hw.num_modes, &ifs->hw.flags); - } - /* Restart sched_scan with updated channel list */ - if (wpa_s->sched_scanning) { - wpa_dbg(wpa_s, MSG_DEBUG, - "Channel list changed restart sched scan."); - wpa_supplicant_cancel_sched_scan(wpa_s); - wpa_supplicant_req_scan(wpa_s, 0, 0); + /* Restart PNO/sched_scan with updated channel list */ + if (ifs->pno) { + wpas_stop_pno(ifs); + wpas_start_pno(ifs); + } else if (ifs->sched_scanning && !ifs->pno_sched_pending) { + wpa_dbg(ifs, MSG_DEBUG, + "Channel list changed - restart sched_scan"); + wpas_scan_restart_sched_scan(ifs); + } } wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_DRIVER); @@ -3120,6 +3336,14 @@ static void wpas_event_rx_mgmt_action(struct wpa_supplicant *wpa_s, #endif /* CONFIG_INTERWORKING */ if (category == WLAN_ACTION_RADIO_MEASUREMENT && + payload[0] == WLAN_RRM_RADIO_MEASUREMENT_REQUEST) { + wpas_rrm_handle_radio_measurement_request(wpa_s, mgmt->sa, + payload + 1, + plen - 1); + return; + } + + if (category == WLAN_ACTION_RADIO_MEASUREMENT && payload[0] == WLAN_RRM_NEIGHBOR_REPORT_RESPONSE) { wpas_rrm_process_neighbor_rep(wpa_s, payload + 1, plen - 1); return; @@ -3209,6 +3433,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED && event != EVENT_INTERFACE_ENABLED && event != EVENT_INTERFACE_STATUS && + event != EVENT_SCAN_RESULTS && event != EVENT_SCHED_SCAN_STOPPED) { wpa_dbg(wpa_s, MSG_DEBUG, "Ignore event %s (%d) while interface is disabled", @@ -3237,18 +3462,43 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, switch (event) { case EVENT_AUTH: +#ifdef CONFIG_FST + if (!wpas_fst_update_mbie(wpa_s, data->auth.ies, + data->auth.ies_len)) + wpa_printf(MSG_DEBUG, + "FST: MB IEs updated from auth IE"); +#endif /* CONFIG_FST */ sme_event_auth(wpa_s, data); break; case EVENT_ASSOC: +#ifdef CONFIG_TESTING_OPTIONS + if (wpa_s->ignore_auth_resp) { + wpa_printf(MSG_INFO, + "EVENT_ASSOC - ignore_auth_resp active!"); + break; + } +#endif /* CONFIG_TESTING_OPTIONS */ wpa_supplicant_event_assoc(wpa_s, data); if (data && data->assoc_info.authorized) wpa_supplicant_event_assoc_auth(wpa_s, data); + if (data) { + wpa_msg(wpa_s, MSG_INFO, + WPA_EVENT_SUBNET_STATUS_UPDATE "status=%u", + data->assoc_info.subnet_status); + } break; case EVENT_DISASSOC: wpas_event_disassoc(wpa_s, data ? &data->disassoc_info : NULL); break; case EVENT_DEAUTH: +#ifdef CONFIG_TESTING_OPTIONS + if (wpa_s->ignore_auth_resp) { + wpa_printf(MSG_INFO, + "EVENT_DEAUTH - ignore_auth_resp active!"); + break; + } +#endif /* CONFIG_TESTING_OPTIONS */ wpas_event_deauth(wpa_s, data ? &data->deauth_info : NULL); break; @@ -3257,10 +3507,11 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, break; #ifndef CONFIG_NO_SCAN_PROCESSING case EVENT_SCAN_STARTED: - os_get_reltime(&wpa_s->scan_start_time); - if (wpa_s->own_scan_requested) { + if (wpa_s->own_scan_requested || + (data && !data->scan_info.external_scan)) { struct os_reltime diff; + os_get_reltime(&wpa_s->scan_start_time); os_reltime_sub(&wpa_s->scan_start_time, &wpa_s->scan_trigger_time, &diff); wpa_dbg(wpa_s, MSG_DEBUG, "Own scan request started a scan in %ld.%06ld seconds", @@ -3283,7 +3534,16 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, } break; case EVENT_SCAN_RESULTS: - if (os_reltime_initialized(&wpa_s->scan_start_time)) { + if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) { + wpa_s->scan_res_handler = NULL; + wpa_s->own_scan_running = 0; + wpa_s->radio->external_scan_running = 0; + wpa_s->last_scan_req = NORMAL_SCAN_REQ; + break; + } + + if (!(data && data->scan_info.external_scan) && + os_reltime_initialized(&wpa_s->scan_start_time)) { struct os_reltime now, diff; os_get_reltime(&now); os_reltime_sub(&now, &wpa_s->scan_start_time, &diff); @@ -3294,8 +3554,10 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, } if (wpa_supplicant_event_scan_results(wpa_s, data)) break; /* interface may have been removed */ - wpa_s->own_scan_running = 0; - wpa_s->radio->external_scan_running = 0; + if (!(data && data->scan_info.external_scan)) + wpa_s->own_scan_running = 0; + if (data && data->scan_info.nl_scan_event) + wpa_s->radio->external_scan_running = 0; radio_work_check_next(wpa_s); break; #endif /* CONFIG_NO_SCAN_PROCESSING */ @@ -3336,13 +3598,17 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, case EVENT_ASSOC_REJECT: if (data->assoc_reject.bssid) wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_ASSOC_REJECT - "bssid=" MACSTR " status_code=%u", + "bssid=" MACSTR " status_code=%u%s", MAC2STR(data->assoc_reject.bssid), - data->assoc_reject.status_code); + data->assoc_reject.status_code, + data->assoc_reject.timed_out ? " timeout" : ""); else wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_ASSOC_REJECT - "status_code=%u", - data->assoc_reject.status_code); + "status_code=%u%s", + data->assoc_reject.status_code, + data->assoc_reject.timed_out ? " timeout" : ""); + wpa_s->assoc_status_code = data->assoc_reject.status_code; + wpas_notify_assoc_status_code(wpa_s); if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) sme_event_assoc_reject(wpa_s, data); else { @@ -3398,17 +3664,20 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, #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)); + MACSTR, MAC2STR(wpa_s->p2pdev->pending_action_dst)); /* * Catch TX status events for Action frames we sent via group - * interface in GO mode. + * interface in GO mode, or via standalone AP interface. + * Note, wpa_s->p2pdev will be the same as wpa_s->parent, + * except when the primary interface is used as a GO interface + * (for drivers which do not have group interface concurrency) */ 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, + os_memcmp(wpa_s->p2pdev->pending_action_dst, data->tx_status.dst, ETH_ALEN) == 0) { offchannel_send_action_tx_status( - wpa_s->parent, data->tx_status.dst, + wpa_s->p2pdev, data->tx_status.dst, data->tx_status.data, data->tx_status.data_len, data->tx_status.ack ? @@ -3451,20 +3720,34 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, data->rx_from_unknown.wds); break; case EVENT_CH_SWITCH: - if (!data) - break; - if (!wpa_s->ap_iface) { - wpa_dbg(wpa_s, MSG_DEBUG, "AP: Ignore channel switch " - "event in non-AP mode"); + if (!data || !wpa_s->current_ssid) break; + + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_CHANNEL_SWITCH + "freq=%d ht_enabled=%d ch_offset=%d ch_width=%s cf1=%d cf2=%d", + data->ch_switch.freq, + data->ch_switch.ht_enabled, + data->ch_switch.ch_offset, + channel_width_to_string(data->ch_switch.ch_width), + data->ch_switch.cf1, + data->ch_switch.cf2); + + wpa_s->assoc_freq = data->ch_switch.freq; + wpa_s->current_ssid->frequency = data->ch_switch.freq; + + if (wpa_s->current_ssid->mode == WPAS_MODE_AP || + wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO || + wpa_s->current_ssid->mode == + WPAS_MODE_P2P_GROUP_FORMATION) { + wpas_ap_ch_switch(wpa_s, data->ch_switch.freq, + data->ch_switch.ht_enabled, + data->ch_switch.ch_offset, + data->ch_switch.ch_width, + data->ch_switch.cf1, + data->ch_switch.cf2); } - wpas_ap_ch_switch(wpa_s, data->ch_switch.freq, - data->ch_switch.ht_enabled, - data->ch_switch.ch_offset, - data->ch_switch.ch_width, - data->ch_switch.cf1, - data->ch_switch.cf2); + wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_CS); break; #ifdef NEED_AP_MLME case EVENT_DFS_RADAR_DETECTED: @@ -3521,12 +3804,14 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, #endif /* CONFIG_AP */ #ifdef CONFIG_P2P if (stype == WLAN_FC_STYPE_PROBE_REQ && - data->rx_mgmt.frame_len > 24) { + data->rx_mgmt.frame_len > IEEE80211_HDRLEN) { 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); + const u8 *ie; + size_t ie_len; + + ie = data->rx_mgmt.frame + IEEE80211_HDRLEN; + ie_len = data->rx_mgmt.frame_len - + IEEE80211_HDRLEN; wpas_p2p_probe_req_rx( wpa_s, src, mgmt->da, mgmt->bssid, ie, ie_len, @@ -3566,11 +3851,12 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, } 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); + data->rx_mgmt.frame_len > IEEE80211_HDRLEN) { + const u8 *ie; + size_t ie_len; + + ie = data->rx_mgmt.frame + IEEE80211_HDRLEN; + ie_len = data->rx_mgmt.frame_len - IEEE80211_HDRLEN; wpas_notify_preq(wpa_s, mgmt->sa, mgmt->da, mgmt->bssid, ie, ie_len, @@ -3713,6 +3999,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, wpa_s, WLAN_REASON_DEAUTH_LEAVING, 1); } wpa_supplicant_mark_disassoc(wpa_s); + wpa_bss_flush(wpa_s); radio_remove_works(wpa_s, NULL, 0); wpa_supplicant_set_state(wpa_s, WPA_INTERFACE_DISABLED); @@ -3771,15 +4058,28 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, data->driver_gtk_rekey.replay_ctr); break; case EVENT_SCHED_SCAN_STOPPED: - wpa_s->pno = 0; wpa_s->sched_scanning = 0; - resched = wpa_s->scanning; + resched = wpa_s->scanning && wpas_scan_scheduled(wpa_s); wpa_supplicant_notify_scanning(wpa_s, 0); if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) break; /* + * If the driver stopped scanning without being requested to, + * request a new scan to continue scanning for networks. + */ + if (!wpa_s->sched_scan_stop_req && + wpa_s->wpa_state == WPA_SCANNING) { + wpa_dbg(wpa_s, MSG_DEBUG, + "Restart scanning after unexpected sched_scan stop event"); + wpa_supplicant_req_scan(wpa_s, 1, 0); + break; + } + + wpa_s->sched_scan_stop_req = 0; + + /* * Start a new sched scan to continue searching for more SSIDs * either if timed out or PNO schedule scan is pending. */ @@ -3820,8 +4120,72 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, data->mesh_peer.ie_len); #endif /* CONFIG_MESH */ break; + case EVENT_SURVEY: +#ifdef CONFIG_AP + if (!wpa_s->ap_iface) + break; + hostapd_event_get_survey(wpa_s->ap_iface, + &data->survey_results); +#endif /* CONFIG_AP */ + break; + case EVENT_ACS_CHANNEL_SELECTED: +#ifdef CONFIG_ACS + if (!wpa_s->ap_iface) + break; + hostapd_acs_channel_selected(wpa_s->ap_iface->bss[0], + &data->acs_selected_channels); +#endif /* CONFIG_ACS */ + break; + case EVENT_P2P_LO_STOP: +#ifdef CONFIG_P2P + wpa_s->p2p_lo_started = 0; + wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_LISTEN_OFFLOAD_STOP + P2P_LISTEN_OFFLOAD_STOP_REASON "reason=%d", + data->p2p_lo_stop.reason_code); +#endif /* CONFIG_P2P */ + break; default: wpa_msg(wpa_s, MSG_INFO, "Unknown event %d", event); break; } } + + +void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event, + union wpa_event_data *data) +{ + struct wpa_supplicant *wpa_s; + + if (event != EVENT_INTERFACE_STATUS) + return; + + wpa_s = wpa_supplicant_get_iface(ctx, data->interface_status.ifname); + if (wpa_s && wpa_s->driver->get_ifindex) { + unsigned int ifindex; + + ifindex = wpa_s->driver->get_ifindex(wpa_s->drv_priv); + if (ifindex != data->interface_status.ifindex) { + wpa_dbg(wpa_s, MSG_DEBUG, + "interface status ifindex %d mismatch (%d)", + ifindex, data->interface_status.ifindex); + return; + } + } +#ifdef CONFIG_MATCH_IFACE + else if (data->interface_status.ievent == EVENT_INTERFACE_ADDED) { + struct wpa_interface *wpa_i; + + wpa_i = wpa_supplicant_match_iface( + ctx, data->interface_status.ifname); + if (!wpa_i) + return; + wpa_s = wpa_supplicant_add_iface(ctx, wpa_i, NULL); + os_free(wpa_i); + if (wpa_s) + wpa_s->matched = 1; + } +#endif /* CONFIG_MATCH_IFACE */ + + if (wpa_s) + wpa_supplicant_event(wpa_s, event, data); +} diff --git a/wpa_supplicant/gas_query.c b/wpa_supplicant/gas_query.c index 10ecce7b4d3d9..691de0345d136 100644 --- a/wpa_supplicant/gas_query.c +++ b/wpa_supplicant/gas_query.c @@ -17,6 +17,7 @@ #include "common/wpa_ctrl.h" #include "rsn_supp/wpa.h" #include "wpa_supplicant_i.h" +#include "config.h" #include "driver_i.h" #include "offchannel.h" #include "gas_query.h" @@ -25,6 +26,9 @@ /** GAS query timeout in seconds */ #define GAS_QUERY_TIMEOUT_PERIOD 2 +/* GAS query wait-time / duration in ms */ +#define GAS_QUERY_WAIT_TIME_INITIAL 1000 +#define GAS_QUERY_WAIT_TIME_COMEBACK 150 /** * struct gas_query_pending - Pending GAS query @@ -37,6 +41,7 @@ struct gas_query_pending { u8 next_frag_id; unsigned int wait_comeback:1; unsigned int offchannel_tx_started:1; + unsigned int retry:1; int freq; u16 status_code; struct wpabuf *req; @@ -63,6 +68,10 @@ struct gas_query { static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx); static void gas_query_timeout(void *eloop_data, void *user_ctx); +static void gas_query_rx_comeback_timeout(void *eloop_data, void *user_ctx); +static void gas_query_tx_initial_req(struct gas_query *gas, + struct gas_query_pending *query); +static int gas_query_new_dialog_token(struct gas_query *gas, const u8 *dst); static int ms_from_time(struct os_reltime *last) @@ -108,8 +117,6 @@ static const char * gas_result_txt(enum gas_query_result result) return "PEER_ERROR"; case GAS_QUERY_INTERNAL_ERROR: return "INTERNAL_ERROR"; - case GAS_QUERY_CANCELLED: - return "CANCELLED"; case GAS_QUERY_DELETED_AT_DEINIT: return "DELETED_AT_DEINIT"; } @@ -151,6 +158,7 @@ static void gas_query_done(struct gas_query *gas, 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); + eloop_cancel_timeout(gas_query_rx_comeback_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); @@ -235,6 +243,13 @@ static void gas_query_tx_status(struct wpa_supplicant *wpa_s, eloop_cancel_timeout(gas_query_timeout, gas, query); eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0, gas_query_timeout, gas, query); + if (query->wait_comeback && !query->retry) { + eloop_cancel_timeout(gas_query_rx_comeback_timeout, + gas, query); + eloop_register_timeout( + 0, (GAS_QUERY_WAIT_TIME_COMEBACK + 10) * 1000, + gas_query_rx_comeback_timeout, gas, query); + } } if (result == OFFCHANNEL_SEND_ACTION_FAILED) { eloop_cancel_timeout(gas_query_timeout, gas, query); @@ -254,10 +269,13 @@ static int pmf_in_use(struct wpa_supplicant *wpa_s, const u8 *addr) static int gas_query_tx(struct gas_query *gas, struct gas_query_pending *query, - struct wpabuf *req) + struct wpabuf *req, unsigned int wait_time) { - unsigned int wait_time; int res, prot = pmf_in_use(gas->wpa_s, query->addr); + const u8 *bssid; + const u8 wildcard_bssid[ETH_ALEN] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff + }; wpa_printf(MSG_DEBUG, "GAS: Send action frame to " MACSTR " len=%u " "freq=%d prot=%d", MAC2STR(query->addr), @@ -267,12 +285,18 @@ static int gas_query_tx(struct gas_query *gas, struct gas_query_pending *query, *categ = WLAN_ACTION_PROTECTED_DUAL; } os_get_reltime(&query->last_oper); - wait_time = 1000; if (gas->wpa_s->max_remain_on_chan && wait_time > gas->wpa_s->max_remain_on_chan) wait_time = gas->wpa_s->max_remain_on_chan; + if (!gas->wpa_s->conf->gas_address3 || + (gas->wpa_s->current_ssid && + gas->wpa_s->wpa_state >= WPA_ASSOCIATED && + os_memcmp(query->addr, gas->wpa_s->bssid, ETH_ALEN) == 0)) + bssid = query->addr; + else + bssid = wildcard_bssid; res = offchannel_send_action(gas->wpa_s, query->freq, query->addr, - gas->wpa_s->own_addr, query->addr, + gas->wpa_s->own_addr, bssid, wpabuf_head(req), wpabuf_len(req), wait_time, gas_query_tx_status, 0); if (res == 0) @@ -285,6 +309,7 @@ static void gas_query_tx_comeback_req(struct gas_query *gas, struct gas_query_pending *query) { struct wpabuf *req; + unsigned int wait_time; req = gas_build_comeback_req(query->dialog_token); if (req == NULL) { @@ -292,7 +317,10 @@ static void gas_query_tx_comeback_req(struct gas_query *gas, return; } - if (gas_query_tx(gas, query, req) < 0) { + wait_time = (query->retry || !query->offchannel_tx_started) ? + GAS_QUERY_WAIT_TIME_INITIAL : GAS_QUERY_WAIT_TIME_COMEBACK; + + if (gas_query_tx(gas, query, req, wait_time) < 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); @@ -302,6 +330,35 @@ static void gas_query_tx_comeback_req(struct gas_query *gas, } +static void gas_query_rx_comeback_timeout(void *eloop_data, void *user_ctx) +{ + struct gas_query *gas = eloop_data; + struct gas_query_pending *query = user_ctx; + int dialog_token; + + wpa_printf(MSG_DEBUG, + "GAS: No response to comeback request received (retry=%u)", + query->retry); + if (gas->current != query || query->retry) + return; + dialog_token = gas_query_new_dialog_token(gas, query->addr); + if (dialog_token < 0) + return; + wpa_printf(MSG_DEBUG, + "GAS: Retry GAS query due to comeback response timeout"); + query->retry = 1; + query->dialog_token = dialog_token; + *(wpabuf_mhead_u8(query->req) + 2) = dialog_token; + query->wait_comeback = 0; + query->next_frag_id = 0; + wpabuf_free(query->adv_proto); + query->adv_proto = NULL; + eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query); + eloop_cancel_timeout(gas_query_timeout, gas, query); + gas_query_tx_initial_req(gas, query); +} + + static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx) { struct gas_query *gas = eloop_data; @@ -319,6 +376,11 @@ static void gas_query_tx_comeback_req_delay(struct gas_query *gas, { unsigned int secs, usecs; + if (comeback_delay > 1 && query->offchannel_tx_started) { + offchannel_send_action_done(gas->wpa_s); + query->offchannel_tx_started = 0; + } + secs = (comeback_delay * 1024) / 1000000; usecs = comeback_delay * 1024 - secs * 1000000; wpa_printf(MSG_DEBUG, "GAS: Send comeback request to " MACSTR @@ -371,6 +433,7 @@ static void gas_query_rx_comeback(struct gas_query *gas, "comeback_delay=%u)", MAC2STR(query->addr), query->dialog_token, frag_id, more_frags, comeback_delay); + eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query); if ((size_t) 2 + adv_proto[1] != wpabuf_len(query->adv_proto) || os_memcmp(adv_proto, wpabuf_head(query->adv_proto), @@ -447,8 +510,16 @@ int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa, 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 */ + prot = categ == WLAN_ACTION_PROTECTED_DUAL; - pmf = pmf_in_use(gas->wpa_s, bssid); + pmf = pmf_in_use(gas->wpa_s, sa); if (prot && !pmf) { wpa_printf(MSG_DEBUG, "GAS: Drop unexpected protected GAS frame when PMF is disabled"); return 0; @@ -458,14 +529,6 @@ int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa, return 0; } - 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 @@ -620,11 +683,18 @@ static void gas_query_start_cb(struct wpa_radio_work *work, int deinit) } gas->work = work; + gas_query_tx_initial_req(gas, query); +} - if (gas_query_tx(gas, query, query->req) < 0) { + +static void gas_query_tx_initial_req(struct gas_query *gas, + struct gas_query_pending *query) +{ + if (gas_query_tx(gas, query, query->req, + GAS_QUERY_WAIT_TIME_INITIAL) < 0) { wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to " MACSTR, MAC2STR(query->addr)); - gas_query_free(query, 1); + gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR); return; } gas->current = query; @@ -633,7 +703,24 @@ static void gas_query_start_cb(struct wpa_radio_work *work, int deinit) query->dialog_token); eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0, gas_query_timeout, gas, query); +} + + +static int gas_query_new_dialog_token(struct gas_query *gas, const u8 *dst) +{ + static int next_start = 0; + int dialog_token; + for (dialog_token = 0; dialog_token < 256; dialog_token++) { + if (gas_query_dialog_token_available( + gas, dst, (next_start + dialog_token) % 256)) + break; + } + if (dialog_token == 256) + return -1; /* Too many pending queries */ + dialog_token = (next_start + dialog_token) % 256; + next_start = (dialog_token + 1) % 256; + return dialog_token; } @@ -658,20 +745,13 @@ int gas_query_req(struct gas_query *gas, const u8 *dst, int freq, { struct gas_query_pending *query; int dialog_token; - static int next_start = 0; if (wpabuf_len(req) < 3) return -1; - for (dialog_token = 0; dialog_token < 256; dialog_token++) { - if (gas_query_dialog_token_available( - gas, dst, (next_start + dialog_token) % 256)) - break; - } - if (dialog_token == 256) - return -1; /* Too many pending queries */ - dialog_token = (next_start + dialog_token) % 256; - next_start = (dialog_token + 1) % 256; + dialog_token = gas_query_new_dialog_token(gas, dst); + if (dialog_token < 0) + return -1; query = os_zalloc(sizeof(*query)); if (query == NULL) @@ -694,26 +774,10 @@ int gas_query_req(struct gas_query *gas, const u8 *dst, int freq, if (radio_add_work(gas->wpa_s, freq, "gas-query", 0, gas_query_start_cb, query) < 0) { + query->req = NULL; /* caller will free this in error case */ gas_query_free(query, 1); return -1; } 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/wpa_supplicant/gas_query.h b/wpa_supplicant/gas_query.h index ad1349088ee1f..ef82097e2424b 100644 --- a/wpa_supplicant/gas_query.h +++ b/wpa_supplicant/gas_query.h @@ -29,7 +29,6 @@ enum gas_query_result { GAS_QUERY_TIMEOUT, GAS_QUERY_PEER_ERROR, GAS_QUERY_INTERNAL_ERROR, - GAS_QUERY_CANCELLED, GAS_QUERY_DELETED_AT_DEINIT }; @@ -40,7 +39,6 @@ int gas_query_req(struct gas_query *gas, const u8 *dst, int freq, 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 */ diff --git a/wpa_supplicant/hs20_supplicant.c b/wpa_supplicant/hs20_supplicant.c index a1afc85ff9bb6..e88f147bbd1b4 100644 --- a/wpa_supplicant/hs20_supplicant.c +++ b/wpa_supplicant/hs20_supplicant.c @@ -25,6 +25,7 @@ #include "gas_query.h" #include "interworking.h" #include "hs20_supplicant.h" +#include "base64.h" #define OSU_MAX_ITEMS 10 @@ -60,6 +61,46 @@ struct osu_provider { }; +void hs20_configure_frame_filters(struct wpa_supplicant *wpa_s) +{ + struct wpa_bss *bss = wpa_s->current_bss; + u8 *bssid = wpa_s->bssid; + const u8 *ie; + const u8 *ext_capa; + u32 filter = 0; + + if (!bss || !is_hs20_network(wpa_s, wpa_s->current_ssid, bss)) { + wpa_printf(MSG_DEBUG, + "Not configuring frame filtering - BSS " MACSTR + " is not a Hotspot 2.0 network", MAC2STR(bssid)); + return; + } + + ie = wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE); + + /* Check if DGAF disabled bit is zero (5th byte in the IE) */ + if (!ie || ie[1] < 5) + wpa_printf(MSG_DEBUG, + "Not configuring frame filtering - Can't extract DGAF bit"); + else if (!(ie[6] & HS20_DGAF_DISABLED)) + filter |= WPA_DATA_FRAME_FILTER_FLAG_GTK; + + ext_capa = wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB); + if (!ext_capa || ext_capa[1] < 2) { + wpa_printf(MSG_DEBUG, + "Not configuring frame filtering - Can't extract Proxy ARP bit"); + return; + } + + /* Check if Proxy ARP is enabled (2nd byte in the IE) */ + if (ext_capa[3] & BIT(4)) + filter |= WPA_DATA_FRAME_FILTER_FLAG_ARP | + WPA_DATA_FRAME_FILTER_FLAG_NA; + + wpa_drv_configure_frame_filters(wpa_s, filter); +} + + void wpas_hs20_add_indication(struct wpabuf *buf, int pps_mo_id) { u8 conf; @@ -164,8 +205,8 @@ void hs20_put_anqp_req(u32 stypes, const u8 *payload, size_t payload_len, } -struct wpabuf * hs20_build_anqp_req(u32 stypes, const u8 *payload, - size_t payload_len) +static struct wpabuf * hs20_build_anqp_req(u32 stypes, const u8 *payload, + size_t payload_len) { struct wpabuf *buf; @@ -180,13 +221,14 @@ struct wpabuf * hs20_build_anqp_req(u32 stypes, const u8 *payload, int hs20_anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, u32 stypes, - const u8 *payload, size_t payload_len) + const u8 *payload, size_t payload_len, int inmem) { struct wpabuf *buf; int ret = 0; int freq; struct wpa_bss *bss; int res; + struct icon_entry *icon_entry; bss = wpa_bss_get_bssid(wpa_s, dst); if (!bss) { @@ -210,15 +252,127 @@ int hs20_anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, u32 stypes, if (res < 0) { wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request"); wpabuf_free(buf); - ret = -1; + return -1; } else wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token " "%u", res); + if (inmem) { + icon_entry = os_zalloc(sizeof(struct icon_entry)); + if (!icon_entry) + return -1; + os_memcpy(icon_entry->bssid, dst, ETH_ALEN); + icon_entry->file_name = os_malloc(payload_len + 1); + if (!icon_entry->file_name) { + os_free(icon_entry); + return -1; + } + os_memcpy(icon_entry->file_name, payload, payload_len); + icon_entry->file_name[payload_len] = '\0'; + icon_entry->dialog_token = res; + + dl_list_add(&wpa_s->icon_head, &icon_entry->list); + } + return ret; } +static struct icon_entry * hs20_find_icon(struct wpa_supplicant *wpa_s, + const u8 *bssid, + const char *file_name) +{ + struct icon_entry *icon; + + dl_list_for_each(icon, &wpa_s->icon_head, struct icon_entry, list) { + if (os_memcmp(icon->bssid, bssid, ETH_ALEN) == 0 && + os_strcmp(icon->file_name, file_name) == 0 && icon->image) + return icon; + } + + return NULL; +} + + +int hs20_get_icon(struct wpa_supplicant *wpa_s, const u8 *bssid, + const char *file_name, size_t offset, size_t size, + char *reply, size_t buf_len) +{ + struct icon_entry *icon; + size_t out_size; + unsigned char *b64; + size_t b64_size; + int reply_size; + + wpa_printf(MSG_DEBUG, "HS20: Get icon " MACSTR " %s @ %u +%u (%u)", + MAC2STR(bssid), file_name, (unsigned int) offset, + (unsigned int) size, (unsigned int) buf_len); + + icon = hs20_find_icon(wpa_s, bssid, file_name); + if (!icon || !icon->image || offset >= icon->image_len) + return -1; + if (size > icon->image_len - offset) + size = icon->image_len - offset; + out_size = buf_len - 3 /* max base64 padding */; + if (size * 4 > out_size * 3) + size = out_size * 3 / 4; + if (size == 0) + return -1; + + b64 = base64_encode(&icon->image[offset], size, &b64_size); + if (b64 && buf_len >= b64_size) { + os_memcpy(reply, b64, b64_size); + reply_size = b64_size; + } else { + reply_size = -1; + } + os_free(b64); + return reply_size; +} + + +static void hs20_free_icon_entry(struct icon_entry *icon) +{ + wpa_printf(MSG_DEBUG, "HS20: Free stored icon from " MACSTR + " dialog_token=%u file_name=%s image_len=%u", + MAC2STR(icon->bssid), icon->dialog_token, + icon->file_name ? icon->file_name : "N/A", + (unsigned int) icon->image_len); + os_free(icon->file_name); + os_free(icon->image); + os_free(icon); +} + + +int hs20_del_icon(struct wpa_supplicant *wpa_s, const u8 *bssid, + const char *file_name) +{ + struct icon_entry *icon, *tmp; + int count = 0; + + if (!bssid) + wpa_printf(MSG_DEBUG, "HS20: Delete all stored icons"); + else if (!file_name) + wpa_printf(MSG_DEBUG, "HS20: Delete all stored icons for " + MACSTR, MAC2STR(bssid)); + else + wpa_printf(MSG_DEBUG, "HS20: Delete stored icons for " + MACSTR " file name %s", MAC2STR(bssid), file_name); + + dl_list_for_each_safe(icon, tmp, &wpa_s->icon_head, struct icon_entry, + list) { + if ((!bssid || os_memcmp(icon->bssid, bssid, ETH_ALEN) == 0) && + (!file_name || + os_strcmp(icon->file_name, file_name) == 0)) { + dl_list_del(&icon->list); + hs20_free_icon_entry(icon); + count++; + } + } + return count == 0 ? -1 : 0; +} + + static void hs20_set_osu_access_permission(const char *osu_dir, const char *fname) { @@ -243,16 +397,53 @@ static void hs20_set_osu_access_permission(const char *osu_dir, } } + +static void hs20_remove_duplicate_icons(struct wpa_supplicant *wpa_s, + struct icon_entry *new_icon) +{ + struct icon_entry *icon, *tmp; + + dl_list_for_each_safe(icon, tmp, &wpa_s->icon_head, struct icon_entry, + list) { + if (icon == new_icon) + continue; + if (os_memcmp(icon->bssid, new_icon->bssid, ETH_ALEN) == 0 && + os_strcmp(icon->file_name, new_icon->file_name) == 0) { + dl_list_del(&icon->list); + hs20_free_icon_entry(icon); + } + } +} + + static int hs20_process_icon_binary_file(struct wpa_supplicant *wpa_s, const u8 *sa, const u8 *pos, - size_t slen) + size_t slen, u8 dialog_token) { char fname[256]; int png; FILE *f; u16 data_len; + struct icon_entry *icon; + + dl_list_for_each(icon, &wpa_s->icon_head, struct icon_entry, list) { + if (icon->dialog_token == dialog_token && !icon->image && + os_memcmp(icon->bssid, sa, ETH_ALEN) == 0) { + icon->image = os_malloc(slen); + if (!icon->image) + return -1; + os_memcpy(icon->image, pos, slen); + icon->image_len = slen; + hs20_remove_duplicate_icons(wpa_s, icon); + wpa_msg(wpa_s, MSG_INFO, + RX_HS20_ICON MACSTR " %s %u", + MAC2STR(sa), icon->file_name, + (unsigned int) icon->image_len); + return 0; + } + } - wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR " Icon Binary File", + wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR " Icon Binary File", MAC2STR(sa)); if (slen < 4) { @@ -315,7 +506,7 @@ static int hs20_process_icon_binary_file(struct wpa_supplicant *wpa_s, } fclose(f); - wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP-ICON %s", fname); + wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP_ICON "%s", fname); return 0; } @@ -358,7 +549,7 @@ static void hs20_osu_icon_fetch_result(struct wpa_supplicant *wpa_s, int res) void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, const u8 *sa, - const u8 *data, size_t slen) + const u8 *data, size_t slen, u8 dialog_token) { const u8 *pos = data; u8 subtype; @@ -379,7 +570,7 @@ void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s, switch (subtype) { case HS20_STYPE_CAPABILITY_LIST: - wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR + wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR " HS Capability List", MAC2STR(sa)); wpa_hexdump_ascii(MSG_DEBUG, "HS Capability List", pos, slen); if (anqp) { @@ -389,7 +580,7 @@ void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s, } break; case HS20_STYPE_OPERATOR_FRIENDLY_NAME: - wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR + 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) { @@ -405,7 +596,7 @@ void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s, "Metrics value from " MACSTR, MAC2STR(sa)); break; } - wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR + 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)); @@ -415,7 +606,7 @@ void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s, } break; case HS20_STYPE_CONNECTION_CAPABILITY: - wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR + 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) { @@ -425,7 +616,7 @@ void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s, } break; case HS20_STYPE_OPERATING_CLASS: - wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR + 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) { @@ -435,7 +626,7 @@ void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s, } break; case HS20_STYPE_OSU_PROVIDERS_LIST: - wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR + wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR " OSU Providers list", MAC2STR(sa)); wpa_s->num_prov_found++; if (anqp) { @@ -445,7 +636,8 @@ void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s, } break; case HS20_STYPE_ICON_BINARY_FILE: - ret = hs20_process_icon_binary_file(wpa_s, sa, pos, slen); + ret = hs20_process_icon_binary_file(wpa_s, sa, pos, slen, + dialog_token); if (wpa_s->fetch_osu_icon_in_progress) { hs20_osu_icon_fetch_result(wpa_s, ret); eloop_cancel_timeout(hs20_continue_icon_fetch, @@ -511,7 +703,10 @@ static void hs20_osu_fetch_done(struct wpa_supplicant *wpa_s) wpa_s->conf->osu_dir); f = fopen(fname, "w"); if (f == NULL) { + wpa_msg(wpa_s, MSG_INFO, + "Could not write OSU provider information"); hs20_free_osu_prov(wpa_s); + wpa_s->fetch_anqp_in_progress = 0; return; } @@ -579,7 +774,8 @@ void hs20_next_osu_icon(struct wpa_supplicant *wpa_s) if (hs20_anqp_send_req(wpa_s, osu->bssid, BIT(HS20_STYPE_ICON_REQUEST), (u8 *) icon->filename, - os_strlen(icon->filename)) < 0) { + os_strlen(icon->filename), + 0) < 0) { icon->failed = 1; continue; } @@ -617,7 +813,7 @@ static void hs20_osu_add_prov(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, prov->osu_ssid_len = osu_ssid_len; /* OSU Friendly Name Length */ - if (pos + 2 > end) { + if (end - pos < 2) { wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU " "Friendly Name Length"); return; @@ -633,9 +829,9 @@ static void hs20_osu_add_prov(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, pos += len2; /* OSU Friendly Name Duples */ - while (pos2 + 4 <= pos && prov->friendly_name_count < OSU_MAX_ITEMS) { + while (pos - pos2 >= 4 && prov->friendly_name_count < OSU_MAX_ITEMS) { struct osu_lang_string *f; - if (pos2 + 1 + pos2[0] > pos || pos2[0] < 3) { + if (1 + pos2[0] > pos - pos2 || pos2[0] < 3) { wpa_printf(MSG_DEBUG, "Invalid OSU Friendly Name"); break; } @@ -646,7 +842,7 @@ static void hs20_osu_add_prov(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, } /* OSU Server URI */ - if (pos + 1 > end) { + if (end - pos < 1) { wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU Server URI length"); return; @@ -661,7 +857,7 @@ static void hs20_osu_add_prov(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, pos += uri_len; /* OSU Method list */ - if (pos + 1 > end) { + if (end - pos < 1) { wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU Method " "list length"); return; @@ -681,7 +877,7 @@ static void hs20_osu_add_prov(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, } /* Icons Available Length */ - if (pos + 2 > end) { + if (end - pos < 2) { wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for Icons " "Available Length"); return; @@ -701,7 +897,7 @@ static void hs20_osu_add_prov(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, struct osu_icon *icon = &prov->icon[prov->icon_count]; u8 flen; - if (pos2 + 2 + 2 + 3 + 1 + 1 > pos) { + if (2 + 2 + 3 + 1 + 1 > pos - pos2) { wpa_printf(MSG_DEBUG, "HS 2.0: Invalid Icon Metadata"); break; } @@ -713,46 +909,46 @@ static void hs20_osu_add_prov(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, os_memcpy(icon->lang, pos2, 3); pos2 += 3; - flen = pos2[0]; - if (flen > pos - pos2 - 1) { + flen = *pos2++; + if (flen > pos - pos2) { wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon Type"); break; } - os_memcpy(icon->icon_type, pos2 + 1, flen); - pos2 += 1 + flen; + os_memcpy(icon->icon_type, pos2, flen); + pos2 += flen; - if (pos2 + 1 > pos) { + if (pos - pos2 < 1) { wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon " "Filename length"); break; } - flen = pos2[0]; - if (flen > pos - pos2 - 1) { + flen = *pos2++; + if (flen > pos - pos2) { wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon " "Filename"); break; } - os_memcpy(icon->filename, pos2 + 1, flen); - pos2 += 1 + flen; + os_memcpy(icon->filename, pos2, flen); + pos2 += flen; prov->icon_count++; } /* OSU_NAI */ - if (pos + 1 > end) { + if (end - pos < 1) { wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU_NAI"); return; } - osu_nai_len = pos[0]; - if (osu_nai_len > end - pos - 1) { + osu_nai_len = *pos++; + if (osu_nai_len > end - pos) { wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU_NAI"); return; } - os_memcpy(prov->osu_nai, pos + 1, osu_nai_len); - pos += 1 + osu_nai_len; + os_memcpy(prov->osu_nai, pos, osu_nai_len); + pos += osu_nai_len; /* OSU Service Description Length */ - if (pos + 2 > end) { + if (end - pos < 2) { wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU " "Service Description Length"); return; @@ -768,20 +964,20 @@ static void hs20_osu_add_prov(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, pos += len2; /* OSU Service Description Duples */ - while (pos2 + 4 <= pos && prov->serv_desc_count < OSU_MAX_ITEMS) { + while (pos - pos2 >= 4 && prov->serv_desc_count < OSU_MAX_ITEMS) { struct osu_lang_string *f; u8 descr_len; - descr_len = pos2[0]; - if (descr_len > pos - pos2 - 1 || descr_len < 3) { + descr_len = *pos2++; + if (descr_len > pos - pos2 || descr_len < 3) { wpa_printf(MSG_DEBUG, "Invalid OSU Service " "Description"); break; } f = &prov->serv_desc[prov->serv_desc_count++]; - os_memcpy(f->lang, pos2 + 1, 3); - os_memcpy(f->text, pos2 + 1 + 3, descr_len - 3); - pos2 += 1 + descr_len; + os_memcpy(f->lang, pos2, 3); + os_memcpy(f->text, pos2 + 3, descr_len - 3); + pos2 += descr_len; } wpa_printf(MSG_DEBUG, "HS 2.0: Added OSU Provider through " MACSTR, @@ -816,9 +1012,9 @@ void hs20_osu_icon_fetch(struct wpa_supplicant *wpa_s) end = pos + wpabuf_len(prov_anqp); /* OSU SSID */ - if (pos + 1 > end) + if (end - pos < 1) continue; - if (pos + 1 + pos[0] > end) { + if (1 + pos[0] > end - pos) { wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for " "OSU SSID"); continue; @@ -832,7 +1028,7 @@ void hs20_osu_icon_fetch(struct wpa_supplicant *wpa_s) osu_ssid = pos; pos += osu_ssid_len; - if (pos + 1 > end) { + if (end - pos < 1) { wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for " "Number of OSU Providers"); continue; @@ -842,7 +1038,7 @@ void hs20_osu_icon_fetch(struct wpa_supplicant *wpa_s) num_providers); /* OSU Providers */ - while (pos + 2 < end && num_providers > 0) { + while (end - pos > 2 && num_providers > 0) { num_providers--; len = WPA_GET_LE16(pos); pos += 2; @@ -882,7 +1078,7 @@ static void hs20_osu_scan_res_handler(struct wpa_supplicant *wpa_s, } -int hs20_fetch_osu(struct wpa_supplicant *wpa_s) +int hs20_fetch_osu(struct wpa_supplicant *wpa_s, int skip_scan) { if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) { wpa_printf(MSG_DEBUG, "HS 2.0: Cannot start fetch_osu - " @@ -913,7 +1109,16 @@ int hs20_fetch_osu(struct wpa_supplicant *wpa_s) wpa_msg(wpa_s, MSG_INFO, "Starting OSU provisioning information fetch"); wpa_s->num_osu_scans = 0; wpa_s->num_prov_found = 0; - hs20_start_osu_scan(wpa_s); + if (skip_scan) { + wpa_s->network_select = 0; + wpa_s->fetch_all_anqp = 1; + wpa_s->fetch_osu_info = 1; + wpa_s->fetch_osu_icon_in_progress = 0; + + interworking_start_fetch_anqp(wpa_s); + } else { + hs20_start_osu_scan(wpa_s); + } return 0; } @@ -1002,8 +1207,16 @@ void hs20_rx_deauth_imminent_notice(struct wpa_supplicant *wpa_s, u8 code, } +void hs20_init(struct wpa_supplicant *wpa_s) +{ + dl_list_init(&wpa_s->icon_head); +} + + void hs20_deinit(struct wpa_supplicant *wpa_s) { eloop_cancel_timeout(hs20_continue_icon_fetch, wpa_s, NULL); hs20_free_osu_prov(wpa_s); + if (wpa_s->icon_head.next) + hs20_del_icon(wpa_s, NULL, NULL); } diff --git a/wpa_supplicant/hs20_supplicant.h b/wpa_supplicant/hs20_supplicant.h index 85b512012a975..0dd559fdbf016 100644 --- a/wpa_supplicant/hs20_supplicant.h +++ b/wpa_supplicant/hs20_supplicant.h @@ -8,17 +8,16 @@ #ifndef HS20_SUPPLICANT_H #define HS20_SUPPLICANT_H +void hs20_configure_frame_filters(struct wpa_supplicant *wpa_s); void wpas_hs20_add_indication(struct wpabuf *buf, int pps_mo_id); int hs20_anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, u32 stypes, - const u8 *payload, size_t payload_len); -struct wpabuf * hs20_build_anqp_req(u32 stypes, const u8 *payload, - size_t payload_len); + const u8 *payload, size_t payload_len, int inmem); void hs20_put_anqp_req(u32 stypes, const u8 *payload, size_t payload_len, struct wpabuf *buf); void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, const u8 *sa, - const u8 *data, size_t slen); + const u8 *data, size_t slen, u8 dialog_token); int is_hs20_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, struct wpa_bss *bss); int hs20_get_pps_mo_id(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); @@ -32,10 +31,16 @@ void hs20_rx_deauth_imminent_notice(struct wpa_supplicant *wpa_s, u8 code, void hs20_free_osu_prov(struct wpa_supplicant *wpa_s); void hs20_next_osu_icon(struct wpa_supplicant *wpa_s); void hs20_osu_icon_fetch(struct wpa_supplicant *wpa_s); -int hs20_fetch_osu(struct wpa_supplicant *wpa_s); +int hs20_fetch_osu(struct wpa_supplicant *wpa_s, int skip_scan); void hs20_cancel_fetch_osu(struct wpa_supplicant *wpa_s); void hs20_icon_fetch_failed(struct wpa_supplicant *wpa_s); void hs20_start_osu_scan(struct wpa_supplicant *wpa_s); +void hs20_init(struct wpa_supplicant *wpa_s); void hs20_deinit(struct wpa_supplicant *wpa_s); +int hs20_get_icon(struct wpa_supplicant *wpa_s, const u8 *bssid, + const char *file_name, size_t offset, size_t size, + char *reply, size_t buf_len); +int hs20_del_icon(struct wpa_supplicant *wpa_s, const u8 *bssid, + const char *file_name); #endif /* HS20_SUPPLICANT_H */ diff --git a/wpa_supplicant/ibss_rsn.c b/wpa_supplicant/ibss_rsn.c index d9d0ae7f10dd6..53d7d57bde350 100644 --- a/wpa_supplicant/ibss_rsn.c +++ b/wpa_supplicant/ibss_rsn.c @@ -221,6 +221,7 @@ static int ibss_rsn_supp_init(struct ibss_rsn_peer *peer, const u8 *own_addr, peer->supp = wpa_sm_init(ctx); if (peer->supp == NULL) { wpa_printf(MSG_DEBUG, "SUPP: wpa_sm_init() failed"); + os_free(ctx); return -1; } @@ -230,7 +231,7 @@ static int ibss_rsn_supp_init(struct ibss_rsn_peer *peer, const u8 *own_addr, wpa_sm_set_param(peer->supp, WPA_PARAM_PAIRWISE, WPA_CIPHER_CCMP); wpa_sm_set_param(peer->supp, WPA_PARAM_GROUP, WPA_CIPHER_CCMP); wpa_sm_set_param(peer->supp, WPA_PARAM_KEY_MGMT, WPA_KEY_MGMT_PSK); - wpa_sm_set_pmk(peer->supp, psk, PMK_LEN, NULL); + wpa_sm_set_pmk(peer->supp, psk, PMK_LEN, NULL, NULL); peer->supp_ie_len = sizeof(peer->supp_ie); if (wpa_sm_set_assoc_wpa_ie_default(peer->supp, peer->supp_ie, @@ -404,7 +405,7 @@ static void auth_set_eapol(void *ctx, const u8 *addr, static int ibss_rsn_auth_init_group(struct ibss_rsn *ibss_rsn, - const u8 *own_addr) + const u8 *own_addr, struct wpa_ssid *ssid) { struct wpa_auth_config conf; struct wpa_auth_callbacks cb; @@ -418,7 +419,7 @@ static int ibss_rsn_auth_init_group(struct ibss_rsn *ibss_rsn, conf.rsn_pairwise = WPA_CIPHER_CCMP; conf.wpa_group = WPA_CIPHER_CCMP; conf.eapol_version = 2; - conf.wpa_group_rekey = 600; + conf.wpa_group_rekey = ssid->group_rekey ? ssid->group_rekey : 600; os_memset(&cb, 0, sizeof(cb)); cb.ctx = ibss_rsn; @@ -665,7 +666,8 @@ void ibss_rsn_stop(struct ibss_rsn *ibss_rsn, const u8 *peermac) } -struct ibss_rsn * ibss_rsn_init(struct wpa_supplicant *wpa_s) +struct ibss_rsn * ibss_rsn_init(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) { struct ibss_rsn *ibss_rsn; @@ -674,7 +676,7 @@ struct ibss_rsn * ibss_rsn_init(struct wpa_supplicant *wpa_s) return NULL; ibss_rsn->wpa_s = wpa_s; - if (ibss_rsn_auth_init_group(ibss_rsn, wpa_s->own_addr) < 0) { + if (ibss_rsn_auth_init_group(ibss_rsn, wpa_s->own_addr, ssid) < 0) { ibss_rsn_deinit(ibss_rsn); return NULL; } diff --git a/wpa_supplicant/ibss_rsn.h b/wpa_supplicant/ibss_rsn.h index 67fae2d14ab78..626c543546c85 100644 --- a/wpa_supplicant/ibss_rsn.h +++ b/wpa_supplicant/ibss_rsn.h @@ -51,7 +51,8 @@ struct ibss_rsn { }; -struct ibss_rsn * ibss_rsn_init(struct wpa_supplicant *wpa_s); +struct ibss_rsn * ibss_rsn_init(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid); 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); diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c index fd47c179ea4b1..1fb40c74e5cf9 100644 --- a/wpa_supplicant/interworking.c +++ b/wpa_supplicant/interworking.c @@ -362,13 +362,13 @@ static const u8 * nai_realm_parse_eap(struct nai_realm_eap *e, const u8 *pos, u8 elen, auth_count, a; const u8 *e_end; - if (pos + 3 > end) { + if (end - pos < 3) { wpa_printf(MSG_DEBUG, "No room for EAP Method fixed fields"); return NULL; } elen = *pos++; - if (pos + elen > end || elen < 2) { + if (elen > end - pos || elen < 2) { wpa_printf(MSG_DEBUG, "No room for EAP Method subfield"); return NULL; } @@ -381,14 +381,19 @@ static const u8 * nai_realm_parse_eap(struct nai_realm_eap *e, const u8 *pos, 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"); + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, + "No room for Authentication Parameter subfield header"); return NULL; } id = *pos++; len = *pos++; + if (len > end - pos) { + wpa_printf(MSG_DEBUG, + "No room for Authentication Parameter subfield"); + return NULL; + } switch (id) { case NAI_REALM_EAP_AUTH_NON_EAP_INNER_AUTH: @@ -463,7 +468,7 @@ static const u8 * nai_realm_parse_realm(struct nai_realm *r, const u8 *pos, len = WPA_GET_LE16(pos); /* NAI Realm Data field Length */ pos += 2; - if (pos + len > end || len < 3) { + if (len > end - pos || len < 3) { wpa_printf(MSG_DEBUG, "No room for NAI Realm Data " "(len=%u; left=%u)", len, (unsigned int) (end - pos)); @@ -473,7 +478,7 @@ static const u8 * nai_realm_parse_realm(struct nai_realm *r, const u8 *pos, r->encoding = *pos++; realm_len = *pos++; - if (pos + realm_len > f_end) { + if (realm_len > f_end - pos) { wpa_printf(MSG_DEBUG, "No room for NAI Realm " "(len=%u; left=%u)", realm_len, (unsigned int) (f_end - pos)); @@ -485,13 +490,13 @@ static const u8 * nai_realm_parse_realm(struct nai_realm *r, const u8 *pos, return NULL; pos += realm_len; - if (pos + 1 > f_end) { + if (f_end - pos < 1) { 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) { + if (r->eap_count * 3 > f_end - pos) { wpa_printf(MSG_DEBUG, "No room for EAP Methods"); return NULL; } @@ -746,7 +751,7 @@ static int plmn_id_match(struct wpabuf *anqp, const char *imsi, int mnc_len) return 0; pos = wpabuf_head_u8(anqp); end = pos + wpabuf_len(anqp); - if (pos + 2 > end) + if (end - pos < 2) return 0; if (*pos != 0) { wpa_printf(MSG_DEBUG, "Unsupported GUD version 0x%x", *pos); @@ -754,7 +759,7 @@ static int plmn_id_match(struct wpabuf *anqp, const char *imsi, int mnc_len) } pos++; udhl = *pos++; - if (pos + udhl > end) { + if (udhl > end - pos) { wpa_printf(MSG_DEBUG, "Invalid UDHL"); return 0; } @@ -764,12 +769,12 @@ static int plmn_id_match(struct wpabuf *anqp, const char *imsi, int mnc_len) plmn[0], plmn[1], plmn[2], plmn2[0], plmn2[1], plmn2[2], imsi, mnc_len); - while (pos + 2 <= end) { + while (end - pos >= 2) { u8 iei, len; const u8 *l_end; iei = *pos++; len = *pos++ & 0x7f; - if (pos + len > end) + if (len > end - pos) break; l_end = pos + len; @@ -780,7 +785,7 @@ static int plmn_id_match(struct wpabuf *anqp, const char *imsi, int mnc_len) pos, len); num = *pos++; for (i = 0; i < num; i++) { - if (pos + 3 > l_end) + if (l_end - pos < 3) break; if (os_memcmp(pos, plmn, 3) == 0 || os_memcmp(pos, plmn2, 3) == 0) @@ -945,11 +950,9 @@ static int interworking_set_hs20_params(struct wpa_supplicant *wpa_s, if (!key_mgmt) key_mgmt = wpa_s->conf->pmf != NO_MGMT_FRAME_PROTECTION ? "WPA-EAP WPA-EAP-SHA256" : "WPA-EAP"; - if (wpa_config_set(ssid, "key_mgmt", key_mgmt, 0) < 0) - return -1; - if (wpa_config_set(ssid, "proto", "RSN", 0) < 0) - return -1; - if (wpa_config_set(ssid, "pairwise", "CCMP", 0) < 0) + if (wpa_config_set(ssid, "key_mgmt", key_mgmt, 0) < 0 || + wpa_config_set(ssid, "proto", "RSN", 0) < 0 || + wpa_config_set(ssid, "pairwise", "CCMP", 0) < 0) return -1; return 0; } @@ -1082,12 +1085,12 @@ static int roaming_consortium_element_match(const u8 *ie, const u8 *rc_id, * OI #1, [OI #2], [OI #3] */ - if (pos + 2 > end) + if (end - pos < 2) return 0; pos++; /* skip Number of ANQP OIs */ lens = *pos++; - if (pos + (lens & 0x0f) + (lens >> 4) > end) + if ((lens & 0x0f) + (lens >> 4) > end - pos) return 0; if ((lens & 0x0f) == rc_len && os_memcmp(pos, rc_id, rc_len) == 0) @@ -1121,7 +1124,7 @@ static int roaming_consortium_anqp_match(const struct wpabuf *anqp, /* Set of <OI Length, OI> duples */ while (pos < end) { len = *pos++; - if (pos + len > end) + if (len > end - pos) break; if (len == rc_len && os_memcmp(pos, rc_id, rc_len) == 0) return 1; @@ -1182,6 +1185,7 @@ static int cred_excluded_ssid(struct wpa_cred *cred, struct wpa_bss *bss) static int cred_below_min_backhaul(struct wpa_supplicant *wpa_s, struct wpa_cred *cred, struct wpa_bss *bss) { +#ifdef CONFIG_HS20 int res; unsigned int dl_bandwidth, ul_bandwidth; const u8 *wan; @@ -1233,6 +1237,7 @@ static int cred_below_min_backhaul(struct wpa_supplicant *wpa_s, if (cred->min_ul_bandwidth_roaming > ul_bandwidth) return 1; } +#endif /* CONFIG_HS20 */ return 0; } @@ -1260,9 +1265,11 @@ static int cred_over_max_bss_load(struct wpa_supplicant *wpa_s, } +#ifdef CONFIG_HS20 + static int has_proto_match(const u8 *pos, const u8 *end, u8 proto) { - while (pos + 4 <= end) { + while (end - pos >= 4) { if (pos[0] == proto && pos[3] == 1 /* Open */) return 1; pos += 4; @@ -1275,7 +1282,7 @@ static int has_proto_match(const u8 *pos, const u8 *end, u8 proto) static int has_proto_port_match(const u8 *pos, const u8 *end, u8 proto, u16 port) { - while (pos + 4 <= end) { + while (end - pos >= 4) { if (pos[0] == proto && WPA_GET_LE16(&pos[1]) == port && pos[3] == 1 /* Open */) return 1; @@ -1285,10 +1292,13 @@ static int has_proto_port_match(const u8 *pos, const u8 *end, u8 proto, return 0; } +#endif /* CONFIG_HS20 */ + static int cred_conn_capab_missing(struct wpa_supplicant *wpa_s, struct wpa_cred *cred, struct wpa_bss *bss) { +#ifdef CONFIG_HS20 int res; const u8 *capab, *end; unsigned int i, j; @@ -1325,6 +1335,7 @@ static int cred_conn_capab_missing(struct wpa_supplicant *wpa_s, } } } +#endif /* CONFIG_HS20 */ return 0; } @@ -1438,7 +1449,24 @@ static int interworking_set_eap_params(struct wpa_ssid *ssid, os_free(anon); } - if (cred->username && cred->username[0] && + if (!ttls && cred->username && cred->username[0] && cred->realm && + !os_strchr(cred->username, '@')) { + char *id; + size_t buflen; + int res; + + buflen = os_strlen(cred->username) + 1 + + os_strlen(cred->realm) + 1; + + id = os_malloc(buflen); + if (!id) + return -1; + os_snprintf(id, buflen, "%s@%s", cred->username, cred->realm); + res = wpa_config_set_quoted(ssid, "identity", id); + os_free(id); + if (res < 0) + return -1; + } else if (cred->username && cred->username[0] && wpa_config_set_quoted(ssid, "identity", cred->username) < 0) return -1; @@ -1560,9 +1588,8 @@ fail: } -static int interworking_connect_helper(struct wpa_supplicant *wpa_s, - struct wpa_bss *bss, int allow_excluded, - int only_add) +int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, + int only_add) { struct wpa_cred *cred, *cred_rc, *cred_3gpp; struct wpa_ssid *ssid; @@ -1570,7 +1597,7 @@ static int interworking_connect_helper(struct wpa_supplicant *wpa_s, struct nai_realm_eap *eap = NULL; u16 count, i; char buf[100]; - int excluded = 0, *excl = allow_excluded ? &excluded : NULL; + int excluded = 0, *excl = &excluded; const char *name; if (wpa_s->conf->cred == NULL || bss == NULL) @@ -1584,8 +1611,8 @@ static int interworking_connect_helper(struct wpa_supplicant *wpa_s, } wpa_printf(MSG_DEBUG, "Interworking: Considering BSS " MACSTR - " for connection (allow_excluded=%d)", - MAC2STR(bss->bssid), allow_excluded); + " for connection", + MAC2STR(bss->bssid)); if (!wpa_bss_get_ie(bss, WLAN_EID_RSN)) { /* @@ -1603,7 +1630,7 @@ static int interworking_connect_helper(struct wpa_supplicant *wpa_s, wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Highest roaming consortium matching credential priority %d sp_priority %d", cred_rc->priority, cred_rc->sp_priority); - if (allow_excluded && excl && !(*excl)) + if (excl && !(*excl)) excl = NULL; } @@ -1612,7 +1639,7 @@ static int interworking_connect_helper(struct wpa_supplicant *wpa_s, wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Highest NAI Realm list matching credential priority %d sp_priority %d", cred->priority, cred->sp_priority); - if (allow_excluded && excl && !(*excl)) + if (excl && !(*excl)) excl = NULL; } @@ -1622,7 +1649,7 @@ static int interworking_connect_helper(struct wpa_supplicant *wpa_s, wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Highest 3GPP matching credential priority %d sp_priority %d", cred_3gpp->priority, cred_3gpp->sp_priority); - if (allow_excluded && excl && !(*excl)) + if (excl && !(*excl)) excl = NULL; } @@ -1635,7 +1662,7 @@ static int interworking_connect_helper(struct wpa_supplicant *wpa_s, wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Highest roaming consortium matching credential priority %d sp_priority %d (ignore BW)", cred_rc->priority, cred_rc->sp_priority); - if (allow_excluded && excl && !(*excl)) + if (excl && !(*excl)) excl = NULL; } @@ -1645,7 +1672,7 @@ static int interworking_connect_helper(struct wpa_supplicant *wpa_s, wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Highest NAI Realm list matching credential priority %d sp_priority %d (ignore BW)", cred->priority, cred->sp_priority); - if (allow_excluded && excl && !(*excl)) + if (excl && !(*excl)) excl = NULL; } @@ -1655,7 +1682,7 @@ static int interworking_connect_helper(struct wpa_supplicant *wpa_s, wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Highest 3GPP matching credential priority %d sp_priority %d (ignore BW)", cred_3gpp->priority, cred_3gpp->sp_priority); - if (allow_excluded && excl && !(*excl)) + if (excl && !(*excl)) excl = NULL; } } @@ -1820,13 +1847,6 @@ fail: } -int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, - int only_add) -{ - return interworking_connect_helper(wpa_s, bss, 1, only_add); -} - - #ifdef PCSC_FUNCS static int interworking_pcsc_read_imsi(struct wpa_supplicant *wpa_s) { @@ -2125,23 +2145,27 @@ int domain_name_list_contains(struct wpabuf *domain_names, pos = wpabuf_head(domain_names); end = pos + wpabuf_len(domain_names); - while (pos + 1 < end) { - if (pos + 1 + pos[0] > end) + while (end - pos > 1) { + u8 elen; + + elen = *pos++; + if (elen > end - pos) 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) + pos, elen); + if (elen == len && + os_strncasecmp(domain, (const char *) pos, len) == 0) return 1; - if (!exact_match && pos[0] > len && pos[pos[0] - len] == '.') { - const char *ap = (const char *) (pos + 1); - int offset = pos[0] - len; + if (!exact_match && elen > len && pos[elen - len - 1] == '.') { + const char *ap = (const char *) pos; + int offset = elen - len; + if (os_strncasecmp(domain, ap + offset, len) == 0) return 1; } - pos += 1 + pos[0]; + pos += elen; } return 0; @@ -2564,11 +2588,13 @@ static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s) return; } +#ifdef CONFIG_HS20 if (wpa_s->fetch_osu_icon_in_progress) { wpa_printf(MSG_DEBUG, "Interworking: Next icon (in progress)"); hs20_next_osu_icon(wpa_s); return; } +#endif /* CONFIG_HS20 */ dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { if (!(bss->caps & IEEE80211_CAP_ESS)) @@ -2602,6 +2628,7 @@ static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s) } if (found == 0) { +#ifdef CONFIG_HS20 if (wpa_s->fetch_osu_info) { if (wpa_s->num_prov_found == 0 && wpa_s->fetch_osu_waiting_scan && @@ -2614,6 +2641,7 @@ static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s) hs20_osu_icon_fetch(wpa_s); return; } +#endif /* CONFIG_HS20 */ wpa_msg(wpa_s, MSG_INFO, "ANQP fetch completed"); wpa_s->fetch_anqp_in_progress = 0; if (wpa_s->network_select) @@ -2664,10 +2692,11 @@ void interworking_stop_fetch_anqp(struct wpa_supplicant *wpa_s) int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, - u16 info_ids[], size_t num_ids, u32 subtypes) + u16 info_ids[], size_t num_ids, u32 subtypes, + int get_cell_pref) { struct wpabuf *buf; - struct wpabuf *hs20_buf = NULL; + struct wpabuf *extra_buf = NULL; int ret = 0; int freq; struct wpa_bss *bss; @@ -2690,15 +2719,31 @@ int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, #ifdef CONFIG_HS20 if (subtypes != 0) { - hs20_buf = wpabuf_alloc(100); - if (hs20_buf == NULL) + extra_buf = wpabuf_alloc(100); + if (extra_buf == NULL) return -1; - hs20_put_anqp_req(subtypes, NULL, 0, hs20_buf); + hs20_put_anqp_req(subtypes, NULL, 0, extra_buf); } #endif /* CONFIG_HS20 */ - buf = anqp_build_req(info_ids, num_ids, hs20_buf); - wpabuf_free(hs20_buf); +#ifdef CONFIG_MBO + if (get_cell_pref) { + struct wpabuf *mbo; + + mbo = mbo_build_anqp_buf(wpa_s, bss); + if (mbo) { + if (wpabuf_resize(&extra_buf, wpabuf_len(mbo))) { + wpabuf_free(extra_buf); + return -1; + } + wpabuf_put_buf(extra_buf, mbo); + wpabuf_free(mbo); + } + } +#endif /* CONFIG_MBO */ + + buf = anqp_build_req(info_ids, num_ids, extra_buf); + wpabuf_free(extra_buf); if (buf == NULL) return -1; @@ -2716,10 +2761,46 @@ int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, } +static void anqp_add_extra(struct wpa_supplicant *wpa_s, + struct wpa_bss_anqp *anqp, u16 info_id, + const u8 *data, size_t slen) +{ + struct wpa_bss_anqp_elem *tmp, *elem = NULL; + + if (!anqp) + return; + + dl_list_for_each(tmp, &anqp->anqp_elems, struct wpa_bss_anqp_elem, + list) { + if (tmp->infoid == info_id) { + elem = tmp; + break; + } + } + + if (!elem) { + elem = os_zalloc(sizeof(*elem)); + if (!elem) + return; + elem->infoid = info_id; + dl_list_add(&anqp->anqp_elems, &elem->list); + } else { + wpabuf_free(elem->payload); + } + + elem->payload = wpabuf_alloc_copy(data, slen); + if (!elem->payload) { + dl_list_del(&elem->list); + os_free(elem); + } +} + + static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, const u8 *sa, u16 info_id, - const u8 *data, size_t slen) + const u8 *data, size_t slen, + u8 dialog_token) { const u8 *pos = data; struct wpa_bss_anqp *anqp = NULL; @@ -2732,7 +2813,7 @@ static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s, switch (info_id) { case ANQP_CAPABILITY_LIST: - wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR + wpa_msg(wpa_s, MSG_INFO, RX_ANQP MACSTR " ANQP Capability list", MAC2STR(sa)); wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Capability list", pos, slen); @@ -2742,7 +2823,7 @@ static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s, } break; case ANQP_VENUE_NAME: - wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR + 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) { @@ -2751,7 +2832,7 @@ static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s, } break; case ANQP_NETWORK_AUTH_TYPE: - wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR + wpa_msg(wpa_s, MSG_INFO, RX_ANQP MACSTR " Network Authentication Type information", MAC2STR(sa)); wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Network Authentication " @@ -2762,7 +2843,7 @@ static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s, } break; case ANQP_ROAMING_CONSORTIUM: - wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR + wpa_msg(wpa_s, MSG_INFO, RX_ANQP MACSTR " Roaming Consortium list", MAC2STR(sa)); wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Roaming Consortium", pos, slen); @@ -2772,7 +2853,7 @@ static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s, } break; case ANQP_IP_ADDR_TYPE_AVAILABILITY: - wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR + wpa_msg(wpa_s, MSG_INFO, RX_ANQP MACSTR " IP Address Type Availability information", MAC2STR(sa)); wpa_hexdump(MSG_MSGDUMP, "ANQP: IP Address Availability", @@ -2784,7 +2865,7 @@ static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s, } break; case ANQP_NAI_REALM: - wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR + 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) { @@ -2793,7 +2874,7 @@ static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s, } break; case ANQP_3GPP_CELLULAR_NETWORK: - wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR + 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); @@ -2803,7 +2884,7 @@ static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s, } break; case ANQP_DOMAIN_NAME: - wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR + 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) { @@ -2829,7 +2910,8 @@ static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s, switch (type) { case HS20_ANQP_OUI_TYPE: hs20_parse_rx_hs20_anqp_resp(wpa_s, bss, sa, - pos, slen); + pos, slen, + dialog_token); break; default: wpa_msg(wpa_s, MSG_DEBUG, @@ -2849,6 +2931,7 @@ static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s, default: wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Unsupported ANQP Info ID %u", info_id); + anqp_add_extra(wpa_s, anqp, info_id, data, slen); break; } } @@ -2871,8 +2954,10 @@ void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token, " dialog_token=%u result=%d status_code=%u", MAC2STR(dst), dialog_token, result, status_code); if (result != GAS_QUERY_SUCCESS) { +#ifdef CONFIG_HS20 if (wpa_s->fetch_osu_icon_in_progress) hs20_icon_fetch_failed(wpa_s); +#endif /* CONFIG_HS20 */ anqp_result = "FAILURE"; goto out; } @@ -2882,8 +2967,10 @@ void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token, pos[1] < 2 || pos[3] != ACCESS_NETWORK_QUERY_PROTOCOL) { wpa_msg(wpa_s, MSG_DEBUG, "ANQP: Unexpected Advertisement Protocol in response"); +#ifdef CONFIG_HS20 if (wpa_s->fetch_osu_icon_in_progress) hs20_icon_fetch_failed(wpa_s); +#endif /* CONFIG_HS20 */ anqp_result = "INVALID_FRAME"; goto out; } @@ -2927,12 +3014,14 @@ void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token, goto out_parse_done; } interworking_parse_rx_anqp_resp(wpa_s, bss, dst, info_id, pos, - slen); + slen, dialog_token); pos += slen; } out_parse_done: +#ifdef CONFIG_HS20 hs20_notify_parse_done(wpa_s); +#endif /* CONFIG_HS20 */ out: wpa_msg(wpa_s, MSG_INFO, ANQP_QUERY_DONE "addr=" MACSTR " result=%s", MAC2STR(dst), anqp_result); diff --git a/wpa_supplicant/interworking.h b/wpa_supplicant/interworking.h index 3743dc00e905a..3d22292618b22 100644 --- a/wpa_supplicant/interworking.h +++ b/wpa_supplicant/interworking.h @@ -12,7 +12,8 @@ enum gas_query_result; int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, - u16 info_ids[], size_t num_ids, u32 subtypes); + u16 info_ids[], size_t num_ids, u32 subtypes, + int get_cell_pref); void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token, enum gas_query_result result, const struct wpabuf *adv_proto, diff --git a/wpa_supplicant/libwpa_test.c b/wpa_supplicant/libwpa_test.c new file mode 100644 index 0000000000000..e51ab72476659 --- /dev/null +++ b/wpa_supplicant/libwpa_test.c @@ -0,0 +1,32 @@ +/* + * libwpa_test - Test program for libwpa_client.* library linking + * Copyright (c) 2015, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common/wpa_ctrl.h" + +int main(int argc, char *argv[]) +{ + struct wpa_ctrl *ctrl; + + ctrl = wpa_ctrl_open("foo"); + if (!ctrl) + return -1; + if (wpa_ctrl_attach(ctrl) == 0) + wpa_ctrl_detach(ctrl); + if (wpa_ctrl_pending(ctrl)) { + char buf[10]; + size_t len; + + len = sizeof(buf); + wpa_ctrl_recv(ctrl, buf, &len); + } + wpa_ctrl_close(ctrl); + + return 0; +} diff --git a/wpa_supplicant/main.c b/wpa_supplicant/main.c index d5d47e1d77b24..e08c2fd266f18 100644 --- a/wpa_supplicant/main.c +++ b/wpa_supplicant/main.c @@ -65,41 +65,44 @@ static void usage(void) " -B = run daemon in the background\n" " -c = Configuration file\n" " -C = ctrl_interface parameter (only used if -c is not)\n" - " -i = interface name\n" - " -I = additional configuration file\n" " -d = increase debugging verbosity (-dd even more)\n" " -D = driver name (can be multiple drivers: nl80211,wext)\n" - " -e = entropy file\n"); + " -e = entropy file\n" #ifdef CONFIG_DEBUG_FILE - printf(" -f = log output to debug file instead of stdout\n"); + " -f = log output to debug file instead of stdout\n" #endif /* CONFIG_DEBUG_FILE */ - printf(" -g = global ctrl_interface\n" + " -g = global ctrl_interface\n" " -G = global ctrl_interface group\n" - " -K = include keys (passwords, etc.) in debug output\n"); -#ifdef CONFIG_DEBUG_SYSLOG - printf(" -s = log output to syslog instead of stdout\n"); -#endif /* CONFIG_DEBUG_SYSLOG */ -#ifdef CONFIG_DEBUG_LINUX_TRACING - printf(" -T = record to Linux tracing in addition to logging\n"); - printf(" (records all messages regardless of debug verbosity)\n"); -#endif /* CONFIG_DEBUG_LINUX_TRACING */ - printf(" -t = include timestamp in debug messages\n" " -h = show this help text\n" + " -i = interface name\n" + " -I = additional configuration file\n" + " -K = include keys (passwords, etc.) in debug output\n" " -L = show license (BSD)\n" +#ifdef CONFIG_P2P + " -m = Configuration file for the P2P Device interface\n" +#endif /* CONFIG_P2P */ +#ifdef CONFIG_MATCH_IFACE + " -M = start describing new matching interface\n" +#endif /* CONFIG_MATCH_IFACE */ + " -N = start describing new interface\n" " -o = override driver parameter for new interfaces\n" " -O = override ctrl_interface parameter for new interfaces\n" " -p = driver parameters\n" " -P = PID file\n" - " -q = decrease debugging verbosity (-qq even less)\n"); + " -q = decrease debugging verbosity (-qq even less)\n" +#ifdef CONFIG_DEBUG_SYSLOG + " -s = log output to syslog instead of stdout\n" +#endif /* CONFIG_DEBUG_SYSLOG */ + " -t = include timestamp in debug messages\n" +#ifdef CONFIG_DEBUG_LINUX_TRACING + " -T = record to Linux tracing in addition to logging\n" + " (records all messages regardless of debug verbosity)\n" +#endif /* CONFIG_DEBUG_LINUX_TRACING */ #ifdef CONFIG_DBUS - printf(" -u = enable DBus control interface\n"); + " -u = enable DBus control interface\n" #endif /* CONFIG_DBUS */ - printf(" -v = show version\n" - " -W = wait for a control interface monitor before starting\n" -#ifdef CONFIG_P2P - " -m = Configuration file for the P2P Device interface\n" -#endif /* CONFIG_P2P */ - " -N = start describing new interface\n"); + " -v = show version\n" + " -W = wait for a control interface monitor before starting\n"); printf("example:\n" " wpa_supplicant -D%s -iwlan0 -c/etc/wpa_supplicant.conf\n", @@ -153,6 +156,28 @@ static void wpa_supplicant_fd_workaround(int start) } +#ifdef CONFIG_MATCH_IFACE +static int wpa_supplicant_init_match(struct wpa_global *global) +{ + /* + * The assumption is that the first driver is the primary driver and + * will handle the arrival / departure of interfaces. + */ + if (wpa_drivers[0]->global_init && !global->drv_priv[0]) { + global->drv_priv[0] = wpa_drivers[0]->global_init(global); + if (!global->drv_priv[0]) { + wpa_printf(MSG_ERROR, + "Failed to initialize driver '%s'", + wpa_drivers[0]->name); + return -1; + } + } + + return 0; +} +#endif /* CONFIG_MATCH_IFACE */ + + int main(int argc, char *argv[]) { int c, i; @@ -176,7 +201,7 @@ int main(int argc, char *argv[]) for (;;) { c = getopt(argc, argv, - "b:Bc:C:D:de:f:g:G:hi:I:KLm:No:O:p:P:qsTtuvW"); + "b:Bc:C:D:de:f:g:G:hi:I:KLMm:No:O:p:P:qsTtuvW"); if (c < 0) break; switch (c) { @@ -282,6 +307,20 @@ int main(int argc, char *argv[]) case 'W': params.wait_for_monitor++; break; +#ifdef CONFIG_MATCH_IFACE + case 'M': + params.match_iface_count++; + iface = os_realloc_array(params.match_ifaces, + params.match_iface_count, + sizeof(struct wpa_interface)); + if (!iface) + goto out; + params.match_ifaces = iface; + iface = ¶ms.match_ifaces[params.match_iface_count - + 1]; + os_memset(iface, 0, sizeof(*iface)); + break; +#endif /* CONFIG_MATCH_IFACE */ case 'N': iface_count++; iface = os_realloc_array(ifaces, iface_count, @@ -328,6 +367,9 @@ int main(int argc, char *argv[]) ifaces[i].ctrl_interface == NULL) || ifaces[i].ifname == NULL) { if (iface_count == 1 && (params.ctrl_interface || +#ifdef CONFIG_MATCH_IFACE + params.match_iface_count || +#endif /* CONFIG_MATCH_IFACE */ params.dbus_ctrl_interface)) break; usage(); @@ -341,6 +383,11 @@ int main(int argc, char *argv[]) } } +#ifdef CONFIG_MATCH_IFACE + if (exitcode == 0) + exitcode = wpa_supplicant_init_match(global); +#endif /* CONFIG_MATCH_IFACE */ + if (exitcode == 0) exitcode = wpa_supplicant_run(global); @@ -351,6 +398,9 @@ int main(int argc, char *argv[]) out: wpa_supplicant_fd_workaround(0); os_free(ifaces); +#ifdef CONFIG_MATCH_IFACE + os_free(params.match_ifaces); +#endif /* CONFIG_MATCH_IFACE */ os_free(params.pid_file); os_program_deinit(); diff --git a/wpa_supplicant/mbo.c b/wpa_supplicant/mbo.c new file mode 100644 index 0000000000000..7e049be3df413 --- /dev/null +++ b/wpa_supplicant/mbo.c @@ -0,0 +1,836 @@ +/* + * wpa_supplicant - MBO + * + * Copyright(c) 2015 Intel Deutschland GmbH + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "common/ieee802_11_defs.h" +#include "common/gas.h" +#include "config.h" +#include "wpa_supplicant_i.h" +#include "driver_i.h" +#include "bss.h" +#include "scan.h" + +/* type + length + oui + oui type */ +#define MBO_IE_HEADER 6 + + +static int wpas_mbo_validate_non_pref_chan(u8 oper_class, u8 chan, u8 reason) +{ + if (reason > MBO_NON_PREF_CHAN_REASON_INT_INTERFERENCE) + return -1; + + /* Only checking the validity of the channel and oper_class */ + if (ieee80211_chan_to_freq(NULL, oper_class, chan) == -1) + return -1; + + return 0; +} + + +const u8 * wpas_mbo_get_bss_attr(struct wpa_bss *bss, enum mbo_attr_id attr) +{ + const u8 *mbo, *end; + + if (!bss) + return NULL; + + mbo = wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE); + if (!mbo) + return NULL; + + end = mbo + 2 + mbo[1]; + mbo += MBO_IE_HEADER; + + return get_ie(mbo, end - mbo, attr); +} + + +static void wpas_mbo_non_pref_chan_attr_body(struct wpa_supplicant *wpa_s, + struct wpabuf *mbo, + u8 start, u8 end) +{ + u8 i; + + wpabuf_put_u8(mbo, wpa_s->non_pref_chan[start].oper_class); + + for (i = start; i < end; i++) + wpabuf_put_u8(mbo, wpa_s->non_pref_chan[i].chan); + + wpabuf_put_u8(mbo, wpa_s->non_pref_chan[start].preference); + wpabuf_put_u8(mbo, wpa_s->non_pref_chan[start].reason); +} + + +static void wpas_mbo_non_pref_chan_attr(struct wpa_supplicant *wpa_s, + struct wpabuf *mbo, u8 start, u8 end) +{ + size_t size = end - start + 3; + + if (size + 2 > wpabuf_tailroom(mbo)) + return; + + wpabuf_put_u8(mbo, MBO_ATTR_ID_NON_PREF_CHAN_REPORT); + wpabuf_put_u8(mbo, size); /* Length */ + + wpas_mbo_non_pref_chan_attr_body(wpa_s, mbo, start, end); +} + + +static void wpas_mbo_non_pref_chan_subelem_hdr(struct wpabuf *mbo, u8 len) +{ + wpabuf_put_u8(mbo, WLAN_EID_VENDOR_SPECIFIC); + wpabuf_put_u8(mbo, len); /* Length */ + wpabuf_put_be24(mbo, OUI_WFA); + wpabuf_put_u8(mbo, MBO_ATTR_ID_NON_PREF_CHAN_REPORT); +} + + +static void wpas_mbo_non_pref_chan_subelement(struct wpa_supplicant *wpa_s, + struct wpabuf *mbo, u8 start, + u8 end) +{ + size_t size = end - start + 7; + + if (size + 2 > wpabuf_tailroom(mbo)) + return; + + wpas_mbo_non_pref_chan_subelem_hdr(mbo, size); + wpas_mbo_non_pref_chan_attr_body(wpa_s, mbo, start, end); +} + + +static void wpas_mbo_non_pref_chan_attrs(struct wpa_supplicant *wpa_s, + struct wpabuf *mbo, int subelement) +{ + u8 i, start = 0; + struct wpa_mbo_non_pref_channel *start_pref; + + if (!wpa_s->non_pref_chan || !wpa_s->non_pref_chan_num) { + if (subelement) + wpas_mbo_non_pref_chan_subelem_hdr(mbo, 4); + return; + } + start_pref = &wpa_s->non_pref_chan[0]; + + for (i = 1; i <= wpa_s->non_pref_chan_num; i++) { + struct wpa_mbo_non_pref_channel *non_pref = NULL; + + if (i < wpa_s->non_pref_chan_num) + non_pref = &wpa_s->non_pref_chan[i]; + if (!non_pref || + non_pref->oper_class != start_pref->oper_class || + non_pref->reason != start_pref->reason || + non_pref->preference != start_pref->preference) { + if (subelement) + wpas_mbo_non_pref_chan_subelement(wpa_s, mbo, + start, i); + else + wpas_mbo_non_pref_chan_attr(wpa_s, mbo, start, + i); + + if (!non_pref) + return; + + start = i; + start_pref = non_pref; + } + } +} + + +int wpas_mbo_ie(struct wpa_supplicant *wpa_s, u8 *buf, size_t len) +{ + struct wpabuf *mbo; + int res; + + if (len < MBO_IE_HEADER + 3 + 7) + return 0; + + /* Leave room for the MBO IE header */ + mbo = wpabuf_alloc(len - MBO_IE_HEADER); + if (!mbo) + return 0; + + /* Add non-preferred channels attribute */ + wpas_mbo_non_pref_chan_attrs(wpa_s, mbo, 0); + + /* + * Send cellular capabilities attribute even if AP does not advertise + * cellular capabilities. + */ + wpabuf_put_u8(mbo, MBO_ATTR_ID_CELL_DATA_CAPA); + wpabuf_put_u8(mbo, 1); + wpabuf_put_u8(mbo, wpa_s->conf->mbo_cell_capa); + + res = mbo_add_ie(buf, len, wpabuf_head_u8(mbo), wpabuf_len(mbo)); + if (!res) + wpa_printf(MSG_ERROR, "Failed to add MBO IE"); + + wpabuf_free(mbo); + return res; +} + + +static void wpas_mbo_send_wnm_notification(struct wpa_supplicant *wpa_s, + const u8 *data, size_t len) +{ + struct wpabuf *buf; + int res; + + /* + * Send WNM-Notification Request frame only in case of a change in + * non-preferred channels list during association, if the AP supports + * MBO. + */ + if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_bss || + !wpa_bss_get_vendor_ie(wpa_s->current_bss, MBO_IE_VENDOR_TYPE)) + return; + + buf = wpabuf_alloc(4 + len); + if (!buf) + return; + + wpabuf_put_u8(buf, WLAN_ACTION_WNM); + wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ); + wpa_s->mbo_wnm_token++; + if (wpa_s->mbo_wnm_token == 0) + wpa_s->mbo_wnm_token++; + wpabuf_put_u8(buf, wpa_s->mbo_wnm_token); + wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); /* Type */ + + wpabuf_put_data(buf, data, len); + + res = 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); + if (res < 0) + wpa_printf(MSG_DEBUG, + "Failed to send WNM-Notification Request frame with non-preferred channel list"); + + wpabuf_free(buf); +} + + +static void wpas_mbo_non_pref_chan_changed(struct wpa_supplicant *wpa_s) +{ + struct wpabuf *buf; + + buf = wpabuf_alloc(512); + if (!buf) + return; + + wpas_mbo_non_pref_chan_attrs(wpa_s, buf, 1); + wpas_mbo_send_wnm_notification(wpa_s, wpabuf_head_u8(buf), + wpabuf_len(buf)); + wpabuf_free(buf); +} + + +static int wpa_non_pref_chan_is_eq(struct wpa_mbo_non_pref_channel *a, + struct wpa_mbo_non_pref_channel *b) +{ + return a->oper_class == b->oper_class && a->chan == b->chan; +} + + +/* + * wpa_non_pref_chan_cmp - Compare two channels for sorting + * + * In MBO IE non-preferred channel subelement we can put many channels in an + * attribute if they are in the same operating class and have the same + * preference and reason. To make it easy for the functions that build + * the IE attributes and WNM Request subelements, save the channels sorted + * by their oper_class and reason. + */ +static int wpa_non_pref_chan_cmp(const void *_a, const void *_b) +{ + const struct wpa_mbo_non_pref_channel *a = _a, *b = _b; + + if (a->oper_class != b->oper_class) + return a->oper_class - b->oper_class; + if (a->reason != b->reason) + return a->reason - b->reason; + return a->preference - b->preference; +} + + +int wpas_mbo_update_non_pref_chan(struct wpa_supplicant *wpa_s, + const char *non_pref_chan) +{ + char *cmd, *token, *context = NULL; + struct wpa_mbo_non_pref_channel *chans = NULL, *tmp_chans; + size_t num = 0, size = 0; + unsigned i; + + wpa_printf(MSG_DEBUG, "MBO: Update non-preferred channels, non_pref_chan=%s", + non_pref_chan ? non_pref_chan : "N/A"); + + /* + * The shortest channel configuration is 10 characters - commas, 3 + * colons, and 4 values that one of them (oper_class) is 2 digits or + * more. + */ + if (!non_pref_chan || os_strlen(non_pref_chan) < 10) + goto update; + + cmd = os_strdup(non_pref_chan); + if (!cmd) + return -1; + + while ((token = str_token(cmd, " ", &context))) { + struct wpa_mbo_non_pref_channel *chan; + int ret; + unsigned int _oper_class; + unsigned int _chan; + unsigned int _preference; + unsigned int _reason; + + if (num == size) { + size = size ? size * 2 : 1; + tmp_chans = os_realloc_array(chans, size, + sizeof(*chans)); + if (!tmp_chans) { + wpa_printf(MSG_ERROR, + "Couldn't reallocate non_pref_chan"); + goto fail; + } + chans = tmp_chans; + } + + chan = &chans[num]; + + ret = sscanf(token, "%u:%u:%u:%u", &_oper_class, + &_chan, &_preference, &_reason); + if (ret != 4 || + _oper_class > 255 || _chan > 255 || + _preference > 255 || _reason > 65535 ) { + wpa_printf(MSG_ERROR, "Invalid non-pref chan input %s", + token); + goto fail; + } + chan->oper_class = _oper_class; + chan->chan = _chan; + chan->preference = _preference; + chan->reason = _reason; + + if (wpas_mbo_validate_non_pref_chan(chan->oper_class, + chan->chan, chan->reason)) { + wpa_printf(MSG_ERROR, + "Invalid non_pref_chan: oper class %d chan %d reason %d", + chan->oper_class, chan->chan, chan->reason); + goto fail; + } + + for (i = 0; i < num; i++) + if (wpa_non_pref_chan_is_eq(chan, &chans[i])) + break; + if (i != num) { + wpa_printf(MSG_ERROR, + "oper class %d chan %d is duplicated", + chan->oper_class, chan->chan); + goto fail; + } + + num++; + } + + os_free(cmd); + + if (chans) { + qsort(chans, num, sizeof(struct wpa_mbo_non_pref_channel), + wpa_non_pref_chan_cmp); + } + +update: + os_free(wpa_s->non_pref_chan); + wpa_s->non_pref_chan = chans; + wpa_s->non_pref_chan_num = num; + wpas_mbo_non_pref_chan_changed(wpa_s); + + return 0; + +fail: + os_free(chans); + os_free(cmd); + return -1; +} + + +void wpas_mbo_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ie) +{ + wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC); + wpabuf_put_u8(ie, 7); + wpabuf_put_be24(ie, OUI_WFA); + wpabuf_put_u8(ie, MBO_OUI_TYPE); + + wpabuf_put_u8(ie, MBO_ATTR_ID_CELL_DATA_CAPA); + wpabuf_put_u8(ie, 1); + wpabuf_put_u8(ie, wpa_s->conf->mbo_cell_capa); +} + + +enum chan_allowed { + NOT_ALLOWED, ALLOWED +}; + +static enum chan_allowed allow_channel(struct hostapd_hw_modes *mode, u8 chan, + unsigned int *flags) +{ + int i; + + for (i = 0; i < mode->num_channels; i++) { + if (mode->channels[i].chan == chan) + break; + } + + if (i == mode->num_channels || + (mode->channels[i].flag & HOSTAPD_CHAN_DISABLED)) + return NOT_ALLOWED; + + if (flags) + *flags = mode->channels[i].flag; + + return ALLOWED; +} + + +static int get_center_80mhz(struct hostapd_hw_modes *mode, u8 channel) +{ + u8 center_channels[] = {42, 58, 106, 122, 138, 155}; + size_t i; + + if (mode->mode != HOSTAPD_MODE_IEEE80211A) + return 0; + + for (i = 0; i < ARRAY_SIZE(center_channels); i++) { + /* + * In 80 MHz, the bandwidth "spans" 12 channels (e.g., 36-48), + * so the center channel is 6 channels away from the start/end. + */ + if (channel >= center_channels[i] - 6 && + channel <= center_channels[i] + 6) + return center_channels[i]; + } + + return 0; +} + + +static enum chan_allowed verify_80mhz(struct hostapd_hw_modes *mode, u8 channel) +{ + u8 center_chan; + unsigned int i; + + center_chan = get_center_80mhz(mode, channel); + if (!center_chan) + return NOT_ALLOWED; + + /* check all the channels are available */ + for (i = 0; i < 4; i++) { + unsigned int flags; + u8 adj_chan = center_chan - 6 + i * 4; + + if (allow_channel(mode, adj_chan, &flags) == NOT_ALLOWED) + return NOT_ALLOWED; + + if ((i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_70)) || + (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_50)) || + (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_30)) || + (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_10))) + return NOT_ALLOWED; + } + + return ALLOWED; +} + + +static int get_center_160mhz(struct hostapd_hw_modes *mode, u8 channel) +{ + u8 center_channels[] = { 50, 114 }; + unsigned int i; + + if (mode->mode != HOSTAPD_MODE_IEEE80211A) + return 0; + + for (i = 0; i < ARRAY_SIZE(center_channels); i++) { + /* + * In 160 MHz, the bandwidth "spans" 28 channels (e.g., 36-64), + * so the center channel is 14 channels away from the start/end. + */ + if (channel >= center_channels[i] - 14 && + channel <= center_channels[i] + 14) + return center_channels[i]; + } + + return 0; +} + + +static enum chan_allowed verify_160mhz(struct hostapd_hw_modes *mode, + u8 channel) +{ + u8 center_chan; + unsigned int i; + + center_chan = get_center_160mhz(mode, channel); + if (!center_chan) + return NOT_ALLOWED; + + /* Check all the channels are available */ + for (i = 0; i < 8; i++) { + unsigned int flags; + u8 adj_chan = center_chan - 14 + i * 4; + + if (allow_channel(mode, adj_chan, &flags) == NOT_ALLOWED) + return NOT_ALLOWED; + + if ((i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_150)) || + (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_130)) || + (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_110)) || + (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_90)) || + (i == 4 && !(flags & HOSTAPD_CHAN_VHT_90_70)) || + (i == 5 && !(flags & HOSTAPD_CHAN_VHT_110_50)) || + (i == 6 && !(flags & HOSTAPD_CHAN_VHT_130_30)) || + (i == 7 && !(flags & HOSTAPD_CHAN_VHT_150_10))) + return NOT_ALLOWED; + } + + return ALLOWED; +} + + +static enum chan_allowed verify_channel(struct hostapd_hw_modes *mode, + u8 channel, u8 bw) +{ + unsigned int flag = 0; + enum chan_allowed res, res2; + + res2 = res = allow_channel(mode, channel, &flag); + if (bw == BW40MINUS) { + if (!(flag & HOSTAPD_CHAN_HT40MINUS)) + return NOT_ALLOWED; + res2 = allow_channel(mode, channel - 4, NULL); + } else if (bw == BW40PLUS) { + if (!(flag & HOSTAPD_CHAN_HT40PLUS)) + return NOT_ALLOWED; + res2 = allow_channel(mode, channel + 4, NULL); + } else if (bw == BW80) { + /* + * channel is a center channel and as such, not necessarily a + * valid 20 MHz channels. Override earlier allow_channel() + * result and use only the 80 MHz specific version. + */ + res2 = res = verify_80mhz(mode, channel); + } else if (bw == BW160) { + /* + * channel is a center channel and as such, not necessarily a + * valid 20 MHz channels. Override earlier allow_channel() + * result and use only the 160 MHz specific version. + */ + res2 = res = verify_160mhz(mode, channel); + } else if (bw == BW80P80) { + /* + * channel is a center channel and as such, not necessarily a + * valid 20 MHz channels. Override earlier allow_channel() + * result and use only the 80 MHz specific version. + */ + res2 = res = verify_80mhz(mode, channel); + } + + if (res == NOT_ALLOWED || res2 == NOT_ALLOWED) + return NOT_ALLOWED; + + return ALLOWED; +} + + +static int wpas_op_class_supported(struct wpa_supplicant *wpa_s, + const struct oper_class_map *op_class) +{ + int chan; + size_t i; + struct hostapd_hw_modes *mode; + int found; + + mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, op_class->mode); + if (!mode) + return 0; + + if (op_class->op_class == 128) { + u8 channels[] = { 42, 58, 106, 122, 138, 155 }; + + for (i = 0; i < ARRAY_SIZE(channels); i++) { + if (verify_channel(mode, channels[i], op_class->bw) == + ALLOWED) + return 1; + } + + return 0; + } + + if (op_class->op_class == 129) { + /* Check if either 160 MHz channels is allowed */ + return verify_channel(mode, 50, op_class->bw) == ALLOWED || + verify_channel(mode, 114, op_class->bw) == ALLOWED; + } + + if (op_class->op_class == 130) { + /* Need at least two non-contiguous 80 MHz segments */ + found = 0; + + if (verify_channel(mode, 42, op_class->bw) == ALLOWED || + verify_channel(mode, 58, op_class->bw) == ALLOWED) + found++; + if (verify_channel(mode, 106, op_class->bw) == ALLOWED || + verify_channel(mode, 122, op_class->bw) == ALLOWED || + verify_channel(mode, 138, op_class->bw) == ALLOWED) + found++; + if (verify_channel(mode, 106, op_class->bw) == ALLOWED && + verify_channel(mode, 138, op_class->bw) == ALLOWED) + found++; + if (verify_channel(mode, 155, op_class->bw) == ALLOWED) + found++; + + if (found >= 2) + return 1; + + return 0; + } + + found = 0; + for (chan = op_class->min_chan; chan <= op_class->max_chan; + chan += op_class->inc) { + if (verify_channel(mode, chan, op_class->bw) == ALLOWED) { + found = 1; + break; + } + } + + return found; +} + + +int wpas_mbo_supp_op_class_ie(struct wpa_supplicant *wpa_s, int freq, u8 *pos, + size_t len) +{ + struct wpabuf *buf; + u8 op, current, chan; + u8 *ie_len; + int res; + + /* + * Assume 20 MHz channel for now. + * TODO: Use the secondary channel and VHT channel width that will be + * used after association. + */ + if (ieee80211_freq_to_channel_ext(freq, 0, VHT_CHANWIDTH_USE_HT, + ¤t, &chan) == NUM_HOSTAPD_MODES) + return 0; + + /* + * Need 3 bytes for EID, length, and current operating class, plus + * 1 byte for every other supported operating class. + */ + buf = wpabuf_alloc(global_op_class_size + 3); + if (!buf) + return 0; + + wpabuf_put_u8(buf, WLAN_EID_SUPPORTED_OPERATING_CLASSES); + /* Will set the length later, putting a placeholder */ + ie_len = wpabuf_put(buf, 1); + wpabuf_put_u8(buf, current); + + for (op = 0; global_op_class[op].op_class; op++) { + if (wpas_op_class_supported(wpa_s, &global_op_class[op])) + wpabuf_put_u8(buf, global_op_class[op].op_class); + } + + *ie_len = wpabuf_len(buf) - 2; + if (*ie_len < 2 || wpabuf_len(buf) > len) { + wpa_printf(MSG_ERROR, + "Failed to add supported operating classes IE"); + res = 0; + } else { + os_memcpy(pos, wpabuf_head(buf), wpabuf_len(buf)); + res = wpabuf_len(buf); + wpa_hexdump_buf(MSG_DEBUG, + "MBO: Added supported operating classes IE", + buf); + } + + wpabuf_free(buf); + return res; +} + + +void wpas_mbo_ie_trans_req(struct wpa_supplicant *wpa_s, const u8 *mbo_ie, + size_t len) +{ + const u8 *pos, *cell_pref = NULL, *reason = NULL; + u8 id, elen; + u16 disallowed_sec = 0; + + if (len <= 4 || WPA_GET_BE24(mbo_ie) != OUI_WFA || + mbo_ie[3] != MBO_OUI_TYPE) + return; + + pos = mbo_ie + 4; + len -= 4; + + while (len >= 2) { + id = *pos++; + elen = *pos++; + len -= 2; + + if (elen > len) + goto fail; + + switch (id) { + case MBO_ATTR_ID_CELL_DATA_PREF: + if (elen != 1) + goto fail; + + if (wpa_s->conf->mbo_cell_capa == + MBO_CELL_CAPA_AVAILABLE) + cell_pref = pos; + else + wpa_printf(MSG_DEBUG, + "MBO: Station does not support Cellular data connection"); + break; + case MBO_ATTR_ID_TRANSITION_REASON: + if (elen != 1) + goto fail; + + reason = pos; + break; + case MBO_ATTR_ID_ASSOC_RETRY_DELAY: + if (elen != 2) + goto fail; + + if (wpa_s->wnm_mode & + WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) { + wpa_printf(MSG_DEBUG, + "MBO: Unexpected association retry delay, BSS is terminating"); + goto fail; + } else if (wpa_s->wnm_mode & + WNM_BSS_TM_REQ_DISASSOC_IMMINENT) { + disallowed_sec = WPA_GET_LE16(pos); + } else { + wpa_printf(MSG_DEBUG, + "MBO: Association retry delay attribute not in disassoc imminent mode"); + } + + break; + case MBO_ATTR_ID_AP_CAPA_IND: + case MBO_ATTR_ID_NON_PREF_CHAN_REPORT: + case MBO_ATTR_ID_CELL_DATA_CAPA: + case MBO_ATTR_ID_ASSOC_DISALLOW: + case MBO_ATTR_ID_TRANSITION_REJECT_REASON: + wpa_printf(MSG_DEBUG, + "MBO: Attribute %d should not be included in BTM Request frame", + id); + break; + default: + wpa_printf(MSG_DEBUG, "MBO: Unknown attribute id %u", + id); + return; + } + + pos += elen; + len -= elen; + } + + if (cell_pref) + wpa_msg(wpa_s, MSG_INFO, MBO_CELL_PREFERENCE "preference=%u", + *cell_pref); + + if (reason) + wpa_msg(wpa_s, MSG_INFO, MBO_TRANSITION_REASON "reason=%u", + *reason); + + if (disallowed_sec && wpa_s->current_bss) + wpa_bss_tmp_disallow(wpa_s, wpa_s->current_bss->bssid, + disallowed_sec); + + return; +fail: + wpa_printf(MSG_DEBUG, "MBO IE parsing failed (id=%u len=%u left=%zu)", + id, elen, len); +} + + +size_t wpas_mbo_ie_bss_trans_reject(struct wpa_supplicant *wpa_s, u8 *pos, + size_t len, + enum mbo_transition_reject_reason reason) +{ + u8 reject_attr[3]; + + reject_attr[0] = MBO_ATTR_ID_TRANSITION_REJECT_REASON; + reject_attr[1] = 1; + reject_attr[2] = reason; + + return mbo_add_ie(pos, len, reject_attr, sizeof(reject_attr)); +} + + +void wpas_mbo_update_cell_capa(struct wpa_supplicant *wpa_s, u8 mbo_cell_capa) +{ + u8 cell_capa[7]; + + if (wpa_s->conf->mbo_cell_capa == mbo_cell_capa) { + wpa_printf(MSG_DEBUG, + "MBO: Cellular capability already set to %u", + mbo_cell_capa); + return; + } + + wpa_s->conf->mbo_cell_capa = mbo_cell_capa; + + cell_capa[0] = WLAN_EID_VENDOR_SPECIFIC; + cell_capa[1] = 5; /* Length */ + WPA_PUT_BE24(cell_capa + 2, OUI_WFA); + cell_capa[5] = MBO_ATTR_ID_CELL_DATA_CAPA; + cell_capa[6] = mbo_cell_capa; + + wpas_mbo_send_wnm_notification(wpa_s, cell_capa, 7); + wpa_supplicant_set_default_scan_ies(wpa_s); +} + + +struct wpabuf * mbo_build_anqp_buf(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss) +{ + struct wpabuf *anqp_buf; + u8 *len_pos; + + if (!wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE)) { + wpa_printf(MSG_INFO, "MBO: " MACSTR + " does not support MBO - cannot request MBO ANQP elements from it", + MAC2STR(bss->bssid)); + return NULL; + } + + anqp_buf = wpabuf_alloc(10); + if (!anqp_buf) + return NULL; + + len_pos = gas_anqp_add_element(anqp_buf, ANQP_VENDOR_SPECIFIC); + wpabuf_put_be24(anqp_buf, OUI_WFA); + wpabuf_put_u8(anqp_buf, MBO_ANQP_OUI_TYPE); + + wpabuf_put_u8(anqp_buf, MBO_ANQP_SUBTYPE_CELL_CONN_PREF); + gas_anqp_set_element_len(anqp_buf, len_pos); + + return anqp_buf; +} diff --git a/wpa_supplicant/mesh.c b/wpa_supplicant/mesh.c index 77f708b42daa9..d67d3b2aa390e 100644 --- a/wpa_supplicant/mesh.c +++ b/wpa_supplicant/mesh.c @@ -66,9 +66,11 @@ void wpa_supplicant_mesh_iface_deinit(struct wpa_supplicant *wpa_s, } -static struct mesh_conf * mesh_config_create(struct wpa_ssid *ssid) +static struct mesh_conf * mesh_config_create(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) { struct mesh_conf *conf; + int cipher; conf = os_zalloc(sizeof(struct mesh_conf)); if (!conf) @@ -82,6 +84,33 @@ static struct mesh_conf * mesh_config_create(struct wpa_ssid *ssid) MESH_CONF_SEC_AMPE; else conf->security |= MESH_CONF_SEC_NONE; + conf->ieee80211w = ssid->ieee80211w; + if (conf->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT) { + if (wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_BIP) + conf->ieee80211w = wpa_s->conf->pmf; + else + conf->ieee80211w = NO_MGMT_FRAME_PROTECTION; + } + + cipher = wpa_pick_pairwise_cipher(ssid->pairwise_cipher, 0); + if (cipher < 0 || cipher == WPA_CIPHER_TKIP) { + wpa_msg(wpa_s, MSG_INFO, "mesh: Invalid pairwise cipher"); + os_free(conf); + return NULL; + } + conf->pairwise_cipher = cipher; + + cipher = wpa_pick_group_cipher(ssid->group_cipher); + if (cipher < 0 || cipher == WPA_CIPHER_TKIP || + cipher == WPA_CIPHER_GTK_NOT_USED) { + wpa_msg(wpa_s, MSG_INFO, "mesh: Invalid group cipher"); + os_free(conf); + return NULL; + } + + conf->group_cipher = cipher; + if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) + conf->mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC; /* defaults */ conf->mesh_pp_id = MESH_PATH_PROTOCOL_HWMP; @@ -149,6 +178,7 @@ static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s, ifmsh->bss[0] = bss = os_zalloc(sizeof(struct hostapd_data)); if (!bss) goto out_free; + dl_list_init(&bss->nr_db); os_memcpy(bss->own_addr, wpa_s->own_addr, ETH_ALEN); bss->driver = wpa_s->driver; @@ -175,24 +205,41 @@ static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s, wpa_s->conf->dot11RSNASAERetransPeriod; os_strlcpy(bss->conf->iface, wpa_s->ifname, sizeof(bss->conf->iface)); - mconf = mesh_config_create(ssid); + mconf = mesh_config_create(wpa_s, ssid); if (!mconf) goto out_free; ifmsh->mconf = mconf; /* need conf->hw_mode for supported rates. */ - if (ssid->frequency == 0) { - conf->hw_mode = HOSTAPD_MODE_IEEE80211G; - conf->channel = 1; - } else { - conf->hw_mode = ieee80211_freq_to_chan(ssid->frequency, - &conf->channel); - } + conf->hw_mode = ieee80211_freq_to_chan(ssid->frequency, &conf->channel); if (conf->hw_mode == NUM_HOSTAPD_MODES) { wpa_printf(MSG_ERROR, "Unsupported mesh mode frequency: %d MHz", ssid->frequency); goto out_free; } + if (ssid->ht40) + conf->secondary_channel = ssid->ht40; + if (conf->hw_mode == HOSTAPD_MODE_IEEE80211A && ssid->vht) { + conf->vht_oper_chwidth = ssid->max_oper_chwidth; + switch (conf->vht_oper_chwidth) { + case VHT_CHANWIDTH_80MHZ: + case VHT_CHANWIDTH_80P80MHZ: + ieee80211_freq_to_chan( + ssid->frequency, + &conf->vht_oper_centr_freq_seg0_idx); + conf->vht_oper_centr_freq_seg0_idx += ssid->ht40 * 2; + break; + case VHT_CHANWIDTH_160MHZ: + ieee80211_freq_to_chan( + ssid->frequency, + &conf->vht_oper_centr_freq_seg0_idx); + conf->vht_oper_centr_freq_seg0_idx += ssid->ht40 * 2; + conf->vht_oper_centr_freq_seg0_idx += 40 / 5; + break; + } + ieee80211_freq_to_chan(ssid->vht_center_freq2, + &conf->vht_oper_centr_freq_seg1_idx); + } if (ssid->mesh_basic_rates == NULL) { /* @@ -318,16 +365,47 @@ int wpa_supplicant_join_mesh(struct wpa_supplicant *wpa_s, wpa_supplicant_mesh_deinit(wpa_s); + wpa_s->pairwise_cipher = WPA_CIPHER_NONE; + wpa_s->group_cipher = WPA_CIPHER_NONE; + wpa_s->mgmt_group_cipher = 0; + os_memset(¶ms, 0, sizeof(params)); params.meshid = ssid->ssid; params.meshid_len = ssid->ssid_len; ibss_mesh_setup_freq(wpa_s, ssid, ¶ms.freq); wpa_s->mesh_ht_enabled = !!params.freq.ht_enabled; + wpa_s->mesh_vht_enabled = !!params.freq.vht_enabled; + if (params.freq.ht_enabled && params.freq.sec_channel_offset) + ssid->ht40 = params.freq.sec_channel_offset; + if (wpa_s->mesh_vht_enabled) { + ssid->vht = 1; + switch (params.freq.bandwidth) { + case 80: + if (params.freq.center_freq2) { + ssid->max_oper_chwidth = VHT_CHANWIDTH_80P80MHZ; + ssid->vht_center_freq2 = + params.freq.center_freq2; + } else { + ssid->max_oper_chwidth = VHT_CHANWIDTH_80MHZ; + } + break; + case 160: + ssid->max_oper_chwidth = VHT_CHANWIDTH_160MHZ; + break; + default: + ssid->max_oper_chwidth = VHT_CHANWIDTH_USE_HT; + break; + } + } if (ssid->beacon_int > 0) params.beacon_int = ssid->beacon_int; else if (wpa_s->conf->beacon_int > 0) params.beacon_int = wpa_s->conf->beacon_int; - params.max_peer_links = wpa_s->conf->max_peer_links; + if (ssid->dtim_period > 0) + params.dtim_period = ssid->dtim_period; + else if (wpa_s->conf->dtim_period > 0) + params.dtim_period = wpa_s->conf->dtim_period; + params.conf.max_peer_links = wpa_s->conf->max_peer_links; if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) { params.flags |= WPA_DRIVER_MESH_FLAG_SAE_AUTH; @@ -337,10 +415,10 @@ int wpa_supplicant_join_mesh(struct wpa_supplicant *wpa_s, if (wpa_s->conf->user_mpm) { params.flags |= WPA_DRIVER_MESH_FLAG_USER_MPM; - params.conf.flags &= ~WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS; + params.conf.auto_plinks = 0; } else { params.flags |= WPA_DRIVER_MESH_FLAG_DRIVER_MPM; - params.conf.flags |= WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS; + params.conf.auto_plinks = 1; } params.conf.peer_link_timeout = wpa_s->conf->mesh_max_inactivity; @@ -351,21 +429,32 @@ int wpa_supplicant_join_mesh(struct wpa_supplicant *wpa_s, goto out; } + if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) { + wpa_s->pairwise_cipher = wpa_s->mesh_rsn->pairwise_cipher; + wpa_s->group_cipher = wpa_s->mesh_rsn->group_cipher; + wpa_s->mgmt_group_cipher = wpa_s->mesh_rsn->mgmt_group_cipher; + } + if (wpa_s->ifmsh) { params.ies = wpa_s->ifmsh->mconf->rsn_ie; params.ie_len = wpa_s->ifmsh->mconf->rsn_ie_len; params.basic_rates = wpa_s->ifmsh->basic_rates; + params.conf.flags |= WPA_DRIVER_MESH_CONF_FLAG_HT_OP_MODE; + params.conf.ht_opmode = wpa_s->ifmsh->bss[0]->iface->ht_op_mode; } wpa_msg(wpa_s, MSG_INFO, "joining mesh %s", wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); ret = wpa_drv_join_mesh(wpa_s, ¶ms); if (ret) - wpa_msg(wpa_s, MSG_ERROR, "mesh join error=%d\n", ret); + wpa_msg(wpa_s, MSG_ERROR, "mesh join error=%d", ret); /* hostapd sets the interface down until we associate */ wpa_drv_set_operstate(wpa_s, 1); + if (!ret) + wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); + out: return ret; } @@ -535,9 +624,22 @@ int wpas_mesh_add_interface(struct wpa_supplicant *wpa_s, char *ifname, if (!mesh_wpa_s) { wpa_printf(MSG_ERROR, "mesh: Failed to create new wpa_supplicant interface"); - wpa_supplicant_remove_iface(wpa_s->global, wpa_s, 0); + wpa_drv_if_remove(wpa_s, WPA_IF_MESH, ifname); return -1; } mesh_wpa_s->mesh_if_created = 1; return 0; } + + +int wpas_mesh_peer_remove(struct wpa_supplicant *wpa_s, const u8 *addr) +{ + return mesh_mpm_close_peer(wpa_s, addr); +} + + +int wpas_mesh_peer_add(struct wpa_supplicant *wpa_s, const u8 *addr, + int duration) +{ + return mesh_mpm_connect_peer(wpa_s, addr, duration); +} diff --git a/wpa_supplicant/mesh.h b/wpa_supplicant/mesh.h index 3cb7f1b1364f7..7317083c99cd8 100644 --- a/wpa_supplicant/mesh.h +++ b/wpa_supplicant/mesh.h @@ -18,6 +18,9 @@ int wpas_mesh_scan_result_text(const u8 *ies, size_t ies_len, char *buf, char *end); int wpas_mesh_add_interface(struct wpa_supplicant *wpa_s, char *ifname, size_t len); +int wpas_mesh_peer_remove(struct wpa_supplicant *wpa_s, const u8 *addr); +int wpas_mesh_peer_add(struct wpa_supplicant *wpa_s, const u8 *addr, + int duration); #ifdef CONFIG_MESH diff --git a/wpa_supplicant/mesh_mpm.c b/wpa_supplicant/mesh_mpm.c index f81b88c89401b..d14c7e3b20458 100644 --- a/wpa_supplicant/mesh_mpm.c +++ b/wpa_supplicant/mesh_mpm.c @@ -14,17 +14,18 @@ #include "ap/hostapd.h" #include "ap/sta_info.h" #include "ap/ieee802_11.h" +#include "ap/wpa_auth.h" #include "wpa_supplicant_i.h" #include "driver_i.h" #include "mesh_mpm.h" #include "mesh_rsn.h" struct mesh_peer_mgmt_ie { - const u8 *proto_id; - const u8 *llid; - const u8 *plid; - const u8 *reason; - const u8 *pmk; + const u8 *proto_id; /* Mesh Peering Protocol Identifier (2 octets) */ + const u8 *llid; /* Local Link ID (2 octets) */ + const u8 *plid; /* Peer Link ID (conditional, 2 octets) */ + const u8 *reason; /* Reason Code (conditional, 2 octets) */ + const u8 *chosen_pmk; /* Chosen PMK (optional, 16 octets) */ }; static void plink_timer(void *eloop_ctx, void *user_data); @@ -34,18 +35,17 @@ enum plink_event { PLINK_UNDEFINED, OPN_ACPT, OPN_RJCT, - OPN_IGNR, CNF_ACPT, CNF_RJCT, - CNF_IGNR, CLS_ACPT, - CLS_IGNR + REQ_RJCT }; static const char * const mplstate[] = { - [PLINK_LISTEN] = "LISTEN", - [PLINK_OPEN_SENT] = "OPEN_SENT", - [PLINK_OPEN_RCVD] = "OPEN_RCVD", + [0] = "UNINITIALIZED", + [PLINK_IDLE] = "IDLE", + [PLINK_OPN_SNT] = "OPN_SNT", + [PLINK_OPN_RCVD] = "OPN_RCVD", [PLINK_CNF_RCVD] = "CNF_RCVD", [PLINK_ESTAB] = "ESTAB", [PLINK_HOLDING] = "HOLDING", @@ -56,12 +56,10 @@ static const char * const mplevent[] = { [PLINK_UNDEFINED] = "UNDEFINED", [OPN_ACPT] = "OPN_ACPT", [OPN_RJCT] = "OPN_RJCT", - [OPN_IGNR] = "OPN_IGNR", [CNF_ACPT] = "CNF_ACPT", [CNF_RJCT] = "CNF_RJCT", - [CNF_IGNR] = "CNF_IGNR", [CLS_ACPT] = "CLS_ACPT", - [CLS_IGNR] = "CLS_IGNR" + [REQ_RJCT] = "REQ_RJCT", }; @@ -72,10 +70,10 @@ static int mesh_mpm_parse_peer_mgmt(struct wpa_supplicant *wpa_s, { os_memset(mpm_ie, 0, sizeof(*mpm_ie)); - /* remove optional PMK at end */ - if (len >= 16) { - len -= 16; - mpm_ie->pmk = ie + len - 16; + /* Remove optional Chosen PMK field at end */ + if (len >= SAE_PMKID_LEN) { + mpm_ie->chosen_pmk = ie + len - SAE_PMKID_LEN; + len -= SAE_PMKID_LEN; } if ((action_field == PLINK_OPEN && len != 4) || @@ -101,8 +99,8 @@ static int mesh_mpm_parse_peer_mgmt(struct wpa_supplicant *wpa_s, len -= 2; } - /* plid, present for confirm, and possibly close */ - if (len) + /* Peer Link ID, present for confirm, and possibly close */ + if (len >= 2) mpm_ie->plid = ie; return 0; @@ -193,12 +191,13 @@ static void mesh_mpm_init_link(struct wpa_supplicant *wpa_s, sta->my_lid = llid; sta->peer_lid = 0; + sta->peer_aid = 0; /* * We do not use wpa_mesh_set_plink_state() here because there is no * entry in kernel yet. */ - sta->plink_state = PLINK_LISTEN; + sta->plink_state = PLINK_IDLE; } @@ -212,9 +211,6 @@ static void mesh_mpm_send_plink_action(struct wpa_supplicant *wpa_s, struct hostapd_data *bss = ifmsh->bss[0]; struct mesh_conf *conf = ifmsh->mconf; u8 supp_rates[2 + 2 + 32]; -#ifdef CONFIG_IEEE80211N - u8 ht_capa_oper[2 + 26 + 2 + 22]; -#endif /* CONFIG_IEEE80211N */ u8 *pos, *cat; u8 ie_len, add_plid = 0; int ret; @@ -239,6 +235,12 @@ static void mesh_mpm_send_plink_action(struct wpa_supplicant *wpa_s, 2 + 22; /* HT operation */ } #endif /* CONFIG_IEEE80211N */ +#ifdef CONFIG_IEEE80211AC + if (type != PLINK_CLOSE && wpa_s->mesh_vht_enabled) { + buf_len += 2 + 12 + /* VHT Capabilities */ + 2 + 5; /* VHT Operation */ + } +#endif /* CONFIG_IEEE80211AC */ if (type != PLINK_CLOSE) buf_len += conf->rsn_ie_len; /* RSN IE */ @@ -258,7 +260,7 @@ static void mesh_mpm_send_plink_action(struct wpa_supplicant *wpa_s, /* aid */ if (type == PLINK_CONFIRM) - wpabuf_put_le16(buf, sta->peer_lid); + wpabuf_put_le16(buf, sta->aid); /* IE: supp + ext. supp rates */ pos = hostapd_eid_supp_rates(bss, supp_rates); @@ -285,7 +287,8 @@ static void mesh_mpm_send_plink_action(struct wpa_supplicant *wpa_s, /* TODO: Add Connected to Mesh Gate/AS subfields */ wpabuf_put_u8(buf, info); /* always forwarding & accepting plinks for now */ - wpabuf_put_u8(buf, 0x1 | 0x8); + wpabuf_put_u8(buf, MESH_CAP_ACCEPT_ADDITIONAL_PEER | + MESH_CAP_FORWARDING); } else { /* Peer closing frame */ /* IE: Mesh ID */ wpabuf_put_u8(buf, WLAN_EID_MESH_ID); @@ -334,11 +337,22 @@ static void mesh_mpm_send_plink_action(struct wpa_supplicant *wpa_s, #ifdef CONFIG_IEEE80211N if (type != PLINK_CLOSE && wpa_s->mesh_ht_enabled) { + u8 ht_capa_oper[2 + 26 + 2 + 22]; + pos = hostapd_eid_ht_capabilities(bss, ht_capa_oper); pos = hostapd_eid_ht_operation(bss, pos); wpabuf_put_data(buf, ht_capa_oper, pos - ht_capa_oper); } #endif /* CONFIG_IEEE80211N */ +#ifdef CONFIG_IEEE80211AC + if (type != PLINK_CLOSE && wpa_s->mesh_vht_enabled) { + u8 vht_capa_oper[2 + 12 + 2 + 5]; + + pos = hostapd_eid_vht_capabilities(bss, vht_capa_oper, 0); + pos = hostapd_eid_vht_operation(bss, pos); + wpabuf_put_data(buf, vht_capa_oper, pos - vht_capa_oper); + } +#endif /* CONFIG_IEEE80211AC */ if (ampe && mesh_rsn_protect_frame(wpa_s->mesh_rsn, sta, cat, buf)) { wpa_msg(wpa_s, MSG_INFO, @@ -346,6 +360,9 @@ static void mesh_mpm_send_plink_action(struct wpa_supplicant *wpa_s, goto fail; } + wpa_msg(wpa_s, MSG_DEBUG, "Mesh MPM: Sending peering frame type %d to " + MACSTR " (my_lid=0x%x peer_lid=0x%x)", + type, MAC2STR(sta->addr), sta->my_lid, sta->peer_lid); ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, sta->addr, wpa_s->own_addr, wpa_s->own_addr, wpabuf_head(buf), wpabuf_len(buf), 0); @@ -366,15 +383,17 @@ void wpa_mesh_set_plink_state(struct wpa_supplicant *wpa_s, struct hostapd_sta_add_params params; int ret; + wpa_msg(wpa_s, MSG_DEBUG, "MPM set " MACSTR " from %s into %s", + MAC2STR(sta->addr), mplstate[sta->plink_state], + mplstate[state]); sta->plink_state = state; os_memset(¶ms, 0, sizeof(params)); params.addr = sta->addr; params.plink_state = state; + params.peer_aid = sta->peer_aid; params.set = 1; - wpa_msg(wpa_s, MSG_DEBUG, "MPM set " MACSTR " into %s", - MAC2STR(sta->addr), mplstate[state]); ret = wpa_drv_sta_add(wpa_s, ¶ms); if (ret) { wpa_msg(wpa_s, MSG_ERROR, "Driver failed to set " MACSTR @@ -400,10 +419,11 @@ static void plink_timer(void *eloop_ctx, void *user_data) struct sta_info *sta = user_data; u16 reason = 0; struct mesh_conf *conf = wpa_s->ifmsh->mconf; + struct hostapd_data *hapd = wpa_s->ifmsh->bss[0]; switch (sta->plink_state) { - case PLINK_OPEN_RCVD: - case PLINK_OPEN_SENT: + case PLINK_OPN_RCVD: + case PLINK_OPN_SNT: /* retry timer */ if (sta->mpm_retries < conf->dot11MeshMaxRetries) { eloop_register_timeout( @@ -429,6 +449,13 @@ static void plink_timer(void *eloop_ctx, void *user_data) break; case PLINK_HOLDING: /* holding timer */ + + if (sta->mesh_sae_pmksa_caching) { + wpa_printf(MSG_DEBUG, "MPM: Peer " MACSTR + " looks like it does not support mesh SAE PMKSA caching, so remove the cached entry for it", + MAC2STR(sta->addr)); + wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr); + } mesh_mpm_fsm_restart(wpa_s, sta); break; default: @@ -453,8 +480,8 @@ mesh_mpm_plink_open(struct wpa_supplicant *wpa_s, struct sta_info *sta, } -int mesh_mpm_plink_close(struct hostapd_data *hapd, - struct sta_info *sta, void *ctx) +static int mesh_mpm_plink_close(struct hostapd_data *hapd, struct sta_info *sta, + void *ctx) { struct wpa_supplicant *wpa_s = ctx; int reason = WLAN_REASON_MESH_PEERING_CANCELLED; @@ -472,6 +499,85 @@ int mesh_mpm_plink_close(struct hostapd_data *hapd, } +int mesh_mpm_close_peer(struct wpa_supplicant *wpa_s, const u8 *addr) +{ + struct hostapd_data *hapd; + struct sta_info *sta; + + if (!wpa_s->ifmsh) { + wpa_msg(wpa_s, MSG_INFO, "Mesh is not prepared yet"); + return -1; + } + + hapd = wpa_s->ifmsh->bss[0]; + sta = ap_get_sta(hapd, addr); + if (!sta) { + wpa_msg(wpa_s, MSG_INFO, "No such mesh peer"); + return -1; + } + + return mesh_mpm_plink_close(hapd, sta, wpa_s) == 0 ? 0 : -1; +} + + +static void peer_add_timer(void *eloop_ctx, void *user_data) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct hostapd_data *hapd = wpa_s->ifmsh->bss[0]; + + os_memset(hapd->mesh_required_peer, 0, ETH_ALEN); +} + + +int mesh_mpm_connect_peer(struct wpa_supplicant *wpa_s, const u8 *addr, + int duration) +{ + struct wpa_ssid *ssid = wpa_s->current_ssid; + struct hostapd_data *hapd; + struct sta_info *sta; + struct mesh_conf *conf; + + if (!wpa_s->ifmsh) { + wpa_msg(wpa_s, MSG_INFO, "Mesh is not prepared yet"); + return -1; + } + + if (!ssid || !ssid->no_auto_peer) { + wpa_msg(wpa_s, MSG_INFO, + "This command is available only with no_auto_peer mesh network"); + return -1; + } + + hapd = wpa_s->ifmsh->bss[0]; + conf = wpa_s->ifmsh->mconf; + + sta = ap_get_sta(hapd, addr); + if (!sta) { + wpa_msg(wpa_s, MSG_INFO, "No such mesh peer"); + return -1; + } + + if ((PLINK_OPN_SNT <= sta->plink_state && + sta->plink_state <= PLINK_ESTAB) || + (sta->sae && sta->sae->state > SAE_NOTHING)) { + wpa_msg(wpa_s, MSG_INFO, + "Specified peer is connecting/connected"); + return -1; + } + + if (conf->security == MESH_CONF_SEC_NONE) { + mesh_mpm_plink_open(wpa_s, sta, PLINK_OPN_SNT); + } else { + mesh_rsn_auth_sae_sta(wpa_s, sta); + os_memcpy(hapd->mesh_required_peer, addr, ETH_ALEN); + eloop_register_timeout(duration == -1 ? 10 : duration, 0, + peer_add_timer, wpa_s, NULL); + } + + return 0; +} + + void mesh_mpm_deinit(struct wpa_supplicant *wpa_s, struct hostapd_iface *ifmsh) { struct hostapd_data *hapd = ifmsh->bss[0]; @@ -481,6 +587,7 @@ void mesh_mpm_deinit(struct wpa_supplicant *wpa_s, struct hostapd_iface *ifmsh) hapd->num_plinks = 0; hostapd_free_stas(hapd); + eloop_cancel_timeout(peer_add_timer, wpa_s, NULL); } @@ -522,7 +629,7 @@ void mesh_mpm_auth_peer(struct wpa_supplicant *wpa_s, const u8 *addr) if (!sta->my_lid) mesh_mpm_init_link(wpa_s, sta); - mesh_mpm_plink_open(wpa_s, sta, PLINK_OPEN_SENT); + mesh_mpm_plink_open(wpa_s, sta, PLINK_OPN_SNT); } /* @@ -541,6 +648,14 @@ static struct sta_info * mesh_mpm_add_peer(struct wpa_supplicant *wpa_s, struct sta_info *sta; int ret; + if (elems->mesh_config_len >= 7 && + !(elems->mesh_config[6] & MESH_CAP_ACCEPT_ADDITIONAL_PEER)) { + wpa_msg(wpa_s, MSG_DEBUG, + "mesh: Ignore a crowded peer " MACSTR, + MAC2STR(addr)); + return NULL; + } + sta = ap_get_sta(data, addr); if (!sta) { sta = ap_sta_add(data, addr); @@ -548,28 +663,45 @@ static struct sta_info * mesh_mpm_add_peer(struct wpa_supplicant *wpa_s, return NULL; } + /* Set WMM by default since Mesh STAs are QoS STAs */ + sta->flags |= WLAN_STA_WMM; + /* initialize sta */ if (copy_supp_rates(wpa_s, sta, elems)) { ap_free_sta(data, sta); return NULL; } - mesh_mpm_init_link(wpa_s, sta); + if (!sta->my_lid) + mesh_mpm_init_link(wpa_s, sta); #ifdef CONFIG_IEEE80211N copy_sta_ht_capab(data, sta, elems->ht_capabilities); update_ht_state(data, sta); #endif /* CONFIG_IEEE80211N */ +#ifdef CONFIG_IEEE80211AC + copy_sta_vht_capab(data, sta, elems->vht_capabilities); + set_sta_vht_opmode(data, sta, elems->vht_opmode_notif); +#endif /* CONFIG_IEEE80211AC */ + + if (hostapd_get_aid(data, sta) < 0) { + wpa_msg(wpa_s, MSG_ERROR, "No AIDs available"); + ap_free_sta(data, sta); + return NULL; + } + /* insert into driver */ os_memset(¶ms, 0, sizeof(params)); params.supp_rates = sta->supported_rates; params.supp_rates_len = sta->supported_rates_len; params.addr = addr; params.plink_state = sta->plink_state; - params.aid = sta->peer_lid; + params.aid = sta->aid; + params.peer_aid = sta->peer_aid; params.listen_interval = 100; params.ht_capabilities = sta->ht_capabilities; + params.vht_capabilities = sta->vht_capabilities; params.flags |= WPA_STA_WMM; params.flags_mask |= WPA_STA_AUTHENTICATED; if (conf->security == MESH_CONF_SEC_NONE) { @@ -605,7 +737,9 @@ void wpa_mesh_new_mesh_peer(struct wpa_supplicant *wpa_s, const u8 *addr, if (!sta) return; - if (ssid && ssid->no_auto_peer) { + if (ssid && ssid->no_auto_peer && + (is_zero_ether_addr(data->mesh_required_peer) || + os_memcmp(data->mesh_required_peer, addr, ETH_ALEN) != 0)) { wpa_msg(wpa_s, MSG_INFO, "will not initiate new peer link with " MACSTR " because of no_auto_peer", MAC2STR(addr)); if (data->mesh_pending_auth) { @@ -634,10 +768,13 @@ void wpa_mesh_new_mesh_peer(struct wpa_supplicant *wpa_s, const u8 *addr, return; } - if (conf->security == MESH_CONF_SEC_NONE) - mesh_mpm_plink_open(wpa_s, sta, PLINK_OPEN_SENT); - else + if (conf->security == MESH_CONF_SEC_NONE) { + if (sta->plink_state < PLINK_OPN_SNT || + sta->plink_state > PLINK_ESTAB) + mesh_mpm_plink_open(wpa_s, sta, PLINK_OPN_SNT); + } else { mesh_rsn_auth_sae_sta(wpa_s, sta); + } } @@ -664,64 +801,85 @@ static void mesh_mpm_plink_estab(struct wpa_supplicant *wpa_s, MAC2STR(sta->addr)); if (conf->security & MESH_CONF_SEC_AMPE) { - wpa_drv_set_key(wpa_s, WPA_ALG_CCMP, sta->addr, 0, 0, - seq, sizeof(seq), sta->mtk, sizeof(sta->mtk)); - wpa_drv_set_key(wpa_s, WPA_ALG_CCMP, sta->addr, 1, 0, - seq, sizeof(seq), - sta->mgtk, sizeof(sta->mgtk)); - wpa_drv_set_key(wpa_s, WPA_ALG_IGTK, sta->addr, 4, 0, - seq, sizeof(seq), - sta->mgtk, sizeof(sta->mgtk)); - - wpa_hexdump_key(MSG_DEBUG, "mtk:", sta->mtk, sizeof(sta->mtk)); - wpa_hexdump_key(MSG_DEBUG, "mgtk:", - sta->mgtk, sizeof(sta->mgtk)); + wpa_hexdump_key(MSG_DEBUG, "mesh: MTK", sta->mtk, sta->mtk_len); + wpa_drv_set_key(wpa_s, wpa_cipher_to_alg(conf->pairwise_cipher), + sta->addr, 0, 0, seq, sizeof(seq), + sta->mtk, sta->mtk_len); + + wpa_hexdump_key(MSG_DEBUG, "mesh: RX MGTK Key RSC", + sta->mgtk_rsc, sizeof(sta->mgtk_rsc)); + wpa_hexdump_key(MSG_DEBUG, "mesh: RX MGTK", + sta->mgtk, sta->mgtk_len); + wpa_drv_set_key(wpa_s, wpa_cipher_to_alg(conf->group_cipher), + sta->addr, sta->mgtk_key_id, 0, + sta->mgtk_rsc, sizeof(sta->mgtk_rsc), + sta->mgtk, sta->mgtk_len); + + if (sta->igtk_len) { + wpa_hexdump_key(MSG_DEBUG, "mesh: RX IGTK Key RSC", + sta->igtk_rsc, sizeof(sta->igtk_rsc)); + wpa_hexdump_key(MSG_DEBUG, "mesh: RX IGTK", + sta->igtk, sta->igtk_len); + wpa_drv_set_key( + wpa_s, + wpa_cipher_to_alg(conf->mgmt_group_cipher), + sta->addr, sta->igtk_key_id, 0, + sta->igtk_rsc, sizeof(sta->igtk_rsc), + sta->igtk, sta->igtk_len); + } } wpa_mesh_set_plink_state(wpa_s, sta, PLINK_ESTAB); hapd->num_plinks++; sta->flags |= WLAN_STA_ASSOC; + sta->mesh_sae_pmksa_caching = 0; + eloop_cancel_timeout(peer_add_timer, wpa_s, NULL); + peer_add_timer(wpa_s, NULL); eloop_cancel_timeout(plink_timer, wpa_s, sta); /* Send ctrl event */ - wpa_msg_ctrl(wpa_s, MSG_INFO, MESH_PEER_CONNECTED MACSTR, - MAC2STR(sta->addr)); + wpa_msg(wpa_s, MSG_INFO, MESH_PEER_CONNECTED MACSTR, + MAC2STR(sta->addr)); } static void mesh_mpm_fsm(struct wpa_supplicant *wpa_s, struct sta_info *sta, - enum plink_event event) + enum plink_event event, u16 reason) { struct hostapd_data *hapd = wpa_s->ifmsh->bss[0]; struct mesh_conf *conf = wpa_s->ifmsh->mconf; - u16 reason = 0; wpa_msg(wpa_s, MSG_DEBUG, "MPM " MACSTR " state %s event %s", MAC2STR(sta->addr), mplstate[sta->plink_state], mplevent[event]); switch (sta->plink_state) { - case PLINK_LISTEN: + case PLINK_IDLE: switch (event) { case CLS_ACPT: mesh_mpm_fsm_restart(wpa_s, sta); break; case OPN_ACPT: - mesh_mpm_plink_open(wpa_s, sta, PLINK_OPEN_RCVD); + mesh_mpm_plink_open(wpa_s, sta, PLINK_OPN_RCVD); mesh_mpm_send_plink_action(wpa_s, sta, PLINK_CONFIRM, 0); break; + case REQ_RJCT: + mesh_mpm_send_plink_action(wpa_s, sta, + PLINK_CLOSE, reason); + break; default: break; } break; - case PLINK_OPEN_SENT: + case PLINK_OPN_SNT: switch (event) { case OPN_RJCT: case CNF_RJCT: - reason = WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION; + if (!reason) + reason = WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION; /* fall-through */ case CLS_ACPT: wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING); @@ -736,12 +894,13 @@ static void mesh_mpm_fsm(struct wpa_supplicant *wpa_s, struct sta_info *sta, break; case OPN_ACPT: /* retry timer is left untouched */ - wpa_mesh_set_plink_state(wpa_s, sta, PLINK_OPEN_RCVD); + wpa_mesh_set_plink_state(wpa_s, sta, PLINK_OPN_RCVD); mesh_mpm_send_plink_action(wpa_s, sta, PLINK_CONFIRM, 0); break; case CNF_ACPT: wpa_mesh_set_plink_state(wpa_s, sta, PLINK_CNF_RCVD); + eloop_cancel_timeout(plink_timer, wpa_s, sta); eloop_register_timeout( conf->dot11MeshConfirmTimeout / 1000, (conf->dot11MeshConfirmTimeout % 1000) * 1000, @@ -751,11 +910,12 @@ static void mesh_mpm_fsm(struct wpa_supplicant *wpa_s, struct sta_info *sta, break; } break; - case PLINK_OPEN_RCVD: + case PLINK_OPN_RCVD: switch (event) { case OPN_RJCT: case CNF_RJCT: - reason = WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION; + if (!reason) + reason = WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION; /* fall-through */ case CLS_ACPT: wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING); @@ -786,7 +946,8 @@ static void mesh_mpm_fsm(struct wpa_supplicant *wpa_s, struct sta_info *sta, switch (event) { case OPN_RJCT: case CNF_RJCT: - reason = WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION; + if (!reason) + reason = WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION; /* fall-through */ case CLS_ACPT: wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING); @@ -801,6 +962,8 @@ static void mesh_mpm_fsm(struct wpa_supplicant *wpa_s, struct sta_info *sta, PLINK_CLOSE, reason); break; case OPN_ACPT: + if (conf->security & MESH_CONF_SEC_AMPE) + mesh_rsn_derive_mtk(wpa_s, sta); mesh_mpm_plink_estab(wpa_s, sta); mesh_mpm_send_plink_action(wpa_s, sta, PLINK_CONFIRM, 0); @@ -811,9 +974,12 @@ static void mesh_mpm_fsm(struct wpa_supplicant *wpa_s, struct sta_info *sta, break; case PLINK_ESTAB: switch (event) { + case OPN_RJCT: + case CNF_RJCT: case CLS_ACPT: wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING); - reason = WLAN_REASON_MESH_CLOSE_RCVD; + if (!reason) + reason = WLAN_REASON_MESH_CLOSE_RCVD; eloop_register_timeout( conf->dot11MeshHoldingTimeout / 1000, @@ -825,9 +991,8 @@ static void mesh_mpm_fsm(struct wpa_supplicant *wpa_s, struct sta_info *sta, " closed with reason %d", MAC2STR(sta->addr), reason); - wpa_msg_ctrl(wpa_s, MSG_INFO, - MESH_PEER_DISCONNECTED MACSTR, - MAC2STR(sta->addr)); + wpa_msg(wpa_s, MSG_INFO, MESH_PEER_DISCONNECTED MACSTR, + MAC2STR(sta->addr)); hapd->num_plinks--; @@ -875,13 +1040,14 @@ void mesh_mpm_action_rx(struct wpa_supplicant *wpa_s, struct hostapd_data *hapd = wpa_s->ifmsh->bss[0]; struct mesh_conf *mconf = wpa_s->ifmsh->mconf; struct sta_info *sta; - u16 plid = 0, llid = 0; + u16 plid = 0, llid = 0, aid = 0; enum plink_event event; struct ieee802_11_elems elems; struct mesh_peer_mgmt_ie peer_mgmt_ie; const u8 *ies; size_t ie_len; int ret; + u16 reason = 0; if (mgmt->u.action.category != WLAN_ACTION_SELF_PROTECTED) return; @@ -912,7 +1078,8 @@ void mesh_mpm_action_rx(struct wpa_supplicant *wpa_s, ie_len -= 2; } if (action_field == PLINK_CONFIRM) { - wpa_printf(MSG_DEBUG, "MPM: AID 0x%x", WPA_GET_LE16(ies)); + aid = WPA_GET_LE16(ies); + wpa_printf(MSG_DEBUG, "MPM: AID 0x%x", aid); ies += 2; /* aid */ ie_len -= 2; } @@ -956,6 +1123,10 @@ void mesh_mpm_action_rx(struct wpa_supplicant *wpa_s, llid = WPA_GET_LE16(peer_mgmt_ie.plid); wpa_printf(MSG_DEBUG, "MPM: plid=0x%x llid=0x%x", plid, llid); + if (action_field == PLINK_CLOSE) + wpa_printf(MSG_DEBUG, "MPM: close reason=%u", + WPA_GET_LE16(peer_mgmt_ie.reason)); + sta = ap_get_sta(hapd, mgmt->sa); /* @@ -963,7 +1134,8 @@ void mesh_mpm_action_rx(struct wpa_supplicant *wpa_s, * open mesh, then go ahead and add the peer before proceeding. */ if (!sta && action_field == PLINK_OPEN && - !(mconf->security & MESH_CONF_SEC_AMPE)) + (!(mconf->security & MESH_CONF_SEC_AMPE) || + wpa_auth_pmksa_get(hapd->wpa_auth, mgmt->sa))) sta = mesh_mpm_add_peer(wpa_s, mgmt->sa, &elems); if (!sta) { @@ -982,12 +1154,24 @@ void mesh_mpm_action_rx(struct wpa_supplicant *wpa_s, if (!sta->my_lid) mesh_mpm_init_link(wpa_s, sta); - if ((mconf->security & MESH_CONF_SEC_AMPE) && - mesh_rsn_process_ampe(wpa_s, sta, &elems, - &mgmt->u.action.category, - ies, ie_len)) { - wpa_printf(MSG_DEBUG, "MPM: RSN process rejected frame"); - return; + if (mconf->security & MESH_CONF_SEC_AMPE) { + int res; + + res = mesh_rsn_process_ampe(wpa_s, sta, &elems, + &mgmt->u.action.category, + peer_mgmt_ie.chosen_pmk, + ies, ie_len); + if (res) { + wpa_printf(MSG_DEBUG, + "MPM: RSN process rejected frame (res=%d)", + res); + if (action_field == PLINK_OPEN && res == -2) { + /* AES-SIV decryption failed */ + mesh_mpm_fsm(wpa_s, sta, OPN_RJCT, + WLAN_REASON_MESH_INVALID_GTK); + } + return; + } } if (sta->plink_state == PLINK_BLOCKED) { @@ -999,12 +1183,16 @@ void mesh_mpm_action_rx(struct wpa_supplicant *wpa_s, switch (action_field) { case PLINK_OPEN: if (plink_free_count(hapd) == 0) { - event = OPN_IGNR; + event = REQ_RJCT; + reason = WLAN_REASON_MESH_MAX_PEERS; wpa_printf(MSG_INFO, "MPM: Peer link num over quota(%d)", hapd->max_plinks); } else if (sta->peer_lid && sta->peer_lid != plid) { - event = OPN_IGNR; + wpa_printf(MSG_DEBUG, + "MPM: peer_lid mismatch: 0x%x != 0x%x", + sta->peer_lid, plid); + return; /* no FSM event */ } else { sta->peer_lid = plid; event = OPN_ACPT; @@ -1012,16 +1200,21 @@ void mesh_mpm_action_rx(struct wpa_supplicant *wpa_s, break; case PLINK_CONFIRM: if (plink_free_count(hapd) == 0) { - event = CNF_IGNR; + event = REQ_RJCT; + reason = WLAN_REASON_MESH_MAX_PEERS; wpa_printf(MSG_INFO, "MPM: Peer link num over quota(%d)", hapd->max_plinks); } else if (sta->my_lid != llid || (sta->peer_lid && sta->peer_lid != plid)) { - event = CNF_IGNR; + wpa_printf(MSG_DEBUG, + "MPM: lid mismatch: my_lid: 0x%x != 0x%x or peer_lid: 0x%x != 0x%x", + sta->my_lid, llid, sta->peer_lid, plid); + return; /* no FSM event */ } else { if (!sta->peer_lid) sta->peer_lid = plid; + sta->peer_aid = aid; event = CNF_ACPT; } break; @@ -1037,12 +1230,19 @@ void mesh_mpm_action_rx(struct wpa_supplicant *wpa_s, * restarted. */ event = CLS_ACPT; - else if (sta->peer_lid != plid) - event = CLS_IGNR; - else if (peer_mgmt_ie.plid && sta->my_lid != llid) - event = CLS_IGNR; - else + else if (sta->peer_lid != plid) { + wpa_printf(MSG_DEBUG, + "MPM: peer_lid mismatch: 0x%x != 0x%x", + sta->peer_lid, plid); + return; /* no FSM event */ + } else if (peer_mgmt_ie.plid && sta->my_lid != llid) { + wpa_printf(MSG_DEBUG, + "MPM: my_lid mismatch: 0x%x != 0x%x", + sta->my_lid, llid); + return; /* no FSM event */ + } else { event = CLS_ACPT; + } break; default: /* @@ -1052,13 +1252,15 @@ void mesh_mpm_action_rx(struct wpa_supplicant *wpa_s, */ return; } - mesh_mpm_fsm(wpa_s, sta, event); + mesh_mpm_fsm(wpa_s, sta, event, reason); } /* called by ap_free_sta */ -void mesh_mpm_free_sta(struct sta_info *sta) +void mesh_mpm_free_sta(struct hostapd_data *hapd, struct sta_info *sta) { + if (sta->plink_state == PLINK_ESTAB) + hapd->num_plinks--; eloop_cancel_timeout(plink_timer, ELOOP_ALL_CTX, sta); eloop_cancel_timeout(mesh_auth_timer, ELOOP_ALL_CTX, sta); } diff --git a/wpa_supplicant/mesh_mpm.h b/wpa_supplicant/mesh_mpm.h index 7ebaef0cd087d..5fc1e6184bcb5 100644 --- a/wpa_supplicant/mesh_mpm.h +++ b/wpa_supplicant/mesh_mpm.h @@ -14,10 +14,13 @@ void wpa_mesh_new_mesh_peer(struct wpa_supplicant *wpa_s, const u8 *addr, struct ieee802_11_elems *elems); void mesh_mpm_deinit(struct wpa_supplicant *wpa_s, struct hostapd_iface *ifmsh); void mesh_mpm_auth_peer(struct wpa_supplicant *wpa_s, const u8 *addr); -void mesh_mpm_free_sta(struct sta_info *sta); +void mesh_mpm_free_sta(struct hostapd_data *hapd, struct sta_info *sta); void wpa_mesh_set_plink_state(struct wpa_supplicant *wpa_s, struct sta_info *sta, enum mesh_plink_state state); +int mesh_mpm_close_peer(struct wpa_supplicant *wpa_s, const u8 *addr); +int mesh_mpm_connect_peer(struct wpa_supplicant *wpa_s, const u8 *addr, + int duration); #ifdef CONFIG_MESH diff --git a/wpa_supplicant/mesh_rsn.c b/wpa_supplicant/mesh_rsn.c index 747f1ae6968b4..27ab8cb364586 100644 --- a/wpa_supplicant/mesh_rsn.c +++ b/wpa_supplicant/mesh_rsn.c @@ -27,12 +27,12 @@ #define MESH_AUTH_TIMEOUT 10 #define MESH_AUTH_RETRY 3 -#define MESH_AUTH_BLOCK_DURATION 3600 void mesh_auth_timer(void *eloop_ctx, void *user_data) { struct wpa_supplicant *wpa_s = eloop_ctx; struct sta_info *sta = user_data; + struct hostapd_data *hapd; if (sta->sae->state != SAE_ACCEPTED) { wpa_printf(MSG_DEBUG, "AUTH: Re-authenticate with " MACSTR @@ -43,23 +43,20 @@ void mesh_auth_timer(void *eloop_ctx, void *user_data) if (sta->sae_auth_retry < MESH_AUTH_RETRY) { mesh_rsn_auth_sae_sta(wpa_s, sta); } else { + hapd = wpa_s->ifmsh->bss[0]; + if (sta->sae_auth_retry > MESH_AUTH_RETRY) { - ap_free_sta(wpa_s->ifmsh->bss[0], sta); + ap_free_sta(hapd, sta); return; } /* block the STA if exceeded the number of attempts */ wpa_mesh_set_plink_state(wpa_s, sta, PLINK_BLOCKED); sta->sae->state = SAE_NOTHING; - if (wpa_s->mesh_auth_block_duration < - MESH_AUTH_BLOCK_DURATION) - wpa_s->mesh_auth_block_duration += 60; - eloop_register_timeout(wpa_s->mesh_auth_block_duration, - 0, mesh_auth_timer, wpa_s, sta); wpa_msg(wpa_s, MSG_INFO, MESH_SAE_AUTH_BLOCKED "addr=" MACSTR " duration=%d", MAC2STR(sta->addr), - wpa_s->mesh_auth_block_duration); + hapd->conf->ap_max_inactivity); } sta->sae_auth_retry++; } @@ -139,7 +136,8 @@ static int auth_start_ampe(void *ctx, const u8 *addr) } -static int __mesh_rsn_auth_init(struct mesh_rsn *rsn, const u8 *addr) +static int __mesh_rsn_auth_init(struct mesh_rsn *rsn, const u8 *addr, + enum mfp_options ieee80211w) { struct wpa_auth_config conf; struct wpa_auth_callbacks cb; @@ -148,13 +146,18 @@ static int __mesh_rsn_auth_init(struct mesh_rsn *rsn, const u8 *addr) wpa_printf(MSG_DEBUG, "AUTH: Initializing group state machine"); os_memset(&conf, 0, sizeof(conf)); - conf.wpa = 2; + conf.wpa = WPA_PROTO_RSN; conf.wpa_key_mgmt = WPA_KEY_MGMT_SAE; - conf.wpa_pairwise = WPA_CIPHER_CCMP; - conf.rsn_pairwise = WPA_CIPHER_CCMP; - conf.wpa_group = WPA_CIPHER_CCMP; + conf.wpa_pairwise = rsn->pairwise_cipher; + conf.rsn_pairwise = rsn->pairwise_cipher; + conf.wpa_group = rsn->group_cipher; conf.eapol_version = 0; conf.wpa_group_rekey = -1; +#ifdef CONFIG_IEEE80211W + conf.ieee80211w = ieee80211w; + if (ieee80211w != NO_MGMT_FRAME_PROTECTION) + conf.group_mgmt_cipher = rsn->mgmt_group_cipher; +#endif /* CONFIG_IEEE80211W */ os_memset(&cb, 0, sizeof(cb)); cb.ctx = rsn; @@ -170,18 +173,34 @@ static int __mesh_rsn_auth_init(struct mesh_rsn *rsn, const u8 *addr) } /* TODO: support rekeying */ - if (random_get_bytes(rsn->mgtk, 16) < 0) { - wpa_deinit(rsn->auth); + rsn->mgtk_len = wpa_cipher_key_len(conf.wpa_group); + if (random_get_bytes(rsn->mgtk, rsn->mgtk_len) < 0) return -1; - } + rsn->mgtk_key_id = 1; - /* group mgmt */ - wpa_drv_set_key(rsn->wpa_s, WPA_ALG_IGTK, NULL, 4, 1, - seq, sizeof(seq), rsn->mgtk, sizeof(rsn->mgtk)); +#ifdef CONFIG_IEEE80211W + if (ieee80211w != NO_MGMT_FRAME_PROTECTION) { + rsn->igtk_len = wpa_cipher_key_len(conf.group_mgmt_cipher); + if (random_get_bytes(rsn->igtk, rsn->igtk_len) < 0) + return -1; + rsn->igtk_key_id = 4; + + /* group mgmt */ + wpa_hexdump_key(MSG_DEBUG, "mesh: Own TX IGTK", + rsn->igtk, rsn->igtk_len); + wpa_drv_set_key(rsn->wpa_s, + wpa_cipher_to_alg(rsn->mgmt_group_cipher), NULL, + rsn->igtk_key_id, 1, + seq, sizeof(seq), rsn->igtk, rsn->igtk_len); + } +#endif /* CONFIG_IEEE80211W */ /* group privacy / data frames */ - wpa_drv_set_key(rsn->wpa_s, WPA_ALG_CCMP, NULL, 1, 1, - seq, sizeof(seq), rsn->mgtk, sizeof(rsn->mgtk)); + wpa_hexdump_key(MSG_DEBUG, "mesh: Own TX MGTK", + rsn->mgtk, rsn->mgtk_len); + wpa_drv_set_key(rsn->wpa_s, wpa_cipher_to_alg(rsn->group_cipher), NULL, + rsn->mgtk_key_id, 1, seq, sizeof(seq), + rsn->mgtk, rsn->mgtk_len); return 0; } @@ -190,6 +209,9 @@ static int __mesh_rsn_auth_init(struct mesh_rsn *rsn, const u8 *addr) static void mesh_rsn_deinit(struct mesh_rsn *rsn) { os_memset(rsn->mgtk, 0, sizeof(rsn->mgtk)); + rsn->mgtk_len = 0; + os_memset(rsn->igtk, 0, sizeof(rsn->igtk)); + rsn->igtk_len = 0; if (rsn->auth) wpa_deinit(rsn->auth); } @@ -207,8 +229,12 @@ struct mesh_rsn *mesh_rsn_auth_init(struct wpa_supplicant *wpa_s, if (mesh_rsn == NULL) return NULL; mesh_rsn->wpa_s = wpa_s; + mesh_rsn->pairwise_cipher = conf->pairwise_cipher; + mesh_rsn->group_cipher = conf->group_cipher; + mesh_rsn->mgmt_group_cipher = conf->mgmt_group_cipher; - if (__mesh_rsn_auth_init(mesh_rsn, wpa_s->own_addr) < 0) { + if (__mesh_rsn_auth_init(mesh_rsn, wpa_s->own_addr, + conf->ieee80211w) < 0) { mesh_rsn_deinit(mesh_rsn); os_free(mesh_rsn); return NULL; @@ -291,6 +317,7 @@ int mesh_rsn_auth_sae_sta(struct wpa_supplicant *wpa_s, { struct hostapd_data *hapd = wpa_s->ifmsh->bss[0]; struct wpa_ssid *ssid = wpa_s->current_ssid; + struct rsn_pmksa_cache_entry *pmksa; unsigned int rnd; int ret; @@ -306,6 +333,29 @@ int mesh_rsn_auth_sae_sta(struct wpa_supplicant *wpa_s, return -1; } + pmksa = wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr); + if (pmksa) { + if (!sta->wpa_sm) + sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, + sta->addr, NULL); + if (!sta->wpa_sm) { + wpa_printf(MSG_ERROR, + "mesh: Failed to initialize RSN state machine"); + return -1; + } + + wpa_printf(MSG_DEBUG, + "AUTH: Mesh PMKSA cache entry found for " MACSTR + " - try to use PMKSA caching instead of new SAE authentication", + MAC2STR(sta->addr)); + wpa_auth_pmksa_set_to_sm(pmksa, sta->wpa_sm, hapd->wpa_auth, + sta->sae->pmkid, sta->sae->pmk); + sae_accept_sta(hapd, sta); + sta->mesh_sae_pmksa_caching = 1; + return 0; + } + sta->mesh_sae_pmksa_caching = 0; + if (mesh_rsn_build_sae_commit(wpa_s, ssid, sta)) return -1; @@ -313,7 +363,6 @@ int mesh_rsn_auth_sae_sta(struct wpa_supplicant *wpa_s, "AUTH: started authentication with SAE peer: " MACSTR, MAC2STR(sta->addr)); - wpa_supplicant_set_state(wpa_s, WPA_AUTHENTICATING); ret = auth_sae_init_committed(hapd, sta); if (ret) return ret; @@ -328,10 +377,7 @@ int mesh_rsn_auth_sae_sta(struct wpa_supplicant *wpa_s, void mesh_rsn_get_pmkid(struct mesh_rsn *rsn, struct sta_info *sta, u8 *pmkid) { - /* don't expect wpa auth to cache the pmkid for now */ - rsn_pmkid(sta->sae->pmk, PMK_LEN, rsn->wpa_s->own_addr, - sta->addr, pmkid, - wpa_key_mgmt_sha256(wpa_auth_sta_key_mgmt(sta->wpa_sm))); + os_memcpy(pmkid, sta->sae->pmkid, SAE_PMKID_LEN); } @@ -340,18 +386,27 @@ mesh_rsn_derive_aek(struct mesh_rsn *rsn, struct sta_info *sta) { u8 *myaddr = rsn->wpa_s->own_addr; u8 *peer = sta->addr; - u8 *addr1 = peer, *addr2 = myaddr; - u8 context[AES_BLOCK_SIZE]; + u8 *addr1, *addr2; + u8 context[RSN_SELECTOR_LEN + 2 * ETH_ALEN], *ptr = context; - /* SAE */ - RSN_SELECTOR_PUT(context, wpa_cipher_to_suite(0, WPA_CIPHER_GCMP)); + /* + * AEK = KDF-Hash-256(PMK, "AEK Derivation", Selected AKM Suite || + * min(localMAC, peerMAC) || max(localMAC, peerMAC)) + */ + /* Selected AKM Suite: SAE */ + RSN_SELECTOR_PUT(ptr, RSN_AUTH_KEY_MGMT_SAE); + ptr += RSN_SELECTOR_LEN; if (os_memcmp(myaddr, peer, ETH_ALEN) < 0) { addr1 = myaddr; addr2 = peer; + } else { + addr1 = peer; + addr2 = myaddr; } - os_memcpy(context + 4, addr1, ETH_ALEN); - os_memcpy(context + 10, addr2, ETH_ALEN); + os_memcpy(ptr, addr1, ETH_ALEN); + ptr += ETH_ALEN; + os_memcpy(ptr, addr2, ETH_ALEN); sha256_prf(sta->sae->pmk, sizeof(sta->sae->pmk), "AEK Derivation", context, sizeof(context), sta->aek, sizeof(sta->aek)); @@ -363,40 +418,44 @@ int mesh_rsn_derive_mtk(struct wpa_supplicant *wpa_s, struct sta_info *sta) { u8 *ptr; u8 *min, *max; - u16 min_lid, max_lid; - size_t nonce_len = sizeof(sta->my_nonce); - size_t lid_len = sizeof(sta->my_lid); u8 *myaddr = wpa_s->own_addr; u8 *peer = sta->addr; - /* 2 nonces, 2 linkids, akm suite, 2 mac addrs */ - u8 context[64 + 4 + 4 + 12]; - + u8 context[2 * WPA_NONCE_LEN + 2 * 2 + RSN_SELECTOR_LEN + 2 * ETH_ALEN]; + + /* + * MTK = KDF-Hash-Length(PMK, "Temporal Key Derivation", min(localNonce, + * peerNonce) || max(localNonce, peerNonce) || min(localLinkID, + * peerLinkID) || max(localLinkID, peerLinkID) || Selected AKM Suite || + * min(localMAC, peerMAC) || max(localMAC, peerMAC)) + */ ptr = context; - if (os_memcmp(sta->my_nonce, sta->peer_nonce, nonce_len) < 0) { + if (os_memcmp(sta->my_nonce, sta->peer_nonce, WPA_NONCE_LEN) < 0) { min = sta->my_nonce; max = sta->peer_nonce; } else { min = sta->peer_nonce; max = sta->my_nonce; } - os_memcpy(ptr, min, nonce_len); - os_memcpy(ptr + nonce_len, max, nonce_len); - ptr += 2 * nonce_len; + os_memcpy(ptr, min, WPA_NONCE_LEN); + ptr += WPA_NONCE_LEN; + os_memcpy(ptr, max, WPA_NONCE_LEN); + ptr += WPA_NONCE_LEN; if (sta->my_lid < sta->peer_lid) { - min_lid = host_to_le16(sta->my_lid); - max_lid = host_to_le16(sta->peer_lid); + WPA_PUT_LE16(ptr, sta->my_lid); + ptr += 2; + WPA_PUT_LE16(ptr, sta->peer_lid); + ptr += 2; } else { - min_lid = host_to_le16(sta->peer_lid); - max_lid = host_to_le16(sta->my_lid); + WPA_PUT_LE16(ptr, sta->peer_lid); + ptr += 2; + WPA_PUT_LE16(ptr, sta->my_lid); + ptr += 2; } - os_memcpy(ptr, &min_lid, lid_len); - os_memcpy(ptr + lid_len, &max_lid, lid_len); - ptr += 2 * lid_len; - /* SAE */ - RSN_SELECTOR_PUT(ptr, wpa_cipher_to_suite(0, WPA_CIPHER_GCMP)); - ptr += 4; + /* Selected AKM Suite: SAE */ + RSN_SELECTOR_PUT(ptr, RSN_AUTH_KEY_MGMT_SAE); + ptr += RSN_SELECTOR_LEN; if (os_memcmp(myaddr, peer, ETH_ALEN) < 0) { min = myaddr; @@ -406,22 +465,24 @@ int mesh_rsn_derive_mtk(struct wpa_supplicant *wpa_s, struct sta_info *sta) max = myaddr; } os_memcpy(ptr, min, ETH_ALEN); - os_memcpy(ptr + ETH_ALEN, max, ETH_ALEN); + ptr += ETH_ALEN; + os_memcpy(ptr, max, ETH_ALEN); - sha256_prf(sta->sae->pmk, sizeof(sta->sae->pmk), + sta->mtk_len = wpa_cipher_key_len(wpa_s->mesh_rsn->pairwise_cipher); + sha256_prf(sta->sae->pmk, SAE_PMK_LEN, "Temporal Key Derivation", context, sizeof(context), - sta->mtk, sizeof(sta->mtk)); + sta->mtk, sta->mtk_len); return 0; } void mesh_rsn_init_ampe_sta(struct wpa_supplicant *wpa_s, struct sta_info *sta) { - if (random_get_bytes(sta->my_nonce, 32) < 0) { + if (random_get_bytes(sta->my_nonce, WPA_NONCE_LEN) < 0) { wpa_printf(MSG_INFO, "mesh: Failed to derive random nonce"); /* TODO: How to handle this more cleanly? */ } - os_memset(sta->peer_nonce, 0, 32); + os_memset(sta->peer_nonce, 0, WPA_NONCE_LEN); mesh_rsn_derive_aek(wpa_s->mesh_rsn, sta); } @@ -437,65 +498,94 @@ int mesh_rsn_protect_frame(struct mesh_rsn *rsn, struct sta_info *sta, { struct ieee80211_ampe_ie *ampe; u8 const *ie = wpabuf_head_u8(buf) + wpabuf_len(buf); - u8 *ampe_ie = NULL, *mic_ie = NULL, *mic_payload; + u8 *ampe_ie, *pos, *mic_payload; const u8 *aad[] = { rsn->wpa_s->own_addr, sta->addr, cat }; const size_t aad_len[] = { ETH_ALEN, ETH_ALEN, ie - cat }; int ret = 0; + size_t len; + + len = sizeof(*ampe); + if (cat[1] == PLINK_OPEN) + len += rsn->mgtk_len + WPA_KEY_RSC_LEN + 4; +#ifdef CONFIG_IEEE80211W + if (cat[1] == PLINK_OPEN && rsn->igtk_len) + len += 2 + 6 + rsn->igtk_len; +#endif /* CONFIG_IEEE80211W */ - if (AES_BLOCK_SIZE + 2 + sizeof(*ampe) + 2 > wpabuf_tailroom(buf)) { + if (2 + AES_BLOCK_SIZE + 2 + len > wpabuf_tailroom(buf)) { wpa_printf(MSG_ERROR, "protect frame: buffer too small"); return -EINVAL; } - ampe_ie = os_zalloc(2 + sizeof(*ampe)); + ampe_ie = os_zalloc(2 + len); if (!ampe_ie) { wpa_printf(MSG_ERROR, "protect frame: out of memory"); return -ENOMEM; } - mic_ie = os_zalloc(2 + AES_BLOCK_SIZE); - if (!mic_ie) { - wpa_printf(MSG_ERROR, "protect frame: out of memory"); - ret = -ENOMEM; - goto free; - } - /* IE: AMPE */ ampe_ie[0] = WLAN_EID_AMPE; - ampe_ie[1] = sizeof(*ampe); + ampe_ie[1] = len; ampe = (struct ieee80211_ampe_ie *) (ampe_ie + 2); RSN_SELECTOR_PUT(ampe->selected_pairwise_suite, - wpa_cipher_to_suite(WPA_PROTO_RSN, WPA_CIPHER_CCMP)); - os_memcpy(ampe->local_nonce, sta->my_nonce, 32); - os_memcpy(ampe->peer_nonce, sta->peer_nonce, 32); - /* incomplete: see 13.5.4 */ + RSN_CIPHER_SUITE_CCMP); + os_memcpy(ampe->local_nonce, sta->my_nonce, WPA_NONCE_LEN); + os_memcpy(ampe->peer_nonce, sta->peer_nonce, WPA_NONCE_LEN); + + pos = (u8 *) (ampe + 1); + if (cat[1] != PLINK_OPEN) + goto skip_keys; + + /* TODO: Key Replay Counter[8] optionally for + * Mesh Group Key Inform/Acknowledge frames */ + /* TODO: static mgtk for now since we don't support rekeying! */ - os_memcpy(ampe->mgtk, rsn->mgtk, 16); - /* TODO: Populate Key RSC */ - /* expire in 13 decades or so */ - os_memset(ampe->key_expiration, 0xff, 4); + /* + * GTKdata[variable]: + * MGTK[variable] || Key RSC[8] || GTKExpirationTime[4] + */ + os_memcpy(pos, rsn->mgtk, rsn->mgtk_len); + pos += rsn->mgtk_len; + wpa_drv_get_seqnum(rsn->wpa_s, NULL, rsn->mgtk_key_id, pos); + pos += WPA_KEY_RSC_LEN; + /* Use fixed GTKExpirationTime for now */ + WPA_PUT_LE32(pos, 0xffffffff); + pos += 4; + +#ifdef CONFIG_IEEE80211W + /* + * IGTKdata[variable]: + * Key ID[2], IPN[6], IGTK[variable] + */ + if (rsn->igtk_len) { + WPA_PUT_LE16(pos, rsn->igtk_key_id); + pos += 2; + wpa_drv_get_seqnum(rsn->wpa_s, NULL, rsn->igtk_key_id, pos); + pos += 6; + os_memcpy(pos, rsn->igtk, rsn->igtk_len); + } +#endif /* CONFIG_IEEE80211W */ + +skip_keys: + wpa_hexdump_key(MSG_DEBUG, "mesh: Plaintext AMPE element", + ampe_ie, 2 + len); /* IE: MIC */ - mic_ie[0] = WLAN_EID_MIC; - mic_ie[1] = AES_BLOCK_SIZE; - wpabuf_put_data(buf, mic_ie, 2); + wpabuf_put_u8(buf, WLAN_EID_MIC); + wpabuf_put_u8(buf, AES_BLOCK_SIZE); /* MIC field is output ciphertext */ /* encrypt after MIC */ - mic_payload = (u8 *) wpabuf_put(buf, 2 + sizeof(*ampe) + - AES_BLOCK_SIZE); + mic_payload = wpabuf_put(buf, 2 + len + AES_BLOCK_SIZE); - if (aes_siv_encrypt(sta->aek, ampe_ie, 2 + sizeof(*ampe), 3, + if (aes_siv_encrypt(sta->aek, ampe_ie, 2 + len, 3, aad, aad_len, mic_payload)) { wpa_printf(MSG_ERROR, "protect frame: failed to encrypt"); ret = -ENOMEM; - goto free; } -free: os_free(ampe_ie); - os_free(mic_ie); return ret; } @@ -503,18 +593,37 @@ free: int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta, struct ieee802_11_elems *elems, const u8 *cat, + const u8 *chosen_pmk, const u8 *start, size_t elems_len) { int ret = 0; struct ieee80211_ampe_ie *ampe; - u8 null_nonce[32] = {}; + u8 null_nonce[WPA_NONCE_LEN] = {}; u8 ampe_eid; u8 ampe_ie_len; - u8 *ampe_buf, *crypt = NULL; + u8 *ampe_buf, *crypt = NULL, *pos, *end; size_t crypt_len; const u8 *aad[] = { sta->addr, wpa_s->own_addr, cat }; const size_t aad_len[] = { ETH_ALEN, ETH_ALEN, (elems->mic - 2) - cat }; + size_t key_len; + + if (!sta->sae) { + struct hostapd_data *hapd = wpa_s->ifmsh->bss[0]; + + if (!wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr)) { + wpa_printf(MSG_INFO, + "Mesh RSN: SAE is not prepared yet"); + return -1; + } + mesh_rsn_auth_sae_sta(wpa_s, sta); + } + + if (chosen_pmk && os_memcmp(chosen_pmk, sta->sae->pmkid, PMKID_LEN)) { + wpa_msg(wpa_s, MSG_DEBUG, + "Mesh RSN: Invalid PMKID (Chosen PMK did not match calculated PMKID)"); + return -1; + } if (!elems->mic || elems->mic_len < AES_BLOCK_SIZE) { wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: missing mic ie"); @@ -526,7 +635,7 @@ int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta, return -1; crypt_len = elems_len - (elems->mic - start); - if (crypt_len < 2) { + if (crypt_len < 2 + AES_BLOCK_SIZE) { wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: missing ampe ie"); return -1; } @@ -544,14 +653,19 @@ int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta, if (aes_siv_decrypt(sta->aek, crypt, crypt_len, 3, aad, aad_len, ampe_buf)) { wpa_printf(MSG_ERROR, "Mesh RSN: frame verification failed!"); - ret = -1; + ret = -2; goto free; } + crypt_len -= AES_BLOCK_SIZE; + wpa_hexdump_key(MSG_DEBUG, "mesh: Decrypted AMPE element", + ampe_buf, crypt_len); + ampe_eid = *ampe_buf++; ampe_ie_len = *ampe_buf++; if (ampe_eid != WLAN_EID_AMPE || + (size_t) 2 + ampe_ie_len > crypt_len || ampe_ie_len < sizeof(struct ieee80211_ampe_ie)) { wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: invalid ampe ie"); ret = -1; @@ -559,17 +673,89 @@ int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta, } ampe = (struct ieee80211_ampe_ie *) ampe_buf; - if (os_memcmp(ampe->peer_nonce, null_nonce, 32) != 0 && - os_memcmp(ampe->peer_nonce, sta->my_nonce, 32) != 0) { + pos = (u8 *) (ampe + 1); + end = ampe_buf + ampe_ie_len; + if (os_memcmp(ampe->peer_nonce, null_nonce, WPA_NONCE_LEN) != 0 && + os_memcmp(ampe->peer_nonce, sta->my_nonce, WPA_NONCE_LEN) != 0) { wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: invalid peer nonce"); ret = -1; goto free; } os_memcpy(sta->peer_nonce, ampe->local_nonce, sizeof(ampe->local_nonce)); - os_memcpy(sta->mgtk, ampe->mgtk, sizeof(ampe->mgtk)); - /* todo parse mgtk expiration */ + /* TODO: Key Replay Counter[8] in Mesh Group Key Inform/Acknowledge + * frames */ + + /* + * GTKdata shall not be included in Mesh Peering Confirm. While the + * standard does not state the same about IGTKdata, that same constraint + * needs to apply for it. It makes no sense to include the keys in Mesh + * Peering Close frames either, so while the standard does not seem to + * have a shall statement for these, they are described without + * mentioning GTKdata. + * + * An earlier implementation used to add GTKdata to both Mesh Peering + * Open and Mesh Peering Confirm frames, so ignore the possibly present + * GTKdata frame without rejecting the frame as a backwards + * compatibility mechanism. + */ + if (cat[1] != PLINK_OPEN) { + if (end > pos) { + wpa_hexdump_key(MSG_DEBUG, + "mesh: Ignore unexpected GTKdata(etc.) fields in the end of AMPE element in Mesh Peering Confirm/Close", + pos, end - pos); + } + goto free; + } + + /* + * GTKdata[variable]: + * MGTK[variable] || Key RSC[8] || GTKExpirationTime[4] + */ + sta->mgtk_key_id = 1; /* FIX: Where to get Key ID? */ + key_len = wpa_cipher_key_len(wpa_s->mesh_rsn->group_cipher); + if ((int) key_len + WPA_KEY_RSC_LEN + 4 > end - pos) { + wpa_dbg(wpa_s, MSG_DEBUG, "mesh: Truncated AMPE element"); + ret = -1; + goto free; + } + sta->mgtk_len = key_len; + os_memcpy(sta->mgtk, pos, sta->mgtk_len); + wpa_hexdump_key(MSG_DEBUG, "mesh: GTKdata - MGTK", + sta->mgtk, sta->mgtk_len); + pos += sta->mgtk_len; + wpa_hexdump(MSG_DEBUG, "mesh: GTKdata - MGTK - Key RSC", + pos, WPA_KEY_RSC_LEN); + os_memcpy(sta->mgtk_rsc, pos, sizeof(sta->mgtk_rsc)); + pos += WPA_KEY_RSC_LEN; + wpa_printf(MSG_DEBUG, + "mesh: GTKdata - MGTK - GTKExpirationTime: %u seconds", + WPA_GET_LE32(pos)); + pos += 4; + +#ifdef CONFIG_IEEE80211W + /* + * IGTKdata[variable]: + * Key ID[2], IPN[6], IGTK[variable] + */ + key_len = wpa_cipher_key_len(wpa_s->mesh_rsn->mgmt_group_cipher); + if (end - pos >= (int) (2 + 6 + key_len)) { + sta->igtk_key_id = WPA_GET_LE16(pos); + wpa_printf(MSG_DEBUG, "mesh: IGTKdata - Key ID %u", + sta->igtk_key_id); + pos += 2; + os_memcpy(sta->igtk_rsc, pos, sizeof(sta->igtk_rsc)); + wpa_hexdump(MSG_DEBUG, "mesh: IGTKdata - IPN", + sta->igtk_rsc, sizeof(sta->igtk_rsc)); + pos += 6; + os_memcpy(sta->igtk, pos, key_len); + sta->igtk_len = key_len; + wpa_hexdump_key(MSG_DEBUG, "mesh: IGTKdata - IGTK", + sta->igtk, sta->igtk_len); + } +#endif /* CONFIG_IEEE80211W */ + free: os_free(crypt); return ret; diff --git a/wpa_supplicant/mesh_rsn.h b/wpa_supplicant/mesh_rsn.h index b1471b2de8ae9..8775cedc3b273 100644 --- a/wpa_supplicant/mesh_rsn.h +++ b/wpa_supplicant/mesh_rsn.h @@ -12,7 +12,15 @@ struct mesh_rsn { struct wpa_supplicant *wpa_s; struct wpa_authenticator *auth; - u8 mgtk[16]; + unsigned int pairwise_cipher; + unsigned int group_cipher; + u8 mgtk[WPA_TK_MAX_LEN]; + size_t mgtk_len; + u8 mgtk_key_id; + unsigned int mgmt_group_cipher; + u8 igtk_key_id; + u8 igtk[WPA_TK_MAX_LEN]; + size_t igtk_len; #ifdef CONFIG_SAE struct wpabuf *sae_token; int sae_group_index; @@ -30,6 +38,7 @@ int mesh_rsn_protect_frame(struct mesh_rsn *rsn, struct sta_info *sta, const u8 *cat, struct wpabuf *buf); int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta, struct ieee802_11_elems *elems, const u8 *cat, + const u8 *chosen_pmk, const u8 *start, size_t elems_len); void mesh_auth_timer(void *eloop_ctx, void *user_data); diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c index 45d06bf357446..67e36ae34cb87 100644 --- a/wpa_supplicant/notify.c +++ b/wpa_supplicant/notify.c @@ -13,6 +13,7 @@ #include "config.h" #include "wpa_supplicant_i.h" #include "wps_supplicant.h" +#include "binder/binder.h" #include "dbus/dbus_common.h" #include "dbus/dbus_old.h" #include "dbus/dbus_new.h" @@ -34,6 +35,12 @@ int wpas_notify_supplicant_initialized(struct wpa_global *global) } #endif /* CONFIG_DBUS */ +#ifdef CONFIG_BINDER + global->binder = wpas_binder_init(global); + if (!global->binder) + return -1; +#endif /* CONFIG_BINDER */ + return 0; } @@ -44,6 +51,11 @@ void wpas_notify_supplicant_deinitialized(struct wpa_global *global) if (global->dbus) wpas_dbus_deinit(global->dbus); #endif /* CONFIG_DBUS */ + +#ifdef CONFIG_BINDER + if (global->binder) + wpas_binder_deinit(global->binder); +#endif /* CONFIG_BINDER */ } @@ -128,6 +140,15 @@ void wpas_notify_disconnect_reason(struct wpa_supplicant *wpa_s) } +void wpas_notify_assoc_status_code(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->p2p_mgmt) + return; + + wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_ASSOC_STATUS_CODE); +} + + void wpas_notify_network_changed(struct wpa_supplicant *wpa_s) { if (wpa_s->p2p_mgmt) @@ -647,13 +668,13 @@ void wpas_notify_p2p_provision_discovery(struct wpa_supplicant *wpa_s, void wpas_notify_p2p_group_started(struct wpa_supplicant *wpa_s, - struct wpa_ssid *ssid, int network_id, + struct wpa_ssid *ssid, int persistent, 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); + wpas_dbus_signal_p2p_group_started(wpa_s, client, persistent); } diff --git a/wpa_supplicant/notify.h b/wpa_supplicant/notify.h index d9f0f5a967323..8cce0f30c2a96 100644 --- a/wpa_supplicant/notify.h +++ b/wpa_supplicant/notify.h @@ -23,6 +23,7 @@ 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_assoc_status_code(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); @@ -112,7 +113,7 @@ void wpas_notify_p2p_provision_discovery(struct wpa_supplicant *wpa_s, 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, + struct wpa_ssid *ssid, int persistent, int client); void wpas_notify_p2p_group_formation_failure(struct wpa_supplicant *wpa_s, const char *reason); diff --git a/wpa_supplicant/offchannel.c b/wpa_supplicant/offchannel.c index 63af83afe1985..26d41a4ad5c63 100644 --- a/wpa_supplicant/offchannel.c +++ b/wpa_supplicant/offchannel.c @@ -23,8 +23,29 @@ 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) + if (os_memcmp(src, wpa_s->own_addr, ETH_ALEN) == 0) { +#ifdef CONFIG_P2P + if (wpa_s->p2p_mgmt && wpa_s != wpa_s->parent && + wpa_s->parent->ap_iface && + os_memcmp(wpa_s->parent->own_addr, + wpa_s->own_addr, ETH_ALEN) == 0 && + wpabuf_len(wpa_s->pending_action_tx) >= 2 && + *wpabuf_head_u8(wpa_s->pending_action_tx) != + WLAN_ACTION_PUBLIC) { + /* + * When P2P Device interface has same MAC address as + * the GO interface, make sure non-Public Action frames + * are sent through the GO interface. The P2P Device + * interface can only send Public Action frames. + */ + wpa_printf(MSG_DEBUG, + "P2P: Use GO interface %s instead of interface %s for Action TX", + wpa_s->parent->ifname, wpa_s->ifname); + return wpa_s->parent; + } +#endif /* CONFIG_P2P */ return wpa_s; + } /* * Try to find a group interface that matches with the source address. @@ -118,8 +139,9 @@ static void wpas_send_action_cb(void *eloop_ctx, void *timeout_ctx) } wpa_printf(MSG_DEBUG, "Off-channel: Sending pending Action frame to " - MACSTR " using interface %s", - MAC2STR(wpa_s->pending_action_dst), iface->ifname); + MACSTR " using interface %s (pending_action_tx=%p)", + MAC2STR(wpa_s->pending_action_dst), iface->ifname, + wpa_s->pending_action_tx); res = wpa_drv_send_action(iface, wpa_s->pending_action_freq, 0, wpa_s->pending_action_dst, wpa_s->pending_action_src, @@ -183,8 +205,12 @@ void offchannel_send_action_tx_status( return; } - wpa_printf(MSG_DEBUG, "Off-channel: Delete matching pending action frame"); - + wpa_printf(MSG_DEBUG, + "Off-channel: Delete matching pending action frame (dst=" + MACSTR " pending_action_tx=%p)", MAC2STR(dst), + wpa_s->pending_action_tx); + wpa_hexdump_buf(MSG_MSGDUMP, "Pending TX frame", + wpa_s->pending_action_tx); wpabuf_free(wpa_s->pending_action_tx); wpa_s->pending_action_tx = NULL; @@ -250,8 +276,11 @@ int offchannel_send_action(struct wpa_supplicant *wpa_s, unsigned int freq, 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)); + "frame TX to " MACSTR " (pending_action_tx=%p)", + MAC2STR(wpa_s->pending_action_dst), + wpa_s->pending_action_tx); + wpa_hexdump_buf(MSG_MSGDUMP, "Pending TX frame", + wpa_s->pending_action_tx); wpabuf_free(wpa_s->pending_action_tx); } wpa_s->pending_action_tx_done = 0; @@ -268,6 +297,12 @@ int offchannel_send_action(struct wpa_supplicant *wpa_s, unsigned int freq, os_memcpy(wpa_s->pending_action_bssid, bssid, ETH_ALEN); wpa_s->pending_action_freq = freq; wpa_s->pending_action_no_cck = no_cck; + wpa_printf(MSG_DEBUG, + "Off-channel: Stored pending action frame (dst=" MACSTR + " pending_action_tx=%p)", + MAC2STR(dst), wpa_s->pending_action_tx); + wpa_hexdump_buf(MSG_MSGDUMP, "Pending TX frame", + wpa_s->pending_action_tx); if (freq != 0 && wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) { struct wpa_supplicant *iface; @@ -428,6 +463,9 @@ const void * offchannel_pending_action_tx(struct wpa_supplicant *wpa_s) */ void offchannel_clear_pending_action_tx(struct wpa_supplicant *wpa_s) { + wpa_printf(MSG_DEBUG, + "Off-channel: Clear pending Action frame TX (pending_action_tx=%p", + wpa_s->pending_action_tx); wpabuf_free(wpa_s->pending_action_tx); wpa_s->pending_action_tx = NULL; } diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c index 78bdd0837e8be..b1fdc2837ff06 100644 --- a/wpa_supplicant/p2p_supplicant.c +++ b/wpa_supplicant/p2p_supplicant.c @@ -53,6 +53,13 @@ */ #define P2P_GO_FREQ_CHANGE_TIME 5 +/** + * Defines CSA parameters which are used when GO evacuates the no longer valid + * channel (and if the driver supports channel switch). + */ +#define P2P_GO_CSA_COUNT 7 +#define P2P_GO_CSA_BLOCK_TX 0 + #ifndef P2P_MAX_CLIENT_IDLE /* * How many seconds to try to reconnect to the GO when connection in P2P client @@ -117,6 +124,10 @@ wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated, int go); static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s, int freq, const u8 *ssid, size_t ssid_len); +static int wpas_p2p_setup_freqs(struct wpa_supplicant *wpa_s, int freq, + int *force_freq, int *pref_freq, int go, + unsigned int *pref_freq_list, + unsigned int *num_pref_freq); static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq, const u8 *ssid, size_t ssid_len); static void wpas_p2p_join_scan(void *eloop_ctx, void *timeout_ctx); @@ -340,6 +351,7 @@ static int wpas_p2p_scan(void *ctx, enum p2p_scan_type type, int freq, int social_channels_freq[] = { 2412, 2437, 2462, 60480 }; size_t ielen; u8 *n, i; + unsigned int bands; if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return -1; @@ -369,28 +381,6 @@ static int wpas_p2p_scan(void *ctx, enum p2p_scan_type type, int freq, if (wps_ie == NULL) goto fail; - ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p); - ies = wpabuf_alloc(wpabuf_len(wps_ie) + ielen); - if (ies == NULL) { - wpabuf_free(wps_ie); - goto fail; - } - wpabuf_put_buf(ies, wps_ie); - wpabuf_free(wps_ie); - - p2p_scan_ie(wpa_s->global->p2p, ies, dev_id); - - params->p2p_probe = 1; - n = os_malloc(wpabuf_len(ies)); - if (n == NULL) { - wpabuf_free(ies); - goto fail; - } - os_memcpy(n, wpabuf_head(ies), wpabuf_len(ies)); - params->extra_ies = n; - params->extra_ies_len = wpabuf_len(ies); - wpabuf_free(ies); - switch (type) { case P2P_SCAN_SOCIAL: params->freqs = os_calloc(ARRAY_SIZE(social_channels_freq) + 1, @@ -431,6 +421,29 @@ static int wpas_p2p_scan(void *ctx, enum p2p_scan_type type, int freq, break; } + ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p); + ies = wpabuf_alloc(wpabuf_len(wps_ie) + ielen); + if (ies == NULL) { + wpabuf_free(wps_ie); + goto fail; + } + wpabuf_put_buf(ies, wps_ie); + wpabuf_free(wps_ie); + + bands = wpas_get_bands(wpa_s, params->freqs); + p2p_scan_ie(wpa_s->global->p2p, ies, dev_id, bands); + + params->p2p_probe = 1; + n = os_malloc(wpabuf_len(ies)); + if (n == NULL) { + wpabuf_free(ies); + goto fail; + } + os_memcpy(n, wpabuf_head(ies), wpabuf_len(ies)); + params->extra_ies = n; + params->extra_ies_len = wpabuf_len(ies); + wpabuf_free(ies); + radio_remove_works(wpa_s, "p2p-scan", 0); if (radio_add_work(wpa_s, 0, "p2p-scan", 0, wpas_p2p_trigger_scan_cb, params) < 0) @@ -538,27 +551,39 @@ static unsigned int p2p_group_go_member_count(struct wpa_supplicant *wpa_s) } +static unsigned int p2p_is_active_persistent_group(struct wpa_supplicant *wpa_s) +{ + return !wpa_s->p2p_mgmt && wpa_s->current_ssid && + !wpa_s->current_ssid->disabled && + wpa_s->current_ssid->p2p_group && + wpa_s->current_ssid->p2p_persistent_group; +} + + +static unsigned int p2p_is_active_persistent_go(struct wpa_supplicant *wpa_s) +{ + return p2p_is_active_persistent_group(wpa_s) && + wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO; +} + + /* Find an interface for a P2P group where we are the GO */ static struct wpa_supplicant * wpas_p2p_get_go_group(struct wpa_supplicant *wpa_s) { struct wpa_supplicant *save = NULL; - struct wpa_ssid *s; if (!wpa_s) return NULL; for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) { - for (s = wpa_s->conf->ssid; s; s = s->next) { - if (s->disabled || !s->p2p_group || - s->mode != WPAS_MODE_P2P_GO) - continue; + if (!p2p_is_active_persistent_go(wpa_s)) + continue; - /* Prefer a group with connected clients */ - if (p2p_get_group_num_members(wpa_s->p2p_group)) - return wpa_s; - save = wpa_s; - } + /* Prefer a group with connected clients */ + if (p2p_get_group_num_members(wpa_s->p2p_group)) + return wpa_s; + save = wpa_s; } /* No group with connected clients, so pick the one without (if any) */ @@ -566,29 +591,23 @@ wpas_p2p_get_go_group(struct wpa_supplicant *wpa_s) } -/* Find an active P2P group where we are the GO */ -static struct wpa_ssid * wpas_p2p_group_go_ssid(struct wpa_supplicant *wpa_s, - u8 *bssid) +static unsigned int p2p_is_active_persistent_cli(struct wpa_supplicant *wpa_s) { - struct wpa_ssid *s, *empty = NULL; + return p2p_is_active_persistent_group(wpa_s) && + wpa_s->current_ssid->mode == WPAS_MODE_INFRA; +} - if (!wpa_s) - return 0; +/* Find an interface for a P2P group where we are the P2P Client */ +static struct wpa_supplicant * +wpas_p2p_get_cli_group(struct wpa_supplicant *wpa_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 || !s->p2p_group || - s->mode != WPAS_MODE_P2P_GO) - continue; - - os_memcpy(bssid, wpa_s->own_addr, ETH_ALEN); - if (p2p_get_group_num_members(wpa_s->p2p_group)) - return s; - empty = s; - } + if (p2p_is_active_persistent_cli(wpa_s)) + return wpa_s; } - return empty; + return NULL; } @@ -607,20 +626,34 @@ wpas_p2p_get_persistent_go(struct wpa_supplicant *wpa_s) } -static u8 p2ps_group_capability(void *ctx, u8 incoming, u8 role) +static u8 p2ps_group_capability(void *ctx, u8 incoming, u8 role, + unsigned int *force_freq, + unsigned int *pref_freq) { - struct wpa_supplicant *wpa_s = ctx, *tmp_wpa_s; + struct wpa_supplicant *wpa_s = ctx; struct wpa_ssid *s; u8 conncap = P2PS_SETUP_NONE; unsigned int owned_members = 0; - unsigned int owner = 0; - unsigned int client = 0; - struct wpa_supplicant *go_wpa_s; + struct wpa_supplicant *go_wpa_s, *cli_wpa_s; struct wpa_ssid *persistent_go; int p2p_no_group_iface; + unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS], size; wpa_printf(MSG_DEBUG, "P2P: Conncap - in:%d role:%d", incoming, role); + if (force_freq) + *force_freq = 0; + if (pref_freq) + *pref_freq = 0; + + size = P2P_MAX_PREF_CHANNELS; + if (force_freq && pref_freq && + !wpas_p2p_setup_freqs(wpa_s, 0, (int *) force_freq, + (int *) pref_freq, 0, pref_freq_list, &size)) + wpas_p2p_set_own_freq_preference(wpa_s, + *force_freq ? *force_freq : + *pref_freq); + /* * For non-concurrent capable devices: * If persistent_go, then no new. @@ -628,36 +661,21 @@ static u8 p2ps_group_capability(void *ctx, u8 incoming, u8 role) * If client, then no GO. */ go_wpa_s = wpas_p2p_get_go_group(wpa_s); + if (go_wpa_s) + owned_members = p2p_get_group_num_members(go_wpa_s->p2p_group); persistent_go = wpas_p2p_get_persistent_go(wpa_s); p2p_no_group_iface = !wpas_p2p_create_iface(wpa_s); + cli_wpa_s = wpas_p2p_get_cli_group(wpa_s); - wpa_printf(MSG_DEBUG, "P2P: GO(iface)=%p persistent(ssid)=%p", - go_wpa_s, persistent_go); - - for (tmp_wpa_s = wpa_s->global->ifaces; tmp_wpa_s; - tmp_wpa_s = tmp_wpa_s->next) { - for (s = tmp_wpa_s->conf->ssid; s; s = s->next) { - wpa_printf(MSG_DEBUG, - "P2P: sup:%p ssid:%p disabled:%d p2p:%d mode:%d", - tmp_wpa_s, s, s->disabled, - s->p2p_group, s->mode); - if (!s->disabled && s->p2p_group) { - if (s->mode == WPAS_MODE_P2P_GO) { - owned_members += - p2p_get_group_num_members( - tmp_wpa_s->p2p_group); - owner++; - } else - client++; - } - } - } + wpa_printf(MSG_DEBUG, + "P2P: GO(iface)=%p members=%u CLI(iface)=%p persistent(ssid)=%p", + go_wpa_s, owned_members, cli_wpa_s, persistent_go); /* If not concurrent, restrict our choices */ if (p2p_no_group_iface) { wpa_printf(MSG_DEBUG, "P2P: p2p_no_group_iface"); - if (client) + if (cli_wpa_s) return P2PS_SETUP_NONE; if (go_wpa_s) { @@ -689,10 +707,20 @@ static u8 p2ps_group_capability(void *ctx, u8 incoming, u8 role) /* If a required role has been specified, handle it here */ if (role && role != P2PS_SETUP_NEW) { switch (incoming) { + case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW: + case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT: + /* + * Peer has an active GO, so if the role allows it and + * we do not have any active roles, become client. + */ + if ((role & P2PS_SETUP_CLIENT) && !go_wpa_s && + !cli_wpa_s) + return P2PS_SETUP_CLIENT; + + /* fall through */ + case P2PS_SETUP_NONE: case P2PS_SETUP_NEW: - case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT: - case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW: conncap = role; goto grp_owner; @@ -701,7 +729,7 @@ static u8 p2ps_group_capability(void *ctx, u8 incoming, u8 role) * Must be a complimentary role - cannot be a client to * more than one peer. */ - if (incoming == role || client) + if (incoming == role || cli_wpa_s) return P2PS_SETUP_NONE; return P2PS_SETUP_CLIENT; @@ -727,7 +755,7 @@ static u8 p2ps_group_capability(void *ctx, u8 incoming, u8 role) switch (incoming) { case P2PS_SETUP_NONE: case P2PS_SETUP_NEW: - if (client) + if (cli_wpa_s) conncap = P2PS_SETUP_GROUP_OWNER; else if (!owned_members) conncap = P2PS_SETUP_NEW; @@ -742,13 +770,13 @@ static u8 p2ps_group_capability(void *ctx, u8 incoming, u8 role) break; case P2PS_SETUP_GROUP_OWNER: - if (!client) + if (!cli_wpa_s) conncap = P2PS_SETUP_CLIENT; break; case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW: case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT: - if (client) + if (cli_wpa_s) conncap = P2PS_SETUP_GROUP_OWNER; else { u8 r; @@ -770,15 +798,14 @@ grp_owner: (!incoming && (conncap & P2PS_SETUP_NEW))) { if (go_wpa_s && p2p_client_limit_reached(go_wpa_s->p2p_group)) conncap &= ~P2PS_SETUP_GROUP_OWNER; - wpa_printf(MSG_DEBUG, "P2P: GOs:%d members:%d conncap:%d", - owner, owned_members, conncap); s = wpas_p2p_get_persistent_go(wpa_s); - - if (!s && !owner && p2p_no_group_iface) { + if (!s && !go_wpa_s && p2p_no_group_iface) { p2p_set_intended_addr(wpa_s->global->p2p, + wpa_s->p2p_mgmt ? + wpa_s->parent->own_addr : wpa_s->own_addr); - } else if (!s && !owner) { + } else if (!s && !go_wpa_s) { if (wpas_p2p_add_group_interface(wpa_s, WPA_IF_P2P_GO) < 0) { wpa_printf(MSG_ERROR, @@ -850,7 +877,7 @@ static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s, if (wpa_s->cross_connect_in_use) { wpa_s->cross_connect_in_use = 0; - wpa_msg_global(wpa_s->parent, MSG_INFO, + wpa_msg_global(wpa_s->p2pdev, MSG_INFO, P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s", wpa_s->ifname, wpa_s->cross_connect_uplink); } @@ -881,7 +908,7 @@ static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s, break; } if (removal_reason != P2P_GROUP_REMOVAL_SILENT) { - wpa_msg_global(wpa_s->parent, MSG_INFO, + wpa_msg_global(wpa_s->p2pdev, MSG_INFO, P2P_EVENT_GROUP_REMOVED "%s %s%s", wpa_s->ifname, gtype, reason); } @@ -891,7 +918,7 @@ static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s, if (eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL) > 0) wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group idle timeout"); if (eloop_cancel_timeout(wpas_p2p_group_formation_timeout, - wpa_s->parent, NULL) > 0) { + wpa_s->p2pdev, NULL) > 0) { wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group formation " "timeout"); wpa_s->p2p_in_provisioning = 0; @@ -926,6 +953,12 @@ static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s, return 1; } + /* + * The primary interface was used for P2P group operations, so + * need to reset its p2pdev. + */ + wpa_s->p2pdev = wpa_s->parent; + if (!wpa_s->p2p_go_group_formation_completed) { wpa_s->global->p2p_group_formation = NULL; wpa_s->p2p_in_provisioning = 0; @@ -1043,7 +1076,7 @@ static int wpas_p2p_persistent_group(struct wpa_supplicant *wpa_s, "go_dev_addr=" MACSTR, MAC2STR(bssid), group_capab, MAC2STR(go_dev_addr)); - return group_capab & P2P_GROUP_CAPAB_PERSISTENT_GROUP; + return !!(group_capab & P2P_GROUP_CAPAB_PERSISTENT_GROUP); } @@ -1101,7 +1134,8 @@ static int wpas_p2p_store_persistent_group(struct wpa_supplicant *wpa_s, 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->pbss = ssid->pbss; + s->pairwise_cipher = ssid->pbss ? WPA_CIPHER_GCMP : WPA_CIPHER_CCMP; s->export_keys = 1; if (ssid->passphrase) { os_free(s->passphrase); @@ -1241,7 +1275,7 @@ static void wpas_p2p_group_started(struct wpa_supplicant *wpa_s, * Include PSK/passphrase only in the control interface message and * leave it out from the debug log entry. */ - wpa_msg_global_ctrl(wpa_s->parent, MSG_INFO, + wpa_msg_global_ctrl(wpa_s->p2pdev, MSG_INFO, P2P_EVENT_GROUP_STARTED "%s %s ssid=\"%s\" freq=%d%s%s%s%s%s go_dev_addr=" MACSTR "%s%s", @@ -1267,7 +1301,6 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s, 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 @@ -1284,7 +1317,7 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s, wpa_s->group_formation_reported = 1; if (!success) { - wpa_msg_global(wpa_s->parent, MSG_INFO, + wpa_msg_global(wpa_s->p2pdev, MSG_INFO, P2P_EVENT_GROUP_FORMATION_FAILURE); wpas_notify_p2p_group_formation_failure(wpa_s, ""); if (already_deleted) @@ -1294,7 +1327,7 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s, return; } - wpa_msg_global(wpa_s->parent, MSG_INFO, + wpa_msg_global(wpa_s->p2pdev, MSG_INFO, P2P_EVENT_GROUP_FORMATION_SUCCESS); ssid = wpa_s->current_ssid; @@ -1342,16 +1375,15 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s, } if (persistent) - network_id = wpas_p2p_store_persistent_group(wpa_s->parent, - ssid, go_dev_addr); + wpas_p2p_store_persistent_group(wpa_s->p2pdev, + ssid, go_dev_addr); else { os_free(wpa_s->global->add_psk); wpa_s->global->add_psk = NULL; } - if (network_id < 0 && ssid) - network_id = ssid->id; + if (!client) { - wpas_notify_p2p_group_started(wpa_s, ssid, network_id, 0); + wpas_notify_p2p_group_started(wpa_s, ssid, persistent, 0); os_get_reltime(&wpa_s->global->p2p_go_wait_client); } } @@ -1368,6 +1400,25 @@ struct send_action_work { }; +static void wpas_p2p_free_send_action_work(struct wpa_supplicant *wpa_s) +{ + struct send_action_work *awork = wpa_s->p2p_send_action_work->ctx; + + wpa_printf(MSG_DEBUG, + "P2P: Free Action frame radio work @%p (freq=%u dst=" + MACSTR " src=" MACSTR " bssid=" MACSTR " wait_time=%u)", + wpa_s->p2p_send_action_work, awork->freq, + MAC2STR(awork->dst), MAC2STR(awork->src), + MAC2STR(awork->bssid), awork->wait_time); + wpa_hexdump(MSG_DEBUG, "P2P: Freeing pending Action frame", + awork->buf, awork->len); + os_free(awork); + wpa_s->p2p_send_action_work->ctx = NULL; + radio_work_done(wpa_s->p2p_send_action_work); + wpa_s->p2p_send_action_work = NULL; +} + + static void wpas_p2p_send_action_work_timeout(void *eloop_ctx, void *timeout_ctx) { @@ -1377,9 +1428,7 @@ static void wpas_p2p_send_action_work_timeout(void *eloop_ctx, return; wpa_printf(MSG_DEBUG, "P2P: Send Action frame radio work timed out"); - os_free(wpa_s->p2p_send_action_work->ctx); - radio_work_done(wpa_s->p2p_send_action_work); - wpa_s->p2p_send_action_work = NULL; + wpas_p2p_free_send_action_work(wpa_s); } @@ -1387,11 +1436,13 @@ static void wpas_p2p_action_tx_clear(struct wpa_supplicant *wpa_s) { if (wpa_s->p2p_send_action_work) { struct send_action_work *awork; + awork = wpa_s->p2p_send_action_work->ctx; + wpa_printf(MSG_DEBUG, + "P2P: Clear Action TX work @%p (wait_time=%u)", + wpa_s->p2p_send_action_work, awork->wait_time); if (awork->wait_time == 0) { - os_free(awork); - radio_work_done(wpa_s->p2p_send_action_work); - wpa_s->p2p_send_action_work = NULL; + wpas_p2p_free_send_action_work(wpa_s); } else { /* * In theory, this should not be needed, but number of @@ -1447,7 +1498,7 @@ static void wpas_p2p_send_action_tx_status(struct wpa_supplicant *wpa_s, wpa_s->pending_pd_before_join = 0; wpa_dbg(wpa_s, MSG_DEBUG, "P2P: No ACK for PD Req " "during p2p_connect-auto"); - wpa_msg_global(wpa_s->parent, MSG_INFO, + wpa_msg_global(wpa_s->p2pdev, MSG_INFO, P2P_EVENT_FALLBACK_TO_GO_NEG "reason=no-ACK-to-PD-Req"); wpas_p2p_fallback_to_go_neg(wpa_s, 0); @@ -1590,11 +1641,11 @@ static void wpas_start_wps_enrollee(struct wpa_supplicant *wpa_s, } else if (res->wps_method == WPS_NFC) { wpas_wps_start_nfc(wpa_s, res->peer_device_addr, res->peer_interface_addr, - wpa_s->parent->p2p_oob_dev_pw, - wpa_s->parent->p2p_oob_dev_pw_id, 1, - wpa_s->parent->p2p_oob_dev_pw_id == + wpa_s->p2pdev->p2p_oob_dev_pw, + wpa_s->p2pdev->p2p_oob_dev_pw_id, 1, + wpa_s->p2pdev->p2p_oob_dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER ? - wpa_s->parent->p2p_peer_oob_pubkey_hash : + wpa_s->p2pdev->p2p_peer_oob_pubkey_hash : NULL, NULL, 0, 0); #endif /* CONFIG_WPS_NFC */ @@ -1620,7 +1671,7 @@ static void wpas_p2p_add_psk_list(struct wpa_supplicant *wpa_s, if (!wpa_s->ap_iface) return; - persistent = wpas_p2p_get_persistent(wpa_s->parent, NULL, ssid->ssid, + persistent = wpas_p2p_get_persistent(wpa_s->p2pdev, NULL, ssid->ssid, ssid->ssid_len); if (persistent == NULL) return; @@ -1685,8 +1736,8 @@ static void p2p_go_save_group_common_freqs(struct wpa_supplicant *wpa_s, static void p2p_config_write(struct wpa_supplicant *wpa_s) { #ifndef CONFIG_NO_CONFIG_WRITE - if (wpa_s->parent->conf->update_config && - wpa_config_write(wpa_s->parent->confname, wpa_s->parent->conf)) + if (wpa_s->p2pdev->conf->update_config && + wpa_config_write(wpa_s->p2pdev->confname, wpa_s->p2pdev->conf)) wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration"); #endif /* CONFIG_NO_CONFIG_WRITE */ } @@ -1697,7 +1748,15 @@ 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; + + wpa_s->ap_configured_cb = NULL; + wpa_s->ap_configured_cb_ctx = NULL; + wpa_s->ap_configured_cb_data = NULL; + if (!wpa_s->go_params) { + wpa_printf(MSG_ERROR, + "P2P: p2p_go_configured() called with wpa_s->go_params == NULL"); + return; + } p2p_go_save_group_common_freqs(wpa_s, params); p2p_go_dump_common_freqs(wpa_s); @@ -1715,8 +1774,8 @@ static void p2p_go_configured(void *ctx, void *data) params->persistent_group, ""); wpa_s->group_formation_reported = 1; - if (wpa_s->parent->p2ps_method_config_any) { - if (is_zero_ether_addr(wpa_s->parent->p2ps_join_addr)) { + if (wpa_s->p2pdev->p2ps_method_config_any) { + if (is_zero_ether_addr(wpa_s->p2pdev->p2ps_join_addr)) { wpa_dbg(wpa_s, MSG_DEBUG, "P2PS: Setting default PIN for ANY"); wpa_supplicant_ap_wps_pin(wpa_s, NULL, @@ -1725,24 +1784,24 @@ static void p2p_go_configured(void *ctx, void *data) } else { wpa_dbg(wpa_s, MSG_DEBUG, "P2PS: Setting default PIN for " MACSTR, - MAC2STR(wpa_s->parent->p2ps_join_addr)); + MAC2STR(wpa_s->p2pdev->p2ps_join_addr)); wpa_supplicant_ap_wps_pin( - wpa_s, wpa_s->parent->p2ps_join_addr, + wpa_s, wpa_s->p2pdev->p2ps_join_addr, "12345670", NULL, 0, 0); } - wpa_s->parent->p2ps_method_config_any = 0; + wpa_s->p2pdev->p2ps_method_config_any = 0; } os_get_reltime(&wpa_s->global->p2p_go_wait_client); if (params->persistent_group) { - network_id = wpas_p2p_store_persistent_group( - wpa_s->parent, ssid, + wpas_p2p_store_persistent_group( + wpa_s->p2pdev, ssid, wpa_s->global->p2p_dev_addr); wpas_p2p_add_psk_list(wpa_s, ssid); } - if (network_id < 0) - network_id = ssid->id; - wpas_notify_p2p_group_started(wpa_s, ssid, network_id, 0); + + wpas_notify_p2p_group_started(wpa_s, ssid, + params->persistent_group, 0); wpas_p2p_cross_connect_setup(wpa_s); wpas_p2p_set_group_idle_timeout(wpa_s); @@ -1753,11 +1812,11 @@ static void p2p_go_configured(void *ctx, void *data) wpa_s->p2p_go_group_formation_completed = 0; wpa_s->global->p2p_group_formation = wpa_s; eloop_cancel_timeout(wpas_p2p_group_formation_timeout, - wpa_s->parent, NULL); + wpa_s->p2pdev, NULL); eloop_register_timeout( wpa_s->p2p_first_connection_timeout, 0, wpas_p2p_group_formation_timeout, - wpa_s->parent, NULL); + wpa_s->p2pdev, NULL); } return; @@ -1775,17 +1834,17 @@ static void p2p_go_configured(void *ctx, void *data) params->peer_device_addr); #ifdef CONFIG_WPS_NFC } else if (params->wps_method == WPS_NFC) { - if (wpa_s->parent->p2p_oob_dev_pw_id != + if (wpa_s->p2pdev->p2p_oob_dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER && - !wpa_s->parent->p2p_oob_dev_pw) { + !wpa_s->p2pdev->p2p_oob_dev_pw) { wpa_printf(MSG_DEBUG, "P2P: No NFC Dev Pw known"); return; } wpas_ap_wps_add_nfc_pw( - wpa_s, wpa_s->parent->p2p_oob_dev_pw_id, - wpa_s->parent->p2p_oob_dev_pw, - wpa_s->parent->p2p_peer_oob_pk_hash_known ? - wpa_s->parent->p2p_peer_oob_pubkey_hash : NULL); + wpa_s, wpa_s->p2pdev->p2p_oob_dev_pw_id, + wpa_s->p2pdev->p2p_oob_dev_pw, + wpa_s->p2pdev->p2p_peer_oob_pk_hash_known ? + wpa_s->p2pdev->p2p_peer_oob_pubkey_hash : NULL); #endif /* CONFIG_WPS_NFC */ } else if (wpa_s->p2p_pin[0]) wpa_supplicant_ap_wps_pin(wpa_s, params->peer_interface_addr, @@ -1822,12 +1881,14 @@ static void wpas_start_wps_go(struct wpa_supplicant *wpa_s, wpa_config_set_network_defaults(ssid); ssid->temporary = 1; ssid->p2p_group = 1; - ssid->p2p_persistent_group = params->persistent_group; + ssid->p2p_persistent_group = !!params->persistent_group; ssid->mode = group_formation ? WPAS_MODE_P2P_GROUP_FORMATION : WPAS_MODE_P2P_GO; ssid->frequency = params->freq; ssid->ht40 = params->ht40; ssid->vht = params->vht; + ssid->max_oper_chwidth = params->max_oper_chwidth; + ssid->vht_center_freq2 = params->vht_center_freq2; ssid->ssid = os_zalloc(params->ssid_len + 1); if (ssid->ssid) { os_memcpy(ssid->ssid, params->ssid, params->ssid_len); @@ -1845,6 +1906,8 @@ static void wpas_start_wps_go(struct wpa_supplicant *wpa_s, */ ssid->pairwise_cipher = WPA_CIPHER_GCMP; ssid->group_cipher = WPA_CIPHER_GCMP; + /* P2P GO in 60 GHz is always a PCP (PBSS) */ + ssid->pbss = 1; } if (os_strlen(params->passphrase) > 0) { ssid->passphrase = os_strdup(params->passphrase); @@ -1861,7 +1924,7 @@ static void wpas_start_wps_go(struct wpa_supplicant *wpa_s, 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; + ssid->ap_max_inactivity = wpa_s->p2pdev->conf->p2p_go_max_inactivity; wpa_s->ap_configured_cb = p2p_go_configured; wpa_s->ap_configured_cb_ctx = wpa_s; @@ -1885,7 +1948,12 @@ static void wpas_p2p_clone_config(struct wpa_supplicant *dst, d = dst->conf; s = src->conf; -#define C(n) if (s->n) d->n = os_strdup(s->n) +#define C(n) \ +do { \ + if (s->n && !d->n) \ + d->n = os_strdup(s->n); \ +} while (0) + C(device_name); C(manufacturer); C(model_name); @@ -1913,7 +1981,10 @@ static void wpas_p2p_clone_config(struct wpa_supplicant *dst, d->disable_scan_offload = s->disable_scan_offload; d->passive_scan = s->passive_scan; - if (s->wps_nfc_dh_privkey && s->wps_nfc_dh_pubkey) { + if (s->wps_nfc_dh_privkey && s->wps_nfc_dh_pubkey && + !d->wps_nfc_pw_from_config) { + wpabuf_free(d->wps_nfc_dh_privkey); + wpabuf_free(d->wps_nfc_dh_pubkey); d->wps_nfc_dh_privkey = wpabuf_dup(s->wps_nfc_dh_privkey); d->wps_nfc_dh_pubkey = wpabuf_dup(s->wps_nfc_dh_pubkey); } @@ -2071,7 +2142,7 @@ static void wpas_p2p_group_formation_failed(struct wpa_supplicant *wpa_s, int already_deleted) { eloop_cancel_timeout(wpas_p2p_group_formation_timeout, - wpa_s->parent, NULL); + wpa_s->p2pdev, NULL); if (wpa_s->global->p2p) p2p_group_formation_failed(wpa_s->global->p2p); wpas_group_formation_completed(wpa_s, 0, already_deleted); @@ -2082,9 +2153,9 @@ static void wpas_p2p_grpform_fail_after_wps(struct wpa_supplicant *wpa_s) { wpa_printf(MSG_DEBUG, "P2P: Reject group formation due to WPS provisioning failure"); eloop_cancel_timeout(wpas_p2p_group_formation_timeout, - wpa_s->parent, NULL); + wpa_s->p2pdev, NULL); eloop_register_timeout(0, 0, wpas_p2p_group_formation_timeout, - wpa_s->parent, NULL); + wpa_s->p2pdev, NULL); wpa_s->global->p2p_fail_on_wps_complete = 0; } @@ -2095,15 +2166,16 @@ void wpas_p2p_ap_setup_failed(struct wpa_supplicant *wpa_s) return; /* Speed up group formation timeout since this cannot succeed */ eloop_cancel_timeout(wpas_p2p_group_formation_timeout, - wpa_s->parent, NULL); + wpa_s->p2pdev, NULL); eloop_register_timeout(0, 0, wpas_p2p_group_formation_timeout, - wpa_s->parent, NULL); + wpa_s->p2pdev, NULL); } static void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res) { struct wpa_supplicant *wpa_s = ctx; + struct wpa_supplicant *group_wpa_s; if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) { wpa_drv_cancel_remain_on_channel(wpa_s); @@ -2129,6 +2201,8 @@ static void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res) res->ht40 = 1; if (wpa_s->p2p_go_vht) res->vht = 1; + res->max_oper_chwidth = wpa_s->p2p_go_max_oper_chwidth; + res->vht_center_freq2 = wpa_s->p2p_go_vht_center_freq2; wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_SUCCESS "role=%s " "freq=%d ht40=%d peer_dev=" MACSTR " peer_iface=" MACSTR @@ -2154,7 +2228,7 @@ static void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res) } if (wpa_s->create_p2p_iface) { - struct wpa_supplicant *group_wpa_s = + 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); @@ -2163,31 +2237,27 @@ static void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res) wpas_p2p_group_formation_failed(wpa_s, 1); return; } - if (group_wpa_s != wpa_s) { - os_memcpy(group_wpa_s->p2p_pin, wpa_s->p2p_pin, - sizeof(group_wpa_s->p2p_pin)); - group_wpa_s->p2p_wps_method = wpa_s->p2p_wps_method; - } os_memset(wpa_s->pending_interface_addr, 0, ETH_ALEN); wpa_s->pending_interface_name[0] = '\0'; - group_wpa_s->p2p_in_provisioning = 1; - - if (res->role_go) { - wpas_start_wps_go(group_wpa_s, res, 1); - } else { - os_get_reltime(&group_wpa_s->scan_min_time); - wpas_start_wps_enrollee(group_wpa_s, res); - } } else { - wpa_s->p2p_in_provisioning = 1; - wpa_s->global->p2p_group_formation = wpa_s; + group_wpa_s = wpa_s->parent; + wpa_s->global->p2p_group_formation = group_wpa_s; + if (group_wpa_s != wpa_s) + wpas_p2p_clone_config(group_wpa_s, wpa_s); + } - if (res->role_go) { - wpas_start_wps_go(wpa_s, res, 1); - } else { - os_get_reltime(&wpa_s->scan_min_time); - wpas_start_wps_enrollee(ctx, res); - } + group_wpa_s->p2p_in_provisioning = 1; + group_wpa_s->p2pdev = wpa_s; + 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; + } + if (res->role_go) { + wpas_start_wps_go(group_wpa_s, res, 1); + } else { + os_get_reltime(&group_wpa_s->scan_min_time); + wpas_start_wps_enrollee(group_wpa_s, res); } wpa_s->p2p_long_listen = 0; @@ -2308,6 +2378,10 @@ static void wpas_dev_lost(void *ctx, const u8 *dev_addr) static void wpas_find_stopped(void *ctx) { struct wpa_supplicant *wpa_s = ctx; + + if (wpa_s->p2p_scan_work && wpas_abort_ongoing_scan(wpa_s) < 0) + wpa_printf(MSG_DEBUG, "P2P: Abort ongoing scan failed"); + wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_FIND_STOPPED); wpas_notify_p2p_find_stopped(wpa_s); } @@ -2521,7 +2595,13 @@ static void wpas_prov_disc_req(void *ctx, const u8 *peer, u16 config_methods, params[sizeof(params) - 1] = '\0'; if (config_methods & WPS_CONFIG_DISPLAY) { - generated_pin = wps_generate_pin(); + if (wps_generate_pin(&generated_pin) < 0) { + wpa_printf(MSG_DEBUG, "P2P: Could not generate PIN"); + wpas_notify_p2p_provision_discovery( + wpa_s, peer, 0 /* response */, + P2P_PROV_DISC_INFO_UNAVAILABLE, 0, 0); + return; + } wpas_prov_disc_local_display(wpa_s, peer, params, generated_pin); } else if (config_methods & WPS_CONFIG_KEYPAD) @@ -2566,7 +2646,13 @@ static void wpas_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods) 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(); + if (wps_generate_pin(&generated_pin) < 0) { + wpa_printf(MSG_DEBUG, "P2P: Could not generate PIN"); + wpas_notify_p2p_provision_discovery( + wpa_s, peer, 0 /* response */, + P2P_PROV_DISC_INFO_UNAVAILABLE, 0, 0); + return; + } wpas_prov_disc_local_display(wpa_s, peer, params, generated_pin); } else if (config_methods & WPS_CONFIG_PUSHBUTTON) @@ -2589,7 +2675,7 @@ static void wpas_prov_disc_fail(void *ctx, const u8 *peer, if (wpa_s->p2p_fallback_to_go_neg) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: PD for p2p_connect-auto " "failed - fall back to GO Negotiation"); - wpa_msg_global(wpa_s->parent, MSG_INFO, + wpa_msg_global(wpa_s->p2pdev, MSG_INFO, P2P_EVENT_FALLBACK_TO_GO_NEG "reason=PD-failed"); wpas_p2p_fallback_to_go_neg(wpa_s, 0); @@ -2685,6 +2771,29 @@ static int wpas_p2p_go_is_peer_freq(struct wpa_supplicant *wpa_s, int freq) } +static int wpas_sta_check_ecsa(struct hostapd_data *hapd, + struct sta_info *sta, void *ctx) +{ + int *ecsa_support = ctx; + + *ecsa_support &= sta->ecsa_supported; + + return 0; +} + + +/* Check if all the peers support eCSA */ +static int wpas_p2p_go_clients_support_ecsa(struct wpa_supplicant *wpa_s) +{ + int ecsa_support = 1; + + ap_for_each_sta(wpa_s->ap_iface->bss[0], wpas_sta_check_ecsa, + &ecsa_support); + + return ecsa_support; +} + + /** * Pick the best frequency to use from all the currently used frequencies. */ @@ -2811,7 +2920,11 @@ static u8 wpas_invitation_process(void *ctx, const u8 *sa, const u8 *bssid, "invitation"); return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; } - os_memcpy(group_bssid, wpa_s->own_addr, ETH_ALEN); + if (wpa_s->p2p_mgmt) + os_memcpy(group_bssid, wpa_s->parent->own_addr, + ETH_ALEN); + else + 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) @@ -2893,12 +3006,31 @@ static void wpas_invitation_received(void *ctx, const u8 *sa, const u8 *bssid, MAC2STR(sa), op_freq, wpa_ssid_txt(ssid, ssid_len)); if (s) { int go = s->mode == WPAS_MODE_P2P_GO; + if (go) { + wpa_msg_global(wpa_s, MSG_INFO, + P2P_EVENT_INVITATION_ACCEPTED + "sa=" MACSTR + " persistent=%d freq=%d", + MAC2STR(sa), s->id, op_freq); + } else { + wpa_msg_global(wpa_s, MSG_INFO, + P2P_EVENT_INVITATION_ACCEPTED + "sa=" MACSTR + " persistent=%d", + MAC2STR(sa), s->id); + } wpas_p2p_group_add_persistent( - wpa_s, s, go, 0, op_freq, 0, 0, NULL, + wpa_s, s, go, 0, op_freq, 0, 0, 0, 0, NULL, go ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0, 1); } else if (bssid) { wpa_s->user_initiated_pd = 0; + wpa_msg_global(wpa_s, MSG_INFO, + P2P_EVENT_INVITATION_ACCEPTED + "sa=" MACSTR " go_dev_addr=" MACSTR + " bssid=" MACSTR " unknown-network", + MAC2STR(sa), MAC2STR(go_dev_addr), + MAC2STR(bssid)); wpas_p2p_join(wpa_s, bssid, go_dev_addr, wpa_s->p2p_wps_method, 0, op_freq, ssid, ssid_len); @@ -2999,7 +3131,7 @@ static void wpas_remove_persistent_client(struct wpa_supplicant *wpa_s, if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GO || !ssid->p2p_persistent_group) return; /* Not operating as a GO in persistent group */ - ssid = wpas_p2p_get_persistent(wpa_s->parent, peer, + ssid = wpas_p2p_get_persistent(wpa_s->p2pdev, peer, ssid->ssid, ssid->ssid_len); wpas_remove_persistent_peer(wpa_s, ssid, peer, 1); } @@ -3027,9 +3159,37 @@ static void wpas_invitation_result(void *ctx, int status, const u8 *bssid, wpa_printf(MSG_DEBUG, "P2P: Invitation result - status=%d peer=" MACSTR, status, MAC2STR(peer)); if (wpa_s->pending_invite_ssid_id == -1) { + struct wpa_supplicant *group_if = + wpa_s->global->p2p_invite_group; + if (status == P2P_SC_FAIL_UNKNOWN_GROUP) wpas_remove_persistent_client(wpa_s, peer); - return; /* Invitation to active group */ + + /* + * Invitation to an active group. If this is successful and we + * are the GO, set the client wait to postpone some concurrent + * operations and to allow provisioning and connection to happen + * more quickly. + */ + if (status == P2P_SC_SUCCESS && + group_if && group_if->current_ssid && + group_if->current_ssid->mode == WPAS_MODE_P2P_GO) { + os_get_reltime(&wpa_s->global->p2p_go_wait_client); +#ifdef CONFIG_TESTING_OPTIONS + if (group_if->p2p_go_csa_on_inv) { + wpa_printf(MSG_DEBUG, + "Testing: force P2P GO CSA after invitation"); + eloop_cancel_timeout( + wpas_p2p_reconsider_moving_go, + wpa_s, NULL); + eloop_register_timeout( + 0, 50000, + wpas_p2p_reconsider_moving_go, + wpa_s, NULL); + } +#endif /* CONFIG_TESTING_OPTIONS */ + } + return; } if (status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) { @@ -3083,7 +3243,9 @@ static void wpas_invitation_result(void *ctx, int status, const u8 *bssid, ssid->mode == WPAS_MODE_P2P_GO, wpa_s->p2p_persistent_go_freq, freq, + wpa_s->p2p_go_vht_center_freq2, wpa_s->p2p_go_ht40, wpa_s->p2p_go_vht, + wpa_s->p2p_go_max_oper_chwidth, channels, ssid->mode == WPAS_MODE_P2P_GO ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : @@ -3169,21 +3331,6 @@ static int wpas_p2p_default_channels(struct wpa_supplicant *wpa_s, } -static struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes, - u16 num_modes, - enum hostapd_hw_mode mode) -{ - u16 i; - - for (i = 0; i < num_modes; i++) { - if (modes[i].mode == mode) - return &modes[i]; - } - - return NULL; -} - - enum chan_allowed { NOT_ALLOWED, NO_IR, ALLOWED }; @@ -3217,49 +3364,12 @@ static int has_channel(struct wpa_global *global, } -struct p2p_oper_class_map { - enum hostapd_hw_mode mode; - u8 op_class; - u8 min_chan; - u8 max_chan; - u8 inc; - enum { BW20, BW40PLUS, BW40MINUS, BW80, BW2160 } bw; -}; - -static const struct p2p_oper_class_map op_class[] = { - { HOSTAPD_MODE_IEEE80211G, 81, 1, 13, 1, BW20 }, -#if 0 /* Do not enable HT40 on 2 GHz for now */ - { HOSTAPD_MODE_IEEE80211G, 83, 1, 9, 1, BW40PLUS }, - { HOSTAPD_MODE_IEEE80211G, 84, 5, 13, 1, BW40MINUS }, -#endif - { HOSTAPD_MODE_IEEE80211A, 115, 36, 48, 4, BW20 }, - { HOSTAPD_MODE_IEEE80211A, 124, 149, 161, 4, BW20 }, - { HOSTAPD_MODE_IEEE80211A, 125, 149, 169, 4, BW20 }, - { HOSTAPD_MODE_IEEE80211A, 116, 36, 44, 8, BW40PLUS }, - { HOSTAPD_MODE_IEEE80211A, 117, 40, 48, 8, BW40MINUS }, - { HOSTAPD_MODE_IEEE80211A, 126, 149, 157, 8, BW40PLUS }, - { HOSTAPD_MODE_IEEE80211A, 127, 153, 161, 8, BW40MINUS }, - - /* - * IEEE P802.11ac/D7.0 Table E-4 actually talks about channel center - * frequency index 42, 58, 106, 122, 138, 155 with channel spacing of - * 80 MHz, but currently use the following definition for simplicity - * (these center frequencies are not actual channels, which makes - * has_channel() fail). wpas_p2p_verify_80mhz() should take care of - * removing invalid channels. - */ - { HOSTAPD_MODE_IEEE80211A, 128, 36, 161, 4, BW80 }, - { HOSTAPD_MODE_IEEE80211AD, 180, 1, 4, 1, BW2160 }, - { -1, 0, 0, 0, 0, BW20 } -}; - - static int wpas_p2p_get_center_80mhz(struct wpa_supplicant *wpa_s, struct hostapd_hw_modes *mode, u8 channel) { u8 center_channels[] = { 42, 58, 106, 122, 138, 155 }; - unsigned int i; + size_t i; if (mode->mode != HOSTAPD_MODE_IEEE80211A) return 0; @@ -3315,6 +3425,75 @@ static enum chan_allowed wpas_p2p_verify_80mhz(struct wpa_supplicant *wpa_s, } +static int wpas_p2p_get_center_160mhz(struct wpa_supplicant *wpa_s, + struct hostapd_hw_modes *mode, + u8 channel) +{ + u8 center_channels[] = { 50, 114 }; + unsigned int i; + + if (mode->mode != HOSTAPD_MODE_IEEE80211A) + return 0; + + for (i = 0; i < ARRAY_SIZE(center_channels); i++) + /* + * In 160 MHz, the bandwidth "spans" 28 channels (e.g., 36-64), + * so the center channel is 14 channels away from the start/end. + */ + if (channel >= center_channels[i] - 14 && + channel <= center_channels[i] + 14) + return center_channels[i]; + + return 0; +} + + +static enum chan_allowed wpas_p2p_verify_160mhz(struct wpa_supplicant *wpa_s, + struct hostapd_hw_modes *mode, + u8 channel, u8 bw) +{ + u8 center_chan; + int i, flags; + enum chan_allowed res, ret = ALLOWED; + + center_chan = wpas_p2p_get_center_160mhz(wpa_s, mode, channel); + if (!center_chan) + return NOT_ALLOWED; + /* VHT 160 MHz uses DFS channels in most countries. */ + + /* Check all the channels are available */ + for (i = 0; i < 8; i++) { + int adj_chan = center_chan - 14 + i * 4; + + res = has_channel(wpa_s->global, mode, adj_chan, &flags); + if (res == NOT_ALLOWED) + return NOT_ALLOWED; + + if (res == NO_IR) + ret = NO_IR; + + if (i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_150)) + return NOT_ALLOWED; + if (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_130)) + return NOT_ALLOWED; + if (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_110)) + return NOT_ALLOWED; + if (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_90)) + return NOT_ALLOWED; + if (i == 4 && !(flags & HOSTAPD_CHAN_VHT_90_70)) + return NOT_ALLOWED; + if (i == 5 && !(flags & HOSTAPD_CHAN_VHT_110_50)) + return NOT_ALLOWED; + if (i == 6 && !(flags & HOSTAPD_CHAN_VHT_130_30)) + return NOT_ALLOWED; + if (i == 7 && !(flags & HOSTAPD_CHAN_VHT_150_10)) + return NOT_ALLOWED; + } + + return ret; +} + + static enum chan_allowed wpas_p2p_verify_channel(struct wpa_supplicant *wpa_s, struct hostapd_hw_modes *mode, u8 channel, u8 bw) @@ -3333,6 +3512,8 @@ static enum chan_allowed wpas_p2p_verify_channel(struct wpa_supplicant *wpa_s, res2 = has_channel(wpa_s->global, mode, channel + 4, NULL); } else if (bw == BW80) { res2 = wpas_p2p_verify_80mhz(wpa_s, mode, channel, bw); + } else if (bw == BW160) { + res2 = wpas_p2p_verify_160mhz(wpa_s, mode, channel, bw); } if (res == NOT_ALLOWED || res2 == NOT_ALLOWED) @@ -3359,11 +3540,14 @@ static int wpas_p2p_setup_channels(struct wpa_supplicant *wpa_s, cla = cli_cla = 0; - for (op = 0; op_class[op].op_class; op++) { - const struct p2p_oper_class_map *o = &op_class[op]; + for (op = 0; global_op_class[op].op_class; op++) { + const struct oper_class_map *o = &global_op_class[op]; u8 ch; struct p2p_reg_class *reg = NULL, *cli_reg = NULL; + if (o->p2p == NO_P2P_SUPP) + continue; + mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, o->mode); if (mode == NULL) continue; @@ -3418,10 +3602,13 @@ int wpas_p2p_get_ht40_mode(struct wpa_supplicant *wpa_s, int op; enum chan_allowed ret; - for (op = 0; op_class[op].op_class; op++) { - const struct p2p_oper_class_map *o = &op_class[op]; + for (op = 0; global_op_class[op].op_class; op++) { + const struct oper_class_map *o = &global_op_class[op]; u8 ch; + if (o->p2p == NO_P2P_SUPP) + continue; + for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) { if (o->mode != HOSTAPD_MODE_IEEE80211A || (o->bw != BW40PLUS && o->bw != BW40MINUS) || @@ -3446,6 +3633,15 @@ int wpas_p2p_get_vht80_center(struct wpa_supplicant *wpa_s, } +int wpas_p2p_get_vht160_center(struct wpa_supplicant *wpa_s, + struct hostapd_hw_modes *mode, u8 channel) +{ + if (!wpas_p2p_verify_channel(wpa_s, mode, channel, BW160)) + return 0; + return wpas_p2p_get_center_160mhz(wpa_s, mode, channel); +} + + static int wpas_get_noa(void *ctx, const u8 *interface_addr, u8 *buf, size_t buf_len) { @@ -3577,6 +3773,7 @@ int wpas_p2p_add_p2pdev_interface(struct wpa_supplicant *wpa_s, return -1; } + p2pdev_wpa_s->p2pdev = p2pdev_wpa_s; wpa_s->pending_interface_name[0] = '\0'; return 0; } @@ -3638,11 +3835,12 @@ static int wpas_get_persistent_group(void *ctx, const u8 *addr, const u8 *ssid, static int wpas_get_go_info(void *ctx, u8 *intended_addr, - u8 *ssid, size_t *ssid_len, int *group_iface) + u8 *ssid, size_t *ssid_len, int *group_iface, + unsigned int *freq) { struct wpa_supplicant *wpa_s = ctx; + struct wpa_supplicant *go; struct wpa_ssid *s; - u8 bssid[ETH_ALEN]; /* * group_iface will be set to 1 only if a dedicated interface for P2P @@ -3652,17 +3850,25 @@ static int wpas_get_go_info(void *ctx, u8 *intended_addr, * that the pending interface should be used. */ *group_iface = 0; - s = wpas_p2p_group_go_ssid(wpa_s, bssid); - if (!s) { + + if (freq) + *freq = 0; + + go = wpas_p2p_get_go_group(wpa_s); + if (!go) { s = wpas_p2p_get_persistent_go(wpa_s); *group_iface = wpas_p2p_create_iface(wpa_s); if (s) - os_memcpy(bssid, s->bssid, ETH_ALEN); + os_memcpy(intended_addr, s->bssid, ETH_ALEN); else return 0; + } else { + s = go->current_ssid; + os_memcpy(intended_addr, go->own_addr, ETH_ALEN); + if (freq) + *freq = go->assoc_freq; } - os_memcpy(intended_addr, bssid, ETH_ALEN); os_memcpy(ssid, s->ssid, s->ssid_len); *ssid_len = s->ssid_len; @@ -3750,11 +3956,13 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev, const u8 *persist_ssid, size_t persist_ssid_size, int response_done, int prov_start, const char *session_info, - const u8 *feat_cap, size_t feat_cap_len) + const u8 *feat_cap, size_t feat_cap_len, + unsigned int freq, + const u8 *group_ssid, size_t group_ssid_len) { struct wpa_supplicant *wpa_s = ctx; u8 mac[ETH_ALEN]; - struct wpa_ssid *persistent_go, *stale, *s; + struct wpa_ssid *persistent_go, *stale, *s = NULL; int save_config = 0; struct wpa_supplicant *go_wpa_s; char feat_cap_str[256]; @@ -3825,8 +4033,9 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev, } /* Clean up stale persistent groups with this device */ - s = wpas_p2p_get_persistent(wpa_s, dev, persist_ssid, - persist_ssid_size); + if (persist_ssid && persist_ssid_size) + s = wpas_p2p_get_persistent(wpa_s, dev, persist_ssid, + persist_ssid_size); if (persist_ssid && s && s->mode != WPAS_MODE_P2P_GO && is_zero_ether_addr(grp_mac)) { @@ -3908,6 +4117,7 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev, go_ifname[0] = '\0'; if (!go_wpa_s) { wpa_s->global->pending_p2ps_group = 1; + wpa_s->global->pending_p2ps_group_freq = freq; if (!wpas_p2p_create_iface(wpa_s)) os_memcpy(go_ifname, wpa_s->ifname, @@ -3922,7 +4132,8 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev, wpa_s, P2P_SC_FAIL_UNKNOWN_GROUP, dev, adv_mac, ses_mac, grp_mac, adv_id, ses_id, 0, 0, - NULL, 0, 0, 0, NULL, NULL, 0); + NULL, 0, 0, 0, NULL, NULL, 0, 0, + NULL, 0); return; } @@ -3930,13 +4141,13 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev, if (response_done && persistent_go) { wpas_p2p_group_add_persistent( wpa_s, persistent_go, - 0, 0, 0, 0, 0, NULL, + 0, 0, freq, 0, 0, 0, 0, NULL, persistent_go->mode == WPAS_MODE_P2P_GO ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0, 0); } else if (response_done) { - wpas_p2p_group_add(wpa_s, 1, 0, 0, 0); + wpas_p2p_group_add(wpa_s, 1, freq, 0, 0, 0, 0); } if (passwd_id == DEV_PW_P2PS_DEFAULT) { @@ -3989,16 +4200,24 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev, } if (conncap == P2PS_SETUP_CLIENT) { + char ssid_hex[32 * 2 + 1]; + + if (group_ssid) + wpa_snprintf_hex(ssid_hex, sizeof(ssid_hex), + group_ssid, group_ssid_len); + else + ssid_hex[0] = '\0'; wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_P2PS_PROVISION_DONE MACSTR " status=%d conncap=%x" " adv_id=%x adv_mac=" MACSTR " session=%x mac=" MACSTR - " dev_passwd_id=%d join=" MACSTR "%s", + " dev_passwd_id=%d join=" MACSTR "%s%s%s", MAC2STR(dev), status, conncap, adv_id, MAC2STR(adv_mac), ses_id, MAC2STR(ses_mac), - passwd_id, MAC2STR(grp_mac), feat_cap_str); + passwd_id, MAC2STR(grp_mac), feat_cap_str, + group_ssid ? " group_ssid=" : "", ssid_hex); } else { wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_P2PS_PROVISION_DONE MACSTR @@ -4025,10 +4244,13 @@ static int wpas_prov_disc_resp_cb(void *ctx) { struct wpa_supplicant *wpa_s = ctx; struct wpa_ssid *persistent_go; + unsigned int freq; if (!wpa_s->global->pending_p2ps_group) return 0; + freq = wpa_s->global->pending_p2ps_group_freq; + wpa_s->global->pending_p2ps_group_freq = 0; wpa_s->global->pending_p2ps_group = 0; if (wpas_p2p_get_go_group(wpa_s)) @@ -4037,11 +4259,11 @@ static int wpas_prov_disc_resp_cb(void *ctx) if (persistent_go) { wpas_p2p_group_add_persistent( - wpa_s, persistent_go, 0, 0, 0, 0, 0, NULL, + wpa_s, persistent_go, 0, 0, 0, 0, 0, 0, 0, NULL, persistent_go->mode == WPAS_MODE_P2P_GO ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0, 0); } else { - wpas_p2p_group_add(wpa_s, 1, 0, 0, 0); + wpas_p2p_group_add(wpa_s, 1, freq, 0, 0, 0, 0); } return 1; @@ -4333,8 +4555,7 @@ static void wpas_p2p_deinit_global(struct wpa_global *global) static int wpas_p2p_create_iface(struct wpa_supplicant *wpa_s) { - if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) && - wpa_s->conf->p2p_no_group_iface) + 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 | @@ -4415,7 +4636,7 @@ static void wpas_p2p_check_join_scan_limit(struct wpa_supplicant *wpa_s) MAC2STR(wpa_s->pending_join_dev_addr)); return; } - wpa_msg_global(wpa_s->parent, MSG_INFO, + wpa_msg_global(wpa_s->p2pdev, MSG_INFO, P2P_EVENT_GROUP_FORMATION_FAILURE); wpas_notify_p2p_group_formation_failure(wpa_s, ""); } @@ -4551,7 +4772,7 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s, if (join < 0) { wpa_printf(MSG_DEBUG, "P2P: Peer was not found to be " "running a GO -> use GO Negotiation"); - wpa_msg_global(wpa_s->parent, MSG_INFO, + wpa_msg_global(wpa_s->p2pdev, MSG_INFO, P2P_EVENT_FALLBACK_TO_GO_NEG "reason=peer-not-running-GO"); wpas_p2p_connect(wpa_s, wpa_s->pending_join_dev_addr, @@ -4559,10 +4780,13 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s, wpa_s->p2p_persistent_group, 0, 0, 0, wpa_s->p2p_go_intent, wpa_s->p2p_connect_freq, + wpa_s->p2p_go_vht_center_freq2, wpa_s->p2p_persistent_id, wpa_s->p2p_pd_before_go_neg, wpa_s->p2p_go_ht40, - wpa_s->p2p_go_vht); + wpa_s->p2p_go_vht, + wpa_s->p2p_go_max_oper_chwidth, + NULL, 0); return; } @@ -4570,7 +4794,7 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s, "try to join the group", join ? "" : " in older scan"); if (!join) { - wpa_msg_global(wpa_s->parent, MSG_INFO, + wpa_msg_global(wpa_s->p2pdev, MSG_INFO, P2P_EVENT_FALLBACK_TO_GO_NEG_ENABLED); wpa_s->p2p_fallback_to_go_neg = 1; } @@ -4608,8 +4832,7 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s, bss = wpa_bss_get(wpa_s, wpa_s->pending_join_iface_addr, wpa_s->p2p_join_ssid, wpa_s->p2p_join_ssid_len); - } - if (!bss) { + } else if (!bss) { wpa_printf(MSG_DEBUG, "P2P: Trying to find target GO BSS entry based on BSSID " MACSTR, MAC2STR(wpa_s->pending_join_iface_addr)); bss = wpa_bss_get_bssid_latest(wpa_s, @@ -4640,7 +4863,7 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s, u16 method; if (wpas_check_freq_conflict(wpa_s, freq) > 0) { - wpa_msg_global(wpa_s->parent, MSG_INFO, + wpa_msg_global(wpa_s->p2pdev, MSG_INFO, P2P_EVENT_GROUP_FORMATION_FAILURE "reason=FREQ_CONFLICT"); wpas_notify_p2p_group_formation_failure( @@ -4708,7 +4931,8 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s, start: /* Start join operation immediately */ - wpas_p2p_join_start(wpa_s, 0, NULL, 0); + wpas_p2p_join_start(wpa_s, 0, wpa_s->p2p_join_ssid, + wpa_s->p2p_join_ssid_len); } @@ -4720,6 +4944,7 @@ static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq, struct wpabuf *wps_ie, *ies; size_t ielen; int freqs[2] = { 0, 0 }; + unsigned int bands; os_memset(¶ms, 0, sizeof(params)); @@ -4745,22 +4970,6 @@ static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq, return; } - ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p); - ies = wpabuf_alloc(wpabuf_len(wps_ie) + ielen); - if (ies == NULL) { - wpabuf_free(wps_ie); - wpas_p2p_scan_res_join(wpa_s, NULL); - return; - } - wpabuf_put_buf(ies, wps_ie); - wpabuf_free(wps_ie); - - p2p_scan_ie(wpa_s->global->p2p, ies, NULL); - - params.p2p_probe = 1; - params.extra_ies = wpabuf_head(ies); - params.extra_ies_len = wpabuf_len(ies); - if (!freq) { int oper_freq; /* @@ -4777,6 +4986,23 @@ static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq, params.freqs = freqs; } + 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); + + bands = wpas_get_bands(wpa_s, freqs); + p2p_scan_ie(wpa_s->global->p2p, ies, NULL, bands); + + params.p2p_probe = 1; + params.extra_ies = wpabuf_head(ies); + params.extra_ies_len = wpabuf_len(ies); + /* * Run a scan to update BSS table and start Provision Discovery once * the new scan results become available. @@ -4874,8 +5100,13 @@ static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s, int freq, res.ssid_len = ssid_len; os_memcpy(res.ssid, ssid, ssid_len); } else { - bss = wpa_bss_get_bssid_latest(wpa_s, - wpa_s->pending_join_iface_addr); + if (ssid && ssid_len) { + bss = wpa_bss_get(wpa_s, wpa_s->pending_join_iface_addr, + ssid, ssid_len); + } else { + bss = wpa_bss_get_bssid_latest( + wpa_s, wpa_s->pending_join_iface_addr); + } if (bss) { res.freq = bss->freq; res.ssid_len = bss->ssid_len; @@ -4883,6 +5114,11 @@ static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s, int freq, wpa_printf(MSG_DEBUG, "P2P: Join target GO operating frequency from BSS table: %d MHz (SSID %s)", bss->freq, wpa_ssid_txt(bss->ssid, bss->ssid_len)); + } else if (ssid && ssid_len) { + res.ssid_len = ssid_len; + os_memcpy(res.ssid, ssid, ssid_len); + wpa_printf(MSG_DEBUG, "P2P: Join target GO (SSID %s)", + wpa_ssid_txt(ssid, ssid_len)); } } @@ -5067,12 +5303,17 @@ exit_free: * initiating Group Owner negotiation * @go_intent: GO Intent or -1 to use default * @freq: Frequency for the group or 0 for auto-selection + * @freq2: Center frequency of segment 1 for the GO operating in VHT 80P80 mode * @persistent_id: Persistent group credentials to use for forcing GO * parameters or -1 to generate new values (SSID/passphrase) * @pd: Whether to send Provision Discovery prior to GO Negotiation as an * interoperability workaround when initiating group formation * @ht40: Start GO with 40 MHz channel width * @vht: Start GO with VHT support + * @vht_chwidth: Channel width supported by GO operating with VHT support + * (VHT_CHANWIDTH_*). + * @group_ssid: Specific Group SSID for join or %NULL if not set + * @group_ssid_len: Length of @group_ssid in octets * 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 @@ -5080,8 +5321,10 @@ exit_free: int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, const char *pin, enum p2p_wps_method wps_method, int persistent_group, int auto_join, int join, int auth, - int go_intent, int freq, int persistent_id, int pd, - int ht40, int vht) + int go_intent, int freq, unsigned int vht_center_freq2, + int persistent_id, int pd, int ht40, int vht, + unsigned int vht_chwidth, const u8 *group_ssid, + size_t group_ssid_len) { int force_freq = 0, pref_freq = 0; int ret = 0, res; @@ -5105,6 +5348,7 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, wpa_s->global->p2p_fail_on_wps_complete = 0; wpa_s->global->pending_p2ps_group = 0; + wpa_s->global->pending_p2ps_group_freq = 0; wpa_s->p2ps_method_config_any = 0; if (go_intent < 0) @@ -5122,17 +5366,23 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, wpa_s->p2p_pd_before_go_neg = !!pd; wpa_s->p2p_go_ht40 = !!ht40; wpa_s->p2p_go_vht = !!vht; + wpa_s->p2p_go_vht_center_freq2 = vht_center_freq2; + wpa_s->p2p_go_max_oper_chwidth = vht_chwidth; 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(); + if (wps_generate_pin((unsigned int *) &ret) < 0) + return -1; res = os_snprintf(wpa_s->p2p_pin, sizeof(wpa_s->p2p_pin), "%08d", ret); if (os_snprintf_error(sizeof(wpa_s->p2p_pin), res)) wpa_s->p2p_pin[sizeof(wpa_s->p2p_pin) - 1] = '\0'; wpa_printf(MSG_DEBUG, "P2P: Randomly generated PIN: %s", wpa_s->p2p_pin); + } else if (wps_method == WPS_P2PS) { + /* Force the P2Ps default PIN to be used */ + os_strlcpy(wpa_s->p2p_pin, "12345670", sizeof(wpa_s->p2p_pin)); } else wpa_s->p2p_pin[0] = '\0'; @@ -5161,7 +5411,8 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, } wpa_s->user_initiated_pd = 1; if (wpas_p2p_join(wpa_s, iface_addr, dev_addr, wps_method, - auto_join, freq, NULL, 0) < 0) + auto_join, freq, + group_ssid, group_ssid_len) < 0) return -1; return ret; } @@ -5191,7 +5442,10 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, if_addr = wpa_s->pending_interface_addr; } else { - if_addr = wpa_s->own_addr; + if (wpa_s->p2p_mgmt) + if_addr = wpa_s->parent->own_addr; + else + if_addr = wpa_s->own_addr; os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN); } @@ -5520,29 +5774,51 @@ out: static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s, struct p2p_go_neg_results *params, - int freq, int ht40, int vht, + int freq, int vht_center_freq2, int ht40, + int vht, int max_oper_chwidth, const struct p2p_channels *channels) { struct wpa_used_freq_data *freqs; unsigned int cand; unsigned int num, i; + int ignore_no_freqs = 0; + int unused_channels = wpas_p2p_num_unused_channels(wpa_s) > 0; os_memset(params, 0, sizeof(*params)); params->role_go = 1; params->ht40 = ht40; params->vht = vht; - - if (wpa_s->p2p_group_common_freqs_num) - wpa_printf(MSG_DEBUG, "P2P: %s called for an active GO", - __func__); + params->max_oper_chwidth = max_oper_chwidth; + params->vht_center_freq2 = vht_center_freq2; freqs = os_calloc(wpa_s->num_multichan_concurrent, sizeof(struct wpa_used_freq_data)); if (!freqs) return -1; - num = wpas_p2p_valid_oper_freqs(wpa_s, freqs, - wpa_s->num_multichan_concurrent); + num = get_shared_radio_freqs_data(wpa_s, freqs, + wpa_s->num_multichan_concurrent); + + if (wpa_s->current_ssid && + wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO && + wpa_s->wpa_state == WPA_COMPLETED) { + wpa_printf(MSG_DEBUG, "P2P: %s called for an active GO", + __func__); + + /* + * If the frequency selection is done for an active P2P GO that + * is not sharing a frequency, allow to select a new frequency + * even if there are no unused frequencies as we are about to + * move the P2P GO so its frequency can be re-used. + */ + for (i = 0; i < num; i++) { + if (freqs[i].freq == wpa_s->current_ssid->frequency && + freqs[i].flags == 0) { + ignore_no_freqs = 1; + break; + } + } + } /* try using the forced freq */ if (freq) { @@ -5563,7 +5839,7 @@ static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s, } } - if (wpas_p2p_num_unused_channels(wpa_s) <= 0) { + if (!ignore_no_freqs && !unused_channels) { wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on freq (%d MHz) as all the channels are in use", freq); @@ -5578,12 +5854,13 @@ static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s, } /* consider using one of the shared frequencies */ - if (num) { + if (num && + (!wpa_s->conf->p2p_ignore_shared_freq || !unused_channels)) { cand = wpas_p2p_pick_best_used_freq(wpa_s, freqs, num); if (wpas_p2p_supported_freq_go(wpa_s, channels, cand)) { wpa_printf(MSG_DEBUG, "P2P: Use shared freq (%d MHz) for GO", - freq); + cand); params->freq = cand; goto success; } @@ -5594,14 +5871,14 @@ static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s, freqs[i].freq)) { wpa_printf(MSG_DEBUG, "P2P: Use shared freq (%d MHz) for GO", - freq); + freqs[i].freq); params->freq = freqs[i].freq; goto success; } } } - if (wpas_p2p_num_unused_channels(wpa_s) <= 0) { + if (!ignore_no_freqs && !unused_channels) { wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on any of the channels we are already using"); goto fail; @@ -5714,9 +5991,20 @@ wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated, 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"); + if (wpa_s->p2p_mgmt) { + /* + * We may be called on the p2p_dev interface which + * cannot be used for group operations, so always use + * the primary interface. + */ + wpa_s->parent->p2pdev = wpa_s; + wpa_s = wpa_s->parent; + } + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: Use primary interface for group operations"); wpa_s->p2p_first_connection_timeout = 0; + if (wpa_s != wpa_s->p2pdev) + wpas_p2p_clone_config(wpa_s, wpa_s->p2pdev); return wpa_s; } @@ -5746,15 +6034,18 @@ wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated, * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface() * @persistent_group: Whether to create a persistent group * @freq: Frequency for the group or 0 to indicate no hardcoding + * @vht_center_freq2: segment_1 center frequency for GO operating in VHT 80P80 * @ht40: Start GO with 40 MHz channel width * @vht: Start GO with VHT support + * @vht_chwidth: channel bandwidth for GO operating with VHT support * Returns: 0 on success, -1 on failure * * This function creates a new P2P group with the local end as the Group Owner, * i.e., without using Group Owner Negotiation. */ int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group, - int freq, int ht40, int vht) + int freq, int vht_center_freq2, int ht40, int vht, + int max_oper_chwidth) { struct p2p_go_neg_results params; @@ -5772,7 +6063,8 @@ int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group, if (freq < 0) return -1; - if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq, ht40, vht, NULL)) + if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq, vht_center_freq2, + ht40, vht, max_oper_chwidth, NULL)) return -1; if (params.freq && !p2p_supported_freq_go(wpa_s->global->p2p, params.freq)) { @@ -5826,8 +6118,10 @@ static int wpas_start_p2p_client(struct wpa_supplicant *wpa_s, 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->pbss = params->pbss; + ssid->pairwise_cipher = params->pbss ? WPA_CIPHER_GCMP : + WPA_CIPHER_CCMP; + ssid->group_cipher = params->pbss ? WPA_CIPHER_GCMP : WPA_CIPHER_CCMP; ssid->key_mgmt = WPA_KEY_MGMT_PSK; ssid->ssid = os_malloc(params->ssid_len); if (ssid->ssid == NULL) { @@ -5848,12 +6142,14 @@ static int wpas_start_p2p_client(struct wpa_supplicant *wpa_s, wpa_s->show_group_started = 1; wpa_s->p2p_in_invitation = 1; wpa_s->p2p_invite_go_freq = freq; + wpa_s->p2p_go_group_formation_completed = 0; + wpa_s->global->p2p_group_formation = wpa_s; - eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->parent, + eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->p2pdev, NULL); eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT, 0, wpas_p2p_group_formation_timeout, - wpa_s->parent, NULL); + wpa_s->p2pdev, NULL); wpa_supplicant_select_network(wpa_s, ssid); return 0; @@ -5862,8 +6158,10 @@ static int wpas_start_p2p_client(struct wpa_supplicant *wpa_s, int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, int addr_allocated, - int force_freq, int neg_freq, int ht40, - int vht, const struct p2p_channels *channels, + int force_freq, int neg_freq, + int vht_center_freq2, int ht40, + int vht, int max_oper_chwidth, + const struct p2p_channels *channels, int connection_timeout, int force_scan) { struct p2p_go_neg_results params; @@ -5878,7 +6176,7 @@ int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s, "already running"); if (go == 0 && eloop_cancel_timeout(wpas_p2p_group_formation_timeout, - wpa_s->parent, NULL)) { + wpa_s->p2pdev, NULL)) { /* * This can happen if Invitation Response frame was lost * and the peer (GO of a persistent group) tries to @@ -5891,7 +6189,7 @@ int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s, "P2P: Reschedule group formation timeout since peer is still trying to invite us"); eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT, 0, wpas_p2p_group_formation_timeout, - wpa_s->parent, NULL); + wpa_s->p2pdev, NULL); } return 0; } @@ -5937,7 +6235,8 @@ int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s, return -1; } - if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq, ht40, vht, channels)) + if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq, vht_center_freq2, + ht40, vht, max_oper_chwidth, channels)) return -1; params.role_go = 1; @@ -6019,7 +6318,8 @@ struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s, struct p2p_group *group; struct p2p_group_config *cfg; - if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL || + !ssid->p2p_group) return NULL; cfg = os_zalloc(sizeof(*cfg)); @@ -6042,6 +6342,8 @@ struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s, cfg->cb_ctx = wpa_s; cfg->ie_update = wpas_p2p_ie_update; cfg->idle_update = wpas_p2p_idle_update; + cfg->ip_addr_alloc = WPA_GET_BE32(wpa_s->p2pdev->conf->ip_addr_start) + != 0; group = p2p_group_init(wpa_s->global->p2p, cfg); if (group == NULL) @@ -6073,7 +6375,7 @@ void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr, p2p_clear_provisioning_info(wpa_s->global->p2p, go_dev_addr); } - eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->parent, + eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->p2pdev, NULL); wpa_s->p2p_go_group_formation_completed = 1; if (ssid && ssid->mode == WPAS_MODE_INFRA) { @@ -6088,7 +6390,9 @@ void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr, P2P_MAX_INITIAL_CONN_WAIT); eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT, 0, wpas_p2p_group_formation_timeout, - wpa_s->parent, NULL); + wpa_s->p2pdev, NULL); + /* Complete group formation on successful data connection. */ + wpa_s->p2p_go_group_formation_completed = 0; } else if (ssid) { /* * Use a separate timeout for initial data connection to @@ -6100,7 +6404,7 @@ void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr, P2P_MAX_INITIAL_CONN_WAIT_GO); eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT_GO, 0, wpas_p2p_group_formation_timeout, - wpa_s->parent, NULL); + wpa_s->p2pdev, NULL); /* * Complete group formation on first successful data connection */ @@ -6139,7 +6443,7 @@ void wpas_p2p_wps_failed(struct wpa_supplicant *wpa_s, wpa_s->global->p2p_fail_on_wps_complete = 1; eloop_deplete_timeout(0, 50000, wpas_p2p_group_formation_timeout, - wpa_s->parent, NULL); + wpa_s->p2pdev, NULL); } } @@ -6164,11 +6468,14 @@ int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr, u16 config_methods; wpa_s->global->pending_p2ps_group = 0; + wpa_s->global->pending_p2ps_group_freq = 0; wpa_s->p2p_fallback_to_go_neg = 0; wpa_s->pending_pd_use = NORMAL_PD; if (p2ps_prov && use == WPAS_P2P_PD_FOR_ASP) { p2ps_prov->conncap = p2ps_group_capability( - wpa_s, P2PS_SETUP_NONE, p2ps_prov->role); + wpa_s, P2PS_SETUP_NONE, p2ps_prov->role, + &p2ps_prov->force_freq, &p2ps_prov->pref_freq); + wpa_printf(MSG_DEBUG, "P2P: %s conncap: %d - ASP parsed: %x %x %d %s", __func__, p2ps_prov->conncap, @@ -6229,7 +6536,12 @@ static void wpas_p2p_clear_pending_action_tx(struct wpa_supplicant *wpa_s) if (!offchannel_pending_action_tx(wpa_s)) return; - wpas_p2p_action_tx_clear(wpa_s); + if (wpa_s->p2p_send_action_work) { + wpas_p2p_free_send_action_work(wpa_s); + eloop_cancel_timeout(wpas_p2p_send_action_work_timeout, + wpa_s, NULL); + offchannel_send_action_done(wpa_s); + } wpa_printf(MSG_DEBUG, "P2P: Drop pending Action TX due to new " "operation request"); @@ -6320,6 +6632,12 @@ int wpas_p2p_listen(struct wpa_supplicant *wpa_s, unsigned int timeout) if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return -1; + if (wpa_s->p2p_lo_started) { + wpa_printf(MSG_DEBUG, + "P2P: Cannot start P2P listen, it is offloaded"); + return -1; + } + wpa_supplicant_cancel_sched_scan(wpa_s); wpas_p2p_clear_pending_action_tx(wpa_s); @@ -6393,7 +6711,7 @@ int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s, const u8 *addr, return 0; switch (p2p_probe_req_rx(wpa_s->global->p2p, addr, dst, bssid, - ie, ie_len, rx_freq)) { + ie, ie_len, rx_freq, wpa_s->p2p_lo_started)) { case P2P_PREQ_NOT_P2P: wpas_notify_preq(wpa_s, addr, dst, bssid, ie, ie_len, ssi_signal); @@ -6425,12 +6743,15 @@ void wpas_p2p_rx_action(struct wpa_supplicant *wpa_s, const u8 *da, void wpas_p2p_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ies) { + unsigned int bands; + if (wpa_s->global->p2p_disabled) return; if (wpa_s->global->p2p == NULL) return; - p2p_scan_ie(wpa_s->global->p2p, ies, NULL); + bands = wpas_get_bands(wpa_s, NULL); + p2p_scan_ie(wpa_s->global->p2p, ies, NULL, bands); } @@ -6460,7 +6781,8 @@ int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *addr) /* Invite to reinvoke a persistent group */ int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr, struct wpa_ssid *ssid, const u8 *go_dev_addr, int freq, - int ht40, int vht, int pref_freq) + int vht_center_freq2, int ht40, int vht, int max_chwidth, + int pref_freq) { enum p2p_invite_role role; u8 *bssid = NULL; @@ -6477,6 +6799,9 @@ int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr, wpa_s->p2p_persistent_go_freq = freq; wpa_s->p2p_go_ht40 = !!ht40; + wpa_s->p2p_go_vht = !!vht; + wpa_s->p2p_go_max_oper_chwidth = max_chwidth; + wpa_s->p2p_go_vht_center_freq2 = vht_center_freq2; if (ssid->mode == WPAS_MODE_P2P_GO) { role = P2P_INVITE_ROLE_GO; if (peer_addr == NULL) { @@ -6493,7 +6818,9 @@ int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr, return -1; } bssid = wpa_s->pending_interface_addr; - } else + } else if (wpa_s->p2p_mgmt) + bssid = wpa_s->parent->own_addr; + else bssid = wpa_s->own_addr; } else { role = P2P_INVITE_ROLE_CLIENT; @@ -6507,11 +6834,12 @@ int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr, pref_freq_list, &size); if (res) return res; - p2p_set_own_pref_freq_list(wpa_s->global->p2p, pref_freq_list, size); if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return -1; + p2p_set_own_pref_freq_list(wpa_s->global->p2p, pref_freq_list, size); + if (wpa_s->parent->conf->p2p_ignore_shared_freq && no_pref_freq_given && pref_freq > 0 && wpa_s->num_multichan_concurrent > 1 && @@ -6549,6 +6877,8 @@ int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname, wpa_s->p2p_persistent_go_freq = 0; wpa_s->p2p_go_ht40 = 0; wpa_s->p2p_go_vht = 0; + wpa_s->p2p_go_vht_center_freq2 = 0; + wpa_s->p2p_go_max_oper_chwidth = 0; for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { if (os_strcmp(wpa_s->ifname, ifname) == 0) @@ -6568,7 +6898,7 @@ int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname, wpa_s->global->p2p_invite_group = wpa_s; persistent = ssid->p2p_persistent_group && - wpas_p2p_get_persistent(wpa_s->parent, peer_addr, + wpas_p2p_get_persistent(wpa_s->p2pdev, peer_addr, ssid->ssid, ssid->ssid_len); if (ssid->mode == WPAS_MODE_P2P_GO) { @@ -6591,7 +6921,7 @@ int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname, freq = wpa_s->current_bss ? wpa_s->current_bss->freq : (int) wpa_s->assoc_freq; } - wpa_s->parent->pending_invite_ssid_id = -1; + wpa_s->p2pdev->pending_invite_ssid_id = -1; if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return -1; @@ -6614,7 +6944,6 @@ void wpas_p2p_completed(struct wpa_supplicant *wpa_s) { struct wpa_ssid *ssid = wpa_s->current_ssid; u8 go_dev_addr[ETH_ALEN]; - int network_id = -1; int persistent; int freq; u8 ip[3 * 4]; @@ -6622,13 +6951,22 @@ void wpas_p2p_completed(struct wpa_supplicant *wpa_s) if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION) { eloop_cancel_timeout(wpas_p2p_group_formation_timeout, - wpa_s->parent, NULL); + wpa_s->p2pdev, NULL); } if (!wpa_s->show_group_started || !ssid) return; wpa_s->show_group_started = 0; + if (!wpa_s->p2p_go_group_formation_completed && + wpa_s->global->p2p_group_formation == wpa_s) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: Marking group formation completed on client on data connection"); + wpa_s->p2p_go_group_formation_completed = 1; + wpa_s->global->p2p_group_formation = NULL; + wpa_s->p2p_in_provisioning = 0; + wpa_s->p2p_in_invitation = 0; + } os_memset(go_dev_addr, 0, ETH_ALEN); if (ssid->bssid_set) @@ -6664,11 +7002,10 @@ void wpas_p2p_completed(struct wpa_supplicant *wpa_s) ip_addr); if (persistent) - network_id = wpas_p2p_store_persistent_group(wpa_s->parent, - ssid, go_dev_addr); - if (network_id < 0) - network_id = ssid->id; - wpas_notify_p2p_group_started(wpa_s, ssid, network_id, 1); + wpas_p2p_store_persistent_group(wpa_s->p2pdev, + ssid, go_dev_addr); + + wpas_notify_p2p_group_started(wpa_s, ssid, persistent, 1); } @@ -7001,7 +7338,7 @@ int wpas_p2p_set_cross_connect(struct wpa_supplicant *wpa_s, int enabled) iface->cross_connect_enabled = 0; iface->cross_connect_in_use = 0; - wpa_msg_global(iface->parent, MSG_INFO, + wpa_msg_global(iface->p2pdev, MSG_INFO, P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s", iface->ifname, iface->cross_connect_uplink); @@ -7031,7 +7368,7 @@ static void wpas_p2p_enable_cross_connect(struct wpa_supplicant *uplink) continue; iface->cross_connect_in_use = 1; - wpa_msg_global(iface->parent, MSG_INFO, + wpa_msg_global(iface->p2pdev, MSG_INFO, P2P_EVENT_CROSS_CONNECT_ENABLE "%s %s", iface->ifname, iface->cross_connect_uplink); } @@ -7051,7 +7388,7 @@ static void wpas_p2p_disable_cross_connect(struct wpa_supplicant *uplink) if (!iface->cross_connect_in_use) continue; - wpa_msg_global(iface->parent, MSG_INFO, + wpa_msg_global(iface->p2pdev, MSG_INFO, P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s", iface->ifname, iface->cross_connect_uplink); iface->cross_connect_in_use = 0; @@ -7114,7 +7451,7 @@ static void wpas_p2p_cross_connect_setup(struct wpa_supplicant *wpa_s) break; wpa_s->cross_connect_in_use = 1; - wpa_msg_global(wpa_s->parent, MSG_INFO, + wpa_msg_global(wpa_s->p2pdev, MSG_INFO, P2P_EVENT_CROSS_CONNECT_ENABLE "%s %s", wpa_s->ifname, wpa_s->cross_connect_uplink); break; @@ -7130,8 +7467,8 @@ int wpas_p2p_notif_pbc_overlap(struct wpa_supplicant *wpa_s) 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 != wpa_s->p2pdev) + wpa_msg_ctrl(wpa_s->p2pdev, MSG_INFO, WPS_EVENT_OVERLAP); wpas_p2p_group_formation_failed(wpa_s, 0); return 1; } @@ -7238,7 +7575,7 @@ int wpas_p2p_cancel(struct wpa_supplicant *wpa_s) wpa_s->ifname); found = 1; eloop_cancel_timeout(wpas_p2p_group_formation_timeout, - wpa_s->parent, NULL); + wpa_s->p2pdev, NULL); if (wpa_s->p2p_in_provisioning) { wpas_group_formation_completed(wpa_s, 0, 0); break; @@ -7251,6 +7588,7 @@ int wpas_p2p_cancel(struct wpa_supplicant *wpa_s) wpa_s->ifname); found = 1; wpas_p2p_group_formation_failed(wpa_s, 0); + break; } } @@ -7367,7 +7705,7 @@ void wpas_p2p_network_removed(struct wpa_supplicant *wpa_s, { if (wpa_s->p2p_in_provisioning && ssid->p2p_group && eloop_cancel_timeout(wpas_p2p_group_formation_timeout, - wpa_s->parent, NULL) > 0) { + wpa_s->p2pdev, NULL) > 0) { /** * Remove the network by scheduling the group formation * timeout to happen immediately. The teardown code @@ -7379,7 +7717,7 @@ void wpas_p2p_network_removed(struct wpa_supplicant *wpa_s, 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); + wpa_s->p2pdev, NULL); } } @@ -7423,7 +7761,7 @@ void wpas_p2p_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s, const u8 *addr) { if (eloop_cancel_timeout(wpas_p2p_group_formation_timeout, - wpa_s->parent, NULL) > 0) { + wpa_s->p2pdev, NULL) > 0) { /* * This can happen if WPS provisioning step is not terminated * cleanly (e.g., P2P Client does not send WSC_Done). Since the @@ -7479,10 +7817,12 @@ static int wpas_p2p_fallback_to_go_neg(struct wpa_supplicant *wpa_s, 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_go_vht_center_freq2, wpa_s->p2p_persistent_id, wpa_s->p2p_pd_before_go_neg, wpa_s->p2p_go_ht40, - wpa_s->p2p_go_vht); + wpa_s->p2p_go_vht, + wpa_s->p2p_go_max_oper_chwidth, NULL, 0); return ret; } @@ -7500,7 +7840,7 @@ int wpas_p2p_scan_no_go_seen(struct wpa_supplicant *wpa_s) wpa_dbg(wpa_s, MSG_DEBUG, "P2P: GO not found for p2p_connect-auto - " "fallback to GO Negotiation"); - wpa_msg_global(wpa_s->parent, MSG_INFO, P2P_EVENT_FALLBACK_TO_GO_NEG + wpa_msg_global(wpa_s->p2pdev, MSG_INFO, P2P_EVENT_FALLBACK_TO_GO_NEG "reason=GO-not-found"); res = wpas_p2p_fallback_to_go_neg(wpa_s, 1); @@ -7609,7 +7949,7 @@ void wpas_p2p_new_psk_cb(struct wpa_supplicant *wpa_s, const u8 *mac_addr, return; } - persistent = wpas_p2p_get_persistent(wpa_s->parent, NULL, ssid->ssid, + persistent = wpas_p2p_get_persistent(wpa_s->p2pdev, NULL, ssid->ssid, ssid->ssid_len); if (!persistent) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Could not find persistent group information to store the new PSK"); @@ -7638,7 +7978,7 @@ void wpas_p2p_new_psk_cb(struct wpa_supplicant *wpa_s, const u8 *mac_addr, os_free(last); } - wpas_p2p_remove_psk_entry(wpa_s->parent, persistent, + wpas_p2p_remove_psk_entry(wpa_s->p2pdev, persistent, p2p_dev_addr ? p2p_dev_addr : mac_addr, p2p_dev_addr == NULL); if (p2p_dev_addr) { @@ -7650,8 +7990,8 @@ void wpas_p2p_new_psk_cb(struct wpa_supplicant *wpa_s, const u8 *mac_addr, } dl_list_add(&persistent->psk_list, &p->list); - if (wpa_s->parent->conf->update_config && - wpa_config_write(wpa_s->parent->confname, wpa_s->parent->conf)) + if (wpa_s->p2pdev->conf->update_config && + wpa_config_write(wpa_s->p2pdev->confname, wpa_s->p2pdev->conf)) wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration"); } @@ -7830,14 +8170,14 @@ int wpas_p2p_4way_hs_failed(struct wpa_supplicant *wpa_s) wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Two 4-way handshake failures for a P2P group - go_dev_addr=" MACSTR, MAC2STR(go_dev_addr)); - persistent = wpas_p2p_get_persistent(wpa_s->parent, go_dev_addr, + persistent = wpas_p2p_get_persistent(wpa_s->p2pdev, go_dev_addr, ssid->ssid, ssid->ssid_len); if (persistent == NULL || persistent->mode != WPAS_MODE_INFRA) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: No matching persistent group stored"); goto disconnect; } - wpa_msg_global(wpa_s->parent, MSG_INFO, + wpa_msg_global(wpa_s->p2pdev, MSG_INFO, P2P_EVENT_PERSISTENT_PSK_FAIL "%d", persistent->id); disconnect: @@ -8016,7 +8356,10 @@ static int wpas_p2p_nfc_join_group(struct wpa_supplicant *wpa_s, return wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL, WPS_NFC, 0, 0, 1, 0, wpa_s->conf->p2p_go_intent, - params->go_freq, -1, 0, 1, 1); + params->go_freq, wpa_s->p2p_go_vht_center_freq2, + -1, 0, 1, 1, wpa_s->p2p_go_max_oper_chwidth, + params->go_ssid_len ? params->go_ssid : NULL, + params->go_ssid_len); } @@ -8043,17 +8386,17 @@ static int wpas_p2p_nfc_auth_join(struct wpa_supplicant *wpa_s, return -1; } - if (wpa_s->parent->p2p_oob_dev_pw_id != + if (wpa_s->p2pdev->p2p_oob_dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER && - !wpa_s->parent->p2p_oob_dev_pw) { + !wpa_s->p2pdev->p2p_oob_dev_pw) { wpa_printf(MSG_DEBUG, "P2P: No NFC Dev Pw known"); return -1; } res = wpas_ap_wps_add_nfc_pw( - wpa_s, wpa_s->parent->p2p_oob_dev_pw_id, - wpa_s->parent->p2p_oob_dev_pw, - wpa_s->parent->p2p_peer_oob_pk_hash_known ? - wpa_s->parent->p2p_peer_oob_pubkey_hash : NULL); + wpa_s, wpa_s->p2pdev->p2p_oob_dev_pw_id, + wpa_s->p2pdev->p2p_oob_dev_pw, + wpa_s->p2pdev->p2p_peer_oob_pk_hash_known ? + wpa_s->p2pdev->p2p_peer_oob_pubkey_hash : NULL); if (res) return res; @@ -8071,16 +8414,16 @@ static int wpas_p2p_nfc_auth_join(struct wpa_supplicant *wpa_s, wpa_s->global->p2p_invite_group = wpa_s; persistent = ssid->p2p_persistent_group && - wpas_p2p_get_persistent(wpa_s->parent, + wpas_p2p_get_persistent(wpa_s->p2pdev, params->peer->p2p_device_addr, ssid->ssid, ssid->ssid_len); - wpa_s->parent->pending_invite_ssid_id = -1; + wpa_s->p2pdev->pending_invite_ssid_id = -1; return p2p_invite(wpa_s->global->p2p, params->peer->p2p_device_addr, P2P_INVITE_ROLE_ACTIVE_GO, wpa_s->own_addr, ssid->ssid, ssid->ssid_len, ssid->frequency, wpa_s->global->p2p_dev_addr, persistent, 0, - wpa_s->parent->p2p_oob_dev_pw_id); + wpa_s->p2pdev->p2p_oob_dev_pw_id); } @@ -8092,7 +8435,9 @@ static int wpas_p2p_nfc_init_go_neg(struct wpa_supplicant *wpa_s, "connection handover"); return wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL, WPS_NFC, 0, 0, 0, 0, wpa_s->conf->p2p_go_intent, - forced_freq, -1, 0, 1, 1); + forced_freq, wpa_s->p2p_go_vht_center_freq2, + -1, 0, 1, 1, wpa_s->p2p_go_max_oper_chwidth, + NULL, 0); } @@ -8106,7 +8451,9 @@ static int wpas_p2p_nfc_resp_go_neg(struct wpa_supplicant *wpa_s, "connection handover"); res = wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL, WPS_NFC, 0, 0, 0, 1, wpa_s->conf->p2p_go_intent, - forced_freq, -1, 0, 1, 1); + forced_freq, wpa_s->p2p_go_vht_center_freq2, + -1, 0, 1, 1, wpa_s->p2p_go_max_oper_chwidth, + NULL, 0); if (res) return res; @@ -8397,7 +8744,9 @@ int wpas_p2p_nfc_tag_enabled(struct wpa_supplicant *wpa_s, int enabled) } if_addr = wpa_s->pending_interface_addr; - } else + } else if (wpa_s->p2p_mgmt) + if_addr = wpa_s->parent->own_addr; + else if_addr = wpa_s->own_addr; wpa_s->p2p_nfc_tag_enabled = enabled; @@ -8473,14 +8822,115 @@ static void wpas_p2p_optimize_listen_channel(struct wpa_supplicant *wpa_s, static int wpas_p2p_move_go_csa(struct wpa_supplicant *wpa_s) { + struct hostapd_config *conf; + struct p2p_go_neg_results params; + struct csa_settings csa_settings; + struct wpa_ssid *current_ssid = wpa_s->current_ssid; + int old_freq = current_ssid->frequency; + int ret; + if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_AP_CSA)) { wpa_dbg(wpa_s, MSG_DEBUG, "CSA is not enabled"); return -1; } - /* TODO: Add CSA support */ - wpa_dbg(wpa_s, MSG_DEBUG, "Moving GO with CSA is not implemented"); - return -1; + /* + * TODO: This function may not always work correctly. For example, + * when we have a running GO and a BSS on a DFS channel. + */ + if (wpas_p2p_init_go_params(wpa_s, ¶ms, 0, 0, 0, 0, 0, NULL)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P CSA: Failed to select new frequency for GO"); + return -1; + } + + if (current_ssid->frequency == params.freq) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P CSA: Selected same frequency - not moving GO"); + return 0; + } + + conf = hostapd_config_defaults(); + if (!conf) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P CSA: Failed to allocate default config"); + return -1; + } + + current_ssid->frequency = params.freq; + if (wpa_supplicant_conf_ap_ht(wpa_s, current_ssid, conf)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P CSA: Failed to create new GO config"); + ret = -1; + goto out; + } + + if (conf->hw_mode != wpa_s->ap_iface->current_mode->mode) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P CSA: CSA to a different band is not supported"); + ret = -1; + goto out; + } + + os_memset(&csa_settings, 0, sizeof(csa_settings)); + csa_settings.cs_count = P2P_GO_CSA_COUNT; + csa_settings.block_tx = P2P_GO_CSA_BLOCK_TX; + csa_settings.freq_params.freq = params.freq; + csa_settings.freq_params.sec_channel_offset = conf->secondary_channel; + csa_settings.freq_params.ht_enabled = conf->ieee80211n; + csa_settings.freq_params.bandwidth = conf->secondary_channel ? 40 : 20; + + if (conf->ieee80211ac) { + int freq1 = 0, freq2 = 0; + u8 chan, opclass; + + if (ieee80211_freq_to_channel_ext(params.freq, + conf->secondary_channel, + conf->vht_oper_chwidth, + &opclass, &chan) == + NUM_HOSTAPD_MODES) { + wpa_printf(MSG_ERROR, "P2P CSA: Bad freq"); + ret = -1; + goto out; + } + + if (conf->vht_oper_centr_freq_seg0_idx) + freq1 = ieee80211_chan_to_freq( + NULL, opclass, + conf->vht_oper_centr_freq_seg0_idx); + + if (conf->vht_oper_centr_freq_seg1_idx) + freq2 = ieee80211_chan_to_freq( + NULL, opclass, + conf->vht_oper_centr_freq_seg1_idx); + + if (freq1 < 0 || freq2 < 0) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P CSA: Selected invalid VHT center freqs"); + ret = -1; + goto out; + } + + csa_settings.freq_params.vht_enabled = conf->ieee80211ac; + csa_settings.freq_params.center_freq1 = freq1; + csa_settings.freq_params.center_freq2 = freq2; + + switch (conf->vht_oper_chwidth) { + case VHT_CHANWIDTH_80MHZ: + case VHT_CHANWIDTH_80P80MHZ: + csa_settings.freq_params.bandwidth = 80; + break; + case VHT_CHANWIDTH_160MHZ: + csa_settings.freq_params.bandwidth = 160; + break; + } + } + + ret = ap_switch_channel(wpa_s, &csa_settings); +out: + current_ssid->frequency = old_freq; + hostapd_config_free(conf); + return ret; } @@ -8500,7 +8950,7 @@ static void wpas_p2p_move_go_no_csa(struct wpa_supplicant *wpa_s) wpa_supplicant_ap_deinit(wpa_s); /* Reselect the GO frequency */ - if (wpas_p2p_init_go_params(wpa_s, ¶ms, 0, 0, 0, NULL)) { + if (wpas_p2p_init_go_params(wpa_s, ¶ms, 0, 0, 0, 0, 0, NULL)) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Failed to reselect freq"); wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_GO_LEAVE_CHANNEL); @@ -8537,6 +8987,13 @@ static void wpas_p2p_move_go(void *eloop_ctx, void *timeout_ctx) wpas_p2p_go_update_common_freqs(wpa_s); + /* Do not move GO in the middle of a CSA */ + if (hostapd_csa_in_progress(wpa_s->ap_iface)) { + wpa_printf(MSG_DEBUG, + "P2P: CSA is in progress - not moving GO"); + return; + } + /* * First, try a channel switch flow. If it is not supported or fails, * take down the GO and bring it up again. @@ -8613,6 +9070,25 @@ static void wpas_p2p_consider_moving_one_go(struct wpa_supplicant *wpa_s, P2P_GO_FREQ_MOVE_SCM_PEER_SUPPORTS && wpas_p2p_go_is_peer_freq(wpa_s, freqs[i].freq)) { policy_move = 1; + } else if ((wpa_s->conf->p2p_go_freq_change_policy == + P2P_GO_FREQ_MOVE_SCM_ECSA) && + wpas_p2p_go_is_peer_freq(wpa_s, freqs[i].freq)) { + if (!p2p_get_group_num_members(wpa_s->p2p_group)) { + policy_move = 1; + } else if ((wpa_s->drv_flags & + WPA_DRIVER_FLAGS_AP_CSA) && + wpas_p2p_go_clients_support_ecsa(wpa_s)) { + u8 chan; + + /* + * We do not support CSA between bands, so move + * GO only within the same band. + */ + if (wpa_s->ap_iface->current_mode->mode == + ieee80211_freq_to_chan(freqs[i].freq, + &chan)) + policy_move = 1; + } } } @@ -8647,6 +9123,16 @@ static void wpas_p2p_consider_moving_one_go(struct wpa_supplicant *wpa_s, return; } + /* + * Do not consider moving GO if it is in the middle of a CSA. When the + * CSA is finished this flow should be retriggered. + */ + if (hostapd_csa_in_progress(wpa_s->ap_iface)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: Not initiating a GO frequency change - CSA is in progress"); + return; + } + if (invalid_freq && !wpas_p2p_disallowed_freq(wpa_s->global, freq)) timeout = P2P_GO_FREQ_CHANGE_TIME; else @@ -8726,3 +9212,86 @@ void wpas_p2p_ap_deinit(struct wpa_supplicant *wpa_s) wpa_s->ap_iface->bss[0]->p2p_group = NULL; wpas_p2p_group_deinit(wpa_s); } + + +int wpas_p2p_lo_start(struct wpa_supplicant *wpa_s, unsigned int freq, + unsigned int period, unsigned int interval, + unsigned int count) +{ + struct p2p_data *p2p = wpa_s->global->p2p; + u8 *device_types; + size_t dev_types_len; + struct wpabuf *buf; + int ret; + + if (wpa_s->p2p_lo_started) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P Listen offload is already started"); + return 0; + } + + if (wpa_s->global->p2p == NULL || + !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD)) { + wpa_printf(MSG_DEBUG, "P2P: Listen offload not supported"); + return -1; + } + + if (!p2p_supported_freq(wpa_s->global->p2p, freq)) { + wpa_printf(MSG_ERROR, "P2P: Input channel not supported: %u", + freq); + return -1; + } + + /* Get device type */ + dev_types_len = (wpa_s->conf->num_sec_device_types + 1) * + WPS_DEV_TYPE_LEN; + device_types = os_malloc(dev_types_len); + if (!device_types) + return -1; + os_memcpy(device_types, wpa_s->conf->device_type, WPS_DEV_TYPE_LEN); + os_memcpy(&device_types[WPS_DEV_TYPE_LEN], wpa_s->conf->sec_device_type, + wpa_s->conf->num_sec_device_types * WPS_DEV_TYPE_LEN); + + /* Get Probe Response IE(s) */ + buf = p2p_build_probe_resp_template(p2p, freq); + if (!buf) { + os_free(device_types); + return -1; + } + + ret = wpa_drv_p2p_lo_start(wpa_s, freq, period, interval, count, + device_types, dev_types_len, + wpabuf_mhead_u8(buf), wpabuf_len(buf)); + if (ret < 0) + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: Failed to start P2P listen offload"); + + os_free(device_types); + wpabuf_free(buf); + + if (ret == 0) { + wpa_s->p2p_lo_started = 1; + + /* Stop current P2P listen if any */ + wpas_stop_listen(wpa_s); + } + + return ret; +} + + +int wpas_p2p_lo_stop(struct wpa_supplicant *wpa_s) +{ + int ret; + + if (!wpa_s->p2p_lo_started) + return 0; + + ret = wpa_drv_p2p_lo_stop(wpa_s); + if (ret < 0) + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: Failed to stop P2P listen offload"); + + wpa_s->p2p_lo_started = 0; + return ret; +} diff --git a/wpa_supplicant/p2p_supplicant.h b/wpa_supplicant/p2p_supplicant.h index 56e683498d66f..63910d1c268ed 100644 --- a/wpa_supplicant/p2p_supplicant.h +++ b/wpa_supplicant/p2p_supplicant.h @@ -34,17 +34,22 @@ struct wpa_supplicant * wpas_get_p2p_client_iface(struct wpa_supplicant *wpa_s, const u8 *peer_dev_addr); int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, const char *pin, enum p2p_wps_method wps_method, - int persistent_group, int auto_join, int join, - int auth, int go_intent, int freq, int persistent_id, - int pd, int ht40, int vht); + int persistent_group, int auto_join, int join, int auth, + int go_intent, int freq, unsigned int vht_center_freq2, + int persistent_id, int pd, int ht40, int vht, + unsigned int vht_chwidth, const u8 *group_ssid, + size_t group_ssid_len); int wpas_p2p_handle_frequency_conflicts(struct wpa_supplicant *wpa_s, int freq, struct wpa_ssid *ssid); int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group, - int freq, int ht40, int vht); + int freq, int vht_center_freq2, int ht40, int vht, + int max_oper_chwidth); int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, int addr_allocated, - int force_freq, int neg_freq, int ht40, - int vht, const struct p2p_channels *channels, + int force_freq, int neg_freq, + int vht_center_freq2, int ht40, + int vht, int max_oper_chwidth, + const struct p2p_channels *channels, int connection_timeout, int force_scan); struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); @@ -111,7 +116,8 @@ void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic, int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *addr); int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr, struct wpa_ssid *ssid, const u8 *go_dev_addr, int freq, - int ht40, int vht, int pref_freq); + int vht_center_freq2, int ht40, int vht, + int max_oper_chwidth, int pref_freq); int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname, const u8 *peer_addr, const u8 *go_dev_addr); int wpas_p2p_presence_req(struct wpa_supplicant *wpa_s, u32 duration1, @@ -140,6 +146,8 @@ int wpas_p2p_get_ht40_mode(struct wpa_supplicant *wpa_s, struct hostapd_hw_modes *mode, u8 channel); int wpas_p2p_get_vht80_center(struct wpa_supplicant *wpa_s, struct hostapd_hw_modes *mode, u8 channel); +int wpas_p2p_get_vht160_center(struct wpa_supplicant *wpa_s, + struct hostapd_hw_modes *mode, u8 channel); unsigned int wpas_p2p_search_delay(struct wpa_supplicant *wpa_s); void wpas_p2p_new_psk_cb(struct wpa_supplicant *wpa_s, const u8 *mac_addr, const u8 *p2p_dev_addr, @@ -199,6 +207,10 @@ int wpas_p2p_wps_eapol_cb(struct wpa_supplicant *wpa_s); void wpas_p2p_wps_failed(struct wpa_supplicant *wpa_s, struct wps_event_fail *fail); int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname); +int wpas_p2p_lo_start(struct wpa_supplicant *wpa_s, unsigned int freq, + unsigned int period, unsigned int interval, + unsigned int count); +int wpas_p2p_lo_stop(struct wpa_supplicant *wpa_s); #else /* CONFIG_P2P */ diff --git a/wpa_supplicant/p2p_supplicant_sd.c b/wpa_supplicant/p2p_supplicant_sd.c index fc07b07462f5a..f8675e68bec48 100644 --- a/wpa_supplicant/p2p_supplicant_sd.c +++ b/wpa_supplicant/p2p_supplicant_sd.c @@ -48,7 +48,7 @@ static int p2p_sd_dns_uncompress_label(char **upos, char *uend, u8 *start, u8 *spos_tmp; /* Offset */ - if (*spos + 2 > end) { + if (end - *spos < 2) { wpa_printf(MSG_DEBUG, "P2P: No room for full " "DNS offset field"); return -1; @@ -74,14 +74,14 @@ static int p2p_sd_dns_uncompress_label(char **upos, char *uend, u8 *start, return 0; (*spos)++; - if (*spos + len > end) { + if (len > end - *spos) { wpa_printf(MSG_DEBUG, "P2P: Invalid domain name " "sequence - no room for label with length " "%u", len); return -1; } - if (*upos + len + 2 > uend) + if (len + 2 > uend - *upos) return -2; os_memcpy(*upos, *spos, len); @@ -722,11 +722,11 @@ void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token, if (resp == NULL) return; - while (pos + 1 < end) { + while (end - pos > 1) { wpa_printf(MSG_DEBUG, "P2P: Service Request TLV"); slen = WPA_GET_LE16(pos); pos += 2; - if (pos + slen > end || slen < 2) { + if (slen > end - pos || slen < 2) { wpa_printf(MSG_DEBUG, "P2P: Unexpected Query Data " "length"); wpabuf_free(resp); @@ -827,10 +827,10 @@ static void wpas_sd_p2ps_serv_response(struct wpa_supplicant *wpa_s, u8 svc_len; /* Sanity check fixed length+svc_str */ - if (pos + 6 >= tlv_end) + if (6 >= tlv_end - pos) break; svc_len = pos[6]; - if (pos + svc_len + 10 > tlv_end) + if (svc_len + 10 > tlv_end - pos) break; /* Advertisement ID */ @@ -917,13 +917,13 @@ void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic, } } - while (pos < end) { + while (end - pos >= 2) { 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) { + if (slen > end - pos || slen < 3) { wpa_printf(MSG_DEBUG, "P2P: Unexpected Response Data " "length"); return; diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c index d7049a1a8164c..fb8ebdf2ecc1c 100644 --- a/wpa_supplicant/scan.c +++ b/wpa_supplicant/scan.c @@ -36,8 +36,7 @@ static void wpa_supplicant_gen_assoc_event(struct wpa_supplicant *wpa_s) if (wpa_s->current_ssid == NULL) { wpa_s->current_ssid = ssid; - if (wpa_s->current_ssid != NULL) - wpas_notify_network_changed(wpa_s); + wpas_notify_network_changed(wpa_s); } wpa_supplicant_initiate_eapol(wpa_s); wpa_dbg(wpa_s, MSG_DEBUG, "Already associated with a configured " @@ -60,10 +59,7 @@ static int wpas_wps_in_use(struct wpa_supplicant *wpa_s, wps = 1; *req_type = wpas_wps_get_req_type(ssid); - if (!ssid->eap.phase1) - continue; - - if (os_strstr(ssid->eap.phase1, "pbc=1")) + if (ssid->eap.phase1 && os_strstr(ssid->eap.phase1, "pbc=1")) return 2; } @@ -166,6 +162,8 @@ static void wpas_trigger_scan_cb(struct wpa_radio_work *work, int deinit) if (wpas_update_random_addr_disassoc(wpa_s) < 0) { wpa_msg(wpa_s, MSG_INFO, "Failed to assign random MAC address for a scan"); + wpa_scan_free_params(params); + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCAN_FAILED "ret=-1"); radio_work_done(work); return; } @@ -229,12 +227,11 @@ int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s, } ctx = wpa_scan_clone_params(params); - if (ctx == NULL) - return -1; - - if (radio_add_work(wpa_s, 0, "scan", 0, wpas_trigger_scan_cb, ctx) < 0) + if (!ctx || + radio_add_work(wpa_s, 0, "scan", 0, wpas_trigger_scan_cb, ctx) < 0) { wpa_scan_free_params(ctx); + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCAN_FAILED "ret=-1"); return -1; } @@ -266,14 +263,14 @@ wpa_supplicant_sched_scan_timeout(void *eloop_ctx, void *timeout_ctx) } -int wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s, - struct wpa_driver_scan_params *params, - int interval) +static int +wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s, + struct wpa_driver_scan_params *params) { int ret; wpa_supplicant_notify_scanning(wpa_s, 1); - ret = wpa_drv_sched_scan(wpa_s, params, interval * 1000); + ret = wpa_drv_sched_scan(wpa_s, params); if (ret) wpa_supplicant_notify_scanning(wpa_s, 0); else @@ -283,7 +280,7 @@ int wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s, } -int wpa_supplicant_stop_sched_scan(struct wpa_supplicant *wpa_s) +static int wpa_supplicant_stop_sched_scan(struct wpa_supplicant *wpa_s) { int ret; @@ -429,6 +426,39 @@ static void wpas_add_interworking_elements(struct wpa_supplicant *wpa_s, #endif /* CONFIG_INTERWORKING */ +void wpa_supplicant_set_default_scan_ies(struct wpa_supplicant *wpa_s) +{ + struct wpabuf *default_ies = NULL; + u8 ext_capab[18]; + int ext_capab_len; + enum wpa_driver_if_type type = WPA_IF_STATION; + +#ifdef CONFIG_P2P + if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT) + type = WPA_IF_P2P_CLIENT; +#endif /* CONFIG_P2P */ + + wpa_drv_get_ext_capa(wpa_s, type); + + ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab, + sizeof(ext_capab)); + if (ext_capab_len > 0 && + wpabuf_resize(&default_ies, ext_capab_len) == 0) + wpabuf_put_data(default_ies, ext_capab, ext_capab_len); + +#ifdef CONFIG_MBO + /* Send cellular capabilities for potential MBO STAs */ + if (wpabuf_resize(&default_ies, 9) == 0) + wpas_mbo_scan_ie(wpa_s, default_ies); +#endif /* CONFIG_MBO */ + + if (default_ies) + wpa_drv_set_default_scan_ies(wpa_s, wpabuf_head(default_ies), + wpabuf_len(default_ies)); + wpabuf_free(default_ies); +} + + static struct wpabuf * wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s) { struct wpabuf *extra_ie = NULL; @@ -439,6 +469,13 @@ static struct wpabuf * wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s) enum wps_request_type req_type = WPS_REQ_ENROLLEE_INFO; #endif /* CONFIG_WPS */ +#ifdef CONFIG_P2P + if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT) + wpa_drv_get_ext_capa(wpa_s, WPA_IF_P2P_CLIENT); + else +#endif /* CONFIG_P2P */ + wpa_drv_get_ext_capa(wpa_s, WPA_IF_STATION); + ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab, sizeof(ext_capab)); if (ext_capab_len > 0 && @@ -491,6 +528,19 @@ static struct wpabuf * wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s) wpabuf_put_buf(extra_ie, wpa_s->fst_ies); #endif /* CONFIG_FST */ +#ifdef CONFIG_MBO + /* Send cellular capabilities for potential MBO STAs */ + if (wpabuf_resize(&extra_ie, 9) == 0) + wpas_mbo_scan_ie(wpa_s, extra_ie); +#endif /* CONFIG_MBO */ + + if (wpa_s->vendor_elem[VENDOR_ELEM_PROBE_REQ]) { + struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_PROBE_REQ]; + + if (wpabuf_resize(&extra_ie, wpabuf_len(buf)) == 0) + wpabuf_put_buf(extra_ie, buf); + } + return extra_ie; } @@ -522,21 +572,6 @@ static int non_p2p_network_enabled(struct wpa_supplicant *wpa_s) #endif /* CONFIG_P2P */ -static struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes, - u16 num_modes, - enum hostapd_hw_mode mode) -{ - u16 i; - - for (i = 0; i < num_modes; i++) { - if (modes[i].mode == mode) - return &modes[i]; - } - - return NULL; -} - - static void wpa_setband_scan_freqs_list(struct wpa_supplicant *wpa_s, enum hostapd_hw_mode band, struct wpa_driver_scan_params *params) @@ -586,6 +621,12 @@ static void wpa_set_scan_ssids(struct wpa_supplicant *wpa_s, unsigned int i; struct wpa_ssid *ssid; + /* + * For devices with max_ssids greater than 1, leave the last slot empty + * for adding the wildcard scan entry. + */ + max_ssids = max_ssids > 1 ? max_ssids - 1 : max_ssids; + for (i = 0; i < wpa_s->scan_id_count; i++) { unsigned int j; @@ -840,12 +881,10 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) * slot for the zero-terminator. */ params.freqs = os_malloc(sizeof(int) * 2); - if (params.freqs == NULL) { - wpa_dbg(wpa_s, MSG_ERROR, "Memory allocation failed"); - return; + if (params.freqs) { + params.freqs[0] = wpa_s->assoc_freq; + params.freqs[1] = 0; } - params.freqs[0] = wpa_s->assoc_freq; - params.freqs[1] = 0; /* * Reset the reattach flag so that we fall back to full scan if @@ -1016,6 +1055,27 @@ ssid_list_set: } } + if (!is_zero_ether_addr(wpa_s->next_scan_bssid)) { + struct wpa_bss *bss; + + params.bssid = wpa_s->next_scan_bssid; + bss = wpa_bss_get_bssid_latest(wpa_s, params.bssid); + if (bss && bss->ssid_len && params.num_ssids == 1 && + params.ssids[0].ssid_len == 0) { + params.ssids[0].ssid = bss->ssid; + params.ssids[0].ssid_len = bss->ssid_len; + wpa_dbg(wpa_s, MSG_DEBUG, + "Scan a previously specified BSSID " MACSTR + " and SSID %s", + MAC2STR(params.bssid), + wpa_ssid_txt(bss->ssid, bss->ssid_len)); + } else { + wpa_dbg(wpa_s, MSG_DEBUG, + "Scan a previously specified BSSID " MACSTR, + MAC2STR(params.bssid)); + } + } + scan_params = ¶ms; scan: @@ -1076,6 +1136,8 @@ scan: #ifdef CONFIG_INTERWORKING wpa_s->interworking_fast_assoc_tried = 0; #endif /* CONFIG_INTERWORKING */ + if (params.bssid) + os_memset(wpa_s->next_scan_bssid, 0, ETH_ALEN); } } @@ -1182,6 +1244,7 @@ int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s) unsigned int max_sched_scan_ssids; int wildcard = 0; int need_ssids; + struct sched_scan_plan scan_plan; if (!wpa_s->sched_scan_supported) return -1; @@ -1193,6 +1256,8 @@ int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s) if (max_sched_scan_ssids < 1 || wpa_s->conf->disable_scan_offload) return -1; + wpa_s->sched_scan_stop_req = 0; + if (wpa_s->sched_scanning) { wpa_dbg(wpa_s, MSG_DEBUG, "Already sched scanning"); return 0; @@ -1271,11 +1336,6 @@ int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s) if (!ssid || !wpa_s->prev_sched_ssid) { wpa_dbg(wpa_s, MSG_DEBUG, "Beginning of SSID list"); - if (wpa_s->conf->sched_scan_interval) - wpa_s->sched_scan_interval = - wpa_s->conf->sched_scan_interval; - if (wpa_s->sched_scan_interval == 0) - wpa_s->sched_scan_interval = 10; wpa_s->sched_scan_timeout = max_sched_scan_ssids * 2; wpa_s->first_sched_scan = 1; ssid = wpa_s->conf->ssid; @@ -1360,14 +1420,51 @@ int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s) scan_params = ¶ms; scan: - if (ssid || !wpa_s->first_sched_scan) { + wpa_s->sched_scan_timed_out = 0; + + /* + * We cannot support multiple scan plans if the scan request includes + * too many SSID's, so in this case use only the last scan plan and make + * it run infinitely. It will be stopped by the timeout. + */ + if (wpa_s->sched_scan_plans_num == 1 || + (wpa_s->sched_scan_plans_num && !ssid && wpa_s->first_sched_scan)) { + params.sched_scan_plans = wpa_s->sched_scan_plans; + params.sched_scan_plans_num = wpa_s->sched_scan_plans_num; + } else if (wpa_s->sched_scan_plans_num > 1) { wpa_dbg(wpa_s, MSG_DEBUG, - "Starting sched scan: interval %d timeout %d", - wpa_s->sched_scan_interval, wpa_s->sched_scan_timeout); + "Too many SSIDs. Default to using single scheduled_scan plan"); + params.sched_scan_plans = + &wpa_s->sched_scan_plans[wpa_s->sched_scan_plans_num - + 1]; + params.sched_scan_plans_num = 1; } else { + if (wpa_s->conf->sched_scan_interval) + scan_plan.interval = wpa_s->conf->sched_scan_interval; + else + scan_plan.interval = 10; + + if (scan_plan.interval > wpa_s->max_sched_scan_plan_interval) { + wpa_printf(MSG_WARNING, + "Scan interval too long(%u), use the maximum allowed(%u)", + scan_plan.interval, + wpa_s->max_sched_scan_plan_interval); + scan_plan.interval = + wpa_s->max_sched_scan_plan_interval; + } + + scan_plan.iterations = 0; + params.sched_scan_plans = &scan_plan; + params.sched_scan_plans_num = 1; + } + + if (ssid || !wpa_s->first_sched_scan) { wpa_dbg(wpa_s, MSG_DEBUG, - "Starting sched scan: interval %d (no timeout)", - wpa_s->sched_scan_interval); + "Starting sched scan: interval %u timeout %d", + params.sched_scan_plans[0].interval, + wpa_s->sched_scan_timeout); + } else { + wpa_dbg(wpa_s, MSG_DEBUG, "Starting sched scan (no timeout)"); } wpa_setband_scan_freqs(wpa_s, scan_params); @@ -1381,8 +1478,7 @@ scan: } } - ret = wpa_supplicant_start_sched_scan(wpa_s, scan_params, - wpa_s->sched_scan_interval); + ret = wpa_supplicant_start_sched_scan(wpa_s, scan_params); wpabuf_free(extra_ie); os_free(params.filter_ssids); if (ret) { @@ -1400,9 +1496,12 @@ scan: wpa_s, NULL); wpa_s->first_sched_scan = 0; wpa_s->sched_scan_timeout /= 2; - wpa_s->sched_scan_interval *= 2; - if (wpa_s->sched_scan_timeout < wpa_s->sched_scan_interval) { - wpa_s->sched_scan_interval = 10; + params.sched_scan_plans[0].interval *= 2; + if ((unsigned int) wpa_s->sched_scan_timeout < + params.sched_scan_plans[0].interval || + params.sched_scan_plans[0].interval > + wpa_s->max_sched_scan_plan_interval) { + params.sched_scan_plans[0].interval = 10; wpa_s->sched_scan_timeout = max_sched_scan_ssids * 2; } } @@ -1457,6 +1556,9 @@ void wpa_supplicant_cancel_sched_scan(struct wpa_supplicant *wpa_s) if (!wpa_s->sched_scanning) return; + if (wpa_s->sched_scanning) + wpa_s->sched_scan_stop_req = 1; + 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); @@ -1516,20 +1618,7 @@ static int wpa_scan_get_max_rate(const struct wpa_scan_res *res) */ const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie) { - const u8 *end, *pos; - - pos = (const u8 *) (res + 1); - end = pos + res->ie_len; - - while (pos + 1 < end) { - if (pos + 2 + pos[1] > end) - break; - if (pos[0] == ie) - return pos; - pos += 2 + pos[1]; - } - - return NULL; + return get_ie((const u8 *) (res + 1), res->ie_len, ie); } @@ -1550,8 +1639,8 @@ const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res, pos = (const u8 *) (res + 1); end = pos + res->ie_len; - while (pos + 1 < end) { - if (pos + 2 + pos[1] > end) + while (end - pos > 1) { + if (2 + pos[1] > end - pos) break; if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && vendor_type == WPA_GET_BE32(&pos[2])) @@ -1587,8 +1676,8 @@ const u8 * wpa_scan_get_vendor_ie_beacon(const struct wpa_scan_res *res, pos += res->ie_len; end = pos + res->beacon_ie_len; - while (pos + 1 < end) { - if (pos + 2 + pos[1] > end) + while (end - pos > 1) { + if (2 + pos[1] > end - pos) break; if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && vendor_type == WPA_GET_BE32(&pos[2])) @@ -1623,8 +1712,8 @@ struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res, pos = (const u8 *) (res + 1); end = pos + res->ie_len; - while (pos + 1 < end) { - if (pos + 2 + pos[1] > end) + while (end - pos > 1) { + if (2 + pos[1] > end - pos) break; if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && vendor_type == WPA_GET_BE32(&pos[2])) @@ -1832,8 +1921,8 @@ int wpa_supplicant_filter_bssid_match(struct wpa_supplicant *wpa_s, } -static void filter_scan_res(struct wpa_supplicant *wpa_s, - struct wpa_scan_results *res) +void filter_scan_res(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *res) { size_t i, j; @@ -1860,13 +1949,13 @@ static void filter_scan_res(struct wpa_supplicant *wpa_s, /* * Noise floor values to use when we have signal strength - * measurements, but no noise floor measurments. These values were + * measurements, but no noise floor measurements. These values were * measured in an office environment with many APs. */ #define DEFAULT_NOISE_FLOOR_2GHZ (-89) #define DEFAULT_NOISE_FLOOR_5GHZ (-92) -static void scan_snr(struct wpa_scan_res *res) +void scan_snr(struct wpa_scan_res *res) { if (res->flags & WPA_SCAN_NOISE_INVALID) { res->noise = IS_5GHZ(res->freq) ? @@ -1950,8 +2039,8 @@ static unsigned int max_vht80_rate(int snr) } -static void scan_est_throughput(struct wpa_supplicant *wpa_s, - struct wpa_scan_res *res) +void scan_est_throughput(struct wpa_supplicant *wpa_s, + struct wpa_scan_res *res) { enum local_hw_capab capab = wpa_s->hw_capab; int rate; /* max legacy rate in 500 kb/s units */ @@ -2148,6 +2237,9 @@ void scan_only_handler(struct wpa_supplicant *wpa_s, wpa_s->scan_work = NULL; radio_work_done(work); } + + if (wpa_s->wpa_state == WPA_SCANNING) + wpa_supplicant_set_state(wpa_s, wpa_s->scan_prev_wpa_state); } @@ -2214,6 +2306,19 @@ wpa_scan_clone_params(const struct wpa_driver_scan_params *src) params->only_new_results = src->only_new_results; params->low_priority = src->low_priority; + if (src->sched_scan_plans_num > 0) { + params->sched_scan_plans = + os_malloc(sizeof(*src->sched_scan_plans) * + src->sched_scan_plans_num); + if (!params->sched_scan_plans) + goto failed; + + os_memcpy(params->sched_scan_plans, src->sched_scan_plans, + sizeof(*src->sched_scan_plans) * + src->sched_scan_plans_num); + params->sched_scan_plans_num = src->sched_scan_plans_num; + } + if (src->mac_addr_rand) { params->mac_addr_rand = src->mac_addr_rand; @@ -2231,6 +2336,17 @@ wpa_scan_clone_params(const struct wpa_driver_scan_params *src) params->mac_addr_mask = mac_addr + ETH_ALEN; } } + + if (src->bssid) { + u8 *bssid; + + bssid = os_malloc(ETH_ALEN); + if (!bssid) + goto failed; + os_memcpy(bssid, src->bssid, ETH_ALEN); + params->bssid = bssid; + } + return params; failed: @@ -2251,6 +2367,7 @@ void wpa_scan_free_params(struct wpa_driver_scan_params *params) os_free((u8 *) params->extra_ies); os_free(params->freqs); os_free(params->filter_ssids); + os_free(params->sched_scan_plans); /* * Note: params->mac_addr_mask points to same memory allocation and @@ -2258,20 +2375,31 @@ void wpa_scan_free_params(struct wpa_driver_scan_params *params) */ os_free((u8 *) params->mac_addr); + os_free((u8 *) params->bssid); + os_free(params); } int wpas_start_pno(struct wpa_supplicant *wpa_s) { - int ret, interval, prio; + int ret, prio; size_t i, num_ssid, num_match_ssid; struct wpa_ssid *ssid; struct wpa_driver_scan_params params; + struct sched_scan_plan scan_plan; + unsigned int max_sched_scan_ssids; if (!wpa_s->sched_scan_supported) return -1; + if (wpa_s->max_sched_scan_ssids > WPAS_MAX_SCAN_SSIDS) + max_sched_scan_ssids = WPAS_MAX_SCAN_SSIDS; + else + max_sched_scan_ssids = wpa_s->max_sched_scan_ssids; + if (max_sched_scan_ssids < 1) + return -1; + if (wpa_s->pno || wpa_s->pno_sched_pending) return 0; @@ -2292,6 +2420,13 @@ int wpas_start_pno(struct wpa_supplicant *wpa_s) } } + if (wpa_s->sched_scan_stop_req) { + wpa_printf(MSG_DEBUG, + "Schedule PNO after previous sched scan has stopped"); + wpa_s->pno_sched_pending = 1; + return 0; + } + os_memset(¶ms, 0, sizeof(params)); num_ssid = num_match_ssid = 0; @@ -2315,10 +2450,10 @@ int wpas_start_pno(struct wpa_supplicant *wpa_s) num_ssid++; } - if (num_ssid > WPAS_MAX_SCAN_SSIDS) { + if (num_ssid > max_sched_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; + "%u", max_sched_scan_ssids, (unsigned int) num_ssid); + num_ssid = max_sched_scan_ssids; } if (num_match_ssid > wpa_s->max_match_sets) { @@ -2361,8 +2496,20 @@ int wpas_start_pno(struct wpa_supplicant *wpa_s) if (wpa_s->conf->filter_rssi) params.filter_rssi = wpa_s->conf->filter_rssi; - interval = wpa_s->conf->sched_scan_interval ? - wpa_s->conf->sched_scan_interval : 10; + if (wpa_s->sched_scan_plans_num) { + params.sched_scan_plans = wpa_s->sched_scan_plans; + params.sched_scan_plans_num = wpa_s->sched_scan_plans_num; + } else { + /* Set one scan plan that will run infinitely */ + if (wpa_s->conf->sched_scan_interval) + scan_plan.interval = wpa_s->conf->sched_scan_interval; + else + scan_plan.interval = 10; + + scan_plan.iterations = 0; + params.sched_scan_plans = &scan_plan; + params.sched_scan_plans_num = 1; + } if (params.freqs == NULL && wpa_s->manual_sched_scan_freqs) { wpa_dbg(wpa_s, MSG_DEBUG, "Limit sched scan to specified channels"); @@ -2377,7 +2524,7 @@ int wpas_start_pno(struct wpa_supplicant *wpa_s) } } - ret = wpa_supplicant_start_sched_scan(wpa_s, ¶ms, interval); + ret = wpa_supplicant_start_sched_scan(wpa_s, ¶ms); os_free(params.filter_ssids); if (ret == 0) wpa_s->pno = 1; @@ -2395,6 +2542,7 @@ int wpas_stop_pno(struct wpa_supplicant *wpa_s) return 0; ret = wpa_supplicant_stop_sched_scan(wpa_s); + wpa_s->sched_scan_stop_req = 1; wpa_s->pno = 0; wpa_s->pno_sched_pending = 0; @@ -2462,3 +2610,160 @@ int wpas_mac_addr_rand_scan_set(struct wpa_supplicant *wpa_s, wpa_s->mac_addr_rand_enable |= type; return 0; } + + +int wpas_abort_ongoing_scan(struct wpa_supplicant *wpa_s) +{ + int scan_work = !!wpa_s->scan_work; + +#ifdef CONFIG_P2P + scan_work |= !!wpa_s->p2p_scan_work; +#endif /* CONFIG_P2P */ + + if (scan_work && wpa_s->own_scan_running) { + wpa_dbg(wpa_s, MSG_DEBUG, "Abort an ongoing scan"); + return wpa_drv_abort_scan(wpa_s); + } + + return 0; +} + + +int wpas_sched_scan_plans_set(struct wpa_supplicant *wpa_s, const char *cmd) +{ + struct sched_scan_plan *scan_plans = NULL; + const char *token, *context = NULL; + unsigned int num = 0; + + if (!cmd) + return -1; + + if (!cmd[0]) { + wpa_printf(MSG_DEBUG, "Clear sched scan plans"); + os_free(wpa_s->sched_scan_plans); + wpa_s->sched_scan_plans = NULL; + wpa_s->sched_scan_plans_num = 0; + return 0; + } + + while ((token = cstr_token(cmd, " ", &context))) { + int ret; + struct sched_scan_plan *scan_plan, *n; + + n = os_realloc_array(scan_plans, num + 1, sizeof(*scan_plans)); + if (!n) + goto fail; + + scan_plans = n; + scan_plan = &scan_plans[num]; + num++; + + ret = sscanf(token, "%u:%u", &scan_plan->interval, + &scan_plan->iterations); + if (ret <= 0 || ret > 2 || !scan_plan->interval) { + wpa_printf(MSG_ERROR, + "Invalid sched scan plan input: %s", token); + goto fail; + } + + if (scan_plan->interval > wpa_s->max_sched_scan_plan_interval) { + wpa_printf(MSG_WARNING, + "scan plan %u: Scan interval too long(%u), use the maximum allowed(%u)", + num, scan_plan->interval, + wpa_s->max_sched_scan_plan_interval); + scan_plan->interval = + wpa_s->max_sched_scan_plan_interval; + } + + if (ret == 1) { + scan_plan->iterations = 0; + break; + } + + if (!scan_plan->iterations) { + wpa_printf(MSG_ERROR, + "scan plan %u: Number of iterations cannot be zero", + num); + goto fail; + } + + if (scan_plan->iterations > + wpa_s->max_sched_scan_plan_iterations) { + wpa_printf(MSG_WARNING, + "scan plan %u: Too many iterations(%u), use the maximum allowed(%u)", + num, scan_plan->iterations, + wpa_s->max_sched_scan_plan_iterations); + scan_plan->iterations = + wpa_s->max_sched_scan_plan_iterations; + } + + wpa_printf(MSG_DEBUG, + "scan plan %u: interval=%u iterations=%u", + num, scan_plan->interval, scan_plan->iterations); + } + + if (!scan_plans) { + wpa_printf(MSG_ERROR, "Invalid scan plans entry"); + goto fail; + } + + if (cstr_token(cmd, " ", &context) || scan_plans[num - 1].iterations) { + wpa_printf(MSG_ERROR, + "All scan plans but the last must specify a number of iterations"); + goto fail; + } + + wpa_printf(MSG_DEBUG, "scan plan %u (last plan): interval=%u", + num, scan_plans[num - 1].interval); + + if (num > wpa_s->max_sched_scan_plans) { + wpa_printf(MSG_WARNING, + "Too many scheduled scan plans (only %u supported)", + wpa_s->max_sched_scan_plans); + wpa_printf(MSG_WARNING, + "Use only the first %u scan plans, and the last one (in infinite loop)", + wpa_s->max_sched_scan_plans - 1); + os_memcpy(&scan_plans[wpa_s->max_sched_scan_plans - 1], + &scan_plans[num - 1], sizeof(*scan_plans)); + num = wpa_s->max_sched_scan_plans; + } + + os_free(wpa_s->sched_scan_plans); + wpa_s->sched_scan_plans = scan_plans; + wpa_s->sched_scan_plans_num = num; + + return 0; + +fail: + os_free(scan_plans); + wpa_printf(MSG_ERROR, "invalid scan plans list"); + return -1; +} + + +/** + * wpas_scan_reset_sched_scan - Reset sched_scan state + * @wpa_s: Pointer to wpa_supplicant data + * + * This function is used to cancel a running scheduled scan and to reset an + * internal scan state to continue with a regular scan on the following + * wpa_supplicant_req_scan() calls. + */ +void wpas_scan_reset_sched_scan(struct wpa_supplicant *wpa_s) +{ + wpa_s->normal_scans = 0; + if (wpa_s->sched_scanning) { + wpa_s->sched_scan_timed_out = 0; + wpa_s->prev_sched_ssid = NULL; + wpa_supplicant_cancel_sched_scan(wpa_s); + } +} + + +void wpas_scan_restart_sched_scan(struct wpa_supplicant *wpa_s) +{ + /* simulate timeout to restart the sched scan */ + wpa_s->sched_scan_timed_out = 1; + wpa_s->prev_sched_ssid = NULL; + wpa_supplicant_cancel_sched_scan(wpa_s); +} diff --git a/wpa_supplicant/scan.h b/wpa_supplicant/scan.h index 7650f5a250958..2aa0a8be0e4d7 100644 --- a/wpa_supplicant/scan.h +++ b/wpa_supplicant/scan.h @@ -39,20 +39,25 @@ void wpa_supplicant_update_scan_int(struct wpa_supplicant *wpa_s, int sec); void scan_only_handler(struct wpa_supplicant *wpa_s, struct wpa_scan_results *scan_res); int wpas_scan_scheduled(struct wpa_supplicant *wpa_s); -int wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s, - struct wpa_driver_scan_params *params, - int interval); -int wpa_supplicant_stop_sched_scan(struct wpa_supplicant *wpa_s); struct wpa_driver_scan_params * wpa_scan_clone_params(const struct wpa_driver_scan_params *src); void wpa_scan_free_params(struct wpa_driver_scan_params *params); int wpas_start_pno(struct wpa_supplicant *wpa_s); int wpas_stop_pno(struct wpa_supplicant *wpa_s); +void wpas_scan_reset_sched_scan(struct wpa_supplicant *wpa_s); +void wpas_scan_restart_sched_scan(struct wpa_supplicant *wpa_s); void wpas_mac_addr_rand_scan_clear(struct wpa_supplicant *wpa_s, unsigned int type); int wpas_mac_addr_rand_scan_set(struct wpa_supplicant *wpa_s, unsigned int type, const u8 *addr, const u8 *mask); +int wpas_abort_ongoing_scan(struct wpa_supplicant *wpa_s); +void filter_scan_res(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *res); +void scan_snr(struct wpa_scan_res *res); +void scan_est_throughput(struct wpa_supplicant *wpa_s, + struct wpa_scan_res *res); +void wpa_supplicant_set_default_scan_ies(struct wpa_supplicant *wpa_s); #endif /* SCAN_H */ diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index f2e5a43b978fd..61fd3b24549cb 100644 --- a/wpa_supplicant/sme.c +++ b/wpa_supplicant/sme.c @@ -161,9 +161,10 @@ static void sme_auth_handle_rrm(struct wpa_supplicant *wpa_s, return; } - if (!(wpa_s->drv_rrm_flags & - WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES) || - !(wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_QUIET)) { + if (!((wpa_s->drv_rrm_flags & + WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES) && + (wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_QUIET)) && + !(wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_SUPPORT_RRM)) { wpa_printf(MSG_DEBUG, "RRM: Insufficient RRM support in driver - do not use RRM"); return; @@ -186,6 +187,9 @@ static void sme_auth_handle_rrm(struct wpa_supplicant *wpa_s, if (wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_TX_POWER_INSERTION) *pos |= WLAN_RRM_CAPS_LINK_MEASUREMENT; + if (wpa_s->lci) + pos[1] |= WLAN_RRM_CAPS_LCI_MEASUREMENT; + wpa_s->sme.assoc_req_ie_len += rrm_ie_len + 2; wpa_s->rrm.rrm_used = 1; } @@ -208,6 +212,9 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, u8 ext_capab[18]; int ext_capab_len; int skip_auth; +#ifdef CONFIG_MBO + const u8 *mbo; +#endif /* CONFIG_MBO */ if (bss == NULL) { wpa_msg(wpa_s, MSG_ERROR, "SME: No scan result available for " @@ -416,28 +423,6 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_P2P */ -#ifdef CONFIG_HS20 - if (is_hs20_network(wpa_s, ssid, bss)) { - struct wpabuf *hs20; - hs20 = wpabuf_alloc(20); - if (hs20) { - int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid); - size_t len; - - wpas_hs20_add_indication(hs20, pps_mo_id); - len = sizeof(wpa_s->sme.assoc_req_ie) - - wpa_s->sme.assoc_req_ie_len; - if (wpabuf_len(hs20) <= len) { - os_memcpy(wpa_s->sme.assoc_req_ie + - wpa_s->sme.assoc_req_ie_len, - wpabuf_head(hs20), wpabuf_len(hs20)); - wpa_s->sme.assoc_req_ie_len += wpabuf_len(hs20); - } - wpabuf_free(hs20); - } - } -#endif /* CONFIG_HS20 */ - #ifdef CONFIG_FST if (wpa_s->fst_ies) { int fst_ies_len = wpabuf_len(wpa_s->fst_ies); @@ -453,6 +438,28 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_FST */ + sme_auth_handle_rrm(wpa_s, bss); + +#ifdef CONFIG_MBO + mbo = wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE); + if (mbo) { + int len; + + len = wpas_mbo_supp_op_class_ie( + wpa_s, bss->freq, + wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len, + sizeof(wpa_s->sme.assoc_req_ie) - + wpa_s->sme.assoc_req_ie_len); + if (len > 0) + wpa_s->sme.assoc_req_ie_len += len; + } +#endif /* CONFIG_MBO */ + + if (params.p2p) + wpa_drv_get_ext_capa(wpa_s, WPA_IF_P2P_CLIENT); + else + wpa_drv_get_ext_capa(wpa_s, WPA_IF_STATION); + ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab, sizeof(ext_capab)); if (ext_capab_len > 0) { @@ -466,6 +473,29 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, os_memcpy(pos, ext_capab, ext_capab_len); } +#ifdef CONFIG_HS20 + if (is_hs20_network(wpa_s, ssid, bss)) { + struct wpabuf *hs20; + + hs20 = wpabuf_alloc(20); + if (hs20) { + int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid); + size_t len; + + wpas_hs20_add_indication(hs20, pps_mo_id); + len = sizeof(wpa_s->sme.assoc_req_ie) - + wpa_s->sme.assoc_req_ie_len; + if (wpabuf_len(hs20) <= len) { + os_memcpy(wpa_s->sme.assoc_req_ie + + wpa_s->sme.assoc_req_ie_len, + wpabuf_head(hs20), wpabuf_len(hs20)); + wpa_s->sme.assoc_req_ie_len += wpabuf_len(hs20); + } + wpabuf_free(hs20); + } + } +#endif /* CONFIG_HS20 */ + if (wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]) { struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]; size_t len; @@ -480,7 +510,18 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, } } - sme_auth_handle_rrm(wpa_s, bss); +#ifdef CONFIG_MBO + if (mbo) { + int len; + + len = wpas_mbo_ie(wpa_s, wpa_s->sme.assoc_req_ie + + wpa_s->sme.assoc_req_ie_len, + sizeof(wpa_s->sme.assoc_req_ie) - + wpa_s->sme.assoc_req_ie_len); + if (len >= 0) + wpa_s->sme.assoc_req_ie_len += len; + } +#endif /* CONFIG_MBO */ #ifdef CONFIG_SAE if (!skip_auth && params.auth_alg == WPA_AUTH_ALG_SAE && @@ -524,6 +565,10 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, if (old_ssid != wpa_s->current_ssid) wpas_notify_network_changed(wpa_s); +#ifdef CONFIG_HS20 + hs20_configure_frame_filters(wpa_s); +#endif /* CONFIG_HS20 */ + #ifdef CONFIG_P2P /* * If multi-channel concurrency is not supported, check for any @@ -632,6 +677,8 @@ void sme_authenticate(struct wpa_supplicant *wpa_s, radio_remove_works(wpa_s, "sme-connect", 0); } + wpas_abort_ongoing_scan(wpa_s); + cwork = os_zalloc(sizeof(*cwork)); if (cwork == NULL) return; @@ -812,7 +859,7 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data) wpa_printf(MSG_DEBUG, "SME: SAE completed - setting PMK for " "4-way handshake"); wpa_sm_set_pmk(wpa_s->wpa, wpa_s->sme.sae.pmk, PMK_LEN, - wpa_s->pending_bssid); + wpa_s->sme.sae.pmkid, wpa_s->pending_bssid); } #endif /* CONFIG_SAE */ @@ -975,8 +1022,8 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode, 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; + if (wpa_s->p2pdev->set_sta_uapsd) + params.uapsd = wpa_s->p2pdev->sta_uapsd; else params.uapsd = -1; @@ -1320,21 +1367,6 @@ int sme_proc_obss_scan(struct wpa_supplicant *wpa_s) } -static struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes, - u16 num_modes, - enum hostapd_hw_mode mode) -{ - u16 i; - - for (i = 0; i < num_modes; i++) { - if (modes[i].mode == mode) - return &modes[i]; - } - - return NULL; -} - - static void wpa_obss_scan_freqs_list(struct wpa_supplicant *wpa_s, struct wpa_driver_scan_params *params) { @@ -1553,8 +1585,10 @@ static void sme_sa_query_timer(void *eloop_ctx, void *timeout_ctx) 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) + if (nbuf == NULL) { + sme_stop_sa_query(wpa_s); return; + } if (wpa_s->sme.sa_query_count == 0) { /* Starting a new SA Query procedure */ os_get_reltime(&wpa_s->sme.sa_query_start); @@ -1565,6 +1599,7 @@ static void sme_sa_query_timer(void *eloop_ctx, void *timeout_ctx) if (os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN) < 0) { wpa_printf(MSG_DEBUG, "Could not generate SA Query ID"); + sme_stop_sa_query(wpa_s); return; } diff --git a/wpa_supplicant/systemd/wpa_supplicant-nl80211.service.arg.in b/wpa_supplicant/systemd/wpa_supplicant-nl80211.service.arg.in new file mode 100644 index 0000000000000..03ac507059959 --- /dev/null +++ b/wpa_supplicant/systemd/wpa_supplicant-nl80211.service.arg.in @@ -0,0 +1,15 @@ +[Unit] +Description=WPA supplicant daemon (interface- and nl80211 driver-specific version) +Requires=sys-subsystem-net-devices-%i.device +After=sys-subsystem-net-devices-%i.device +Before=network.target +Wants=network.target + +# NetworkManager users will probably want the dbus version instead. + +[Service] +Type=simple +ExecStart=@BINDIR@/wpa_supplicant -c/etc/wpa_supplicant/wpa_supplicant-nl80211-%I.conf -Dnl80211 -i%I + +[Install] +Alias=multi-user.target.wants/wpa_supplicant-nl80211@%i.service diff --git a/wpa_supplicant/systemd/wpa_supplicant-wired.service.arg.in b/wpa_supplicant/systemd/wpa_supplicant-wired.service.arg.in new file mode 100644 index 0000000000000..c8a744d6e138a --- /dev/null +++ b/wpa_supplicant/systemd/wpa_supplicant-wired.service.arg.in @@ -0,0 +1,15 @@ +[Unit] +Description=WPA supplicant daemon (interface- and wired driver-specific version) +Requires=sys-subsystem-net-devices-%i.device +After=sys-subsystem-net-devices-%i.device +Before=network.target +Wants=network.target + +# NetworkManager users will probably want the dbus version instead. + +[Service] +Type=simple +ExecStart=@BINDIR@/wpa_supplicant -c/etc/wpa_supplicant/wpa_supplicant-wired-%I.conf -Dwired -i%I + +[Install] +Alias=multi-user.target.wants/wpa_supplicant-wired@%i.service diff --git a/wpa_supplicant/systemd/wpa_supplicant.service.arg.in b/wpa_supplicant/systemd/wpa_supplicant.service.arg.in new file mode 100644 index 0000000000000..7788b380c4a29 --- /dev/null +++ b/wpa_supplicant/systemd/wpa_supplicant.service.arg.in @@ -0,0 +1,15 @@ +[Unit] +Description=WPA supplicant daemon (interface-specific version) +Requires=sys-subsystem-net-devices-%i.device +After=sys-subsystem-net-devices-%i.device +Before=network.target +Wants=network.target + +# NetworkManager users will probably want the dbus version instead. + +[Service] +Type=simple +ExecStart=@BINDIR@/wpa_supplicant -c/etc/wpa_supplicant/wpa_supplicant-%I.conf -i%I + +[Install] +Alias=multi-user.target.wants/wpa_supplicant@%i.service diff --git a/wpa_supplicant/systemd/wpa_supplicant.service.in b/wpa_supplicant/systemd/wpa_supplicant.service.in new file mode 100644 index 0000000000000..bc5d49af86551 --- /dev/null +++ b/wpa_supplicant/systemd/wpa_supplicant.service.in @@ -0,0 +1,13 @@ +[Unit] +Description=WPA supplicant +Before=network.target +Wants=network.target + +[Service] +Type=dbus +BusName=@DBUS_INTERFACE@ +ExecStart=@BINDIR@/wpa_supplicant -u + +[Install] +WantedBy=multi-user.target +Alias=dbus-@DBUS_INTERFACE@.service diff --git a/wpa_supplicant/tests/link_test.c b/wpa_supplicant/tests/link_test.c deleted file mode 100644 index 3bfbed577d8c0..0000000000000 --- a/wpa_supplicant/tests/link_test.c +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Dummy functions to allow link_test to be linked. The need for these - * functions should be removed to allow IEEE 802.1X/EAPOL authenticator to - * be built outside hostapd. - */ - -#include "includes.h" - -#include "common.h" - - -struct hostapd_data; -struct sta_info; -struct rsn_pmksa_cache_entry; -struct eapol_state_machine; -struct hostapd_eap_user; -struct hostapd_bss_config; -struct hostapd_vlan; - - -struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta) -{ - return NULL; -} - - -int ap_for_each_sta(struct hostapd_data *hapd, - int (*cb)(struct hostapd_data *hapd, struct sta_info *sta, - void *ctx), - void *ctx) -{ - return 0; -} - - -void ap_sta_session_timeout(struct hostapd_data *hapd, struct sta_info *sta, - u32 session_timeout) -{ -} - - -int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, - int old_vlanid) -{ - return 0; -} - - -void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta, - int success) -{ -} - - -void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta, - u8 *buf, size_t len) -{ -} - - -void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta) -{ -} - - -void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry, - struct eapol_state_machine *eapol) -{ -} - - -const struct hostapd_eap_user * -hostapd_get_eap_user(const struct hostapd_bss_config *conf, const u8 *identity, - size_t identity_len, int phase2) -{ - return NULL; -} - - -const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, int vlan_id) -{ - return NULL; -} diff --git a/wpa_supplicant/tests/test_eap_sim_common.c b/wpa_supplicant/tests/test_eap_sim_common.c deleted file mode 100644 index f60b1821e2596..0000000000000 --- a/wpa_supplicant/tests/test_eap_sim_common.c +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Test program for EAP-SIM PRF - * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> - * - * This software may be distributed under the terms of the BSD license. - * See README for more details. - */ - -#include "eap_common/eap_sim_common.c" - - -static int test_eap_sim_prf(void) -{ - /* http://csrc.nist.gov/encryption/dss/Examples-1024bit.pdf */ - u8 xkey[] = { - 0xbd, 0x02, 0x9b, 0xbe, 0x7f, 0x51, 0x96, 0x0b, - 0xcf, 0x9e, 0xdb, 0x2b, 0x61, 0xf0, 0x6f, 0x0f, - 0xeb, 0x5a, 0x38, 0xb6 - }; - u8 w[] = { - 0x20, 0x70, 0xb3, 0x22, 0x3d, 0xba, 0x37, 0x2f, - 0xde, 0x1c, 0x0f, 0xfc, 0x7b, 0x2e, 0x3b, 0x49, - 0x8b, 0x26, 0x06, 0x14, 0x3c, 0x6c, 0x18, 0xba, - 0xcb, 0x0f, 0x6c, 0x55, 0xba, 0xbb, 0x13, 0x78, - 0x8e, 0x20, 0xd7, 0x37, 0xa3, 0x27, 0x51, 0x16 - }; - u8 buf[40]; - - 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) { - printf("eap_sim_prf failed\n"); - return 1; - } - - return 0; -} - - -int main(int argc, char *argv[]) -{ - int errors = 0; - - errors += test_eap_sim_prf(); - - return errors; -} diff --git a/wpa_supplicant/tests/test_wpa.c b/wpa_supplicant/tests/test_wpa.c deleted file mode 100644 index 39971f285de34..0000000000000 --- a/wpa_supplicant/tests/test_wpa.c +++ /dev/null @@ -1,369 +0,0 @@ -/* - * Test program for combined WPA authenticator/supplicant - * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> - * - * This software may be distributed under the terms of the BSD license. - * See README for more details. - */ - -#include "includes.h" - -#include "common.h" -#include "eloop.h" -#include "common/ieee802_11_defs.h" -#include "../config.h" -#include "rsn_supp/wpa.h" -#include "rsn_supp/wpa_ie.h" -#include "ap/wpa_auth.h" - - -struct wpa { - u8 auth_addr[ETH_ALEN]; - u8 supp_addr[ETH_ALEN]; - u8 psk[PMK_LEN]; - - /* from authenticator */ - u8 auth_eapol_dst[ETH_ALEN]; - u8 *auth_eapol; - size_t auth_eapol_len; - - /* from supplicant */ - u8 *supp_eapol; - size_t supp_eapol_len; - - struct wpa_sm *supp; - struct wpa_authenticator *auth_group; - struct wpa_state_machine *auth; - - struct wpa_ssid ssid; - u8 supp_ie[80]; - size_t supp_ie_len; -}; - - -static int supp_get_bssid(void *ctx, u8 *bssid) -{ - struct wpa *wpa = ctx; - wpa_printf(MSG_DEBUG, "SUPP: %s", __func__); - os_memcpy(bssid, wpa->auth_addr, ETH_ALEN); - return 0; -} - - -static void supp_set_state(void *ctx, enum wpa_states state) -{ - wpa_printf(MSG_DEBUG, "SUPP: %s(state=%d)", __func__, state); -} - - -static void auth_eapol_rx(void *eloop_data, void *user_ctx) -{ - struct wpa *wpa = eloop_data; - - wpa_printf(MSG_DEBUG, "AUTH: RX EAPOL frame"); - wpa_receive(wpa->auth_group, wpa->auth, wpa->supp_eapol, - wpa->supp_eapol_len); -} - - -static int supp_ether_send(void *ctx, const u8 *dest, u16 proto, const u8 *buf, - size_t len) -{ - struct wpa *wpa = ctx; - - wpa_printf(MSG_DEBUG, "SUPP: %s(dest=" MACSTR " proto=0x%04x " - "len=%lu)", - __func__, MAC2STR(dest), proto, (unsigned long) len); - - os_free(wpa->supp_eapol); - wpa->supp_eapol = os_malloc(len); - if (wpa->supp_eapol == NULL) - return -1; - os_memcpy(wpa->supp_eapol, buf, len); - wpa->supp_eapol_len = len; - eloop_register_timeout(0, 0, auth_eapol_rx, wpa, NULL); - - return 0; -} - - -static u8 * supp_alloc_eapol(void *ctx, u8 type, const void *data, - u16 data_len, size_t *msg_len, void **data_pos) -{ - struct ieee802_1x_hdr *hdr; - - wpa_printf(MSG_DEBUG, "SUPP: %s(type=%d data_len=%d)", - __func__, type, data_len); - - *msg_len = sizeof(*hdr) + data_len; - hdr = os_malloc(*msg_len); - if (hdr == NULL) - return NULL; - - hdr->version = 2; - hdr->type = type; - hdr->length = host_to_be16(data_len); - - if (data) - os_memcpy(hdr + 1, data, data_len); - else - os_memset(hdr + 1, 0, data_len); - - if (data_pos) - *data_pos = hdr + 1; - - return (u8 *) hdr; -} - - -static int supp_get_beacon_ie(void *ctx) -{ - struct wpa *wpa = ctx; - const u8 *ie; - size_t ielen; - - wpa_printf(MSG_DEBUG, "SUPP: %s", __func__); - - ie = wpa_auth_get_wpa_ie(wpa->auth_group, &ielen); - if (ie == NULL || ielen < 1) - return -1; - if (ie[0] == WLAN_EID_RSN) - return wpa_sm_set_ap_rsn_ie(wpa->supp, ie, 2 + ie[1]); - return wpa_sm_set_ap_wpa_ie(wpa->supp, ie, 2 + ie[1]); -} - - -static int supp_set_key(void *ctx, enum wpa_alg alg, - const u8 *addr, int key_idx, int set_tx, - const u8 *seq, size_t seq_len, - const u8 *key, size_t key_len) -{ - wpa_printf(MSG_DEBUG, "SUPP: %s(alg=%d addr=" MACSTR " key_idx=%d " - "set_tx=%d)", - __func__, alg, MAC2STR(addr), key_idx, set_tx); - wpa_hexdump(MSG_DEBUG, "SUPP: set_key - seq", seq, seq_len); - wpa_hexdump(MSG_DEBUG, "SUPP: set_key - key", key, key_len); - return 0; -} - - -static int supp_mlme_setprotection(void *ctx, const u8 *addr, - int protection_type, int key_type) -{ - wpa_printf(MSG_DEBUG, "SUPP: %s(addr=" MACSTR " protection_type=%d " - "key_type=%d)", - __func__, MAC2STR(addr), protection_type, key_type); - return 0; -} - - -static void supp_cancel_auth_timeout(void *ctx) -{ - wpa_printf(MSG_DEBUG, "SUPP: %s", __func__); -} - - -static int supp_init(struct wpa *wpa) -{ - struct wpa_sm_ctx *ctx = os_zalloc(sizeof(*ctx)); - if (ctx == NULL) - return -1; - - ctx->ctx = wpa; - ctx->msg_ctx = wpa; - ctx->set_state = supp_set_state; - ctx->get_bssid = supp_get_bssid; - ctx->ether_send = supp_ether_send; - ctx->get_beacon_ie = supp_get_beacon_ie; - ctx->alloc_eapol = supp_alloc_eapol; - ctx->set_key = supp_set_key; - ctx->mlme_setprotection = supp_mlme_setprotection; - ctx->cancel_auth_timeout = supp_cancel_auth_timeout; - wpa->supp = wpa_sm_init(ctx); - if (wpa->supp == NULL) { - wpa_printf(MSG_DEBUG, "SUPP: wpa_sm_init() failed"); - return -1; - } - - wpa_sm_set_own_addr(wpa->supp, wpa->supp_addr); - wpa_sm_set_param(wpa->supp, WPA_PARAM_RSN_ENABLED, 1); - wpa_sm_set_param(wpa->supp, WPA_PARAM_PROTO, WPA_PROTO_RSN); - wpa_sm_set_param(wpa->supp, WPA_PARAM_PAIRWISE, WPA_CIPHER_CCMP); - wpa_sm_set_param(wpa->supp, WPA_PARAM_GROUP, WPA_CIPHER_CCMP); - wpa_sm_set_param(wpa->supp, WPA_PARAM_KEY_MGMT, WPA_KEY_MGMT_PSK); - wpa_sm_set_pmk(wpa->supp, wpa->psk, PMK_LEN); - - wpa->supp_ie_len = sizeof(wpa->supp_ie); - if (wpa_sm_set_assoc_wpa_ie_default(wpa->supp, wpa->supp_ie, - &wpa->supp_ie_len) < 0) { - wpa_printf(MSG_DEBUG, "SUPP: wpa_sm_set_assoc_wpa_ie_default()" - " failed"); - return -1; - } - - wpa_sm_notify_assoc(wpa->supp, wpa->auth_addr); - - return 0; -} - - -static void auth_logger(void *ctx, const u8 *addr, logger_level level, - const char *txt) -{ - if (addr) - wpa_printf(MSG_DEBUG, "AUTH: " MACSTR " - %s", - MAC2STR(addr), txt); - else - wpa_printf(MSG_DEBUG, "AUTH: %s", txt); -} - - -static void supp_eapol_rx(void *eloop_data, void *user_ctx) -{ - struct wpa *wpa = eloop_data; - - wpa_printf(MSG_DEBUG, "SUPP: RX EAPOL frame"); - wpa_sm_rx_eapol(wpa->supp, wpa->auth_addr, wpa->auth_eapol, - wpa->auth_eapol_len); -} - - -static int auth_send_eapol(void *ctx, const u8 *addr, const u8 *data, - size_t data_len, int encrypt) -{ - struct wpa *wpa = ctx; - - wpa_printf(MSG_DEBUG, "AUTH: %s(addr=" MACSTR " data_len=%lu " - "encrypt=%d)", - __func__, MAC2STR(addr), (unsigned long) data_len, encrypt); - - os_free(wpa->auth_eapol); - wpa->auth_eapol = os_malloc(data_len); - if (wpa->auth_eapol == NULL) - return -1; - os_memcpy(wpa->auth_eapol_dst, addr, ETH_ALEN); - os_memcpy(wpa->auth_eapol, data, data_len); - wpa->auth_eapol_len = data_len; - eloop_register_timeout(0, 0, supp_eapol_rx, wpa, NULL); - - return 0; -} - - -static const u8 * auth_get_psk(void *ctx, const u8 *addr, const u8 *prev_psk) -{ - struct wpa *wpa = ctx; - wpa_printf(MSG_DEBUG, "AUTH: %s (addr=" MACSTR " prev_psk=%p)", - __func__, MAC2STR(addr), prev_psk); - if (prev_psk) - return NULL; - return wpa->psk; -} - - -static int auth_init_group(struct wpa *wpa) -{ - struct wpa_auth_config conf; - struct wpa_auth_callbacks cb; - - wpa_printf(MSG_DEBUG, "AUTH: Initializing group state machine"); - - os_memset(&conf, 0, sizeof(conf)); - conf.wpa = 2; - conf.wpa_key_mgmt = WPA_KEY_MGMT_PSK; - conf.wpa_pairwise = WPA_CIPHER_CCMP; - conf.rsn_pairwise = WPA_CIPHER_CCMP; - conf.wpa_group = WPA_CIPHER_CCMP; - conf.eapol_version = 2; - - os_memset(&cb, 0, sizeof(cb)); - cb.ctx = wpa; - cb.logger = auth_logger; - cb.send_eapol = auth_send_eapol; - cb.get_psk = auth_get_psk; - - wpa->auth_group = wpa_init(wpa->auth_addr, &conf, &cb); - if (wpa->auth_group == NULL) { - wpa_printf(MSG_DEBUG, "AUTH: wpa_init() failed"); - return -1; - } - - return 0; -} - - -static int auth_init(struct wpa *wpa) -{ - wpa->auth = wpa_auth_sta_init(wpa->auth_group, wpa->supp_addr, NULL); - if (wpa->auth == NULL) { - wpa_printf(MSG_DEBUG, "AUTH: wpa_auth_sta_init() failed"); - return -1; - } - - if (wpa_validate_wpa_ie(wpa->auth_group, wpa->auth, wpa->supp_ie, - wpa->supp_ie_len, NULL, 0) != WPA_IE_OK) { - wpa_printf(MSG_DEBUG, "AUTH: wpa_validate_wpa_ie() failed"); - return -1; - } - - wpa_auth_sm_event(wpa->auth, WPA_ASSOC); - - wpa_auth_sta_associated(wpa->auth_group, wpa->auth); - - return 0; -} - - -static void deinit(struct wpa *wpa) -{ - wpa_auth_sta_deinit(wpa->auth); - wpa_sm_deinit(wpa->supp); - wpa_deinit(wpa->auth_group); - os_free(wpa->auth_eapol); - wpa->auth_eapol = NULL; - os_free(wpa->supp_eapol); - wpa->supp_eapol = NULL; -} - - -int main(int argc, char *argv[]) -{ - struct wpa wpa; - - if (os_program_init()) - return -1; - - os_memset(&wpa, 0, sizeof(wpa)); - os_memset(wpa.auth_addr, 0x12, ETH_ALEN); - os_memset(wpa.supp_addr, 0x32, ETH_ALEN); - os_memset(wpa.psk, 0x44, PMK_LEN); - - wpa_debug_level = 0; - wpa_debug_show_keys = 1; - - if (eloop_init()) { - wpa_printf(MSG_ERROR, "Failed to initialize event loop"); - return -1; - } - - if (auth_init_group(&wpa) < 0) - return -1; - - if (supp_init(&wpa) < 0) - return -1; - - if (auth_init(&wpa) < 0) - return -1; - - wpa_printf(MSG_DEBUG, "Starting eloop"); - eloop_run(); - wpa_printf(MSG_DEBUG, "eloop done"); - - deinit(&wpa); - - eloop_destroy(); - - os_program_deinit(); - - return 0; -} diff --git a/wpa_supplicant/vs2005/eapol_test/eapol_test.vcproj b/wpa_supplicant/vs2005/eapol_test/eapol_test.vcproj new file mode 100755 index 0000000000000..af7b3fe9ceb01 --- /dev/null +++ b/wpa_supplicant/vs2005/eapol_test/eapol_test.vcproj @@ -0,0 +1,473 @@ +<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="eapol_test"
+ ProjectGUID="{0E3F2C6D-1372-48D6-BCAB-E584917C4DE3}"
+ RootNamespace="eapol_test"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\..;..\..\..\src;..\..\..\src\utils;C:\dev\WpdPack\include;C:\dev\openssl\include"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="4"
+ DisableSpecificWarnings="4244;4267;4311"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="ws2_32.lib Crypt32.lib Winscard.lib Packet.lib wpcap.lib libeay32MT.lib ssleay32Mt.lib"
+ LinkIncremental="2"
+ AdditionalLibraryDirectories="C:\dev\WpdPack\lib;C:\dev\openssl\lib"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="..\..;..\..\..\src;..\..\..\src\utils;C:\dev\WpdPack\include;C:\dev\openssl\include"
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="3"
+ DisableSpecificWarnings="4244;4267;4311"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="ws2_32.lib Crypt32.lib Winscard.lib Packet.lib wpcap.lib libeay32MT.lib ssleay32Mt.lib"
+ LinkIncremental="1"
+ AdditionalLibraryDirectories="C:\dev\WpdPack\lib;C:\dev\openssl\lib"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath="..\..\..\src\crypto\aes-cbc.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\aes-ctr.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\aes-eax.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\aes-encblock.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\aes-omac1.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\aes-unwrap.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\aes-wrap.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\utils\base64.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\blacklist.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\bss.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_common\chap.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\utils\common.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\config.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\config_file.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\crypto_openssl.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\ctrl_iface.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\ctrl_iface_named_pipe.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\drivers\driver_common.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_aka.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_common\eap_common.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_gtc.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_leap.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_md5.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_methods.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_mschapv2.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_otp.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_peap.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_common\eap_peap_common.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\eap_register.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_sim.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_common\eap_sim_common.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_tls.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_tls_common.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_tnc.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_ttls.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eapol_supp\eapol_supp_sm.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\eapol_test.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\utils\eloop_win.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\events.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\fips_prf_openssl.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\utils\ip_addr.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\l2_packet\l2_packet_winpcap.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\md5.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\ms_funcs.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\mschapv2.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\notify.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\utils\os_win32.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\utils\pcsc_funcs.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\rsn_supp\peerkey.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\rsn_supp\pmksa_cache.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\rsn_supp\preauth.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\radius\radius.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\radius\radius_client.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\scan.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\sha1.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\sha1-pbkdf2.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\sha1-prf.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\sha1-tlsprf.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\tls_openssl.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\tncc.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\rsn_supp\wpa.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\common\wpa_common.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\utils\wpa_debug.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\rsn_supp\wpa_ie.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\wpa_supplicant.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\utils\wpabuf.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\wpas_glue.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+ >
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/wpa_supplicant/vs2005/win_if_list/win_if_list.vcproj b/wpa_supplicant/vs2005/win_if_list/win_if_list.vcproj new file mode 100755 index 0000000000000..e79fc0f4666f8 --- /dev/null +++ b/wpa_supplicant/vs2005/win_if_list/win_if_list.vcproj @@ -0,0 +1,203 @@ +<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="win_if_list"
+ ProjectGUID="{9E87CD9C-60CE-4533-85CF-85CA3A9BF26A}"
+ RootNamespace="win_if_list"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="0"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\..\..\src\utils;C:\dev\WpdPack\include"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="wpcap.lib"
+ LinkIncremental="2"
+ AdditionalLibraryDirectories="C:\dev\WpdPack\lib"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="0"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="..\..\..\src\utils;C:\dev\WpdPack\include"
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="wpcap.lib"
+ LinkIncremental="1"
+ AdditionalLibraryDirectories="C:\dev\WpdPack\lib"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath="..\..\win_if_list.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+ >
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/wpa_supplicant/vs2005/wpa_cli/wpa_cli.vcproj b/wpa_supplicant/vs2005/wpa_cli/wpa_cli.vcproj new file mode 100755 index 0000000000000..d2de768e7cdca --- /dev/null +++ b/wpa_supplicant/vs2005/wpa_cli/wpa_cli.vcproj @@ -0,0 +1,215 @@ +<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="wpa_cli"
+ ProjectGUID="{E3A7B181-22CC-4DA3-8410-6AD69879A9EC}"
+ RootNamespace="wpa_cli"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="0"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\..\..\src;..\..\..\src\utils"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="4"
+ DisableSpecificWarnings="4244;4267"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="ws2_32.lib"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="0"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="..\..\..\src;..\..\..\src\utils"
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="3"
+ DisableSpecificWarnings="4244;4267"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="ws2_32.lib"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath="..\..\..\src\utils\common.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\utils\os_win32.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\wpa_cli.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\common\wpa_ctrl.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+ >
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/wpa_supplicant/vs2005/wpa_passphrase/wpa_passphrase.vcproj b/wpa_supplicant/vs2005/wpa_passphrase/wpa_passphrase.vcproj new file mode 100755 index 0000000000000..97aa2c5aecb5e --- /dev/null +++ b/wpa_supplicant/vs2005/wpa_passphrase/wpa_passphrase.vcproj @@ -0,0 +1,236 @@ +<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="wpa_passphrase"
+ ProjectGUID="{ADBE4EA8-F0C5-40C2-AE89-C56D0F2EC1DF}"
+ RootNamespace="wpa_passphrase"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="0"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\..\..\src;..\..\..\src\utils;C:\dev\openssl\include"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="4"
+ DisableSpecificWarnings="4244;4267"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="ws2_32.lib"
+ LinkIncremental="2"
+ AdditionalLibraryDirectories=""
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="0"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="..\..\..\src;..\..\..\src\utils;C:\dev\openssl\include"
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="3"
+ DisableSpecificWarnings="4244;4267"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="ws2_32.lib"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath="..\..\..\src\utils\common.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\md5.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\md5-internal.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\utils\os_win32.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\sha1.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\sha1-internal.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\sha1-prf.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\sha1-pbkdf2.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\wpa_passphrase.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+ >
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/wpa_supplicant/vs2005/wpa_supplicant.sln b/wpa_supplicant/vs2005/wpa_supplicant.sln new file mode 100755 index 0000000000000..df89e3198d2f6 --- /dev/null +++ b/wpa_supplicant/vs2005/wpa_supplicant.sln @@ -0,0 +1,52 @@ +
+Microsoft Visual Studio Solution File, Format Version 9.00
+# Visual Studio 2005
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wpa_supplicant", "wpa_supplicant\wpa_supplicant.vcproj", "{8BCFDA77-AEDC-4168-8897-5B73105BBB87}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wpa_cli", "wpa_cli\wpa_cli.vcproj", "{E3A7B181-22CC-4DA3-8410-6AD69879A9EC}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wpasvc", "wpasvc\wpasvc.vcproj", "{E2A4A85F-CA77-406D-8ABF-63EF94545ACC}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wpa_passphrase", "wpa_passphrase\wpa_passphrase.vcproj", "{ADBE4EA8-F0C5-40C2-AE89-C56D0F2EC1DF}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "win_if_list", "win_if_list\win_if_list.vcproj", "{9E87CD9C-60CE-4533-85CF-85CA3A9BF26A}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "eapol_test", "eapol_test\eapol_test.vcproj", "{0E3F2C6D-1372-48D6-BCAB-E584917C4DE3}"
+EndProject
+Global
+ GlobalSection(DPCodeReviewSolutionGUID) = preSolution
+ DPCodeReviewSolutionGUID = {00000000-0000-0000-0000-000000000000}
+ EndGlobalSection
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {8BCFDA77-AEDC-4168-8897-5B73105BBB87}.Debug|Win32.ActiveCfg = Debug|Win32
+ {8BCFDA77-AEDC-4168-8897-5B73105BBB87}.Debug|Win32.Build.0 = Debug|Win32
+ {8BCFDA77-AEDC-4168-8897-5B73105BBB87}.Release|Win32.ActiveCfg = Release|Win32
+ {8BCFDA77-AEDC-4168-8897-5B73105BBB87}.Release|Win32.Build.0 = Release|Win32
+ {E3A7B181-22CC-4DA3-8410-6AD69879A9EC}.Debug|Win32.ActiveCfg = Debug|Win32
+ {E3A7B181-22CC-4DA3-8410-6AD69879A9EC}.Debug|Win32.Build.0 = Debug|Win32
+ {E3A7B181-22CC-4DA3-8410-6AD69879A9EC}.Release|Win32.ActiveCfg = Release|Win32
+ {E3A7B181-22CC-4DA3-8410-6AD69879A9EC}.Release|Win32.Build.0 = Release|Win32
+ {E2A4A85F-CA77-406D-8ABF-63EF94545ACC}.Debug|Win32.ActiveCfg = Debug|Win32
+ {E2A4A85F-CA77-406D-8ABF-63EF94545ACC}.Debug|Win32.Build.0 = Debug|Win32
+ {E2A4A85F-CA77-406D-8ABF-63EF94545ACC}.Release|Win32.ActiveCfg = Release|Win32
+ {E2A4A85F-CA77-406D-8ABF-63EF94545ACC}.Release|Win32.Build.0 = Release|Win32
+ {ADBE4EA8-F0C5-40C2-AE89-C56D0F2EC1DF}.Debug|Win32.ActiveCfg = Debug|Win32
+ {ADBE4EA8-F0C5-40C2-AE89-C56D0F2EC1DF}.Debug|Win32.Build.0 = Debug|Win32
+ {ADBE4EA8-F0C5-40C2-AE89-C56D0F2EC1DF}.Release|Win32.ActiveCfg = Release|Win32
+ {ADBE4EA8-F0C5-40C2-AE89-C56D0F2EC1DF}.Release|Win32.Build.0 = Release|Win32
+ {9E87CD9C-60CE-4533-85CF-85CA3A9BF26A}.Debug|Win32.ActiveCfg = Debug|Win32
+ {9E87CD9C-60CE-4533-85CF-85CA3A9BF26A}.Debug|Win32.Build.0 = Debug|Win32
+ {9E87CD9C-60CE-4533-85CF-85CA3A9BF26A}.Release|Win32.ActiveCfg = Release|Win32
+ {9E87CD9C-60CE-4533-85CF-85CA3A9BF26A}.Release|Win32.Build.0 = Release|Win32
+ {0E3F2C6D-1372-48D6-BCAB-E584917C4DE3}.Debug|Win32.ActiveCfg = Debug|Win32
+ {0E3F2C6D-1372-48D6-BCAB-E584917C4DE3}.Debug|Win32.Build.0 = Debug|Win32
+ {0E3F2C6D-1372-48D6-BCAB-E584917C4DE3}.Release|Win32.ActiveCfg = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/wpa_supplicant/vs2005/wpa_supplicant/wpa_supplicant.vcproj b/wpa_supplicant/vs2005/wpa_supplicant/wpa_supplicant.vcproj new file mode 100755 index 0000000000000..51acab9270c67 --- /dev/null +++ b/wpa_supplicant/vs2005/wpa_supplicant/wpa_supplicant.vcproj @@ -0,0 +1,461 @@ +<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="wpa_supplicant"
+ ProjectGUID="{8BCFDA77-AEDC-4168-8897-5B73105BBB87}"
+ RootNamespace="wpa_supplicant"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="0"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\..;..\..\..\src;..\..\..\src\utils;C:\dev\WpdPack\include;C:\dev\openssl\include"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="4"
+ DisableSpecificWarnings="4244;4267;4311"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="wbemuuid.lib ws2_32.lib Crypt32.lib Winscard.lib Packet.lib wpcap.lib libeay32MT.lib ssleay32Mt.lib"
+ LinkIncremental="2"
+ AdditionalLibraryDirectories="C:\dev\WpdPack\lib;C:\dev\openssl\lib"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="0"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="..\..;..\..\..\src;..\..\..\src\utils;C:\dev\WpdPack\include;C:\dev\openssl\include"
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="3"
+ DisableSpecificWarnings="4244;4267;4311"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="wbemuuid.lib ws2_32.lib Crypt32.lib Winscard.lib Packet.lib wpcap.lib libeay32MT.lib ssleay32Mt.lib"
+ LinkIncremental="1"
+ AdditionalLibraryDirectories="C:\dev\WpdPack\lib;C:\dev\openssl\lib"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath="..\..\..\src\crypto\aes-cbc.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\aes-ctr.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\aes-eax.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\aes-encblock.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\aes-omac1.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\aes-unwrap.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\aes-wrap.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\utils\base64.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\blacklist.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\bss.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_common\chap.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\utils\common.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\config.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\config_file.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\crypto_openssl.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\ctrl_iface.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\ctrl_iface_named_pipe.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\drivers\driver_common.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\drivers\driver_ndis.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\drivers\driver_ndis_.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\drivers\drivers.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_common\eap_common.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_gtc.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_leap.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_md5.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_methods.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_mschapv2.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_otp.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_peap.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_common\eap_peap_common.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\eap_register.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_tls.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_tls_common.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_tnc.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_ttls.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eapol_supp\eapol_supp_sm.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\utils\eloop_win.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\events.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\l2_packet\l2_packet_winpcap.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\main.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\md5.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\ms_funcs.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\mschapv2.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\drivers\ndis_events.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\notify.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\utils\os_win32.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\utils\pcsc_funcs.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\rsn_supp\peerkey.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\rsn_supp\pmksa_cache.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\rsn_supp\preauth.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\scan.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\sha1.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\sha1-pbkdf2.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\sha1-prf.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\sha1-tlsprf.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\tls_openssl.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\tncc.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\rsn_supp\wpa.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\common\wpa_common.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\utils\wpa_debug.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\rsn_supp\wpa_ie.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\wpa_supplicant.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\utils\wpabuf.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\wpas_glue.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+ >
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/wpa_supplicant/vs2005/wpasvc/wpasvc.vcproj b/wpa_supplicant/vs2005/wpasvc/wpasvc.vcproj new file mode 100755 index 0000000000000..6fd8af80303b0 --- /dev/null +++ b/wpa_supplicant/vs2005/wpasvc/wpasvc.vcproj @@ -0,0 +1,461 @@ +<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="wpasvc"
+ ProjectGUID="{E2A4A85F-CA77-406D-8ABF-63EF94545ACC}"
+ RootNamespace="wpasvc"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="0"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\..;..\..\..\src;..\..\..\src\utils;C:\dev\WpdPack\include;C:\dev\openssl\include"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="4"
+ DisableSpecificWarnings="4244;4267;4311"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="wbemuuid.lib ws2_32.lib Crypt32.lib Winscard.lib Packet.lib wpcap.lib libeay32MT.lib ssleay32Mt.lib"
+ LinkIncremental="2"
+ AdditionalLibraryDirectories="C:\dev\WpdPack\lib;C:\dev\openssl\lib"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="0"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="..\..;..\..\..\src;..\..\..\src\utils;C:\dev\WpdPack\include;C:\dev\openssl\include"
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="3"
+ DisableSpecificWarnings="4244;4267;4311"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="wbemuuid.lib ws2_32.lib Crypt32.lib Winscard.lib Packet.lib wpcap.lib libeay32MT.lib ssleay32Mt.lib"
+ LinkIncremental="1"
+ AdditionalLibraryDirectories="C:\dev\WpdPack\lib;C:\dev\openssl\lib"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath="..\..\..\src\crypto\aes-cbc.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\aes-ctr.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\aes-eax.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\aes-encblock.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\aes-omac1.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\aes-unwrap.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\aes-wrap.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\utils\base64.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\blacklist.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\bss.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_common\chap.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\utils\common.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\config.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\config_winreg.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\crypto_openssl.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\ctrl_iface.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\ctrl_iface_named_pipe.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\drivers\driver_common.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\drivers\driver_ndis.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\drivers\driver_ndis_.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\drivers\drivers.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_common\eap_common.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_gtc.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_leap.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_md5.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_methods.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_mschapv2.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_otp.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_peap.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_common\eap_peap_common.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\eap_register.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_tls.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_tls_common.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_tnc.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_ttls.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eapol_supp\eapol_supp_sm.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\utils\eloop_win.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\events.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\l2_packet\l2_packet_winpcap.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\main_winsvc.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\md5.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\ms_funcs.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\mschapv2.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\drivers\ndis_events.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\notify.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\utils\os_win32.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\utils\pcsc_funcs.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\rsn_supp\peerkey.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\rsn_supp\pmksa_cache.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\rsn_supp\preauth.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\scan.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\sha1.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\sha1-pbkdf2.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\sha1-prf.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\sha1-tlsprf.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\tls_openssl.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\tncc.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\rsn_supp\wpa.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\common\wpa_common.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\utils\wpa_debug.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\rsn_supp\wpa_ie.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\wpa_supplicant.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\utils\wpabuf.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\wpas_glue.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+ >
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/wpa_supplicant/wmm_ac.h b/wpa_supplicant/wmm_ac.h index 5171b1683ef7b..0d15ad01cc589 100644 --- a/wpa_supplicant/wmm_ac.h +++ b/wpa_supplicant/wmm_ac.h @@ -88,7 +88,7 @@ enum ts_dir_idx { */ struct wmm_ac_addts_request { /* - * dialog token - Used to link the recived ADDTS response with this + * dialog token - Used to link the received ADDTS response with this * saved ADDTS request when ADDTS response is being handled */ u8 dialog_token; diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c index 954de67c2aa38..1b3409c1fb713 100644 --- a/wpa_supplicant/wnm_sta.c +++ b/wpa_supplicant/wnm_sta.c @@ -24,6 +24,7 @@ #define MAX_TFS_IE_LEN 1024 #define WNM_MAX_NEIGHBOR_REPORT 10 +#define WNM_SCAN_RESULT_AGE 2 /* 2 seconds */ /* get the TFS IE from driver */ static int ieee80211_11_get_tfs_ie(struct wpa_supplicant *wpa_s, u8 *buf, @@ -37,12 +38,14 @@ static int ieee80211_11_get_tfs_ie(struct wpa_supplicant *wpa_s, u8 *buf, /* 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, + const u8 *addr, const u8 *buf, u16 buf_len, enum wnm_oper oper) { + u16 len = buf_len; + wpa_printf(MSG_DEBUG, "%s: TFS set operation %d", __func__, oper); - return wpa_drv_wnm_oper(wpa_s, oper, addr, buf, buf_len); + return wpa_drv_wnm_oper(wpa_s, oper, addr, (u8 *) buf, &len); } @@ -137,6 +140,8 @@ int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s, if (res < 0) wpa_printf(MSG_DEBUG, "Failed to send WNM-Sleep Request " "(action=%d, intval=%d)", action, intval); + else + wpa_s->wnmsleep_used = 1; os_free(wnmsleep_ie); os_free(wnmtfs_ie); @@ -147,8 +152,8 @@ int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s, static void wnm_sleep_mode_enter_success(struct wpa_supplicant *wpa_s, - u8 *tfsresp_ie_start, - u8 *tfsresp_ie_end) + const u8 *tfsresp_ie_start, + const u8 *tfsresp_ie_end) { wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_CONFIRM, wpa_s->bssid, NULL, NULL); @@ -164,7 +169,7 @@ static void wnm_sleep_mode_enter_success(struct wpa_supplicant *wpa_s, /* 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, + tfsresp_ie_len, WNM_SLEEP_TFS_RESP_IE_SET)) wpa_printf(MSG_DEBUG, "WNM: Fail to set TFS Resp IE"); } @@ -187,8 +192,14 @@ static void wnm_sleep_mode_exit_success(struct wpa_supplicant *wpa_s, 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) { + if (key_len_total && !wpa_sm_pmf_enabled(wpa_s->wpa)) { + wpa_msg(wpa_s, MSG_INFO, + "WNM: Ignore Key Data in WNM-Sleep Mode Response - PMF not enabled"); + return; + } + + while (end - ptr > 1) { + if (2 + ptr[1] > end - ptr) { wpa_printf(MSG_DEBUG, "WNM: Invalid Key Data element " "length"); if (end > ptr) { @@ -239,14 +250,20 @@ static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s, * Action [1] | Dialog Token [1] | Key Data Len [2] | Key Data | * WNM-Sleep Mode IE | TFS Response IE */ - u8 *pos = (u8 *) frm; /* point to payload after the action field */ + const u8 *pos = frm; /* point to payload after the action field */ u16 key_len_total; struct wnm_sleep_element *wnmsleep_ie = NULL; /* multiple TFS Resp IE (assuming consecutive) */ - u8 *tfsresp_ie_start = NULL; - u8 *tfsresp_ie_end = NULL; + const u8 *tfsresp_ie_start = NULL; + const u8 *tfsresp_ie_end = NULL; size_t left; + if (!wpa_s->wnmsleep_used) { + wpa_printf(MSG_DEBUG, + "WNM: Ignore WNM-Sleep Mode Response frame since WNM-Sleep Mode has not been used in this association"); + return; + } + if (len < 3) return; key_len_total = WPA_GET_LE16(frm + 1); @@ -259,14 +276,14 @@ static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s, return; } pos += 3 + key_len_total; - while (pos - frm < len) { + while (pos - frm + 1 < len) { u8 ie_len = *(pos + 1); - if (pos + 2 + ie_len > frm + len) { + if (2 + ie_len > frm + len - pos) { 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) + if (*pos == WLAN_EID_WNMSLEEP && ie_len >= 4) wnmsleep_ie = (struct wnm_sleep_element *) pos; else if (*pos == WLAN_EID_TFS_RESP) { if (!tfsresp_ie_start) @@ -413,6 +430,7 @@ static int wnm_nei_get_chan(struct wpa_supplicant *wpa_s, u8 op_class, u8 chan) { struct wpa_bss *bss = wpa_s->current_bss; const char *country = NULL; + int freq; if (bss) { const u8 *elem = wpa_bss_get_ie(bss, WLAN_EID_COUNTRY); @@ -421,7 +439,21 @@ static int wnm_nei_get_chan(struct wpa_supplicant *wpa_s, u8 op_class, u8 chan) country = (const char *) (elem + 2); } - return ieee80211_chan_to_freq(country, op_class, chan); + freq = ieee80211_chan_to_freq(country, op_class, chan); + if (freq <= 0 && op_class == 0) { + /* + * Some APs do not advertise correct operating class + * information. Try to determine the most likely operating + * frequency based on the channel number. + */ + if (chan >= 1 && chan <= 13) + freq = 2407 + chan * 5; + else if (chan == 14) + freq = 2484; + else if (chan >= 36 && chan <= 169) + freq = 5000 + chan * 5; + } + return freq; } @@ -468,7 +500,7 @@ static void wnm_parse_neighbor_report(struct wpa_supplicant *wpa_s, static struct wpa_bss * -compare_scan_neighbor_results(struct wpa_supplicant *wpa_s) +compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, os_time_t age_secs) { u8 i; @@ -476,7 +508,7 @@ compare_scan_neighbor_results(struct wpa_supplicant *wpa_s) struct wpa_bss *target; if (!bss) - return 0; + return NULL; wpa_printf(MSG_DEBUG, "WNM: Current BSS " MACSTR " RSSI %d", MAC2STR(wpa_s->bssid), bss->level); @@ -501,6 +533,19 @@ compare_scan_neighbor_results(struct wpa_supplicant *wpa_s) continue; } + if (age_secs) { + struct os_reltime now; + + if (os_get_reltime(&now) == 0 && + os_reltime_expired(&now, &target->last_update, + age_secs)) { + wpa_printf(MSG_DEBUG, + "Candidate BSS is more than %ld seconds old", + age_secs); + continue; + } + } + if (bss->ssid_len != target->ssid_len || os_memcmp(bss->ssid, target->ssid, bss->ssid_len) != 0) { /* @@ -515,6 +560,25 @@ compare_scan_neighbor_results(struct wpa_supplicant *wpa_s) continue; } + if (wpa_s->current_ssid && + !wpa_scan_res_match(wpa_s, 0, target, wpa_s->current_ssid, + 1)) { + wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR + " (pref %d) does not match the current network profile", + MAC2STR(nei->bssid), + nei->preference_present ? nei->preference : + -1); + continue; + } + + if (wpa_is_bss_tmp_disallowed(wpa_s, target->bssid)) { + wpa_printf(MSG_DEBUG, + "MBO: Candidate BSS " MACSTR + " retry delay is not over yet", + MAC2STR(nei->bssid)); + continue; + } + if (target->level < bss->level && target->level < -80) { wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR " (pref %d) does not have sufficient signal level (%d)", @@ -536,12 +600,190 @@ compare_scan_neighbor_results(struct wpa_supplicant *wpa_s) } +static int wpa_bss_ies_eq(struct wpa_bss *a, struct wpa_bss *b, u8 eid) +{ + const u8 *ie_a, *ie_b; + + if (!a || !b) + return 0; + + ie_a = wpa_bss_get_ie(a, eid); + ie_b = wpa_bss_get_ie(b, eid); + + if (!ie_a || !ie_b || ie_a[1] != ie_b[1]) + return 0; + + return os_memcmp(ie_a, ie_b, ie_a[1]) == 0; +} + + +static u32 wnm_get_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) +{ + u32 info = 0; + + info |= NEI_REP_BSSID_INFO_AP_UNKNOWN_REACH; + + /* + * Leave the security and key scope bits unset to indicate that the + * security information is not available. + */ + + if (bss->caps & WLAN_CAPABILITY_SPECTRUM_MGMT) + info |= NEI_REP_BSSID_INFO_SPECTRUM_MGMT; + if (bss->caps & WLAN_CAPABILITY_QOS) + info |= NEI_REP_BSSID_INFO_QOS; + if (bss->caps & WLAN_CAPABILITY_APSD) + info |= NEI_REP_BSSID_INFO_APSD; + if (bss->caps & WLAN_CAPABILITY_RADIO_MEASUREMENT) + info |= NEI_REP_BSSID_INFO_RM; + if (bss->caps & WLAN_CAPABILITY_DELAYED_BLOCK_ACK) + info |= NEI_REP_BSSID_INFO_DELAYED_BA; + if (bss->caps & WLAN_CAPABILITY_IMM_BLOCK_ACK) + info |= NEI_REP_BSSID_INFO_IMM_BA; + if (wpa_bss_ies_eq(bss, wpa_s->current_bss, WLAN_EID_MOBILITY_DOMAIN)) + info |= NEI_REP_BSSID_INFO_MOBILITY_DOMAIN; + if (wpa_bss_ies_eq(bss, wpa_s->current_bss, WLAN_EID_HT_CAP)) + info |= NEI_REP_BSSID_INFO_HT; + + return info; +} + + +static int wnm_add_nei_rep(u8 *buf, size_t len, const u8 *bssid, u32 bss_info, + u8 op_class, u8 chan, u8 phy_type, u8 pref) +{ + u8 *pos = buf; + + if (len < 18) { + wpa_printf(MSG_DEBUG, + "WNM: Not enough room for Neighbor Report element"); + return -1; + } + + *pos++ = WLAN_EID_NEIGHBOR_REPORT; + /* length: 13 for basic neighbor report + 3 for preference subelement */ + *pos++ = 16; + os_memcpy(pos, bssid, ETH_ALEN); + pos += ETH_ALEN; + WPA_PUT_LE32(pos, bss_info); + pos += 4; + *pos++ = op_class; + *pos++ = chan; + *pos++ = phy_type; + *pos++ = WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE; + *pos++ = 1; + *pos++ = pref; + return pos - buf; +} + + +static int wnm_nei_rep_add_bss(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss, u8 *buf, size_t len, + u8 pref) +{ + const u8 *ie; + u8 op_class, chan; + int sec_chan = 0, vht = 0; + enum phy_type phy_type; + u32 info; + struct ieee80211_ht_operation *ht_oper = NULL; + struct ieee80211_vht_operation *vht_oper = NULL; + + ie = wpa_bss_get_ie(bss, WLAN_EID_HT_OPERATION); + if (ie && ie[1] >= 2) { + ht_oper = (struct ieee80211_ht_operation *) (ie + 2); + + if (ht_oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE) + sec_chan = 1; + else if (ht_oper->ht_param & + HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW) + sec_chan = -1; + } + + ie = wpa_bss_get_ie(bss, WLAN_EID_VHT_OPERATION); + if (ie && ie[1] >= 1) { + vht_oper = (struct ieee80211_vht_operation *) (ie + 2); + + if (vht_oper->vht_op_info_chwidth == VHT_CHANWIDTH_80MHZ || + vht_oper->vht_op_info_chwidth == VHT_CHANWIDTH_160MHZ || + vht_oper->vht_op_info_chwidth == VHT_CHANWIDTH_80P80MHZ) + vht = vht_oper->vht_op_info_chwidth; + } + + if (ieee80211_freq_to_channel_ext(bss->freq, sec_chan, vht, &op_class, + &chan) == NUM_HOSTAPD_MODES) { + wpa_printf(MSG_DEBUG, + "WNM: Cannot determine operating class and channel"); + return -2; + } + + phy_type = ieee80211_get_phy_type(bss->freq, (ht_oper != NULL), + (vht_oper != NULL)); + if (phy_type == PHY_TYPE_UNSPECIFIED) { + wpa_printf(MSG_DEBUG, + "WNM: Cannot determine BSS phy type for Neighbor Report"); + return -2; + } + + info = wnm_get_bss_info(wpa_s, bss); + + return wnm_add_nei_rep(buf, len, bss->bssid, info, op_class, chan, + phy_type, pref); +} + + +static int wnm_add_cand_list(struct wpa_supplicant *wpa_s, u8 *buf, size_t len) +{ + u8 *pos = buf; + unsigned int i, pref = 255; + struct os_reltime now; + struct wpa_ssid *ssid = wpa_s->current_ssid; + + if (!ssid) + return 0; + + /* + * TODO: Define when scan results are no longer valid for the candidate + * list. + */ + os_get_reltime(&now); + if (os_reltime_expired(&now, &wpa_s->last_scan, 10)) + return 0; + + wpa_printf(MSG_DEBUG, + "WNM: Add candidate list to BSS Transition Management Response frame"); + for (i = 0; i < wpa_s->last_scan_res_used && pref; i++) { + struct wpa_bss *bss = wpa_s->last_scan_res[i]; + int res; + + if (wpa_scan_res_match(wpa_s, i, bss, ssid, 1)) { + res = wnm_nei_rep_add_bss(wpa_s, bss, pos, len, pref--); + if (res == -2) + continue; /* could not build entry for BSS */ + if (res < 0) + break; /* no more room for candidates */ + if (pref == 1) + break; + + pos += res; + len -= res; + } + } + + wpa_hexdump(MSG_DEBUG, + "WNM: BSS Transition Management Response candidate list", + buf, pos - buf); + + return pos - buf; +} + + static void wnm_send_bss_transition_mgmt_resp( struct wpa_supplicant *wpa_s, u8 dialog_token, enum bss_trans_mgmt_status_code status, u8 delay, const u8 *target_bssid) { - u8 buf[1000], *pos; + u8 buf[2000], *pos; struct ieee80211_mgmt *mgmt; size_t len; int res; @@ -581,6 +823,17 @@ static void wnm_send_bss_transition_mgmt_resp( pos += ETH_ALEN; } + if (status == WNM_BSS_TM_ACCEPT) + pos += wnm_add_cand_list(wpa_s, pos, buf + sizeof(buf) - pos); + +#ifdef CONFIG_MBO + if (status != WNM_BSS_TM_ACCEPT) { + pos += wpas_mbo_ie_bss_trans_reject( + wpa_s, pos, buf + sizeof(buf) - pos, + MBO_TRANSITION_REJECT_REASON_UNSPECIFIED); + } +#endif /* CONFIG_MBO */ + len = pos - (u8 *) &mgmt->u.action.category; res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, @@ -593,6 +846,41 @@ static void wnm_send_bss_transition_mgmt_resp( } +static void wnm_bss_tm_connect(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss, struct wpa_ssid *ssid, + int after_new_scan) +{ + wpa_dbg(wpa_s, MSG_DEBUG, + "WNM: Transition to BSS " MACSTR + " based on BSS Transition Management Request (old BSSID " + MACSTR " after_new_scan=%d)", + MAC2STR(bss->bssid), MAC2STR(wpa_s->bssid), after_new_scan); + + /* Send the BSS Management Response - Accept */ + if (wpa_s->wnm_reply) { + wpa_s->wnm_reply = 0; + wpa_printf(MSG_DEBUG, + "WNM: Sending successful BSS Transition Management Response"); + wnm_send_bss_transition_mgmt_resp(wpa_s, + wpa_s->wnm_dialog_token, + WNM_BSS_TM_ACCEPT, + 0, bss->bssid); + } + + if (bss == wpa_s->current_bss) { + wpa_printf(MSG_DEBUG, + "WNM: Already associated with the preferred candidate"); + wnm_deallocate_memory(wpa_s); + return; + } + + wpa_s->reassociate = 1; + wpa_printf(MSG_DEBUG, "WNM: Issuing connect"); + wpa_supplicant_connect(wpa_s, bss, ssid); + wnm_deallocate_memory(wpa_s); +} + + int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail) { struct wpa_bss *bss; @@ -602,6 +890,8 @@ int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail) if (!wpa_s->wnm_neighbor_report_elements) return 0; + wpa_dbg(wpa_s, MSG_DEBUG, + "WNM: Process scan results for BSS Transition Management"); if (os_reltime_before(&wpa_s->wnm_cand_valid_until, &wpa_s->scan_trigger_time)) { wpa_printf(MSG_DEBUG, "WNM: Previously stored BSS transition candidate list is not valid anymore - drop it"); @@ -617,7 +907,7 @@ int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail) } /* Compare the Neighbor Report and scan results */ - bss = compare_scan_neighbor_results(wpa_s); + bss = compare_scan_neighbor_results(wpa_s, 0); if (!bss) { wpa_printf(MSG_DEBUG, "WNM: No BSS transition candidate match found"); status = WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES; @@ -625,24 +915,7 @@ int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail) } /* Associate to the network */ - /* Send the BSS Management Response - Accept */ - if (wpa_s->wnm_reply) { - wpa_s->wnm_reply = 0; - wnm_send_bss_transition_mgmt_resp(wpa_s, - wpa_s->wnm_dialog_token, - WNM_BSS_TM_ACCEPT, - 0, bss->bssid); - } - - if (bss == wpa_s->current_bss) { - wpa_printf(MSG_DEBUG, - "WNM: Already associated with the preferred candidate"); - return 1; - } - - wpa_s->reassociate = 1; - wpa_supplicant_connect(wpa_s, bss, ssid); - wnm_deallocate_memory(wpa_s); + wnm_bss_tm_connect(wpa_s, bss, ssid, 1); return 1; send_bss_resp_fail: @@ -783,14 +1056,90 @@ static void wnm_set_scan_freqs(struct wpa_supplicant *wpa_s) } +static int wnm_fetch_scan_results(struct wpa_supplicant *wpa_s) +{ + struct wpa_scan_results *scan_res; + struct wpa_bss *bss; + struct wpa_ssid *ssid = wpa_s->current_ssid; + u8 i, found = 0; + size_t j; + + wpa_dbg(wpa_s, MSG_DEBUG, + "WNM: Fetch current scan results from the driver for checking transition candidates"); + scan_res = wpa_drv_get_scan_results2(wpa_s); + if (!scan_res) { + wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Failed to get scan results"); + return 0; + } + + if (scan_res->fetch_time.sec == 0) + os_get_reltime(&scan_res->fetch_time); + + filter_scan_res(wpa_s, scan_res); + + for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { + struct neighbor_report *nei; + + nei = &wpa_s->wnm_neighbor_report_elements[i]; + if (nei->preference_present && nei->preference == 0) + continue; + + for (j = 0; j < scan_res->num; j++) { + struct wpa_scan_res *res; + const u8 *ssid_ie; + + res = scan_res->res[j]; + if (os_memcmp(nei->bssid, res->bssid, ETH_ALEN) != 0 || + res->age > WNM_SCAN_RESULT_AGE * 1000) + continue; + bss = wpa_s->current_bss; + ssid_ie = wpa_scan_get_ie(res, WLAN_EID_SSID); + if (bss && ssid_ie && + (bss->ssid_len != ssid_ie[1] || + os_memcmp(bss->ssid, ssid_ie + 2, + bss->ssid_len) != 0)) + continue; + + /* Potential candidate found */ + found = 1; + scan_snr(res); + scan_est_throughput(wpa_s, res); + wpa_bss_update_scan_res(wpa_s, res, + &scan_res->fetch_time); + } + } + + wpa_scan_results_free(scan_res); + if (!found) { + wpa_dbg(wpa_s, MSG_DEBUG, + "WNM: No transition candidate matches existing scan results"); + return 0; + } + + bss = compare_scan_neighbor_results(wpa_s, WNM_SCAN_RESULT_AGE); + if (!bss) { + wpa_dbg(wpa_s, MSG_DEBUG, + "WNM: Comparison of scan results against transition candidates did not find matches"); + return 0; + } + + /* Associate to the network */ + wnm_bss_tm_connect(wpa_s, bss, ssid, 0); + return 1; +} + + static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, const u8 *pos, const u8 *end, int reply) { unsigned int beacon_int; u8 valid_int; +#ifdef CONFIG_MBO + const u8 *vendor; +#endif /* CONFIG_MBO */ - if (pos + 5 > end) + if (end - pos < 5) return; if (wpa_s->current_bss) @@ -810,10 +1159,23 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, wpa_s->wnm_dialog_token, wpa_s->wnm_mode, wpa_s->wnm_dissoc_timer, valid_int); +#if defined(CONFIG_MBO) && defined(CONFIG_TESTING_OPTIONS) + if (wpa_s->reject_btm_req_reason) { + wpa_printf(MSG_INFO, + "WNM: Testing - reject BSS Transition Management Request: reject_btm_req_reason=%d", + wpa_s->reject_btm_req_reason); + wnm_send_bss_transition_mgmt_resp(wpa_s, + wpa_s->wnm_dialog_token, + wpa_s->reject_btm_req_reason, + 0, NULL); + return; + } +#endif /* CONFIG_MBO && CONFIG_TESTING_OPTIONS */ + pos += 5; if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) { - if (pos + 12 > end) { + if (end - pos < 12) { wpa_printf(MSG_DEBUG, "WNM: Too short BSS TM Request"); return; } @@ -824,7 +1186,7 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) { char url[256]; - if (pos + 1 > end || pos + 1 + pos[0] > end) { + if (end - pos < 1 || 1 + pos[0] > end - pos) { wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition " "Management Request (URL)"); return; @@ -849,6 +1211,12 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, } } +#ifdef CONFIG_MBO + vendor = get_ie(pos, end - pos, WLAN_EID_VENDOR_SPECIFIC); + if (vendor) + wpas_mbo_ie_trans_req(wpa_s, vendor + 2, vendor[1]); +#endif /* CONFIG_MBO */ + if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED) { unsigned int valid_ms; @@ -860,7 +1228,7 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, if (wpa_s->wnm_neighbor_report_elements == NULL) return; - while (pos + 2 <= end && + while (end - pos >= 2 && wpa_s->wnm_num_neighbor_report < WNM_MAX_NEIGHBOR_REPORT) { u8 tag = *pos++; @@ -868,7 +1236,7 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, wpa_printf(MSG_DEBUG, "WNM: Neighbor report tag %u", tag); - if (pos + len > end) { + if (len > end - pos) { wpa_printf(MSG_DEBUG, "WNM: Truncated request"); return; } @@ -877,11 +1245,22 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, rep = &wpa_s->wnm_neighbor_report_elements[ wpa_s->wnm_num_neighbor_report]; wnm_parse_neighbor_report(wpa_s, pos, len, rep); + wpa_s->wnm_num_neighbor_report++; } pos += len; - wpa_s->wnm_num_neighbor_report++; } + + if (!wpa_s->wnm_num_neighbor_report) { + wpa_printf(MSG_DEBUG, + "WNM: Candidate list included bit is set, but no candidates found"); + wnm_send_bss_transition_mgmt_resp( + wpa_s, wpa_s->wnm_dialog_token, + WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES, + 0, NULL); + return; + } + wnm_sort_cand_list(wpa_s); wnm_dump_cand_list(wpa_s); valid_ms = valid_int * beacon_int * 128 / 125; @@ -895,6 +1274,20 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, wpa_s->wnm_cand_valid_until.usec %= 1000000; os_memcpy(wpa_s->wnm_cand_from_bss, wpa_s->bssid, ETH_ALEN); + /* + * Fetch the latest scan results from the kernel and check for + * candidates based on those results first. This can help in + * finding more up-to-date information should the driver has + * done some internal scanning operations after the last scan + * result update in wpa_supplicant. + */ + if (wnm_fetch_scan_results(wpa_s) > 0) + return; + + /* + * Try to use previously received scan results, if they are + * recent enough to use for a connection. + */ if (wpa_s->last_scan_res_used > 0) { struct os_reltime now; @@ -910,6 +1303,14 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, } wnm_set_scan_freqs(wpa_s); + if (wpa_s->wnm_num_neighbor_report == 1) { + os_memcpy(wpa_s->next_scan_bssid, + wpa_s->wnm_neighbor_report_elements[0].bssid, + ETH_ALEN); + wpa_printf(MSG_DEBUG, + "WNM: Scan only for a specific BSSID since there is only a single candidate " + MACSTR, MAC2STR(wpa_s->next_scan_bssid)); + } wpa_supplicant_req_scan(wpa_s, 0, 0); } else if (reply) { enum bss_trans_mgmt_status_code status; @@ -927,16 +1328,17 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s, - u8 query_reason) + u8 query_reason, int cand_list) { - u8 buf[1000], *pos; + u8 buf[2000], *pos; struct ieee80211_mgmt *mgmt; size_t len; int ret; wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Query to " - MACSTR " query_reason=%u", - MAC2STR(wpa_s->bssid), query_reason); + MACSTR " query_reason=%u%s", + MAC2STR(wpa_s->bssid), query_reason, + cand_list ? " candidate list" : ""); mgmt = (struct ieee80211_mgmt *) buf; os_memset(&buf, 0, sizeof(buf)); @@ -951,6 +1353,9 @@ int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s, mgmt->u.action.u.bss_tm_query.query_reason = query_reason; pos = mgmt->u.action.u.bss_tm_query.variable; + if (cand_list) + pos += wnm_add_cand_list(wpa_s, pos, buf + sizeof(buf) - pos); + len = pos - (u8 *) &mgmt->u.action.category; ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, @@ -971,7 +1376,7 @@ static void ieee802_11_rx_wnm_notif_req_wfa(struct wpa_supplicant *wpa_s, pos = data; end = data + len; - while (pos + 1 < end) { + while (end - pos > 1) { ie = *pos++; ie_len = *pos++; wpa_printf(MSG_DEBUG, "WNM: WFA subelement %u len %u", @@ -1009,7 +1414,7 @@ static void ieee802_11_rx_wnm_notif_req_wfa(struct wpa_supplicant *wpa_s, url = NULL; osu_method = 1; } else { - if (pos + url_len + 1 > ie_end) { + if (url_len + 1 > ie_end - pos) { wpa_printf(MSG_DEBUG, "WNM: Not enough room for Server URL (len=%u) and Server Method (left %d)", url_len, (int) (ie_end - pos)); @@ -1048,7 +1453,7 @@ static void ieee802_11_rx_wnm_notif_req_wfa(struct wpa_supplicant *wpa_s, "Imminent - Reason Code %u " "Re-Auth Delay %u URL Length %u", code, reauth_delay, url_len); - if (pos + url_len > ie_end) + if (url_len > ie_end - pos) break; url = os_malloc(url_len + 1); if (url == NULL) diff --git a/wpa_supplicant/wnm_sta.h b/wpa_supplicant/wnm_sta.h index 8de434807f190..81d815359634e 100644 --- a/wpa_supplicant/wnm_sta.h +++ b/wpa_supplicant/wnm_sta.h @@ -56,7 +56,7 @@ void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s, const struct ieee80211_mgmt *mgmt, size_t len); int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s, - u8 query_reason); + u8 query_reason, int cand_list); void wnm_deallocate_memory(struct wpa_supplicant *wpa_s); diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c index 7ddae3d3b6b88..a848b7737db51 100644 --- a/wpa_supplicant/wpa_cli.c +++ b/wpa_supplicant/wpa_cli.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - command line interface for wpa_supplicant daemon - * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -14,6 +14,7 @@ #include <dirent.h> #endif /* CONFIG_CTRL_IFACE_UNIX */ +#include "common/cli.h" #include "common/wpa_ctrl.h" #include "utils/common.h" #include "utils/eloop.h" @@ -28,43 +29,13 @@ static const char *const wpa_cli_version = "wpa_cli v" VERSION_STR "\n" -"Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> and contributors"; - - -static const char *const wpa_cli_license = -"This software may be distributed under the terms of the BSD license.\n" -"See README for more details.\n"; - -static const char *const wpa_cli_full_license = -"This software may be distributed under the terms of the BSD license.\n" -"\n" -"Redistribution and use in source and binary forms, with or without\n" -"modification, are permitted provided that the following conditions are\n" -"met:\n" -"\n" -"1. Redistributions of source code must retain the above copyright\n" -" notice, this list of conditions and the following disclaimer.\n" -"\n" -"2. Redistributions in binary form must reproduce the above copyright\n" -" notice, this list of conditions and the following disclaimer in the\n" -" documentation and/or other materials provided with the distribution.\n" -"\n" -"3. Neither the name(s) of the above-listed copyright holder(s) nor the\n" -" names of its contributors may be used to endorse or promote products\n" -" derived from this software without specific prior written permission.\n" -"\n" -"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n" -"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n" -"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n" -"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n" -"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n" -"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n" -"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n" -"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n" -"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n" -"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n" -"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" -"\n"; +"Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi> and contributors"; + +#define VENDOR_ELEM_FRAME_ID \ + " 0: Probe Req (P2P), 1: Probe Resp (P2P) , 2: Probe Resp (GO), " \ + "3: Beacon (GO), 4: PD Req, 5: PD Resp, 6: GO Neg Req, " \ + "7: GO Neg Resp, 8: GO Neg Conf, 9: Inv Req, 10: Inv Resp, " \ + "11: Assoc Req (P2P), 12: Assoc Resp (P2P)" static struct wpa_ctrl *ctrl_conn; static struct wpa_ctrl *mon_conn; @@ -84,11 +55,6 @@ static int ping_interval = 5; static int interactive = 0; static char *ifname_prefix = NULL; -struct cli_txt_entry { - struct dl_list list; - char *txt; -}; - static DEFINE_DL_LIST(bsses); /* struct cli_txt_entry */ static DEFINE_DL_LIST(p2p_peers); /* struct cli_txt_entry */ static DEFINE_DL_LIST(p2p_groups); /* struct cli_txt_entry */ @@ -124,168 +90,6 @@ static void usage(void) } -static void cli_txt_list_free(struct cli_txt_entry *e) -{ - dl_list_del(&e->list); - os_free(e->txt); - os_free(e); -} - - -static void cli_txt_list_flush(struct dl_list *list) -{ - struct cli_txt_entry *e; - while ((e = dl_list_first(list, struct cli_txt_entry, list))) - cli_txt_list_free(e); -} - - -static struct cli_txt_entry * cli_txt_list_get(struct dl_list *txt_list, - const char *txt) -{ - struct cli_txt_entry *e; - dl_list_for_each(e, txt_list, struct cli_txt_entry, list) { - if (os_strcmp(e->txt, txt) == 0) - return e; - } - return NULL; -} - - -static void cli_txt_list_del(struct dl_list *txt_list, const char *txt) -{ - struct cli_txt_entry *e; - e = cli_txt_list_get(txt_list, txt); - if (e) - cli_txt_list_free(e); -} - - -static void cli_txt_list_del_addr(struct dl_list *txt_list, const char *txt) -{ - u8 addr[ETH_ALEN]; - char buf[18]; - if (hwaddr_aton(txt, addr) < 0) - return; - os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr)); - cli_txt_list_del(txt_list, buf); -} - - -#ifdef CONFIG_P2P -static void cli_txt_list_del_word(struct dl_list *txt_list, const char *txt, - int separator) -{ - const char *end; - char *buf; - end = os_strchr(txt, separator); - if (end == NULL) - end = txt + os_strlen(txt); - buf = dup_binstr(txt, end - txt); - if (buf == NULL) - return; - cli_txt_list_del(txt_list, buf); - os_free(buf); -} -#endif /* CONFIG_P2P */ - - -static int cli_txt_list_add(struct dl_list *txt_list, const char *txt) -{ - struct cli_txt_entry *e; - e = cli_txt_list_get(txt_list, txt); - if (e) - return 0; - e = os_zalloc(sizeof(*e)); - if (e == NULL) - return -1; - e->txt = os_strdup(txt); - if (e->txt == NULL) { - os_free(e); - return -1; - } - dl_list_add(txt_list, &e->list); - return 0; -} - - -#ifdef CONFIG_P2P -static int cli_txt_list_add_addr(struct dl_list *txt_list, const char *txt) -{ - u8 addr[ETH_ALEN]; - char buf[18]; - if (hwaddr_aton(txt, addr) < 0) - return -1; - os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr)); - return cli_txt_list_add(txt_list, buf); -} -#endif /* CONFIG_P2P */ - - -static int cli_txt_list_add_word(struct dl_list *txt_list, const char *txt, - int separator) -{ - const char *end; - char *buf; - int ret; - end = os_strchr(txt, separator); - if (end == NULL) - end = txt + os_strlen(txt); - buf = dup_binstr(txt, end - txt); - if (buf == NULL) - return -1; - ret = cli_txt_list_add(txt_list, buf); - os_free(buf); - return ret; -} - - -static char ** cli_txt_list_array(struct dl_list *txt_list) -{ - unsigned int i, count = dl_list_len(txt_list); - char **res; - struct cli_txt_entry *e; - - res = os_calloc(count + 1, sizeof(char *)); - if (res == NULL) - return NULL; - - i = 0; - dl_list_for_each(e, txt_list, struct cli_txt_entry, list) { - res[i] = os_strdup(e->txt); - if (res[i] == NULL) - break; - i++; - } - - return res; -} - - -static int get_cmd_arg_num(const char *str, int pos) -{ - int arg = 0, i; - - for (i = 0; i <= pos; i++) { - if (str[i] != ' ') { - arg++; - while (i <= pos && str[i] != ' ') - i++; - } - } - - if (arg > 0) - arg--; - return arg; -} - - -static int str_starts(const char *src, const char *match) -{ - return os_strncmp(src, match, os_strlen(match)) == 0; -} - - static int wpa_cli_show_event(const char *event) { const char *start; @@ -452,36 +256,6 @@ 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 (os_snprintf_error(end - pos, res)) - goto fail; - pos += res; - - for (i = 0; i < argc; i++) { - res = os_snprintf(pos, end - pos, " %s", argv[i]); - if (os_snprintf_error(end - pos, res)) - goto fail; - pos += res; - } - - buf[buflen - 1] = '\0'; - return 0; - -fail: - printf("Too long command\n"); - return -1; -} - - static int wpa_cli_cmd(struct wpa_ctrl *ctrl, const char *cmd, int min_args, int argc, char *argv[]) { @@ -581,7 +355,7 @@ static char ** wpa_cli_complete_help(const char *str, int pos) 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); + printf("%s\n\n%s\n", wpa_cli_version, cli_full_license); return 0; } @@ -677,7 +451,10 @@ static char ** wpa_cli_complete_set(const char *str, int pos) "tdls_external_control", "osu_dir", "wowlan_triggers", "p2p_search_delay", "mac_addr", "rand_addr_lifetime", "preassoc_mac_addr", "key_mgmt_offload", "passive_scan", - "reassoc_same_bss_optim", "wps_priority" + "reassoc_same_bss_optim", "wps_priority", +#ifdef CONFIG_TESTING_OPTIONS + "ignore_auth_resp", +#endif /* CONFIG_TESTING_OPTIONS */ }; int i, num_fields = ARRAY_SIZE(fields); @@ -705,6 +482,13 @@ static int wpa_cli_cmd_dump(struct wpa_ctrl *ctrl, int argc, char *argv[]) } +static int wpa_cli_cmd_driver_flags(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "DRIVER_FLAGS"); +} + + static int wpa_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[]) { return wpa_cli_cmd(ctrl, "GET", 1, argc, argv); @@ -1548,7 +1332,7 @@ static const char *network_fields[] = { "ssid", "scan_ssid", "bssid", "bssid_blacklist", "bssid_whitelist", "psk", "proto", "key_mgmt", "bg_scan_period", "pairwise", "group", "auth_alg", "scan_freq", - "freq_list", + "freq_list", "max_oper_chwidth", #ifdef IEEE8021X_EAPOL "eap", "identity", "anonymous_identity", "password", "ca_cert", "ca_path", "client_cert", "private_key", "private_key_passwd", @@ -1606,7 +1390,7 @@ static const char *network_fields[] = { #ifdef CONFIG_HS20 "update_identifier", #endif /* CONFIG_HS20 */ - "mac_addr" + "mac_addr", "pbss", "wps_disabled" }; @@ -1764,6 +1548,13 @@ static int wpa_cli_cmd_scan_results(struct wpa_ctrl *ctrl, int argc, } +static int wpa_cli_cmd_abort_scan(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "ABORT_SCAN"); +} + + static int wpa_cli_cmd_bss(struct wpa_ctrl *ctrl, int argc, char *argv[]) { return wpa_cli_cmd(ctrl, "BSS", 1, argc, argv); @@ -1804,6 +1595,48 @@ static int wpa_cli_cmd_get_capability(struct wpa_ctrl *ctrl, int argc, } +static char ** wpa_cli_complete_get_capability(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + const char *fields[] = { + "eap", "pairwise", "group", "group_mgmt", "key_mgmt", + "proto", "auth_alg", "modes", "channels", "freq", +#ifdef CONFIG_TDLS + "tdls", +#endif /* CONFIG_TDLS */ +#ifdef CONFIG_ERP + "erp", +#endif /* CONFIG_ERP */ +#ifdef CONFIG_FIPS + "fips", +#endif /* CONFIG_FIPS */ +#ifdef CONFIG_ACS + "acs", +#endif /* CONFIG_ACS */ + }; + int i, num_fields = ARRAY_SIZE(fields); + char **res = NULL; + + if (arg == 1) { + res = os_calloc(num_fields + 1, sizeof(char *)); + if (res == NULL) + return NULL; + for (i = 0; i < num_fields; i++) { + res[i] = os_strdup(fields[i]); + if (res[i] == NULL) + return res; + } + } + if (arg == 2) { + res = os_calloc(1 + 1, sizeof(char *)); + if (res == NULL) + return NULL; + res[0] = os_strdup("strict"); + } + return res; +} + + static int wpa_cli_list_interfaces(struct wpa_ctrl *ctrl) { printf("Available interfaces:\n"); @@ -1866,14 +1699,15 @@ static int wpa_cli_cmd_interface_add(struct wpa_ctrl *ctrl, int argc, /* * INTERFACE_ADD <ifname>TAB<confname>TAB<driver>TAB<ctrl_interface>TAB - * <driver_param>TAB<bridge_name>[TAB<create>] + * <driver_param>TAB<bridge_name>[TAB<create>[TAB<type>]] */ res = os_snprintf(cmd, sizeof(cmd), - "INTERFACE_ADD %s\t%s\t%s\t%s\t%s\t%s\t%s", + "INTERFACE_ADD %s\t%s\t%s\t%s\t%s\t%s\t%s\t%s", argv[0], argc > 1 ? argv[1] : "", argc > 2 ? argv[2] : "", argc > 3 ? argv[3] : "", argc > 4 ? argv[4] : "", - argc > 5 ? argv[5] : "", argc > 6 ? argv[6] : ""); + argc > 5 ? argv[5] : "", argc > 6 ? argv[6] : "", + argc > 7 ? argv[7] : ""); if (os_snprintf_error(sizeof(cmd), res)) return -1; cmd[sizeof(cmd) - 1] = '\0'; @@ -1913,6 +1747,12 @@ static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd, printf("Not connected to hostapd - command dropped.\n"); return -1; } + if (ifname_prefix) { + os_snprintf(buf, sizeof(buf), "IFNAME=%s %s", + ifname_prefix, cmd); + buf[sizeof(buf) - 1] = '\0'; + cmd = buf; + } len = sizeof(buf) - 1; ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len, wpa_cli_msg_cb); @@ -2022,6 +1862,20 @@ static int wpa_cli_cmd_mesh_group_remove(struct wpa_ctrl *ctrl, int argc, return wpa_cli_cmd(ctrl, "MESH_GROUP_REMOVE", 1, argc, argv); } + +static int wpa_cli_cmd_mesh_peer_remove(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "MESH_PEER_REMOVE", 1, argc, argv); +} + + +static int wpa_cli_cmd_mesh_peer_add(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "MESH_PEER_ADD", 1, argc, argv); +} + #endif /* CONFIG_MESH */ @@ -2141,6 +1995,13 @@ static int wpa_cli_cmd_p2p_group_add(struct wpa_ctrl *ctrl, int argc, } +static int wpa_cli_cmd_p2p_group_member(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "P2P_GROUP_MEMBER", 1, argc, argv); +} + + static int wpa_cli_cmd_p2p_prov_disc(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -2477,6 +2338,27 @@ static int wpa_cli_cmd_p2p_remove_client(struct wpa_ctrl *ctrl, int argc, return wpa_cli_cmd(ctrl, "P2P_REMOVE_CLIENT", 1, argc, argv); } + +static int wpa_cli_cmd_vendor_elem_add(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "VENDOR_ELEM_ADD", 2, argc, argv); +} + + +static int wpa_cli_cmd_vendor_elem_get(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "VENDOR_ELEM_GET", 1, argc, argv); +} + + +static int wpa_cli_cmd_vendor_elem_remove(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "VENDOR_ELEM_REMOVE", 2, argc, argv); +} + #endif /* CONFIG_P2P */ #ifdef CONFIG_WIFI_DISPLAY @@ -2719,6 +2601,13 @@ static int wpa_cli_cmd_signal_poll(struct wpa_ctrl *ctrl, int argc, } +static int wpa_cli_cmd_signal_monitor(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "SIGNAL_MONITOR", 0, argc, argv); +} + + static int wpa_cli_cmd_pktcnt_poll(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -2823,6 +2712,20 @@ static int wpa_cli_cmd_get_pref_freq_list(struct wpa_ctrl *ctrl, int argc, } +static int wpa_cli_cmd_p2p_lo_start(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "P2P_LO_START", 4, argc, argv); +} + + +static int wpa_cli_cmd_p2p_lo_stop(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "P2P_LO_STOP", 0, argc, argv); +} + + enum wpa_cli_cmd_flags { cli_cmd_flag_none = 0x00, cli_cmd_flag_sensitive = 0x01 @@ -2880,6 +2783,9 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = { { "get", wpa_cli_cmd_get, wpa_cli_complete_get, cli_cmd_flag_none, "<name> = get information" }, + { "driver_flags", wpa_cli_cmd_driver_flags, NULL, + cli_cmd_flag_none, + "= list driver flags" }, { "logon", wpa_cli_cmd_logon, NULL, cli_cmd_flag_none, "= IEEE 802.1X EAPOL state machine logon" }, @@ -3001,11 +2907,14 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = { { "scan_results", wpa_cli_cmd_scan_results, NULL, cli_cmd_flag_none, "= get latest scan results" }, + { "abort_scan", wpa_cli_cmd_abort_scan, NULL, + cli_cmd_flag_none, + "= request ongoing scan to be aborted" }, { "bss", wpa_cli_cmd_bss, wpa_cli_complete_bss, cli_cmd_flag_none, "<<idx> | <bssid>> = get detailed scan result info" }, - { "get_capability", wpa_cli_cmd_get_capability, NULL, - cli_cmd_flag_none, + { "get_capability", wpa_cli_cmd_get_capability, + wpa_cli_complete_get_capability, cli_cmd_flag_none, "<eap/pairwise/group/key_mgmt/proto/auth_alg/channels/freq/modes> " "= get capabilities" }, { "reconfigure", wpa_cli_cmd_reconfigure, NULL, @@ -3017,8 +2926,10 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = { { "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" }, + " <bridge_name> <create> <type> = adds new interface, all " + "parameters but\n" + " <ifname> are optional. Supported types are station ('sta') and " + "AP ('ap')" }, { "interface_remove", wpa_cli_cmd_interface_remove, NULL, cli_cmd_flag_none, "<ifname> = removes the interface" }, @@ -3157,6 +3068,12 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = { { "mesh_group_remove", wpa_cli_cmd_mesh_group_remove, NULL, cli_cmd_flag_none, "<ifname> = Remove mesh group interface" }, + { "mesh_peer_remove", wpa_cli_cmd_mesh_peer_remove, NULL, + cli_cmd_flag_none, + "<addr> = Remove a mesh peer" }, + { "mesh_peer_add", wpa_cli_cmd_mesh_peer_add, NULL, + cli_cmd_flag_none, + "<addr> [duration=<seconds>] = Add a mesh peer" }, #endif /* CONFIG_MESH */ #ifdef CONFIG_P2P { "p2p_find", wpa_cli_cmd_p2p_find, wpa_cli_complete_p2p_find, @@ -3180,6 +3097,9 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = { "<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_group_member", wpa_cli_cmd_p2p_group_member, NULL, + cli_cmd_flag_none, + "<dev_addr> = Get peer interface address on local GO using peer Device Address" }, { "p2p_prov_disc", wpa_cli_cmd_p2p_prov_disc, wpa_cli_complete_p2p_peer, cli_cmd_flag_none, "<addr> <method> = request provisioning discovery" }, @@ -3248,6 +3168,18 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = { { "p2p_remove_client", wpa_cli_cmd_p2p_remove_client, wpa_cli_complete_p2p_peer, cli_cmd_flag_none, "<address|iface=address> = remove a peer from all groups" }, + { "vendor_elem_add", wpa_cli_cmd_vendor_elem_add, NULL, + cli_cmd_flag_none, + "<frame id> <hexdump of elem(s)> = add vendor specific IEs to frame(s)\n" + VENDOR_ELEM_FRAME_ID }, + { "vendor_elem_get", wpa_cli_cmd_vendor_elem_get, NULL, + cli_cmd_flag_none, + "<frame id> = get vendor specific IE(s) to frame(s)\n" + VENDOR_ELEM_FRAME_ID }, + { "vendor_elem_remove", wpa_cli_cmd_vendor_elem_remove, NULL, + cli_cmd_flag_none, + "<frame id> <hexdump of elem(s)> = remove vendor specific IE(s) in frame(s)\n" + VENDOR_ELEM_FRAME_ID }, #endif /* CONFIG_P2P */ #ifdef CONFIG_WIFI_DISPLAY { "wfd_subelem_set", wpa_cli_cmd_wfd_subelem_set, NULL, @@ -3336,6 +3268,9 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = { { "signal_poll", wpa_cli_cmd_signal_poll, NULL, cli_cmd_flag_none, "= get signal parameters" }, + { "signal_monitor", wpa_cli_cmd_signal_monitor, NULL, + cli_cmd_flag_none, + "= set signal monitor parameters" }, { "pktcnt_poll", wpa_cli_cmd_pktcnt_poll, NULL, cli_cmd_flag_none, "= get TX/RX packet counters" }, @@ -3350,7 +3285,7 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = { { "wnm_sleep", wpa_cli_cmd_wnm_sleep, NULL, cli_cmd_flag_none, "<enter/exit> [interval=#] = enter/exit WNM-Sleep mode" }, { "wnm_bss_query", wpa_cli_cmd_wnm_bss_query, NULL, cli_cmd_flag_none, - "<query reason> = Send BSS Transition Management Query" }, + "<query reason> [list] = Send BSS Transition Management Query" }, #endif /* CONFIG_WNM */ { "raw", wpa_cli_cmd_raw, NULL, cli_cmd_flag_sensitive, "<params..> = Sent unprocessed command" }, @@ -3367,8 +3302,7 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = { }, { "neighbor_rep_request", wpa_cli_cmd_neighbor_rep_request, NULL, cli_cmd_flag_none, - "[ssid=<SSID>] = Trigger request to AP for neighboring AP report " - "(with optional given SSID, default: current SSID)" + "[ssid=<SSID>] [lci] [civic] = Trigger request to AP for neighboring AP report (with optional given SSID in hex or enclosed in double quotes, default: current SSID; with optional LCI and location civic request)" }, { "erp_flush", wpa_cli_cmd_erp_flush, NULL, cli_cmd_flag_none, "= flush ERP keys" }, @@ -3380,6 +3314,12 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = { { "get_pref_freq_list", wpa_cli_cmd_get_pref_freq_list, NULL, cli_cmd_flag_none, "<interface type> = retrieve preferred freq list for the specified interface type" }, + { "p2p_lo_start", wpa_cli_cmd_p2p_lo_start, NULL, + cli_cmd_flag_none, + "<freq> <period> <interval> <count> = start P2P listen offload" }, + { "p2p_lo_stop", wpa_cli_cmd_p2p_lo_stop, NULL, + cli_cmd_flag_none, + "= stop P2P listen offload" }, { NULL, NULL, NULL, cli_cmd_flag_none, NULL } }; @@ -3578,12 +3518,6 @@ static int wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[]) } -static int str_match(const char *a, const char *b) -{ - return os_strncmp(a, b, os_strlen(b)) == 0; -} - - static int wpa_cli_exec(const char *program, const char *arg1, const char *arg2) { @@ -3591,6 +3525,10 @@ static int wpa_cli_exec(const char *program, const char *arg1, size_t len; int res; + /* If no interface is specified, set the global */ + if (!arg1) + arg1 = "global"; + len = os_strlen(arg1) + os_strlen(arg2) + 2; arg = os_malloc(len); if (arg == NULL) @@ -3635,7 +3573,7 @@ static void wpa_cli_action_process(const char *msg) pos = prev; } - if (str_match(pos, WPA_EVENT_CONNECTED)) { + if (str_starts(pos, WPA_EVENT_CONNECTED)) { int new_id = -1; os_unsetenv("WPA_ID"); os_unsetenv("WPA_ID_STR"); @@ -3671,44 +3609,48 @@ static void wpa_cli_action_process(const char *msg) wpa_cli_last_id = new_id; wpa_cli_exec(action_file, ifname, "CONNECTED"); } - } else if (str_match(pos, WPA_EVENT_DISCONNECTED)) { + } else if (str_starts(pos, WPA_EVENT_DISCONNECTED)) { if (wpa_cli_connected) { wpa_cli_connected = 0; wpa_cli_exec(action_file, ifname, "DISCONNECTED"); } - } else if (str_match(pos, MESH_GROUP_STARTED)) { + } else if (str_starts(pos, AP_EVENT_ENABLED)) { + wpa_cli_exec(action_file, ctrl_ifname, pos); + } else if (str_starts(pos, AP_EVENT_DISABLED)) { + wpa_cli_exec(action_file, ctrl_ifname, pos); + } else if (str_starts(pos, MESH_GROUP_STARTED)) { wpa_cli_exec(action_file, ctrl_ifname, pos); - } else if (str_match(pos, MESH_GROUP_REMOVED)) { + } else if (str_starts(pos, MESH_GROUP_REMOVED)) { wpa_cli_exec(action_file, ctrl_ifname, pos); - } else if (str_match(pos, MESH_PEER_CONNECTED)) { + } else if (str_starts(pos, MESH_PEER_CONNECTED)) { wpa_cli_exec(action_file, ctrl_ifname, pos); - } else if (str_match(pos, MESH_PEER_DISCONNECTED)) { + } else if (str_starts(pos, MESH_PEER_DISCONNECTED)) { wpa_cli_exec(action_file, ctrl_ifname, pos); - } else if (str_match(pos, P2P_EVENT_GROUP_STARTED)) { + } else if (str_starts(pos, P2P_EVENT_GROUP_STARTED)) { wpa_cli_exec(action_file, ifname, pos); - } else if (str_match(pos, P2P_EVENT_GROUP_REMOVED)) { + } else if (str_starts(pos, P2P_EVENT_GROUP_REMOVED)) { wpa_cli_exec(action_file, ifname, pos); - } else if (str_match(pos, P2P_EVENT_CROSS_CONNECT_ENABLE)) { + } else if (str_starts(pos, P2P_EVENT_CROSS_CONNECT_ENABLE)) { wpa_cli_exec(action_file, ifname, pos); - } else if (str_match(pos, P2P_EVENT_CROSS_CONNECT_DISABLE)) { + } else if (str_starts(pos, P2P_EVENT_CROSS_CONNECT_DISABLE)) { wpa_cli_exec(action_file, ifname, pos); - } else if (str_match(pos, P2P_EVENT_GO_NEG_FAILURE)) { + } else if (str_starts(pos, P2P_EVENT_GO_NEG_FAILURE)) { wpa_cli_exec(action_file, ifname, pos); - } else if (str_match(pos, WPS_EVENT_SUCCESS)) { + } else if (str_starts(pos, WPS_EVENT_SUCCESS)) { wpa_cli_exec(action_file, ifname, pos); - } else if (str_match(pos, WPS_EVENT_FAIL)) { + } else if (str_starts(pos, WPS_EVENT_FAIL)) { wpa_cli_exec(action_file, ifname, pos); - } else if (str_match(pos, AP_STA_CONNECTED)) { + } else if (str_starts(pos, AP_STA_CONNECTED)) { wpa_cli_exec(action_file, ifname, pos); - } else if (str_match(pos, AP_STA_DISCONNECTED)) { + } else if (str_starts(pos, AP_STA_DISCONNECTED)) { wpa_cli_exec(action_file, ifname, pos); - } else if (str_match(pos, ESS_DISASSOC_IMMINENT)) { + } else if (str_starts(pos, ESS_DISASSOC_IMMINENT)) { wpa_cli_exec(action_file, ifname, pos); - } else if (str_match(pos, HS20_SUBSCRIPTION_REMEDIATION)) { + } else if (str_starts(pos, HS20_SUBSCRIPTION_REMEDIATION)) { wpa_cli_exec(action_file, ifname, pos); - } else if (str_match(pos, HS20_DEAUTH_IMMINENT_NOTICE)) { + } else if (str_starts(pos, HS20_DEAUTH_IMMINENT_NOTICE)) { wpa_cli_exec(action_file, ifname, pos); - } else if (str_match(pos, WPA_EVENT_TERMINATING)) { + } else if (str_starts(pos, WPA_EVENT_TERMINATING)) { printf("wpa_supplicant is terminating - stop monitoring\n"); wpa_cli_quit = 1; } @@ -3818,7 +3760,7 @@ static int check_terminating(const char *msg) pos = msg; } - if (str_match(pos, WPA_EVENT_TERMINATING) && ctrl_conn) { + if (str_starts(pos, WPA_EVENT_TERMINATING) && ctrl_conn) { edit_clear_line(); printf("\rConnection to wpa_supplicant lost - trying to " "reconnect\n"); @@ -3869,37 +3811,6 @@ static void wpa_cli_recv_pending(struct wpa_ctrl *ctrl, int action_monitor) } } -#define max_args 10 - -static int tokenize_cmd(char *cmd, char *argv[]) -{ - char *pos; - int argc = 0; - - pos = cmd; - for (;;) { - while (*pos == ' ') - pos++; - if (*pos == '\0') - break; - argv[argc] = pos; - argc++; - if (argc == max_args) - break; - if (*pos == '"') { - char *pos2 = os_strrchr(pos, '"'); - if (pos2) - pos = pos2 + 1; - } - while (*pos != '\0' && *pos != ' ') - pos++; - if (*pos == ' ') - *pos++ = '\0'; - } - - return argc; -} - static void wpa_cli_ping(void *eloop_ctx, void *timeout_ctx) { @@ -4084,7 +3995,7 @@ 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 (wpa_cli_open_connection(ctrl_ifname, 1)) { if (!warning_displayed) { printf("Could not connect to wpa_supplicant: " "%s - re-trying\n", @@ -4309,7 +4220,7 @@ int main(int argc, char *argv[]) interactive = (argc == optind) && (action_file == NULL); if (interactive) - printf("%s\n\n%s\n\n", wpa_cli_version, wpa_cli_license); + printf("%s\n\n%s\n\n", wpa_cli_version, cli_license); if (eloop_init()) return -1; @@ -4373,7 +4284,7 @@ int main(int argc, char *argv[]) } } - if (daemonize && os_daemonize(pid_file)) + if (daemonize && os_daemonize(pid_file) && eloop_sock_requeue()) return -1; if (action_file) diff --git a/wpa_supplicant/wpa_gui-qt4/.gitignore b/wpa_supplicant/wpa_gui-qt4/.gitignore new file mode 100644 index 0000000000000..da818cb665579 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/.gitignore @@ -0,0 +1,4 @@ +.moc +.obj +.ui +qrc_icons.cpp diff --git a/wpa_supplicant/wpa_gui-qt4/addinterface.cpp b/wpa_supplicant/wpa_gui-qt4/addinterface.cpp new file mode 100644 index 0000000000000..7d92f63d1b1d8 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/addinterface.cpp @@ -0,0 +1,239 @@ +/* + * wpa_gui - AddInterface class + * Copyright (c) 2008, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include <cstdio> +#include "common/wpa_ctrl.h" + +#include <QMessageBox> + +#include "wpagui.h" +#include "addinterface.h" + +#ifdef CONFIG_NATIVE_WINDOWS +#include <windows.h> + +#ifndef WPA_KEY_ROOT +#define WPA_KEY_ROOT HKEY_LOCAL_MACHINE +#endif +#ifndef WPA_KEY_PREFIX +#define WPA_KEY_PREFIX TEXT("SOFTWARE\\wpa_supplicant") +#endif +#endif /* CONFIG_NATIVE_WINDOWS */ + + +AddInterface::AddInterface(WpaGui *_wpagui, QWidget *parent) + : QDialog(parent), wpagui(_wpagui) +{ + setWindowTitle(tr("Select network interface to add")); + resize(400, 200); + vboxLayout = new QVBoxLayout(this); + + interfaceWidget = new QTreeWidget(this); + interfaceWidget->setEditTriggers(QAbstractItemView::NoEditTriggers); + interfaceWidget->setUniformRowHeights(true); + interfaceWidget->setSortingEnabled(true); + interfaceWidget->setColumnCount(3); + interfaceWidget->headerItem()->setText(0, tr("driver")); + interfaceWidget->headerItem()->setText(1, tr("interface")); + interfaceWidget->headerItem()->setText(2, tr("description")); + interfaceWidget->setItemsExpandable(false); + interfaceWidget->setRootIsDecorated(false); + vboxLayout->addWidget(interfaceWidget); + + connect(interfaceWidget, + SIGNAL(itemActivated(QTreeWidgetItem *, int)), this, + SLOT(interfaceSelected(QTreeWidgetItem *))); + + addInterfaces(); +} + + +void AddInterface::addInterfaces() +{ +#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE + struct wpa_ctrl *ctrl; + int ret; + char buf[2048]; + size_t len; + + ctrl = wpa_ctrl_open(NULL); + if (ctrl == NULL) + return; + + len = sizeof(buf) - 1; + ret = wpa_ctrl_request(ctrl, "INTERFACE_LIST", 14, buf, &len, NULL); + if (ret < 0) { + wpa_ctrl_close(ctrl); + return; + } + buf[len] = '\0'; + + wpa_ctrl_close(ctrl); + + QString ifaces(buf); + QStringList lines = ifaces.split(QRegExp("\\n")); + for (QStringList::Iterator it = lines.begin(); + it != lines.end(); it++) { + QStringList arg = (*it).split(QChar('\t')); + if (arg.size() < 3) + continue; + QTreeWidgetItem *item = new QTreeWidgetItem(interfaceWidget); + if (!item) + break; + + item->setText(0, arg[0]); + item->setText(1, arg[1]); + item->setText(2, arg[2]); + } + + interfaceWidget->resizeColumnToContents(0); + interfaceWidget->resizeColumnToContents(1); + interfaceWidget->resizeColumnToContents(2); +#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */ +} + + +#ifdef CONFIG_NATIVE_WINDOWS +bool AddInterface::addRegistryInterface(const QString &ifname) +{ + HKEY hk, ihk; + LONG ret; + int id, tmp; + TCHAR name[10]; + DWORD val, i; + + ret = RegOpenKeyEx(WPA_KEY_ROOT, WPA_KEY_PREFIX TEXT("\\interfaces"), + 0, KEY_ENUMERATE_SUB_KEYS | KEY_CREATE_SUB_KEY, + &hk); + if (ret != ERROR_SUCCESS) + return false; + + id = -1; + + for (i = 0; ; i++) { + TCHAR name[255]; + DWORD namelen; + + namelen = 255; + ret = RegEnumKeyEx(hk, i, name, &namelen, NULL, NULL, NULL, + NULL); + + if (ret == ERROR_NO_MORE_ITEMS) + break; + + if (ret != ERROR_SUCCESS) + break; + + if (namelen >= 255) + namelen = 255 - 1; + name[namelen] = '\0'; + +#ifdef UNICODE + QString s((QChar *) name, namelen); +#else /* UNICODE */ + QString s(name); +#endif /* UNICODE */ + tmp = s.toInt(); + if (tmp > id) + id = tmp; + } + + id += 1; + +#ifdef UNICODE + wsprintf(name, L"%04d", id); +#else /* UNICODE */ + os_snprintf(name, sizeof(name), "%04d", id); +#endif /* UNICODE */ + ret = RegCreateKeyEx(hk, name, 0, NULL, 0, KEY_WRITE, NULL, &ihk, + NULL); + RegCloseKey(hk); + if (ret != ERROR_SUCCESS) + return false; + +#ifdef UNICODE + RegSetValueEx(ihk, TEXT("adapter"), 0, REG_SZ, + (LPBYTE) ifname.unicode(), + (ifname.length() + 1) * sizeof(TCHAR)); + +#else /* UNICODE */ + RegSetValueEx(ihk, TEXT("adapter"), 0, REG_SZ, + (LPBYTE) ifname.toLocal8Bit(), ifname.length() + 1); +#endif /* UNICODE */ + RegSetValueEx(ihk, TEXT("config"), 0, REG_SZ, + (LPBYTE) TEXT("default"), 8 * sizeof(TCHAR)); + RegSetValueEx(ihk, TEXT("ctrl_interface"), 0, REG_SZ, + (LPBYTE) TEXT(""), 1 * sizeof(TCHAR)); + val = 1; + RegSetValueEx(ihk, TEXT("skip_on_error"), 0, REG_DWORD, (LPBYTE) &val, + sizeof(val)); + + RegCloseKey(ihk); + return true; +} +#endif /* CONFIG_NATIVE_WINDOWS */ + + +void AddInterface::interfaceSelected(QTreeWidgetItem *sel) +{ + if (!sel) + return; + +#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE + struct wpa_ctrl *ctrl; + int ret; + char buf[20], cmd[256]; + size_t len; + + /* + * INTERFACE_ADD <ifname>TAB<confname>TAB<driver>TAB<ctrl_interface>TAB + * <driver_param>TAB<bridge_name> + */ + snprintf(cmd, sizeof(cmd), + "INTERFACE_ADD %s\t%s\t%s\t%s\t%s\t%s", + sel->text(1).toLocal8Bit().constData(), + "default", + sel->text(0).toLocal8Bit().constData(), + "yes", "", ""); + cmd[sizeof(cmd) - 1] = '\0'; + + ctrl = wpa_ctrl_open(NULL); + if (ctrl == NULL) + return; + + len = sizeof(buf) - 1; + ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len, NULL); + wpa_ctrl_close(ctrl); + + if (ret < 0) { + QMessageBox::warning(this, "wpa_gui", + tr("Add interface command could not be " + "completed.")); + return; + } + + buf[len] = '\0'; + if (buf[0] != 'O' || buf[1] != 'K') { + QMessageBox::warning(this, "wpa_gui", + tr("Failed to add the interface.")); + return; + } + +#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */ + +#ifdef CONFIG_NATIVE_WINDOWS + if (!addRegistryInterface(sel->text(1))) { + QMessageBox::information(this, "wpa_gui", + tr("Failed to add the interface into " + "registry.")); + } +#endif /* CONFIG_NATIVE_WINDOWS */ + + wpagui->selectAdapter(sel->text(1)); + close(); +} diff --git a/wpa_supplicant/wpa_gui-qt4/addinterface.h b/wpa_supplicant/wpa_gui-qt4/addinterface.h new file mode 100644 index 0000000000000..332fc7100f572 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/addinterface.h @@ -0,0 +1,39 @@ +/* + * wpa_gui - AddInterface class + * Copyright (c) 2008, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef ADDINTERFACE_H +#define ADDINTERFACE_H + +#include <QObject> + +#include <QDialog> +#include <QTreeWidget> +#include <QVBoxLayout> + +class WpaGui; + +class AddInterface : public QDialog +{ + Q_OBJECT + +public: + AddInterface(WpaGui *_wpagui, QWidget *parent = 0); + +public slots: + virtual void interfaceSelected(QTreeWidgetItem *sel); + +private: + void addInterfaces(); + bool addRegistryInterface(const QString &ifname); + + QVBoxLayout *vboxLayout; + QTreeWidget *interfaceWidget; + WpaGui *wpagui; +}; + +#endif /* ADDINTERFACE_H */ diff --git a/wpa_supplicant/wpa_gui-qt4/eventhistory.cpp b/wpa_supplicant/wpa_gui-qt4/eventhistory.cpp new file mode 100644 index 0000000000000..09145cd9d5874 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/eventhistory.cpp @@ -0,0 +1,124 @@ +/* + * wpa_gui - EventHistory class + * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include <QHeaderView> +#include <QScrollBar> + +#include "eventhistory.h" + + +int EventListModel::rowCount(const QModelIndex &) const +{ + return msgList.count(); +} + + +int EventListModel::columnCount(const QModelIndex &) const +{ + return 2; +} + + +QVariant EventListModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + if (role == Qt::DisplayRole) + if (index.column() == 0) { + if (index.row() >= timeList.size()) + return QVariant(); + return timeList.at(index.row()); + } else { + if (index.row() >= msgList.size()) + return QVariant(); + return msgList.at(index.row()); + } + else + return QVariant(); +} + + +QVariant EventListModel::headerData(int section, Qt::Orientation orientation, + int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) { + switch (section) { + case 0: + return QString(tr("Timestamp")); + case 1: + return QString(tr("Message")); + default: + return QVariant(); + } + } else + return QString("%1").arg(section); +} + + +void EventListModel::addEvent(QString time, QString msg) +{ + beginInsertRows(QModelIndex(), msgList.size(), msgList.size() + 1); + timeList << time; + msgList << msg; + endInsertRows(); +} + + +EventHistory::EventHistory(QWidget *parent, const char *, bool, Qt::WindowFlags) + : QDialog(parent) +{ + setupUi(this); + + connect(closeButton, SIGNAL(clicked()), this, SLOT(close())); + + eventListView->setItemsExpandable(false); + eventListView->setRootIsDecorated(false); + elm = new EventListModel(parent); + eventListView->setModel(elm); +} + + +EventHistory::~EventHistory() +{ + destroy(); + delete elm; +} + + +void EventHistory::languageChange() +{ + retranslateUi(this); +} + + +void EventHistory::addEvents(WpaMsgList msgs) +{ + WpaMsgList::iterator it; + for (it = msgs.begin(); it != msgs.end(); it++) + addEvent(*it); +} + + +void EventHistory::addEvent(WpaMsg msg) +{ + bool scroll = true; + + if (eventListView->verticalScrollBar()->value() < + eventListView->verticalScrollBar()->maximum()) + scroll = false; + + elm->addEvent(msg.getTimestamp().toString("yyyy-MM-dd hh:mm:ss.zzz"), + msg.getMsg()); + + if (scroll) + eventListView->scrollToBottom(); +} diff --git a/wpa_supplicant/wpa_gui-qt4/eventhistory.h b/wpa_supplicant/wpa_gui-qt4/eventhistory.h new file mode 100644 index 0000000000000..afd7b63469a20 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/eventhistory.h @@ -0,0 +1,57 @@ +/* + * wpa_gui - EventHistory class + * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EVENTHISTORY_H +#define EVENTHISTORY_H + +#include <QObject> +#include "ui_eventhistory.h" + + +class EventListModel : public QAbstractTableModel +{ + Q_OBJECT + +public: + EventListModel(QObject *parent = 0) + : QAbstractTableModel(parent) {} + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + void addEvent(QString time, QString msg); + +private: + QStringList timeList; + QStringList msgList; +}; + + +class EventHistory : public QDialog, public Ui::EventHistory +{ + Q_OBJECT + +public: + EventHistory(QWidget *parent = 0, const char *name = 0, + bool modal = false, Qt::WindowFlags fl = 0); + ~EventHistory(); + +public slots: + virtual void addEvents(WpaMsgList msgs); + virtual void addEvent(WpaMsg msg); + +protected slots: + virtual void languageChange(); + +private: + EventListModel *elm; +}; + +#endif /* EVENTHISTORY_H */ diff --git a/wpa_supplicant/wpa_gui-qt4/eventhistory.ui b/wpa_supplicant/wpa_gui-qt4/eventhistory.ui new file mode 100644 index 0000000000000..afe9149cfa0fd --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/eventhistory.ui @@ -0,0 +1,61 @@ +<ui version="4.0" > + <class>EventHistory</class> + <widget class="QDialog" name="EventHistory" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>533</width> + <height>285</height> + </rect> + </property> + <property name="windowTitle" > + <string>Event history</string> + </property> + <layout class="QGridLayout" > + <item row="0" column="0" colspan="2" > + <widget class="QTreeView" name="eventListView" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Expanding" hsizetype="Expanding" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="verticalScrollBarPolicy" > + <enum>Qt::ScrollBarAlwaysOn</enum> + </property> + <property name="selectionMode" > + <enum>QAbstractItemView::NoSelection</enum> + </property> + </widget> + </item> + <item row="1" column="0" > + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" > + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="1" > + <widget class="QPushButton" name="closeButton" > + <property name="text" > + <string>Close</string> + </property> + </widget> + </item> + </layout> + </widget> + <layoutdefault spacing="6" margin="11" /> + <pixmapfunction></pixmapfunction> + <includes> + <include location="local" >wpamsg.h</include> + </includes> + <resources/> + <connections/> +</ui> diff --git a/wpa_supplicant/wpa_gui-qt4/icons.qrc b/wpa_supplicant/wpa_gui-qt4/icons.qrc new file mode 100644 index 0000000000000..dd72c7ef10081 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/icons.qrc @@ -0,0 +1,9 @@ +<RCC> + <qresource prefix="/icons" > + <file alias="wpa_gui.svg">icons/wpa_gui.svg</file> + <file alias="ap.svg">icons/ap.svg</file> + <file alias="laptop.svg">icons/laptop.svg</file> + <file alias="group.svg">icons/group.svg</file> + <file alias="invitation.svg">icons/invitation.svg</file> + </qresource> +</RCC> diff --git a/wpa_supplicant/wpa_gui-qt4/icons/Makefile b/wpa_supplicant/wpa_gui-qt4/icons/Makefile new file mode 100644 index 0000000000000..709514c127467 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/icons/Makefile @@ -0,0 +1,23 @@ +#!/usr/bin/make -f + +NAMES := wpa_gui ap laptop group invitation +SIZES := 16x16 22x22 32x32 48x48 64x64 128x128 +ICONS := $(addsuffix .png, $(foreach name, $(NAMES), $(foreach size, $(SIZES), $(size)/$(name)))) +ICONS += $(addsuffix .xpm, $(NAMES)) + +all: $(ICONS) + +%.png: + mkdir -p hicolor/$(word 1, $(subst /, ,$(@)))/apps/ + inkscape $(subst .png,.svg, $(word 2, $(subst /, , $(@)))) --without-gui \ + --export-width=$(word 1, $(subst x, , $(@))) \ + --export-height=$(word 2, $(subst x, , $(subst /, , $(@)))) \ + --export-png=hicolor/$(word 1, $(subst /, ,$(@)))/apps/$(word 2, $(subst /, , $@)) + +%.xpm: + mkdir -p pixmaps/ + convert hicolor/16x16/apps/$(@:.xpm=.png) pixmaps/$(@:.xpm=-16.xpm) + convert hicolor/32x32/apps/$(@:.xpm=.png) pixmaps/$@ + +clean: + $(RM) -r pixmaps hicolor diff --git a/wpa_supplicant/wpa_gui-qt4/icons/README b/wpa_supplicant/wpa_gui-qt4/icons/README new file mode 100644 index 0000000000000..39532389766e2 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/icons/README @@ -0,0 +1,74 @@ +wpa_gui icon files + +To convert the svg icons to other formats, make sure inkscape and imagemagick +are installed and use `make' to create various sized png and xpm icons. + + +wpa_gui.svg +----------- + +Copyright (c) 2008 Bernard Gray <bernard.gray@gmail.com> + +The wpa_gui icon is licensed under the GPL version 2. Alternatively, the icon +may be distributed under the terms of BSD license. + + +ap.svg +------ + +mystica_Wireless_Router.svg + +http://openclipart.org/media/files/mystica/8390 +Wireless Router +by: mystica +last change: April 20, 2008 10:32 pm (File added) +date: April 20, 2008 10:31 pm +license: PD + + +laptop.svg +---------- + +metalmarious_Laptop.svg + +http://openclipart.org/media/files/metalmarious/4056 +Laptop +by: metalmarious +last change: May 18, 2008 07:04 pm (File added) +date: August 27, 2007 04:44 am +license: PD + + +group.svg +--------- + +http://www.openclipart.org/detail/25428 +http://www.openclipart.org/people/Anonymous/Anonymous_Network.svg +Uploader: + Anonymous +Drawn by: + Andrew Fitzsimon / Anonymous +Created: + 2009-04-29 04:07:37 +Description: + A network icon by Andrew Fitzsimon. Etiquette Icon set. + From 0.18 OCAL database. + +Public Domain + + + +invitation.svg +-------------- + +http://www.openclipart.org/detail/974 +http://www.openclipart.org/people/jean_victor_balin/jean_victor_balin_unknown_green.svg +Uploader: + jean_victor_balin +Drawn by: + jean_victor_balin +Created: + 2006-10-27 02:12:13 +Description: + +Public Domain diff --git a/wpa_supplicant/wpa_gui-qt4/icons/ap.svg b/wpa_supplicant/wpa_gui-qt4/icons/ap.svg new file mode 100644 index 0000000000000..51cc8ce646ad6 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/icons/ap.svg @@ -0,0 +1,832 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="546" + height="482.67157" + id="svg2" + sodipodi:version="0.32" + inkscape:version="0.45.1+0.46pre1+devel" + sodipodi:docname="Wireless Router.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape" + version="1.0" + inkscape:export-filename="C:\Documents and Settings\Dan\Skrivbord\Clipart egna (InkScape)\Original\Kanske Upload\Wireless Router.png" + inkscape:export-xdpi="310" + inkscape:export-ydpi="310"> + <defs + id="defs4"> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="-50 : 600 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="700 : 600 : 1" + inkscape:persp3d-origin="300 : 400 : 1" + id="perspective148" /> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="-50 : 600 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="700 : 600 : 1" + inkscape:persp3d-origin="300 : 400 : 1" + id="perspective138" /> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="-50 : 600 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="700 : 600 : 1" + inkscape:persp3d-origin="300 : 400 : 1" + id="perspective10" /> + <inkscape:perspective + id="perspective2395" + inkscape:persp3d-origin="300 : 400 : 1" + inkscape:vp_z="700 : 600 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_x="-50 : 600 : 1" + sodipodi:type="inkscape:persp3d" /> + <filter + inkscape:collect="always" + id="filter3304"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.27999283" + id="feGaussianBlur3306" /> + </filter> + <filter + inkscape:collect="always" + id="filter3336"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.5315371" + id="feGaussianBlur3338" /> + </filter> + <filter + inkscape:collect="always" + id="filter3368"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.26473573" + id="feGaussianBlur3370" /> + </filter> + <filter + inkscape:collect="always" + id="filter3564" + x="-0.37202433" + width="1.7440487" + y="-0.43252525" + height="1.8650506"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="1.1017616" + id="feGaussianBlur3566" /> + </filter> + <filter + inkscape:collect="always" + id="filter3748" + x="-0.41952851" + width="1.839057" + y="-0.39121628" + height="1.7824326"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="1.1235829" + id="feGaussianBlur3750" /> + </filter> + <filter + inkscape:collect="always" + id="filter3862" + x="-0.33298156" + width="1.6659631" + y="-1.6699424" + height="4.3398848"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3864" /> + </filter> + <filter + inkscape:collect="always" + id="filter3866" + x="-0.33298156" + width="1.6659631" + y="-0.20756502" + height="1.4151301"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3868" /> + </filter> + <filter + inkscape:collect="always" + id="filter3870" + x="-0.33298156" + width="1.6659631" + y="-1.6699424" + height="4.3398848"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3872" /> + </filter> + <filter + inkscape:collect="always" + id="filter3874" + x="-0.3380883" + width="1.6761765" + y="-0.21154897" + height="1.4230978"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3876" /> + </filter> + <filter + inkscape:collect="always" + id="filter3878" + x="-0.33298156" + width="1.6659631" + y="-1.6699424" + height="4.3398848"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3880" /> + </filter> + <filter + inkscape:collect="always" + id="filter3882" + x="-0.36018598" + width="1.720372" + y="-0.20953795" + height="1.4190758"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3884" /> + </filter> + <filter + inkscape:collect="always" + id="filter3886" + x="-0.33298156" + width="1.6659631" + y="-1.6699424" + height="4.3398848"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3888" /> + </filter> + <filter + inkscape:collect="always" + id="filter3890" + x="-0.35439494" + width="1.7087899" + y="-0.20953795" + height="1.4190758"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3892" /> + </filter> + <filter + inkscape:collect="always" + id="filter3894" + x="-0.33298156" + width="1.6659631" + y="-1.6699424" + height="4.3398848"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3896" /> + </filter> + <filter + inkscape:collect="always" + id="filter3898" + x="-0.38537359" + width="1.7707472" + y="-0.20562869" + height="1.4112574"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3900" /> + </filter> + <filter + inkscape:collect="always" + id="filter3902" + x="-0.33298156" + width="1.6659631" + y="-1.6699424" + height="4.3398848"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3904" /> + </filter> + <filter + inkscape:collect="always" + id="filter3906" + x="-0.38537359" + width="1.7707472" + y="-0.21359873" + height="1.4271975"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3908" /> + </filter> + <filter + inkscape:collect="always" + id="filter3910" + x="-0.33298156" + width="1.6659631" + y="-1.6699424" + height="4.3398848"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3912" /> + </filter> + <filter + inkscape:collect="always" + id="filter3914" + x="-0.36018598" + width="1.720372" + y="-0.20562869" + height="1.4112574"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3916" /> + </filter> + <filter + inkscape:collect="always" + id="filter3918" + x="-0.33298156" + width="1.6659631" + y="-1.6699424" + height="4.3398848"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3920" /> + </filter> + <filter + inkscape:collect="always" + id="filter3922" + x="-0.36616942" + width="1.7323389" + y="-0.20953795" + height="1.4190758"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3924" /> + </filter> + <filter + inkscape:collect="always" + id="filter3926" + x="-0.33298156" + width="1.6659631" + y="-1.6699424" + height="4.3398848"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3928" /> + </filter> + <filter + inkscape:collect="always" + id="filter3930" + x="-0.34878778" + width="1.6975756" + y="-0.20186263" + height="1.4037253"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3932" /> + </filter> + <filter + inkscape:collect="always" + id="filter3934" + x="-0.33298156" + width="1.6659631" + y="-1.6699424" + height="4.3398848"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3936" /> + </filter> + <filter + inkscape:collect="always" + id="filter3938" + x="-0.32802677" + width="1.6560535" + y="-0.21568884" + height="1.4313776"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3940" /> + </filter> + <filter + inkscape:collect="always" + id="filter3942" + x="-0.33298156" + width="1.6659631" + y="-1.6699424" + height="4.3398848"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3944" /> + </filter> + <filter + inkscape:collect="always" + id="filter3946" + x="-0.32321677" + width="1.6464336" + y="-0.21568884" + height="1.4313776"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3948" /> + </filter> + <filter + inkscape:collect="always" + id="filter3950" + x="-0.33298156" + width="1.6659631" + y="-1.6699424" + height="4.3398848"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3952" /> + </filter> + <filter + inkscape:collect="always" + id="filter3954" + x="-0.3185463" + width="1.6370926" + y="-0.21359873" + height="1.4271975"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3956" /> + </filter> + <filter + inkscape:collect="always" + id="filter3958" + x="-0.33298156" + width="1.6659631" + y="-1.6699424" + height="4.3398848"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3960" /> + </filter> + <filter + inkscape:collect="always" + id="filter3962" + x="-0.28553614" + width="1.5710723" + y="-0.21568884" + height="1.4313776"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.82006695" + id="feGaussianBlur3964" /> + </filter> + <filter + inkscape:collect="always" + id="filter3982" + x="-0.0048889387" + width="1.0097779" + y="-0.26385465" + height="1.5277092"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="1.0547249" + id="feGaussianBlur3984" /> + </filter> + <filter + inkscape:collect="always" + id="filter3996"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.8032234" + id="feGaussianBlur3998" /> + </filter> + <filter + inkscape:collect="always" + id="filter3517" + x="-0.25713229" + width="1.5142646" + y="-0.087099633" + height="1.1741993"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.33984317" + id="feGaussianBlur3519" /> + </filter> + <filter + inkscape:collect="always" + id="filter3329" + x="-0.18025071" + width="1.3605014" + y="-1.1780664" + height="3.3561328"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.49086099" + id="feGaussianBlur3331" /> + </filter> + <filter + inkscape:collect="always" + id="filter3333" + x="-0.15131117" + width="1.3026223" + y="-0.1853139" + height="1.3706278"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.49086099" + id="feGaussianBlur3335" /> + </filter> + <filter + inkscape:collect="always" + id="filter3337" + x="-0.14412392" + width="1.2882478" + y="-0.18013415" + height="1.3602683"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.49086099" + id="feGaussianBlur3339" /> + </filter> + <filter + inkscape:collect="always" + id="filter3341" + x="-1.1780664" + width="3.3561328" + y="-0.23067047" + height="1.4613409"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.49086099" + id="feGaussianBlur3343" /> + </filter> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + gridtolerance="10000" + guidetolerance="10" + objecttolerance="10" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="0.98994949" + inkscape:cx="233.05018" + inkscape:cy="176.49031" + inkscape:document-units="px" + inkscape:current-layer="layer2" + showgrid="false" + inkscape:window-width="1152" + inkscape:window-height="838" + inkscape:window-x="0" + inkscape:window-y="0" /> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:groupmode="layer" + id="layer2" + transform="translate(-45.788597,-496.6196)"> + <path + style="fill:#606060;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 75.574315,977.86259 L 535.56828,979.20564 L 564.86002,979.29116 C 564.86002,979.29116 573.43146,977.86259 573.43146,973.57687 C 573.43146,969.29116 574.14573,959.29117 574.14573,959.29117 L 566.03832,959.7296 L 72.464255,956.66717 L 58.640665,955.63307 C 58.640665,955.63307 59.860025,973.57688 65.574315,975.71973 C 71.288595,977.86259 75.574315,977.14831 75.574315,977.86259 z" + id="path2402" + sodipodi:nodetypes="cccsccccsc" /> + <path + style="fill:#dddddd;fill-opacity:1;fill-rule:evenodd;stroke:#b2b2b2;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 67.002885,957.14831 L 566.28859,960.00545 C 566.28859,960.00545 577.71717,959.29116 583.43146,955.71973 C 589.14574,952.14831 588.43146,942.86259 588.43146,942.86259 C 588.43146,942.86259 591.28859,907.14831 591.28859,902.14831 C 591.28859,897.14831 574.86002,839.29116 572.00288,827.86259 C 569.14574,816.43402 557.00288,757.1483 557.00288,757.1483 C 557.00288,757.1483 555.57431,749.29116 552.71717,748.57688 C 549.86002,747.86259 548.43146,748.57688 548.43146,748.57688 L 558.43146,797.86259 L 577.71717,880.00545 L 578.07431,881.34473 L 579.05004,882.28742 L 584.86002,897.14831 C 584.86002,897.14831 584.93925,904.12528 581.70701,906.43402 C 576.70701,910.00545 557.71717,910.71973 557.71717,910.71973 L 68.431455,907.86259 C 68.431455,907.86259 57.002885,906.43402 54.145745,903.57688 C 51.288595,900.71973 52.298745,895.51053 52.298745,895.51053 L 94.860025,745.00545 C 94.860025,745.00545 86.288605,747.86259 84.145745,752.1483 C 82.002885,756.43402 71.288595,811.43402 68.431455,820.00545 C 65.574315,828.57688 47.002885,893.57688 47.002885,893.57688 C 47.002885,893.57688 46.288597,900.00545 46.288597,903.57688 C 46.288597,907.14831 48.431455,946.43402 48.431455,946.43402 C 48.431455,946.43402 52.002885,953.57688 55.574315,955.00545 C 59.145745,956.43402 68.431455,957.14831 67.002885,957.14831 z" + id="path2404" + sodipodi:nodetypes="ccscsscsccccccsccsccsscscsc" /> + <path + style="fill:#ececec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3336)" + d="M 562.71717,958.57688 C 562.71717,958.57688 572.00288,957.86259 572.00288,951.43402 C 572.00288,945.00545 575.57431,922.14831 572.00288,918.57688 C 568.43146,915.00545 564.86002,912.86259 564.86002,912.86259 C 564.86002,912.86259 586.28859,903.57688 585.57431,907.86259 C 584.86002,912.14831 581.28859,914.29116 581.28859,920.00545 C 581.28859,925.71973 580.57431,948.57688 580.57431,948.57688 C 580.57431,948.57688 578.43146,952.86259 581.28859,953.57688 C 584.14574,954.29116 582.71717,955.71973 582.71717,955.71973 L 576.28859,957.86259 L 570.57431,958.57688 L 562.71717,958.57688 z" + id="path2406" /> + <path + style="fill:#ededed;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 75.574315,913.57688 L 560.57431,915.71973 L 557.71717,955.71973 L 77.002885,954.29116 L 75.574315,913.57688 z" + id="path2408" /> + <path + style="fill:#020202;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 100.57432,925.00545 L 541.28859,927.86259 C 541.28859,927.86259 548.43146,932.14831 548.43146,936.43402 C 548.43146,940.71973 540.57431,945.71973 540.57431,945.71973 L 98.431455,942.86259 C 98.431455,942.86259 89.145745,938.57688 89.860025,932.86259 C 90.574315,927.14831 101.2886,924.29116 100.57432,925.00545 z" + id="path2410" /> + <path + style="fill:#121212;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 71.497795,907.90591 L 557.85419,910.32545 L 573.61002,909.11259 C 573.61002,909.11259 581.28967,908.48718 583.43146,904.46973 C 584.17421,903.07651 585.22722,901.79095 585.2275,899.8245 C 585.22862,892.20247 577.89573,880.63045 577.89573,880.63045 C 577.89573,880.63045 578.20783,882.29016 577.30114,882.74322 C 575.50038,883.64304 573.664,884.53037 571.28859,885.00545 C 567.71717,885.71973 66.036055,879.91879 66.036055,879.91879 L 59.860025,880.00545 L 56.288605,877.68402 L 52.002885,896.43402 C 52.002885,896.43402 51.828665,903.29437 57.673845,905.51053 C 62.003235,907.15199 72.212085,908.6202 71.497795,907.90591 z" + id="path2412" + sodipodi:nodetypes="cccsscssccccsc" /> + <path + style="fill:#2c2c2c;fill-opacity:1;fill-rule:evenodd;stroke:#252525;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 93.431455,743.57688 L 59.145745,868.57688 C 59.145745,868.57688 57.538605,871.96973 60.574315,873.57688 C 65.381975,876.12212 74.681455,875.18402 74.681455,875.18402 C 74.681455,875.18402 567.00288,879.29116 569.86002,878.57688 C 572.71717,877.86259 575.03859,876.96974 575.03859,876.96974 L 575.21717,868.75545 C 575.21717,868.75545 579.61685,882.27622 576.28859,883.75545 C 573.07431,885.18402 566.28859,885.00545 566.28859,885.00545 L 64.860025,880.71973 L 58.431455,879.29116 L 56.645745,876.79116 L 57.861935,870.85997 L 93.431455,743.57688 z" + id="path2414" + sodipodi:nodetypes="ccscsccscccccc" /> + <path + style="fill:#d5d5d5;fill-opacity:1;fill-rule:evenodd;stroke:#d1d1d1;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3748)" + d="M 62.002885,879.82688 L 62.181455,874.29116 L 60.217165,873.93402 L 57.360025,874.82688 L 56.824315,876.25545 C 56.824315,876.25545 56.467165,877.86259 57.360025,878.21973 C 58.252885,878.57688 59.860025,879.11259 59.860025,879.11259 L 62.002885,879.82688 z" + id="path2416" /> + <path + style="fill:#d5d5d5;fill-opacity:1;fill-rule:evenodd;stroke:#dadada;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3564)" + d="M 571.82431,883.04116 C 571.82431,882.32688 570.57431,879.82688 572.18146,878.93402 C 573.78859,878.04116 575.03859,878.57688 575.03859,878.57688 C 575.03859,878.57688 578.07431,881.25545 577.36002,881.43402 C 576.64574,881.61259 576.46717,882.50545 574.68146,883.21973 C 572.89574,883.93402 572.36002,883.21973 571.82431,883.04116 z" + id="path2418" /> + <path + style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#2aea00;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3341)" + d="M 194.42891,930.61513 L 194.68145,934.46973" + id="path2420" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#2aea00;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3337)" + d="M 192.18145,932.32688 C 192.18145,932.32688 189.18906,937.68402 194.50288,937.86259 C 200.57352,938.06659 197.36003,932.50545 197.36003,932.50545" + id="path2422" + sodipodi:nodetypes="csc" /> + <path + sodipodi:type="arc" + style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#444444;stroke-width:0.92299998;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path2424" + sodipodi:cx="289.82144" + sodipodi:cy="742.89789" + sodipodi:rx="4.2857141" + sodipodi:ry="4.2857141" + d="M 294.10716,742.89789 A 4.2857141,4.2857141 0 1 1 285.53573,742.89789 A 4.2857141,4.2857141 0 1 1 294.10716,742.89789 z" + transform="translate(-57.282828,191.92898)" /> + <path + sodipodi:type="arc" + style="fill:#101010;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.92626119;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path2439" + sodipodi:cx="289.82144" + sodipodi:cy="742.89789" + sodipodi:rx="4.2857141" + sodipodi:ry="4.2857141" + d="M 294.10716,742.89789 A 4.2857141,4.2857141 0 1 1 285.53573,742.89789 A 4.2857141,4.2857141 0 1 1 294.10716,742.89789 z" + transform="matrix(0.4791666,0,0,0.4791666,93.755115,578.94427)" /> + <path + sodipodi:type="arc" + style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#7b7b7b;stroke-width:1.70648665;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path3211" + sodipodi:cx="289.82144" + sodipodi:cy="742.89789" + sodipodi:rx="4.2857141" + sodipodi:ry="4.2857141" + d="M 294.10716,742.89789 A 4.2857141,4.2857141 0 1 1 285.53573,742.89789 A 4.2857141,4.2857141 0 1 1 294.10716,742.89789 z" + transform="matrix(0.5109914,0,0,0.5109914,116.22806,556.99816)" /> + <path + sodipodi:type="arc" + style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#7b7b7b;stroke-width:1.70648665;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path3213" + sodipodi:cx="289.82144" + sodipodi:cy="742.89789" + sodipodi:rx="4.2857141" + sodipodi:ry="4.2857141" + d="M 294.10716,742.89789 A 4.2857141,4.2857141 0 1 1 285.53573,742.89789 A 4.2857141,4.2857141 0 1 1 294.10716,742.89789 z" + transform="matrix(0.5109914,0,0,0.5109914,122.47805,556.99816)" /> + <path + sodipodi:type="arc" + style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#7b7b7b;stroke-width:1.70648665;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path3215" + sodipodi:cx="289.82144" + sodipodi:cy="742.89789" + sodipodi:rx="4.2857141" + sodipodi:ry="4.2857141" + d="M 294.10716,742.89789 A 4.2857141,4.2857141 0 1 1 285.53573,742.89789 A 4.2857141,4.2857141 0 1 1 294.10716,742.89789 z" + transform="matrix(0.5109914,0,0,0.5109914,119.44234,552.71244)" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#606060;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 308.07431,934.29116 C 308.07431,934.29116 307.00288,935.71973 306.28859,936.61259 C 305.57431,937.50544 305.57431,939.11259 304.32431,938.57688 C 303.07431,938.04116 301.11002,936.07688 301.28859,934.64831 C 301.46717,933.21974 304.32431,931.07688 304.32431,931.07688 C 304.32431,931.07688 305.03859,932.1483 306.11002,932.86259 C 307.18145,933.57688 307.89574,934.11259 308.07431,934.29116 z" + id="path3217" + sodipodi:nodetypes="cssscsc" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#28cc03;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3333)" + d="M 336.28859,931.43402 L 340.57431,931.43402 L 341.82431,932.68402 L 341.82431,935.00545 L 340.93145,936.79116 L 336.46717,936.79116 L 335.03859,935.71973 L 335.03859,932.50545 L 336.28859,931.43402 z" + id="path3221" + sodipodi:nodetypes="ccccccccc" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#28cc03;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3329)" + d="M 335.75288,938.57688 L 341.28859,938.57688" + id="path3223" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#4d4d4d;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 369.86002,931.43402 L 374.14574,931.43402 L 375.39574,932.68402 L 375.39574,935.00545 L 374.50288,936.79116 L 370.0386,936.79116 L 368.61002,935.71973 L 368.61002,932.50545 L 369.86002,931.43402 z" + id="path3225" + sodipodi:nodetypes="ccccccccc" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#4d4d4d;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 369.32431,938.57688 L 374.86002,938.57688" + id="path3227" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#4d4d4d;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 407.00288,931.79116 L 411.2886,931.79116 L 412.5386,933.04116 L 412.5386,935.36259 L 411.64574,937.1483 L 407.18147,937.1483 L 405.75288,936.07687 L 405.75288,932.86259 L 407.00288,931.79116 z" + id="path3229" + sodipodi:nodetypes="ccccccccc" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#4d4d4d;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 406.46717,938.93402 L 412.00288,938.93402" + id="path3231" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#4d4d4d;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 444.14574,931.96973 L 448.43145,931.96973 L 449.68145,933.21973 L 449.68145,935.54116 L 448.78859,937.32687 L 444.32431,937.32687 L 442.89574,936.25544 L 442.89574,933.04116 L 444.14574,931.96973 z" + id="path3233" + sodipodi:nodetypes="ccccccccc" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#4d4d4d;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 443.61002,939.11259 L 449.14574,939.11259" + id="path3235" /> + <path + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:#28cc03;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Dominican;-inkscape-font-specification:Dominican;filter:url(#filter3517)" + d="M 329.05831,937.86898 L 329.10629,932.30075 L 328.72213,932.54098 C 328.65816,932.59689 328.60226,932.62485 328.55441,932.62484 C 328.52242,932.62485 328.47243,932.59085 328.40444,932.52285 C 328.33646,932.45487 328.30245,932.38486 328.30245,932.31283 C 328.30245,932.22495 328.34841,932.12497 328.44034,932.0129 C 328.53225,931.90085 328.62618,931.78879 328.72213,931.67672 L 329.68234,930.33273 L 330.47445,930.78903 L 330.46236,938.5527 C 330.46236,938.64889 330.38631,938.69699 330.23421,938.69699 C 330.1783,938.69699 330.13437,938.69296 330.10238,938.6849 C 329.86239,938.62094 329.6224,938.56088 329.38241,938.50472 C 329.16634,938.40072 329.05831,938.18881 329.05831,937.86898 L 329.05831,937.86898 z" + id="text3237" /> + <path + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:#939393;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Dominican;-inkscape-font-specification:Dominican" + d="M 364.94248,932.84854 C 364.94246,933.5126 364.7065,934.32059 364.23458,935.27249 C 363.84249,936.05667 363.33443,936.86868 362.71041,937.70852 L 362.50643,937.97256 L 363.8984,937.94876 C 364.14644,937.94876 364.37044,937.97476 364.5704,938.02676 C 364.77034,938.07876 364.91036,938.16067 364.99044,938.27249 C 365.16646,938.51248 365.25447,938.70449 365.25448,938.84854 C 365.25447,938.98452 365.19448,939.06851 365.07449,939.10049 C 364.95449,939.13247 364.85042,939.14846 364.76229,939.14846 L 360.97054,939.34072 L 360.5743,937.93667 C 360.83846,937.62466 361.10653,937.31265 361.3785,937.00064 C 361.96248,936.20865 362.44246,935.46463 362.81844,934.76858 C 363.3304,933.83255 363.58638,933.12857 363.58639,932.65664 C 363.58638,932.55264 363.5784,932.46866 363.5624,932.40469 C 363.54641,932.34073 363.51046,932.30875 363.45455,932.30874 C 363.30245,932.30875 362.99044,932.50064 362.51852,932.88442 C 362.21456,933.13248 361.81051,933.50052 361.30636,933.98855 C 360.90646,934.38065 360.70248,934.58462 360.69442,934.60049 L 360.02242,933.65273 C 360.02242,933.58853 360.19845,933.3605 360.5505,932.96865 C 360.95846,932.51261 361.36642,932.1326 361.77438,931.82864 C 362.32638,931.42069 362.79439,931.21671 363.17843,931.2167 C 363.40255,931.21671 363.59053,931.27664 363.74239,931.39651 C 364.29439,931.80448 364.60237,932.04849 364.66634,932.12856 C 364.85042,932.35269 364.94246,932.59268 364.94248,932.84854 L 364.94248,932.84854 z" + id="text3241" /> + <path + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:#939393;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Dominican;-inkscape-font-specification:Dominican" + d="M 398.55154,939.8793 L 397.90371,938.83523 L 398.62368,938.71511 C 399.16762,938.61917 399.70754,938.33914 400.24343,937.87502 C 400.73952,937.44314 400.98757,937.09915 400.98757,936.84304 C 400.98757,936.69119 400.84755,936.55526 400.56753,936.43527 C 400.2875,936.31527 399.97951,936.25528 399.64358,936.25527 C 399.42751,936.25528 399.23348,936.28128 399.06149,936.33328 C 398.88949,936.38528 398.74355,936.47525 398.62368,936.60317 L 398.17947,936.27908 L 398.17947,935.23501 L 398.50356,935.079 C 399.04751,934.83902 399.59548,934.46304 400.14748,933.95107 C 400.69948,933.43912 400.97548,933.05521 400.97549,932.79934 C 400.97548,932.7432 400.95547,932.6951 400.91544,932.65505 C 400.81948,932.55911 400.65957,932.51114 400.43569,932.51113 C 400.10756,932.51114 399.69155,932.61112 399.18765,932.81106 C 398.7797,932.97122 398.5436,933.08328 398.47939,933.14724 L 397.97549,932.00723 C 398.06363,931.91129 398.32363,931.77127 398.75552,931.58718 C 399.29165,931.36307 399.75564,931.25101 400.14748,931.251 C 400.25955,931.25101 400.3716,931.26309 400.48367,931.28726 C 400.85965,931.37516 401.16762,931.52713 401.40762,931.74319 C 401.59169,931.90311 401.78371,932.15506 401.98367,932.49905 C 402.03151,932.57913 402.06149,932.6572 402.07357,932.73324 C 402.08565,932.8093 402.09169,932.88333 402.0917,932.95535 C 402.09169,933.25125 401.98366,933.5712 401.76761,933.91519 C 401.55154,934.25919 401.28347,934.56717 400.9634,934.83914 L 400.50747,935.21121 C 400.8915,935.21121 401.28951,935.43722 401.7015,935.88925 C 402.11348,936.34128 402.31948,936.8273 402.31948,937.34731 C 402.31948,937.65127 402.24947,937.91726 402.10947,938.14529 C 401.96944,938.37332 401.76748,938.6033 401.50356,938.83523 C 401.16762,939.13113 400.79567,939.37112 400.38772,939.5552 C 399.88357,939.78713 399.3915,939.9031 398.91152,939.9031 C 398.84756,939.9031 398.78158,939.89913 398.71359,939.8912 C 398.64559,939.88326 398.59157,939.8793 398.55154,939.8793 L 398.55154,939.8793 z" + id="text3245" /> + <path + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:#939393;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Dominican;-inkscape-font-specification:Dominican" + d="M 433.97515,936.58822 L 436.87884,931.5964 L 438.17486,932.11239 L 438.01885,936.03634 L 438.42718,936.13229 C 438.62712,936.1882 438.73503,936.22018 438.75091,936.22824 C 438.799,936.26022 438.83904,936.31222 438.87103,936.38424 C 438.93499,936.52829 438.98094,936.63431 439.0089,936.7023 C 439.03686,936.77029 439.05083,936.84823 439.05083,936.93612 C 439.05083,937.00033 439.03887,937.06442 439.01495,937.12838 L 437.92291,937.20016 L 437.93499,939.73214 C 437.93499,939.94015 437.86296,940.03219 437.71893,940.00826 C 437.31902,939.95236 437.08709,939.91232 437.02312,939.88815 C 436.78313,939.78414 436.66314,939.57223 436.66314,939.25241 L 436.66314,937.28439 L 434.71893,937.28439 L 433.97515,936.58822 z M 435.48687,936.08431 L 436.80706,936.10812 L 437.01104,933.13229 L 435.48687,936.08431 z" + id="text3249" /> + <path + style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#979797;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Dominican;-inkscape-font-specification:Dominican" + d="M 109.34409,932.3674 C 109.08016,933.58322 108.9001,934.43124 108.80392,934.91146 C 108.65206,935.67123 108.49617,936.52719 108.33628,937.47933 L 107.04026,936.98715 L 106.27194,932.81124 L 105.39597,937.73129 L 104.01609,937.28744 L 103.3082,929.55929 C 103.3082,929.47141 103.38021,929.42746 103.52426,929.42745 C 103.63608,929.43527 103.71604,929.43918 103.76413,929.43917 C 103.91622,929.43918 104.04623,929.46518 104.15414,929.51718 C 104.26206,929.56919 104.32004,929.65927 104.3281,929.78744 L 104.77194,935.69113 L 105.64792,929.97933 L 107.04026,930.03939 L 107.65219,935.5234 C 107.76425,934.39523 107.95627,933.16318 108.22824,931.82723 C 108.35617,931.17124 108.46811,930.65927 108.56405,930.29135 C 108.63607,930.00327 108.78011,929.49521 108.99618,928.76718 C 109.02011,928.7352 109.04806,928.69919 109.08005,928.65914 C 109.15206,928.58713 109.23213,928.55918 109.32028,928.57528 C 109.55221,928.61533 109.73819,928.69132 109.87821,928.80325 C 110.0182,928.91519 110.08821,929.06723 110.08822,929.25936 C 109.91219,929.97129 109.78413,930.48728 109.70407,930.80734 C 109.57613,931.32736 109.45614,931.84738 109.34409,932.3674 L 109.34409,932.3674 z M 111.18136,931.43136 L 111.18136,930.65133 C 111.18136,930.49924 111.22733,930.36521 111.31924,930.24923 C 111.41116,930.13327 111.52914,930.07529 111.67319,930.07528 C 111.80917,930.07529 111.9332,930.12729 112.04525,930.23129 C 112.15732,930.3353 112.21335,930.45932 112.21335,930.60336 L 112.21335,931.68331 C 112.21335,931.79538 112.16135,931.87741 112.05734,931.92941 C 111.95334,931.98141 111.82932,932.00742 111.68527,932.00741 C 111.54122,932.00742 111.42123,931.95139 111.32529,931.83932 C 111.22933,931.72726 111.18136,931.59128 111.18136,931.43136 L 111.18136,931.43136 z M 111.13339,937.22738 L 111.13339,932.83541 C 111.13339,932.64328 111.24533,932.54721 111.46921,932.54721 C 111.74117,932.54721 111.93515,932.58121 112.05112,932.64919 C 112.16708,932.71719 112.22507,932.81125 112.22507,932.93136 L 112.1174,937.67123 C 112.1174,937.76718 112.04538,937.81515 111.90134,937.81515 C 111.82126,937.81515 111.75118,937.80715 111.69112,937.79116 C 111.63107,937.77517 111.58505,937.76327 111.55307,937.75546 C 111.27329,937.66732 111.13339,937.4913 111.13339,937.22738 L 111.13339,937.22738 z M 113.68845,932.8713 L 113.79648,932.73947 L 114.0726,932.73947 C 114.13657,932.73947 114.21053,932.77743 114.29452,932.85336 C 114.37851,932.92929 114.44455,932.97531 114.49265,932.99142 C 114.60446,933.02341 114.69638,932.93741 114.76839,932.73342 C 114.84042,932.52945 114.92641,932.41342 115.0264,932.38534 C 115.12636,932.35727 115.19638,932.34323 115.23641,932.34323 C 115.38852,932.34323 115.52255,932.39725 115.63852,932.50527 C 115.75449,932.61331 115.81247,932.74326 115.81247,932.89511 L 115.80039,933.36312 C 115.80038,933.5953 115.77237,933.80539 115.71634,933.99337 C 115.6603,934.18136 115.57236,934.27536 115.45248,934.27535 C 115.26839,934.27536 115.14437,934.0953 115.08042,933.73519 C 115.04843,933.55917 114.9924,933.47116 114.91233,933.47116 C 114.84054,933.47116 114.78261,933.53116 114.73855,933.65115 C 114.69449,933.77115 114.67246,934.0112 114.67246,934.3713 C 114.67246,934.48337 114.67246,934.58139 114.67246,934.66537 C 114.67246,934.74936 114.67246,934.81527 114.67246,934.86312 L 114.67246,937.79135 C 114.58456,937.83138 114.48459,937.8514 114.37252,937.8514 C 114.30051,937.8514 114.2125,937.83938 114.10849,937.81533 C 114.00449,937.79128 113.90451,937.73324 113.80857,937.6412 C 113.71261,937.54916 113.66464,937.43124 113.66464,937.28744 L 113.68845,932.8713 z M 119.45774,933.78317 L 119.52951,935.22311 L 117.82552,935.49923 C 117.68173,935.53122 117.58579,935.56723 117.53769,935.60726 C 117.43368,935.68734 117.38168,935.7994 117.38169,935.94345 L 117.40548,936.49533 C 117.40548,936.6152 117.44351,936.72518 117.51955,936.82528 C 117.59561,936.92538 117.68563,936.9714 117.78964,936.96334 C 118.01376,936.93136 118.29379,936.75534 118.62973,936.43527 C 118.96567,936.1152 119.11764,935.91916 119.08567,935.84713 C 119.11764,935.83126 119.16965,935.82333 119.24167,935.82333 C 119.37765,935.82333 119.47763,935.86734 119.5416,935.95535 C 119.60556,936.04336 119.63754,936.13534 119.63755,936.23129 C 119.63754,936.27133 119.62557,936.32333 119.60165,936.38729 C 119.55356,936.57138 119.48556,936.73141 119.39767,936.8674 C 119.35763,936.93136 119.24556,937.07138 119.0615,937.28744 C 118.90964,937.46322 118.75571,937.59115 118.59969,937.67123 C 118.44369,937.75131 118.25765,937.79135 118.0416,937.79135 C 117.89755,937.79135 117.77956,937.77731 117.68766,937.74923 C 117.59573,937.72116 117.50168,937.6672 117.40548,937.58737 L 116.78182,937.0234 C 116.75765,937.00729 116.73361,936.97122 116.70969,936.91519 C 116.68576,936.85916 116.66574,936.81515 116.64963,936.78317 C 116.59372,936.63937 116.56576,936.48739 116.56576,936.32723 L 116.56576,935.3912 C 116.56576,934.78329 116.69974,934.1553 116.96768,933.50723 C 117.23562,932.85916 117.52158,932.53513 117.82552,932.53512 C 117.87363,932.53513 117.90964,932.53915 117.93357,932.54721 C 118.08566,932.59531 118.27969,932.63132 118.51566,932.65524 C 118.75162,932.67917 118.93563,932.76315 119.06771,932.90719 C 119.19979,933.05124 119.29782,933.18924 119.36178,933.32119 C 119.42575,933.45315 119.45773,933.60715 119.45774,933.78317 L 119.45774,933.78317 z M 118.58176,933.77145 C 118.58176,933.63547 118.55576,933.51547 118.50376,933.41146 C 118.45175,933.30746 118.36972,933.23935 118.25765,933.20712 C 118.13754,933.1832 118.0256,933.31125 117.92185,933.59127 C 117.84982,933.79123 117.80173,933.93124 117.77756,934.01132 C 117.71358,934.17929 117.66562,934.32327 117.63363,934.44326 C 117.60165,934.56326 117.58566,934.69125 117.58567,934.82723 L 118.58176,934.59945 L 118.58176,933.77145 z M 120.69589,937.04721 L 120.80356,929.73947 C 120.80356,929.64328 120.85361,929.5712 120.95371,929.52322 C 121.0538,929.47525 121.16379,929.46323 121.28366,929.48715 C 121.45163,929.51914 121.56564,929.57517 121.6257,929.65524 C 121.68576,929.73532 121.71579,929.85129 121.71579,930.00314 L 121.53562,937.61117 C 121.53561,937.6993 121.45969,937.74337 121.30783,937.74337 C 121.25168,937.74337 121.19162,937.73532 121.12766,937.7192 C 120.91965,937.62326 120.79568,937.5433 120.75576,937.47933 C 120.71585,937.41537 120.69589,937.27133 120.69589,937.04721 L 120.69589,937.04721 z M 125.71554,933.78317 L 125.78733,935.22311 L 124.08335,935.49923 C 123.93955,935.53122 123.84359,935.56723 123.7955,935.60726 C 123.6915,935.68734 123.6395,935.7994 123.6395,935.94345 L 123.6633,936.49533 C 123.6633,936.6152 123.70132,936.72518 123.77738,936.82528 C 123.85343,936.92538 123.94344,936.9714 124.04746,936.96334 C 124.27158,936.93136 124.5516,936.75534 124.88755,936.43527 C 125.22348,936.1152 125.37546,935.91916 125.34348,935.84713 C 125.37546,935.83126 125.42746,935.82333 125.49948,935.82333 C 125.63547,935.82333 125.73543,935.86734 125.79941,935.95535 C 125.86337,936.04336 125.89534,936.13534 125.89535,936.23129 C 125.89534,936.27133 125.88339,936.32333 125.85947,936.38729 C 125.81136,936.57138 125.74338,936.73141 125.65548,936.8674 C 125.61544,936.93136 125.50339,937.07138 125.31931,937.28744 C 125.16745,937.46322 125.01352,937.59115 124.85752,937.67123 C 124.7015,937.75131 124.51547,937.79135 124.29941,937.79135 C 124.15537,937.79135 124.03737,937.77731 123.94547,937.74923 C 123.85354,937.72116 123.75949,937.6672 123.6633,937.58737 L 123.03964,937.0234 C 123.01547,937.00729 122.99142,936.97122 122.9675,936.91519 C 122.94357,936.85916 122.92355,936.81515 122.90743,936.78317 C 122.85153,936.63937 122.82358,936.48739 122.82358,936.32723 L 122.82358,935.3912 C 122.82358,934.78329 122.95755,934.1553 123.22549,933.50723 C 123.49344,932.85916 123.77938,932.53513 124.08335,932.53512 C 124.13144,932.53513 124.16745,932.53915 124.19137,932.54721 C 124.34348,932.59531 124.53751,932.63132 124.77347,932.65524 C 125.00942,932.67917 125.19344,932.76315 125.32552,932.90719 C 125.45761,933.05124 125.55562,933.18924 125.6196,933.32119 C 125.68356,933.45315 125.71554,933.60715 125.71554,933.78317 L 125.71554,933.78317 z M 124.83956,933.77145 C 124.83956,933.63547 124.81357,933.51547 124.76157,933.41146 C 124.70955,933.30746 124.62752,933.23935 124.51547,933.20712 C 124.39535,933.1832 124.28341,933.31125 124.17965,933.59127 C 124.10764,933.79123 124.05954,933.93124 124.03537,934.01132 C 123.97141,934.17929 123.92343,934.32327 123.89145,934.44326 C 123.85947,934.56326 123.84348,934.69125 123.84348,934.82723 L 124.83956,934.59945 L 124.83956,933.77145 z M 126.83358,937.26327 C 126.73764,937.19931 126.68966,937.09933 126.68966,936.96334 C 126.68966,936.88327 126.71366,936.80722 126.76163,936.73519 C 126.8096,936.66317 126.86758,936.60916 126.93558,936.57315 C 127.00357,936.53714 127.06955,936.53524 127.13352,936.56747 C 127.34152,936.70346 127.49752,936.80343 127.60153,936.8674 C 127.80148,936.99533 127.94942,937.05929 128.04538,937.05929 C 128.14937,937.05929 128.2134,937.02127 128.23746,936.94522 C 128.2615,936.86917 128.27353,936.81918 128.27353,936.79525 C 128.27353,936.75521 128.27353,936.72726 128.27353,936.71139 C 128.27353,936.54342 128.18149,936.37936 127.99741,936.2192 C 127.82138,936.08322 127.64737,935.94522 127.47537,935.8052 C 127.30338,935.66519 127.17141,935.52719 127.0795,935.3912 C 126.98757,935.25522 126.92362,935.09921 126.88761,934.92318 C 126.85159,934.74716 126.83358,934.59518 126.83358,934.46725 C 126.83358,934.05929 126.94956,933.6893 127.18149,933.35726 C 127.41342,933.02524 127.70944,932.85922 128.06955,932.85922 C 128.17355,932.85922 128.23752,932.85922 128.26145,932.85922 C 128.3176,932.85922 128.35764,932.86728 128.38155,932.88339 L 128.95761,933.23129 C 129.08553,933.31137 129.17355,933.42343 129.22164,933.56747 C 129.24556,933.63144 129.2695,933.78732 129.29343,934.03512 C 129.29343,934.21921 129.28341,934.37527 129.2634,934.50332 C 129.24338,934.63138 129.20139,934.76742 129.13742,934.91146 L 128.5134,934.91146 C 128.48946,934.84726 128.4515,934.79318 128.39951,934.74923 C 128.3475,934.70529 128.32149,934.48337 128.32151,934.08346 C 128.32149,933.9714 128.32149,933.85531 128.32151,933.73519 C 128.29757,933.67123 128.26157,933.63925 128.21347,933.63925 C 128.13363,933.63925 128.03366,933.73526 127.91354,933.92727 C 127.79343,934.11929 127.72141,934.28732 127.69747,934.43136 C 127.67355,934.6152 127.71761,934.79123 127.82968,934.95944 C 127.90951,935.07931 128.05344,935.22323 128.26145,935.3912 C 128.46945,935.55917 128.68552,935.72714 128.90964,935.89511 C 129.16549,936.12728 129.29343,936.3674 129.29343,936.61544 C 129.29343,936.92745 129.15347,937.21145 128.87357,937.46743 C 128.59366,937.72341 128.32968,937.8514 128.08164,937.8514 C 127.89755,937.8514 127.72549,937.81539 127.56546,937.74337 C 127.40543,937.67135 127.16146,937.51132 126.83358,937.26327 L 126.83358,937.26327 z M 130.2789,937.26327 C 130.18295,937.19931 130.13497,937.09933 130.13497,936.96334 C 130.13497,936.88327 130.15896,936.80722 130.20694,936.73519 C 130.25492,936.66317 130.31289,936.60916 130.38089,936.57315 C 130.44888,936.53714 130.51486,936.53524 130.57882,936.56747 C 130.78683,936.70346 130.94284,936.80343 131.04685,936.8674 C 131.24679,936.99533 131.39475,937.05929 131.49069,937.05929 C 131.5947,937.05929 131.65872,937.02127 131.68277,936.94522 C 131.70682,936.86917 131.71884,936.81918 131.71884,936.79525 C 131.71884,936.75521 131.71884,936.72726 131.71884,936.71139 C 131.71884,936.54342 131.6268,936.37936 131.44271,936.2192 C 131.26668,936.08322 131.09268,935.94522 130.92068,935.8052 C 130.74868,935.66519 130.61673,935.52719 130.52481,935.3912 C 130.43288,935.25522 130.36893,935.09921 130.33292,934.92318 C 130.29691,934.74716 130.2789,934.59518 130.2789,934.46725 C 130.2789,934.05929 130.39487,933.6893 130.6268,933.35726 C 130.85873,933.02524 131.15475,932.85922 131.51486,932.85922 C 131.61886,932.85922 131.68283,932.85922 131.70676,932.85922 C 131.76291,932.85922 131.80294,932.86728 131.82688,932.88339 L 132.40293,933.23129 C 132.53084,933.31137 132.61886,933.42343 132.66696,933.56747 C 132.69089,933.63144 132.71481,933.78732 132.73873,934.03512 C 132.73873,934.21921 132.72872,934.37527 132.70871,934.50332 C 132.68868,934.63138 132.64669,934.76742 132.58274,934.91146 L 131.95871,934.91146 C 131.93478,934.84726 131.89682,934.79318 131.84482,934.74923 C 131.79282,934.70529 131.76682,934.48337 131.76682,934.08346 C 131.76682,933.9714 131.76682,933.85531 131.76682,933.73519 C 131.74289,933.67123 131.70687,933.63925 131.65878,933.63925 C 131.57895,933.63925 131.47897,933.73526 131.35886,933.92727 C 131.23873,934.11929 131.16672,934.28732 131.14279,934.43136 C 131.11886,934.6152 131.16293,934.79123 131.275,934.95944 C 131.35483,935.07931 131.49875,935.22323 131.70676,935.3912 C 131.91476,935.55917 132.13083,935.72714 132.35495,935.89511 C 132.6108,936.12728 132.73873,936.3674 132.73873,936.61544 C 132.73873,936.92745 132.59878,937.21145 132.31887,937.46743 C 132.03897,937.72341 131.77499,937.8514 131.52695,937.8514 C 131.34287,937.8514 131.17081,937.81539 131.01077,937.74337 C 130.85074,937.67135 130.60677,937.51132 130.2789,937.26327 L 130.2789,937.26327 z M 145.64951,931.63534 C 145.6014,932.07529 145.41745,932.48727 145.09762,932.8713 C 144.84152,933.17526 144.45345,933.50326 143.93344,933.85531 L 143.41745,934.20321 L 145.36167,937.13143 C 145.37752,937.21127 145.38546,937.2712 145.38547,937.31124 C 145.38546,937.51119 145.29745,937.6672 145.12143,937.77926 C 145.03354,937.82736 144.94955,937.8514 144.86947,937.8514 C 144.74959,937.8514 144.63961,937.82137 144.53951,937.76132 C 144.43941,937.70126 144.34945,937.6152 144.26962,937.50314 L 141.84567,934.22738 L 141.78562,937.58737 C 141.78561,937.70724 141.68955,937.76718 141.49741,937.76718 C 141.38558,937.76718 141.29757,937.75118 141.23336,937.7192 C 141.0815,937.67135 140.94956,937.62539 140.8375,937.58132 C 140.72543,937.53726 140.6694,937.41525 140.66941,937.2153 L 140.69358,930.0632 C 140.69357,929.91135 140.71554,929.7814 140.75949,929.67336 C 140.80344,929.56534 140.8895,929.48337 141.01766,929.42745 C 141.08164,929.40329 141.14963,929.39121 141.22164,929.3912 C 141.39767,929.39121 141.53769,929.45529 141.64169,929.58346 C 141.84969,929.47946 142.03769,929.41342 142.20566,929.38534 C 142.37363,929.35727 142.60556,929.34323 142.90145,929.34323 L 143.52548,929.34323 C 144.03744,929.34323 144.52144,929.56723 144.97751,930.01522 C 145.43356,930.46323 145.66159,930.95126 145.66159,931.47933 C 145.65353,931.55136 145.6495,931.60336 145.64951,931.63534 L 145.64951,931.63534 z M 141.86947,933.48324 C 142.29354,933.37924 142.61758,933.24124 142.84159,933.06924 C 143.06557,932.89725 143.24959,932.75522 143.39364,932.64315 C 143.70566,932.39535 143.95163,932.11942 144.13155,931.81533 C 144.31149,931.51126 144.40145,931.19919 144.40145,930.87911 C 144.40145,930.68723 144.30544,930.54129 144.11344,930.44131 C 143.92141,930.34134 143.68538,930.29135 143.40537,930.29135 C 143.07747,930.29135 142.76552,930.34336 142.46952,930.44735 C 142.17348,930.55136 141.99752,930.67929 141.94162,930.83114 L 141.86947,933.48324 z M 147.50216,937.62325 C 147.1423,937.47921 146.87833,937.25119 146.71024,936.93917 C 146.54215,936.62716 146.4581,936.25521 146.4581,935.82333 C 146.4581,935.71933 146.4581,935.63131 146.4581,935.55929 C 146.4581,935.48727 146.47409,935.36325 146.50608,935.18722 C 146.52219,934.89132 146.53817,934.65133 146.55405,934.46725 C 146.61826,933.83517 146.87436,933.17917 147.32236,932.49923 C 147.3702,932.41916 147.4421,932.37912 147.53806,932.37911 C 147.59421,932.37912 147.7103,932.41513 147.88631,932.48715 C 148.06234,932.55917 148.19833,932.59518 148.29427,932.59518 C 148.75034,932.59518 149.09432,932.85922 149.32627,933.38729 C 149.52621,933.84335 149.62618,934.42331 149.62618,935.12716 C 149.62618,935.84713 149.53414,936.43112 149.35007,936.87911 C 149.12618,937.4233 148.7822,937.75533 148.31808,937.87521 C 148.23825,937.89132 148.18625,937.89938 148.16208,937.89938 C 148.09811,937.89938 148.03817,937.88339 147.98227,937.8514 C 147.92636,937.81942 147.87033,937.78744 147.81418,937.75546 L 147.50216,937.62325 z M 148.03025,933.43527 C 147.91012,933.69919 147.79208,933.96518 147.67612,934.23324 C 147.56015,934.50131 147.50216,934.83127 147.50216,935.22311 C 147.50216,935.34323 147.49019,935.51931 147.46627,935.75137 C 147.44235,935.98343 147.43039,936.15145 147.43039,936.25546 C 147.43039,936.45541 147.46035,936.62539 147.5203,936.76541 C 147.58023,936.90542 147.69016,937.01937 147.85007,937.10726 C 147.97824,937.17929 148.11826,937.01132 148.27011,936.60336 C 148.38217,936.29135 148.47421,935.91134 148.54623,935.46334 C 148.57015,935.3193 148.58212,935.16732 148.58212,935.00741 C 148.58212,934.62338 148.53011,934.25137 148.42612,933.89138 C 148.3221,933.5314 148.22214,933.35141 148.12618,933.3514 C 148.08615,933.35141 148.05417,933.37936 148.03025,933.43527 L 148.03025,933.43527 z M 151.76157,937.77926 C 151.64145,937.77926 151.54147,937.75521 151.46164,937.70712 L 151.05331,937.45516 C 150.98154,937.41513 150.92362,937.36715 150.87955,937.31124 C 150.83548,937.25534 150.78146,937.16732 150.7175,937.04721 C 150.6135,936.84725 150.56149,936.64328 150.5615,936.43527 L 150.72959,933.02731 C 150.72959,932.94723 150.78159,932.9072 150.88558,932.90719 C 150.93344,932.9072 150.99741,932.91721 151.07748,932.93722 C 151.15756,932.95724 151.23959,932.97726 151.32358,932.99728 C 151.40756,933.0173 151.50949,933.07529 151.62937,933.17123 L 151.46164,935.79916 C 151.46164,936.23935 151.48557,936.56344 151.53341,936.77145 C 151.58956,936.84323 151.6456,936.91122 151.7015,936.97543 C 151.97348,936.78329 152.14945,936.61526 152.2294,936.47134 C 152.30936,936.32742 152.34932,936.17538 152.34933,936.01522 L 152.4094,932.67941 L 152.72141,932.64315 C 153.01755,932.64316 153.27353,932.74716 153.48935,932.95516 L 153.47763,937.83932 C 153.40561,937.8713 153.32149,937.88729 153.2253,937.88729 C 153.20138,937.88729 153.14742,937.87728 153.06345,937.85726 C 152.97946,937.83724 152.90347,937.80721 152.83548,937.76718 C 152.76748,937.72714 152.72946,937.6672 152.72141,937.58737 L 152.64963,937.08346 L 152.13363,937.63534 C 152.03744,937.73129 151.91342,937.77926 151.76157,937.77926 L 151.76157,937.77926 z M 155.04757,933.75936 L 154.85567,933.66342 L 154.66342,933.66342 L 154.48361,933.43527 L 154.45944,932.85922 L 155.07174,932.72738 L 155.13144,930.44735 C 155.2596,930.41538 155.38363,930.39939 155.50351,930.39938 C 155.57552,930.39939 155.66153,930.40738 155.7615,930.42336 C 155.86148,930.43936 155.99545,930.4874 156.16342,930.56747 L 156.13961,932.60726 L 156.6677,932.60726 L 156.83542,932.91928 L 156.8237,933.49533 L 156.0195,933.57919 L 156.0195,937.75546 C 155.93161,937.77938 155.85959,937.79135 155.80344,937.79135 C 155.74752,937.79135 155.68154,937.78134 155.60549,937.76132 C 155.52945,937.7413 155.45553,937.72323 155.38376,937.70712 C 155.10372,937.62728 154.96372,937.4714 154.96372,937.23947 L 155.04757,933.75936 z M 160.42649,933.78317 L 160.49826,935.22311 L 158.79427,935.49923 C 158.65048,935.53122 158.55454,935.56723 158.50644,935.60726 C 158.40243,935.68734 158.35043,935.7994 158.35044,935.94345 L 158.37423,936.49533 C 158.37423,936.6152 158.41226,936.72518 158.4883,936.82528 C 158.56436,936.92538 158.65438,936.9714 158.75839,936.96334 C 158.98251,936.93136 159.26254,936.75534 159.59848,936.43527 C 159.93442,936.1152 160.08639,935.91916 160.05442,935.84713 C 160.08639,935.83126 160.1384,935.82333 160.21042,935.82333 C 160.3464,935.82333 160.44638,935.86734 160.51035,935.95535 C 160.57431,936.04336 160.60629,936.13534 160.6063,936.23129 C 160.60629,936.27133 160.59432,936.32333 160.5704,936.38729 C 160.52231,936.57138 160.45431,936.73141 160.36642,936.8674 C 160.32638,936.93136 160.21431,937.07138 160.03025,937.28744 C 159.87839,937.46322 159.72446,937.59115 159.56844,937.67123 C 159.41244,937.75131 159.2264,937.79135 159.01035,937.79135 C 158.8663,937.79135 158.74831,937.77731 158.65641,937.74923 C 158.56448,937.72116 158.47043,937.6672 158.37423,937.58737 L 157.75057,937.0234 C 157.7264,937.00729 157.70236,936.97122 157.67844,936.91519 C 157.65451,936.85916 157.63449,936.81515 157.61838,936.78317 C 157.56247,936.63937 157.53451,936.48739 157.53451,936.32723 L 157.53451,935.3912 C 157.53451,934.78329 157.66849,934.1553 157.93643,933.50723 C 158.20437,932.85916 158.49033,932.53513 158.79427,932.53512 C 158.84238,932.53513 158.87839,932.53915 158.90232,932.54721 C 159.05441,932.59531 159.24844,932.63132 159.48441,932.65524 C 159.72037,932.67917 159.90438,932.76315 160.03646,932.90719 C 160.16854,933.05124 160.26657,933.18924 160.33053,933.32119 C 160.3945,933.45315 160.42648,933.60715 160.42649,933.78317 L 160.42649,933.78317 z M 159.55051,933.77145 C 159.55051,933.63547 159.52451,933.51547 159.47251,933.41146 C 159.4205,933.30746 159.33847,933.23935 159.2264,933.20712 C 159.10629,933.1832 158.99435,933.31125 158.8906,933.59127 C 158.81857,933.79123 158.77048,933.93124 158.74631,934.01132 C 158.68233,934.17929 158.63437,934.32327 158.60238,934.44326 C 158.5704,934.56326 158.55441,934.69125 158.55442,934.82723 L 159.55051,934.59945 L 159.55051,933.77145 z M 161.68845,932.8713 L 161.79648,932.73947 L 162.0726,932.73947 C 162.13657,932.73947 162.21053,932.77743 162.29452,932.85336 C 162.37851,932.92929 162.44455,932.97531 162.49265,932.99142 C 162.60446,933.02341 162.69638,932.93741 162.76839,932.73342 C 162.84042,932.52945 162.92641,932.41342 163.0264,932.38534 C 163.12636,932.35727 163.19638,932.34323 163.23641,932.34323 C 163.38852,932.34323 163.52255,932.39725 163.63852,932.50527 C 163.75449,932.61331 163.81247,932.74326 163.81247,932.89511 L 163.80039,933.36312 C 163.80038,933.5953 163.77237,933.80539 163.71634,933.99337 C 163.6603,934.18136 163.57236,934.27536 163.45248,934.27535 C 163.26839,934.27536 163.14437,934.0953 163.08042,933.73519 C 163.04843,933.55917 162.9924,933.47116 162.91233,933.47116 C 162.84054,933.47116 162.78261,933.53116 162.73855,933.65115 C 162.69449,933.77115 162.67246,934.0112 162.67246,934.3713 C 162.67246,934.48337 162.67246,934.58139 162.67246,934.66537 C 162.67246,934.74936 162.67246,934.81527 162.67246,934.86312 L 162.67246,937.79135 C 162.58456,937.83138 162.48459,937.8514 162.37252,937.8514 C 162.30051,937.8514 162.2125,937.83938 162.10849,937.81533 C 162.00449,937.79128 161.90451,937.73324 161.80857,937.6412 C 161.71261,937.54916 161.66464,937.43124 161.66464,937.28744 L 161.68845,932.8713 z" + id="text3253" /> + <path + style="fill:#ececec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3304)" + d="M 62.895745,955.18402 C 62.360025,954.46973 57.538595,951.43402 57.181455,948.04116 C 56.824315,944.6483 57.895745,913.75545 57.895745,913.75545 C 57.895745,913.75545 60.217165,909.11259 62.181455,908.57688 C 64.145745,908.04116 65.217165,907.50545 65.217165,907.50545 L 53.610025,904.82688 L 50.931455,901.43402 L 51.467165,913.93402 L 52.360025,944.6483 C 52.360025,944.6483 51.645745,947.68402 50.395745,948.04116 C 49.145745,948.3983 53.074315,953.57688 53.967165,953.93402 C 54.860025,954.29116 62.895745,955.54116 62.895745,955.18402 z" + id="path3282" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#fdfdfd;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3368)" + d="M 47.895745,901.79116 C 47.895745,901.79116 51.288595,907.68402 54.502885,908.04116 C 57.717165,908.3983 64.681455,909.6483 68.967165,909.6483 C 73.252885,909.6483 73.252885,909.6483 73.252885,909.6483" + id="path3284" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#fdfdfd;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 76.467165,910.00545 L 264.32431,909.29116" + id="path3286" /> + <path + style="fill:#2c2c2c;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 59.598995,868.26188 L 91.671345,744.01311 C 91.671345,744.01311 93.439105,737.19458 100.76272,733.65905 C 108.08633,730.12352 119.45054,729.61844 119.45054,729.61844 C 119.45054,729.61844 522.5014,731.13367 523.51155,731.13367 C 524.5217,731.13367 538.91638,733.91159 542.19938,737.19458 C 545.48237,740.47758 548.51283,747.54865 548.51283,747.54865 L 575.02933,869.52457 C 575.02933,869.52457 576.03949,876.3431 573.76664,877.35325 C 571.4938,878.3634 564.92782,878.3634 564.92782,878.3634 L 65.407375,874.82786 C 65.407375,874.82786 60.609145,873.56518 59.851535,872.30249 C 59.093915,871.0398 59.851535,868.51442 59.598995,868.26188 z" + id="path3752" + sodipodi:nodetypes="ccscssccsccsc" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#868686;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3982)" + d="M 59.346455,869.77711 C 59.346455,869.77711 59.598995,872.30249 61.366765,873.0601 C 63.134535,873.81772 67.175145,873.81772 67.175145,873.81772 L 570.23111,877.85833 C 570.23111,877.85833 573.00902,878.11086 574.52425,876.3431 C 576.03949,874.57533 575.53441,870.02964 575.53441,870.02964" + id="path3754" /> + <path + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3962)" + d="M 163.96716,740.00545 L 162.18145,748.13045 L 166.02075,748.13045 L 168.07432,740.18402 L 163.96716,740.00545 z" + id="path3810" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3958)" + d="M 168.69932,739.20188 C 168.25288,739.20188 163.7886,739.0233 163.7886,739.0233" + id="path3812" /> + <path + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3954)" + d="M 189.36894,739.91617 L 188.29753,748.13046 L 192.13682,748.13046 L 193.4761,740.09474 L 189.36894,739.91617 z" + id="path3814" + sodipodi:nodetypes="ccccc" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3950)" + d="M 193.92253,739.1126 C 193.4761,739.1126 189.01181,738.93402 189.01181,738.93402" + id="path3816" /> + <path + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3946)" + d="M 215.44037,739.46974 L 214.45824,747.59474 L 218.29752,747.59474 L 219.54752,739.64831 L 215.44037,739.46974 z" + id="path3818" + sodipodi:nodetypes="ccccc" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3942)" + d="M 220.17252,738.66617 C 219.7261,738.66617 215.26181,738.48759 215.26181,738.48759" + id="path3820" /> + <path + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3938)" + d="M 241.06539,740.00545 L 240.17254,748.13045 L 244.01183,748.13045 L 245.17254,740.18402 L 241.06539,740.00545 z" + id="path3822" + sodipodi:nodetypes="ccccc" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3934)" + d="M 245.79754,739.20188 C 245.3511,739.20188 240.88681,739.0233 240.88681,739.0233" + id="path3824" /> + <path + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3930)" + d="M 266.33324,739.64831 L 265.79752,748.39831 L 269.63681,748.39831 L 270.44038,739.82688 L 266.33324,739.64831 z" + id="path3826" + sodipodi:nodetypes="ccccc" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3926)" + d="M 271.06538,738.84474 C 270.61895,738.84474 266.15466,738.66616 266.15466,738.66616" + id="path3828" /> + <path + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3922)" + d="M 291.95824,740.36259 L 291.69039,748.75545 L 295.52968,748.75545 L 296.06539,740.54116 L 291.95824,740.36259 z" + id="path3830" + sodipodi:nodetypes="ccccc" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3918)" + d="M 296.69039,739.55902 C 296.24397,739.55902 291.77967,739.38044 291.77967,739.38044" + id="path3832" /> + <path + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3914)" + d="M 318.11895,740.63045 L 317.76181,749.20188 L 321.6011,749.20188 L 322.2261,740.80902 L 318.11895,740.63045 z" + id="path3834" + sodipodi:nodetypes="ccccc" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3910)" + d="M 322.8511,739.82688 C 322.40467,739.82688 317.94038,739.6483 317.94038,739.6483" + id="path3836" /> + <path + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3906)" + d="M 343.56537,740.63045 L 343.65466,748.84474 L 347.49395,748.84474 L 347.67252,740.80902 L 343.56537,740.63045 z" + id="path3838" + sodipodi:nodetypes="ccccc" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3902)" + d="M 348.29752,739.82688 C 347.85109,739.82688 343.3868,739.6483 343.3868,739.6483" + id="path3840" /> + <path + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3898)" + d="M 369.81537,740.36259 L 369.81537,748.93402 L 373.65466,748.93402 L 373.92252,740.54116 L 369.81537,740.36259 z" + id="path3842" + sodipodi:nodetypes="ccccc" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3894)" + d="M 374.54752,739.55902 C 374.10109,739.55902 369.6368,739.38044 369.6368,739.38044" + id="path3844" /> + <path + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3890)" + d="M 394.27966,740.98759 L 394.99395,749.38045 L 398.83324,749.38045 L 398.38681,741.16616 L 394.27966,740.98759 z" + id="path3846" + sodipodi:nodetypes="ccccc" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3886)" + d="M 399.01181,740.18402 C 398.56538,740.18402 394.10109,740.00544 394.10109,740.00544" + id="path3848" /> + <path + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3882)" + d="M 420.61895,740.63045 L 421.24395,749.02331 L 425.08324,749.02331 L 424.7261,740.80902 L 420.61895,740.63045 z" + id="path3850" + sodipodi:nodetypes="ccccc" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3878)" + d="M 425.3511,739.82688 C 424.90467,739.82688 420.44038,739.6483 420.44038,739.6483" + id="path3852" /> + <path + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3874)" + d="M 446.15466,741.34473 L 447.13681,749.6483 L 450.9761,749.6483 L 450.26181,741.5233 L 446.15466,741.34473 z" + id="path3854" + sodipodi:nodetypes="ccccc" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3870)" + d="M 450.88681,740.54116 C 450.44038,740.54116 445.97609,740.36258 445.97609,740.36258" + id="path3856" /> + <path + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3866)" + d="M 472.1368,741.07688 L 473.20823,749.55902 L 477.04752,749.55902 L 476.24396,741.25545 L 472.1368,741.07688 z" + id="path3858" + sodipodi:nodetypes="ccccc" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3862)" + d="M 476.86896,740.27331 C 476.42252,740.27331 471.95823,740.09473 471.95823,740.09473" + id="path3860" /> + <path + style="fill:#010101;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3996)" + d="M 457.00288,731.43402 L 458.43145,635.00545 L 457.71716,507.86259 C 457.71716,507.86259 460.10513,497.86259 471.28859,497.1483 C 478.45249,496.69074 482.71717,509.29116 482.71717,509.29116 L 482.71717,651.43402 L 484.14574,731.43402 L 457.00288,731.43402 z" + id="path3986" + sodipodi:nodetypes="cccscccc" /> + </g> +</svg> diff --git a/wpa_supplicant/wpa_gui-qt4/icons/group.svg b/wpa_supplicant/wpa_gui-qt4/icons/group.svg new file mode 100644 index 0000000000000..4ea959b5779fd --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/icons/group.svg @@ -0,0 +1,616 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + height="160.00000" + id="Andysvg" + inkscape:version="0.46" + sodipodi:docbase="/home/andy/Desktop/etiquette-icons-0.4/scalable/filesystems" + sodipodi:docname="gnome-fs-network.svg" + sodipodi:version="0.32" + version="1.0" + width="160.00000" + x="0.00000000" + y="0.00000000" + inkscape:output_extension="org.inkscape.output.svg.inkscape" + inkscape:export-filename="C:\Documents and Settings\All Users\Documents\Ubuntu Brig\Andy Fitzsimon\gnome-fs-network.png" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90"> + <metadata + id="metadata3"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:title>Etiquette Icons</dc:title> + <dc:description /> + <dc:subject> + <rdf:Bag> + <rdf:li>hash</rdf:li> + <rdf:li /> + <rdf:li>filesystem</rdf:li> + <rdf:li>computer</rdf:li> + <rdf:li>icons</rdf:li> + </rdf:Bag> + </dc:subject> + <dc:publisher> + <cc:Agent + rdf:about="http://www.openclipart.org"> + <dc:title>Andy Fitzsimon</dc:title> + </cc:Agent> + </dc:publisher> + <dc:creator> + <cc:Agent> + <dc:title>Andy Fitzsimon</dc:title> + </cc:Agent> + </dc:creator> + <dc:rights> + <cc:Agent> + <dc:title>Andy Fitzsimon</dc:title> + </cc:Agent> + </dc:rights> + <dc:date /> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <cc:license + rdf:resource="http://web.resource.org/cc/PublicDomain" /> + <dc:language>en</dc:language> + </cc:Work> + <cc:License + rdf:about="http://web.resource.org/cc/PublicDomain"> + <cc:permits + rdf:resource="http://web.resource.org/cc/Reproduction" /> + <cc:permits + rdf:resource="http://web.resource.org/cc/Distribution" /> + <cc:permits + rdf:resource="http://web.resource.org/cc/DerivativeWorks" /> + </cc:License> + </rdf:RDF> + </metadata> + <defs + id="defs3"> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="0 : 80 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="160 : 80 : 1" + inkscape:persp3d-origin="80 : 53.333333 : 1" + id="perspective97" /> + <linearGradient + id="linearGradient4894"> + <stop + id="stop4895" + offset="0.0000000" + style="stop-color:#ffffff;stop-opacity:1.0000000;" /> + <stop + id="stop4896" + offset="0.47000000" + style="stop-color:#ffffff;stop-opacity:0.85567009;" /> + <stop + id="stop4897" + offset="1.0000000" + style="stop-color:#ffffff;stop-opacity:0.0000000;" /> + </linearGradient> + <linearGradient + id="linearGradient1853"> + <stop + id="stop1854" + offset="0.0000000" + style="stop-color:#ffffff;stop-opacity:1.0000000;" /> + <stop + id="stop1855" + offset="0.47000000" + style="stop-color:#ffffff;stop-opacity:0.85567009;" /> + <stop + id="stop1856" + offset="1.0000000" + style="stop-color:#ffffff;stop-opacity:0.0000000;" /> + </linearGradient> + <linearGradient + id="linearGradient1806"> + <stop + id="stop1807" + offset="0.0000000" + style="stop-color:#000000;stop-opacity:0.35051546;" /> + <stop + id="stop3276" + offset="0.64999998" + style="stop-color:#000000;stop-opacity:0.13402061;" /> + <stop + id="stop1808" + offset="1.0000000" + style="stop-color:#000000;stop-opacity:0.0000000;" /> + </linearGradient> + <linearGradient + id="linearGradient893"> + <stop + id="stop895" + offset="0" + style="stop-color:#000;stop-opacity:1;" /> + <stop + id="stop896" + offset="1" + style="stop-color:#fff;stop-opacity:1;" /> + </linearGradient> + <linearGradient + id="linearGradient1317"> + <stop + id="stop1318" + offset="0.00000000" + style="stop-color:#000000;stop-opacity:0.52892560;" /> + <stop + id="stop1320" + offset="0.50000000" + style="stop-color:#000000;stop-opacity:0.17355372;" /> + <stop + id="stop1319" + offset="1.0000000" + style="stop-color:#000000;stop-opacity:0.00000000;" /> + </linearGradient> + <linearGradient + id="linearGradient1133"> + <stop + id="stop1134" + offset="0.00000000" + style="stop-color:#8bb7df;stop-opacity:1.0000000;" /> + <stop + id="stop1136" + offset="0.76209301" + style="stop-color:#2a6092;stop-opacity:1.0000000;" /> + <stop + id="stop1135" + offset="1.0000000" + style="stop-color:#375e82;stop-opacity:1.0000000;" /> + </linearGradient> + <linearGradient + id="linearGradient1098"> + <stop + id="stop1099" + offset="0.00000000" + style="stop-color:#ffffff;stop-opacity:1.0000000;" /> + <stop + id="stop1101" + offset="0.50000000" + style="stop-color:#ffffff;stop-opacity:0.22314049;" /> + <stop + id="stop1102" + offset="0.59930235" + style="stop-color:#ffffff;stop-opacity:0.00000000;" /> + <stop + id="stop1100" + offset="1.0000000" + style="stop-color:#ffffff;stop-opacity:0.60330576;" /> + </linearGradient> + <linearGradient + id="linearGradient902"> + <stop + id="stop903" + offset="0.00000000" + style="stop-color:#000000;stop-opacity:0.00000000;" /> + <stop + id="stop904" + offset="1.0000000" + style="stop-color:#000000;stop-opacity:0.22000000;" /> + </linearGradient> + <linearGradient + id="linearGradient892"> + <stop + id="stop893" + offset="0.0000000" + style="stop-color:#ffffff;stop-opacity:0.0000000;" /> + <stop + id="stop894" + offset="1" + style="stop-color:#fff;stop-opacity:1;" /> + </linearGradient> + <linearGradient + id="linearGradient888"> + <stop + id="stop889" + offset="0.0000000" + style="stop-color:#626262;stop-opacity:1.0000000;" /> + <stop + id="stop890" + offset="1" + style="stop-color:#fff;stop-opacity:1;" /> + </linearGradient> + <linearGradient + id="linearGradient891" + x1="1.3485916" + x2="0.024647888" + xlink:href="#linearGradient888" + y1="-0.85185188" + y2="1.0899471" /> + <linearGradient + id="linearGradient901" + spreadMethod="pad" + x1="1.5803921" + x2="0.14117648" + xlink:href="#linearGradient888" + y1="2.4285715" + y2="-0.38571429" /> + <linearGradient + id="linearGradient905" + x1="-1.5389611" + x2="1.0909091" + xlink:href="#linearGradient888" + y1="2.7890625" + y2="-0.19531250" /> + <radialGradient + cx="0.10362694" + cy="0.093750000" + fx="0.10362694" + fy="0.093750000" + id="radialGradient1132" + r="1.2958785" + xlink:href="#linearGradient1133" /> + <linearGradient + id="linearGradient1138" + xlink:href="#linearGradient4894" /> + <linearGradient + id="linearGradient1140" + x1="0.54117650" + x2="0.57647061" + xlink:href="#linearGradient888" + y1="-2.4210527" + y2="4.6315789" /> + <linearGradient + id="linearGradient1141" + x1="1.8281938" + x2="-0.0088105723" + xlink:href="#linearGradient888" + y1="3.0546875" + y2="-0.44531250" /> + <linearGradient + id="linearGradient1144" + x1="0.21960784" + x2="0.59607846" + xlink:href="#linearGradient1853" + y1="-11.111111" + y2="5.2777777" /> + <linearGradient + id="linearGradient1146" + x1="0.51351351" + x2="-0.076576576" + xlink:href="#linearGradient892" + y1="0.55468750" + y2="1.1875000" /> + <linearGradient + id="linearGradient1148" + x1="0.23245615" + x2="1.0789474" + xlink:href="#linearGradient892" + y1="0.15625000" + y2="-0.64843750" /> + <linearGradient + id="linearGradient1150" + x1="0.25221238" + x2="-0.57522124" + xlink:href="#linearGradient892" + y1="0.57812500" + y2="1.4765625" /> + <linearGradient + id="linearGradient1156" + x1="0.48260871" + x2="0.48260871" + xlink:href="#linearGradient888" + y1="-0.40000001" + y2="1.8750000" /> + <linearGradient + id="linearGradient1157" + x1="1.5528169" + x2="-1.2077465" + xlink:href="#linearGradient888" + y1="3.3265307" + y2="-0.48979592" /> + <linearGradient + id="linearGradient1166" + x1="0.52941179" + x2="0.57647061" + xlink:href="#linearGradient1317" + y1="-3.5714285" + y2="4.6315789" /> + <linearGradient + id="linearGradient1167" + x1="1.6111112" + x2="-0.083333336" + xlink:href="#linearGradient888" + y1="3.0703125" + y2="0.046875000" /> + <linearGradient + id="linearGradient1169" + x1="1.4780220" + x2="-0.13028169" + xlink:href="#linearGradient893" + y1="2.9218750" + y2="-0.26732674" /> + <linearGradient + gradientTransform="scale(0.998371,1.001632)" + id="linearGradient1170" + x1="0.47284532" + x2="0.48655096" + xlink:href="#linearGradient902" + y1="-0.016295359" + y2="1.8378206" /> + <linearGradient + id="linearGradient1171" + x1="0.83050847" + x2="0.56355929" + xlink:href="#linearGradient902" + y1="0.57812500" + y2="0.36718750" /> + <radialGradient + cx="0.088082902" + cy="0.093750000" + fx="0.090673581" + fy="0.10937500" + id="radialGradient1315" + r="1.1765809" + xlink:href="#linearGradient1133" /> + <radialGradient + cx="0.50000000" + cy="0.50000006" + fx="0.50352114" + fy="0.18269235" + id="radialGradient1316" + r="0.34964636" + xlink:href="#linearGradient1317" /> + <linearGradient + id="linearGradient1404" + x1="0.53169012" + x2="0.54577464" + xlink:href="#linearGradient892" + y1="0.28888890" + y2="1.1000000" /> + <linearGradient + gradientTransform="scale(0.997825,1.002180)" + id="linearGradient1505" + x1="0.47157744" + x2="0.48548824" + xlink:href="#linearGradient902" + y1="-0.024853170" + y2="1.8570156" /> + <linearGradient + gradientTransform="scale(0.995847,1.004170)" + id="linearGradient1506" + x1="0.47042510" + x2="0.48481107" + xlink:href="#linearGradient902" + y1="-0.043652620" + y2="1.9025002" /> + <linearGradient + gradientTransform="scale(0.997153,1.002855)" + id="linearGradient2740" + x1="0.47041038" + x2="0.48453596" + xlink:href="#linearGradient902" + y1="-0.033741195" + y2="1.8771822" /> + <linearGradient + id="linearGradient4283" + x1="-0.77314812" + x2="0.99074072" + xlink:href="#linearGradient893" + y1="2.0837989" + y2="-0.033519555" /> + <linearGradient + id="linearGradient4284" + x1="-2.3960868e-17" + x2="0.92957747" + xlink:href="#linearGradient893" + y1="3.3012049" + y2="-0.45783132" /> + <radialGradient + cx="0.50000000" + cy="0.50000000" + fx="0.50000000" + fy="0.50000000" + id="radialGradient1977" + r="0.50000000" + xlink:href="#linearGradient1853" /> + </defs> + <sodipodi:namedview + bordercolor="#666666" + borderopacity="1.0" + id="base" + inkscape:cx="62.122256" + inkscape:cy="81.091465" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:window-height="667" + inkscape:window-width="573" + inkscape:window-x="380" + inkscape:window-y="151" + inkscape:zoom="2" + pagecolor="#ffffff" + showborder="true" + showgrid="false" + inkscape:current-layer="Andysvg" /> + <path + d="M 26.564473,83.749649 L 26.564473,121.41271 L 57.756286,121.41271" + id="path3723" + sodipodi:nodetypes="ccc" + style="fill:none;fill-rule:evenodd;stroke:#9c9c9c;stroke-width:5.7184987;stroke-linecap:round;stroke-linejoin:round;" /> + <g + id="g2843" + transform="matrix(0.999379,0.000000,0.000000,0.999379,1.227893e-3,3.986513)"> + <rect + height="8.3153667" + id="rect1906" + style="fill:url(#linearGradient1156);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient1157);stroke-width:1.4473482pt;" + transform="matrix(0.716224,0.000000,0.000000,0.716224,-12.57051,-9.652832)" + width="57.567924" + x="33.326111" + y="78.658051" /> + <rect + height="60.126495" + id="rect1907" + rx="5.4369707" + ry="5.4369707" + style="fill:url(#linearGradient905);fill-opacity:1;fill-rule:evenodd;stroke-width:1.6282668;" + transform="matrix(0.716224,0.000000,0.000000,0.716224,-12.57051,-9.652832)" + width="72.279724" + x="26.015469" + y="22.413721" /> + <rect + height="38.044163" + id="rect1908" + style="fill:url(#radialGradient1315);fill-rule:evenodd;stroke:url(#linearGradient891);stroke-width:1.4649456pt;" + transform="matrix(0.716224,0.000000,0.000000,0.716224,-12.57051,-9.652832)" + width="58.178177" + x="33.386066" + y="31.695871" /> + <path + d="M 27.690431,52.841444 L 27.370609,74.749236 C 27.319624,78.241665 29.310209,80.477938 32.807578,80.506029 L 72.625393,80.825852 L 76.463254,71.870840 L 32.008024,71.551020 L 31.688202,52.681533 L 27.690431,52.841444 z " + id="path1909" + sodipodi:nodetypes="czzccccc" + style="fill:url(#linearGradient1146);fill-opacity:1;fill-rule:evenodd;stroke-width:1.0000000pt;" + transform="matrix(0.716224,0.000000,0.000000,0.716224,-12.57051,-9.652832)" /> + <rect + height="26.147448" + id="rect1913" + rx="7.4449978" + ry="7.4449978" + style="fill:url(#linearGradient901);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient1157);stroke-width:2.3625000;" + transform="matrix(0.571582,0.000000,0.000000,0.571582,-77.72566,72.35541)" + width="104.09673" + x="140.62315" + y="-34.316952" /> + <rect + height="15.829688" + id="rect1914" + rx="3.7576280" + ry="3.7576280" + style="fill:url(#linearGradient901);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient1157);stroke-width:1.3591428;" + transform="matrix(0.571582,0.000000,0.000000,0.571582,-77.72566,72.35541)" + width="56.908955" + x="184.04552" + y="-28.539845" /> + <rect + height="15.829688" + id="rect1915" + rx="2.9970589" + ry="2.9970589" + style="fill:url(#linearGradient1141);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient1157);stroke-width:0.96249998;" + transform="matrix(0.571582,0.000000,0.000000,0.571582,-77.72566,72.35541)" + width="28.796961" + x="145.28902" + y="-28.227346" /> + <rect + height="3.3627598" + id="rect1916" + rx="1.6813799" + ry="1.6813799" + style="fill-opacity:0.13836475;fill-rule:evenodd;stroke-width:0.46326005;" + transform="matrix(0.571582,0.000000,0.000000,0.571582,-77.72566,72.35541)" + width="49.231453" + x="187.88426" + y="-21.681381" /> + </g> + <path + style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:helvetica" + d="M 7.0612345,-14.660837 L 7.0612345,-23.250681 L 8.2272501,-23.250681 L 12.738969,-16.50654 L 12.738969,-23.250681 L 13.828813,-23.250681 L 13.828813,-14.660837 L 12.662797,-14.660837 L 8.1510782,-21.410837 L 8.1510782,-14.660837 L 7.0612345,-14.660837 z M 19.869828,-16.664743 L 20.959672,-16.529978 C 20.787791,-15.893258 20.469432,-15.399118 20.004594,-15.047556 C 19.539745,-14.695993 18.945996,-14.520212 18.223344,-14.520212 C 17.313185,-14.520212 16.591506,-14.800485 16.058305,-15.361032 C 15.525101,-15.921578 15.2585,-16.70771 15.2585,-17.719431 C 15.2585,-18.766302 15.528031,-19.578801 16.067094,-20.156931 C 16.606155,-20.73505 17.305373,-21.024112 18.16475,-21.024118 C 18.996777,-21.024112 19.676464,-20.740909 20.203813,-20.174509 C 20.73115,-19.608098 20.994822,-18.811224 20.994828,-17.783884 C 20.994822,-17.721381 20.992869,-17.627631 20.988969,-17.502634 L 16.348344,-17.502634 C 16.387405,-16.819038 16.580764,-16.295601 16.928422,-15.932322 C 17.276076,-15.569039 17.709669,-15.387399 18.229203,-15.3874 C 18.615918,-15.387399 18.945996,-15.488961 19.219438,-15.692087 C 19.49287,-15.895211 19.709667,-16.219429 19.869828,-16.664743 L 19.869828,-16.664743 z M 16.406938,-18.369822 L 19.881547,-18.369822 C 19.834667,-18.893255 19.701855,-19.285833 19.483109,-19.547556 C 19.147168,-19.953801 18.711621,-20.156925 18.176469,-20.156931 C 17.692091,-20.156925 17.284865,-19.994816 16.954789,-19.670603 C 16.624709,-19.346379 16.442092,-18.912786 16.406938,-18.369822 L 16.406938,-18.369822 z M 24.592484,-15.604197 L 24.744828,-14.672556 C 24.44795,-14.610056 24.182326,-14.578806 23.947953,-14.578806 C 23.565139,-14.578806 23.268264,-14.639353 23.057328,-14.760447 C 22.846389,-14.88154 22.697952,-15.04072 22.612016,-15.237986 C 22.526077,-15.43525 22.483108,-15.850289 22.483109,-16.483103 L 22.483109,-20.063181 L 21.709672,-20.063181 L 21.709672,-20.883493 L 22.483109,-20.883493 L 22.483109,-22.424509 L 23.531938,-23.057322 L 23.531938,-20.883493 L 24.592484,-20.883493 L 24.592484,-20.063181 L 23.531938,-20.063181 L 23.531938,-16.424509 C 23.531936,-16.123726 23.55049,-15.930367 23.587602,-15.844431 C 23.624709,-15.758492 23.685256,-15.690133 23.769242,-15.639353 C 23.853224,-15.588571 23.973341,-15.56318 24.129594,-15.563181 C 24.246779,-15.56318 24.401075,-15.576852 24.592484,-15.604197 L 24.592484,-15.604197 z M 26.766313,-14.660837 L 24.862016,-20.883493 L 25.951859,-20.883493 L 26.942094,-17.291697 L 27.311234,-15.955759 C 27.326857,-16.022164 27.434279,-16.449898 27.6335,-17.238962 L 28.623734,-20.883493 L 29.707719,-20.883493 L 30.639359,-17.274118 L 30.949906,-16.084665 L 31.307328,-17.285837 L 32.373734,-20.883493 L 33.399125,-20.883493 L 31.453813,-14.660837 L 30.358109,-14.660837 L 29.367875,-18.3874 L 29.127641,-19.447947 L 27.867875,-14.660837 L 26.766313,-14.660837 z M 33.897172,-17.772165 C 33.897172,-18.924505 34.217484,-19.77802 34.858109,-20.332712 C 35.393264,-20.793644 36.045607,-21.024112 36.815141,-21.024118 C 37.670605,-21.024112 38.369823,-20.743839 38.912797,-20.183298 C 39.45576,-19.622746 39.727244,-18.848333 39.72725,-17.860056 C 39.727244,-17.059272 39.607127,-16.42939 39.366899,-15.970407 C 39.126659,-15.511422 38.77705,-15.154977 38.31807,-14.901072 C 37.859082,-14.647165 37.358106,-14.520212 36.815141,-14.520212 C 35.944045,-14.520212 35.239944,-14.799509 34.702836,-15.358103 C 34.165726,-15.916695 33.897172,-16.721382 33.897172,-17.772165 L 33.897172,-17.772165 z M 34.981156,-17.772165 C 34.981155,-16.975288 35.154983,-16.378609 35.502641,-15.982126 C 35.850295,-15.585641 36.287794,-15.387399 36.815141,-15.3874 C 37.338574,-15.387399 37.774121,-15.586617 38.121781,-15.985056 C 38.469433,-16.383492 38.643261,-16.990913 38.643266,-17.807322 C 38.643261,-18.576849 38.468456,-19.159856 38.118852,-19.556345 C 37.769238,-19.952824 37.334668,-20.151066 36.815141,-20.151072 C 36.287794,-20.151066 35.850295,-19.953801 35.502641,-19.559275 C 35.154983,-19.164739 34.981155,-18.569036 34.981156,-17.772165 L 34.981156,-17.772165 z M 40.957719,-14.660837 L 40.957719,-20.883493 L 41.906938,-20.883493 L 41.906938,-19.940134 C 42.149123,-20.381535 42.372756,-20.67255 42.577836,-20.813181 C 42.782912,-20.9538 43.008497,-21.024112 43.254594,-21.024118 C 43.610059,-21.024112 43.971387,-20.910831 44.338578,-20.684275 L 43.975297,-19.705759 C 43.717481,-19.858098 43.459669,-19.934269 43.201859,-19.934275 C 42.971388,-19.934269 42.764357,-19.864934 42.580766,-19.726267 C 42.39717,-19.58759 42.266311,-19.395207 42.188188,-19.149118 C 42.070998,-18.774114 42.012405,-18.363958 42.012406,-17.91865 L 42.012406,-14.660837 L 40.957719,-14.660837 z M 44.983109,-14.660837 L 44.983109,-23.250681 L 46.037797,-23.250681 L 46.037797,-18.352243 L 48.533891,-20.883493 L 49.899125,-20.883493 L 47.520219,-18.5749 L 50.139359,-14.660837 L 48.838578,-14.660837 L 46.781938,-17.842478 L 46.037797,-17.127634 L 46.037797,-14.660837 L 44.983109,-14.660837 z" + id="text1232" /> + <path + transform="scale(0.246729,0.246729)" + style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:helvetica" + d="M 91.619637,-37.962852 L 92.756355,-37.675743 C 92.518066,-36.742148 92.089356,-36.030234 91.470222,-35.540001 C 90.851076,-35.049766 90.09424,-34.804649 89.199715,-34.804649 C 88.27393,-34.804649 87.521001,-34.993126 86.940926,-35.370079 C 86.360846,-35.747031 85.91944,-36.292929 85.616707,-37.007774 C 85.313972,-37.722615 85.162605,-38.490193 85.162605,-39.310509 C 85.162605,-40.205035 85.333503,-40.985307 85.675301,-41.651329 C 86.017096,-42.317337 86.503424,-42.823196 87.134285,-43.168907 C 87.765141,-43.514602 88.459476,-43.687453 89.217293,-43.687462 C 90.076662,-43.687453 90.799318,-43.468703 91.385262,-43.031212 C 91.971192,-42.593704 92.379394,-41.97847 92.609871,-41.185509 L 91.49073,-40.921837 C 91.291505,-41.54683 91.002443,-42.001908 90.623543,-42.287071 C 90.244631,-42.57222 89.768069,-42.714798 89.193855,-42.714806 C 88.533695,-42.714798 87.981938,-42.556595 87.538582,-42.240196 C 87.09522,-41.923783 86.783697,-41.498979 86.604012,-40.965782 C 86.424322,-40.432574 86.334479,-39.882769 86.33448,-39.316368 C 86.334479,-38.585896 86.440924,-37.948201 86.653816,-37.403282 C 86.866705,-36.858358 87.197759,-36.451132 87.64698,-36.181602 C 88.096196,-35.91207 88.582523,-35.777305 89.105965,-35.777306 C 89.742678,-35.777305 90.28174,-35.960898 90.723152,-36.328087 C 91.164552,-36.695273 91.46338,-37.240194 91.619637,-37.962852 L 91.619637,-37.962852 z M 94.016121,-34.951134 L 94.016121,-41.17379 L 94.96534,-41.17379 L 94.96534,-40.230431 C 95.207525,-40.671831 95.431158,-40.962846 95.636238,-41.103477 C 95.841314,-41.244096 96.066899,-41.314409 96.312996,-41.314415 C 96.668461,-41.314409 97.029789,-41.201127 97.39698,-40.974571 L 97.033699,-39.996056 C 96.775883,-40.148394 96.518071,-40.224566 96.260262,-40.224571 C 96.02979,-40.224566 95.822759,-40.15523 95.639168,-40.016563 C 95.455572,-39.877887 95.324713,-39.685504 95.24659,-39.439415 C 95.1294,-39.064411 95.070807,-38.654255 95.070808,-38.208946 L 95.070808,-34.951134 L 94.016121,-34.951134 z M 102.29542,-36.95504 L 103.38526,-36.820274 C 103.21338,-36.183554 102.89502,-35.689414 102.43018,-35.337852 C 101.96533,-34.98629 101.37159,-34.810509 100.64893,-34.810509 C 99.738775,-34.810509 99.017096,-35.090782 98.483894,-35.651329 C 97.950691,-36.211875 97.684089,-36.998007 97.68409,-38.009727 C 97.684089,-39.056598 97.95362,-39.869098 98.492683,-40.447227 C 99.031744,-41.025346 99.730962,-41.314409 100.59034,-41.314415 C 101.42237,-41.314409 102.10205,-41.031206 102.6294,-40.464806 C 103.15674,-39.898394 103.42041,-39.10152 103.42042,-38.074181 C 103.42041,-38.011678 103.41846,-37.917928 103.41456,-37.792931 L 98.773933,-37.792931 C 98.812994,-37.109335 99.006354,-36.585898 99.354012,-36.222618 C 99.701665,-35.859336 100.13526,-35.677696 100.65479,-35.677696 C 101.04151,-35.677696 101.37159,-35.779258 101.64503,-35.982384 C 101.91846,-36.185507 102.13526,-36.509726 102.29542,-36.95504 L 102.29542,-36.95504 z M 98.832527,-38.660118 L 102.30714,-38.660118 C 102.26026,-39.183551 102.12744,-39.576129 101.9087,-39.837852 C 101.57276,-40.244097 101.13721,-40.447222 100.60206,-40.447227 C 100.11768,-40.447222 99.710454,-40.285113 99.380379,-39.960899 C 99.050299,-39.636676 98.867682,-39.203083 98.832527,-38.660118 L 98.832527,-38.660118 z M 108.77589,-35.718712 C 108.38526,-35.38668 108.00928,-35.152305 107.64796,-35.015587 C 107.28663,-34.878868 106.89893,-34.810509 106.48487,-34.810509 C 105.80128,-34.810509 105.27589,-34.977501 104.9087,-35.311485 C 104.54151,-35.645469 104.35792,-36.072226 104.35792,-36.591759 C 104.35792,-36.896444 104.42725,-37.174764 104.56593,-37.42672 C 104.7046,-37.67867 104.88624,-37.880818 105.11085,-38.033165 C 105.33546,-38.185505 105.58838,-38.30074 105.86964,-38.378868 C 106.07667,-38.433552 106.38917,-38.486286 106.80714,-38.537071 C 107.6587,-38.63863 108.28565,-38.759724 108.688,-38.900352 C 108.6919,-39.04488 108.69385,-39.136676 108.69386,-39.175743 C 108.69385,-39.605426 108.59424,-39.90816 108.39503,-40.083946 C 108.12549,-40.322222 107.7251,-40.441363 107.19386,-40.441368 C 106.69776,-40.441363 106.33155,-40.354449 106.09522,-40.180626 C 105.85889,-40.006793 105.68409,-39.699176 105.57081,-39.257774 L 104.53956,-39.398399 C 104.63331,-39.839801 104.7876,-40.196246 105.00245,-40.467735 C 105.21729,-40.739214 105.52784,-40.948198 105.93409,-41.094688 C 106.34034,-41.241167 106.81104,-41.314409 107.3462,-41.314415 C 107.87745,-41.314409 108.30909,-41.251909 108.64112,-41.126915 C 108.97315,-41.001909 109.21729,-40.844683 109.37354,-40.655235 C 109.52979,-40.465777 109.63916,-40.226519 109.70167,-39.937462 C 109.73682,-39.75777 109.7544,-39.433551 109.7544,-38.964806 L 109.7544,-37.558556 C 109.7544,-36.578085 109.77686,-35.957969 109.82178,-35.698204 C 109.8667,-35.438438 109.95557,-35.189415 110.08839,-34.951134 L 108.98682,-34.951134 C 108.87744,-35.169884 108.80713,-35.425743 108.77589,-35.718712 L 108.77589,-35.718712 z M 108.688,-38.074181 C 108.30518,-37.917928 107.73096,-37.785115 106.96534,-37.675743 C 106.53174,-37.61324 106.2251,-37.542928 106.04542,-37.464806 C 105.86573,-37.386678 105.72706,-37.27242 105.6294,-37.122032 C 105.53174,-36.97164 105.48292,-36.804647 105.48292,-36.621056 C 105.48292,-36.339804 105.58936,-36.105429 105.80225,-35.917931 C 106.01514,-35.73043 106.32667,-35.63668 106.73682,-35.636681 C 107.14307,-35.63668 107.5044,-35.725547 107.82081,-35.903282 C 108.13721,-36.081015 108.36963,-36.324179 108.51807,-36.632774 C 108.63135,-36.871054 108.68799,-37.222616 108.688,-37.687462 L 108.688,-38.074181 z M 113.69776,-35.894493 L 113.85011,-34.962852 C 113.55323,-34.900353 113.2876,-34.869103 113.05323,-34.869102 C 112.67042,-34.869103 112.37354,-34.929649 112.16261,-35.050743 C 111.95167,-35.171837 111.80323,-35.331016 111.71729,-35.528282 C 111.63135,-35.725547 111.58839,-36.140586 111.58839,-36.773399 L 111.58839,-40.353477 L 110.81495,-40.353477 L 110.81495,-41.17379 L 111.58839,-41.17379 L 111.58839,-42.714806 L 112.63721,-43.347618 L 112.63721,-41.17379 L 113.69776,-41.17379 L 113.69776,-40.353477 L 112.63721,-40.353477 L 112.63721,-36.714806 C 112.63721,-36.414023 112.65577,-36.220664 112.69288,-36.134727 C 112.72999,-36.048789 112.79053,-35.98043 112.87452,-35.929649 C 112.9585,-35.878867 113.07862,-35.853477 113.23487,-35.853477 C 113.35206,-35.853477 113.50635,-35.867148 113.69776,-35.894493 L 113.69776,-35.894493 z M 118.98292,-36.95504 L 120.07276,-36.820274 C 119.90088,-36.183554 119.58252,-35.689414 119.11768,-35.337852 C 118.65283,-34.98629 118.05909,-34.810509 117.33643,-34.810509 C 116.42627,-34.810509 115.7046,-35.090782 115.17139,-35.651329 C 114.63819,-36.211875 114.37159,-36.998007 114.37159,-38.009727 C 114.37159,-39.056598 114.64112,-39.869098 115.18018,-40.447227 C 115.71924,-41.025346 116.41846,-41.314409 117.27784,-41.314415 C 118.10987,-41.314409 118.78955,-41.031206 119.3169,-40.464806 C 119.84424,-39.898394 120.10791,-39.10152 120.10792,-38.074181 C 120.10791,-38.011678 120.10596,-37.917928 120.10206,-37.792931 L 115.46143,-37.792931 C 115.50049,-37.109335 115.69385,-36.585898 116.04151,-36.222618 C 116.38917,-35.859336 116.82276,-35.677696 117.34229,-35.677696 C 117.72901,-35.677696 118.05909,-35.779258 118.33253,-35.982384 C 118.60596,-36.185507 118.82276,-36.509726 118.98292,-36.95504 L 118.98292,-36.95504 z M 115.52003,-38.660118 L 118.99464,-38.660118 C 118.94776,-39.183551 118.81494,-39.576129 118.5962,-39.837852 C 118.26026,-40.244097 117.82471,-40.447222 117.28956,-40.447227 C 116.80518,-40.447222 116.39795,-40.285113 116.06788,-39.960899 C 115.7378,-39.636676 115.55518,-39.203083 115.52003,-38.660118 L 115.52003,-38.660118 z M 125.43995,-34.951134 L 125.43995,-35.73629 C 125.04541,-35.119102 124.46534,-34.810509 123.69971,-34.810509 C 123.20362,-34.810509 122.74756,-34.947227 122.33155,-35.220665 C 121.91553,-35.494102 121.59327,-35.875937 121.36475,-36.366173 C 121.13624,-36.856405 121.02198,-37.419881 121.02198,-38.056602 C 121.02198,-38.677693 121.1255,-39.241169 121.33253,-39.747032 C 121.53956,-40.252886 121.8501,-40.640581 122.26417,-40.910118 C 122.67823,-41.179643 123.14112,-41.314409 123.65284,-41.314415 C 124.02784,-41.314409 124.36182,-41.235307 124.65479,-41.07711 C 124.94776,-40.918901 125.18604,-40.712847 125.36964,-40.458946 L 125.36964,-43.540977 L 126.41846,-43.540977 L 126.41846,-34.951134 L 125.43995,-34.951134 z M 122.10596,-38.056602 C 122.10596,-37.259725 122.27393,-36.664023 122.60987,-36.269493 C 122.94581,-35.874961 123.34229,-35.677696 123.79932,-35.677696 C 124.26026,-35.677696 124.65186,-35.866172 124.97413,-36.243126 C 125.29639,-36.620077 125.45752,-37.195272 125.45753,-37.968712 C 125.45752,-38.82027 125.29346,-39.44527 124.96534,-39.843712 C 124.63721,-40.242144 124.23291,-40.441363 123.75245,-40.441368 C 123.2837,-40.441363 122.8921,-40.249957 122.57764,-39.867149 C 122.26319,-39.484332 122.10596,-38.880817 122.10596,-38.056602 L 122.10596,-38.056602 z M 132.38331,-34.951134 L 131.40479,-34.951134 L 131.40479,-43.540977 L 132.45948,-43.540977 L 132.45948,-40.476524 C 132.90479,-41.035112 133.47315,-41.314409 134.16456,-41.314415 C 134.54737,-41.314409 134.90967,-41.23726 135.25147,-41.08297 C 135.59326,-40.928667 135.87451,-40.71187 136.09522,-40.432579 C 136.31592,-40.153277 136.48877,-39.816363 136.61378,-39.421837 C 136.73877,-39.027302 136.80127,-38.605427 136.80128,-38.156212 C 136.80127,-37.089803 136.5376,-36.265586 136.01026,-35.683556 C 135.48291,-35.101524 134.8501,-34.810509 134.11182,-34.810509 C 133.37745,-34.810509 132.80127,-35.117149 132.38331,-35.730431 L 132.38331,-34.951134 z M 132.37159,-38.109337 C 132.37159,-37.363241 132.47315,-36.824179 132.67628,-36.492149 C 133.00831,-35.94918 133.45752,-35.677696 134.02393,-35.677696 C 134.48487,-35.677696 134.8833,-35.877891 135.21925,-36.278282 C 135.55518,-36.678671 135.72315,-37.27535 135.72315,-38.068321 C 135.72315,-38.880817 135.56201,-39.480426 135.23975,-39.867149 C 134.91748,-40.253863 134.52784,-40.447222 134.07081,-40.447227 C 133.60987,-40.447222 133.21143,-40.247027 132.8755,-39.846642 C 132.53956,-39.446246 132.37159,-38.867145 132.37159,-38.109337 L 132.37159,-38.109337 z M 138.04346,-32.554649 L 137.92628,-33.544884 C 138.15675,-33.482385 138.35792,-33.451135 138.52979,-33.451134 C 138.76417,-33.451135 138.95167,-33.490198 139.09229,-33.568321 C 139.23292,-33.646448 139.34815,-33.755822 139.438,-33.896446 C 139.5044,-34.001916 139.61182,-34.263634 139.76026,-34.681602 C 139.77979,-34.740196 139.81104,-34.826134 139.85401,-34.939415 L 137.49268,-41.17379 L 138.6294,-41.17379 L 139.92432,-37.570274 C 140.09229,-37.113241 140.24268,-36.632773 140.3755,-36.128868 C 140.49659,-36.613241 140.64112,-37.085897 140.80909,-37.546837 L 142.13917,-41.17379 L 143.19386,-41.17379 L 140.82667,-34.845665 C 140.57276,-34.162072 140.37549,-33.691369 140.23487,-33.433556 C 140.04737,-33.085901 139.83252,-32.831019 139.59034,-32.668907 C 139.34815,-32.5068 139.05909,-32.425746 138.72315,-32.425743 C 138.52003,-32.425746 138.29346,-32.468714 138.04346,-32.554649 L 138.04346,-32.554649 z M 146.60987,-34.951134 L 149.9087,-43.540977 L 151.13331,-43.540977 L 154.64893,-34.951134 L 153.35401,-34.951134 L 152.35206,-37.552696 L 148.76026,-37.552696 L 147.8169,-34.951134 L 146.60987,-34.951134 z M 149.08839,-38.478477 L 152.0005,-38.478477 L 151.10401,-40.857384 C 150.83057,-41.580033 150.62745,-42.173783 150.49464,-42.638634 C 150.38526,-42.087845 150.23096,-41.540971 150.03175,-40.998009 L 149.08839,-38.478477 z M 155.43409,-34.951134 L 155.43409,-41.17379 L 156.38331,-41.17379 L 156.38331,-40.289024 C 156.84034,-40.972612 157.50049,-41.314409 158.36378,-41.314415 C 158.73877,-41.314409 159.0835,-41.247026 159.39796,-41.112267 C 159.7124,-40.977495 159.94776,-40.800737 160.10401,-40.581993 C 160.26026,-40.363238 160.36963,-40.103472 160.43214,-39.802696 C 160.47119,-39.607379 160.49072,-39.265583 160.49073,-38.777306 L 160.49073,-34.951134 L 159.43604,-34.951134 L 159.43604,-38.73629 C 159.43604,-39.165973 159.39502,-39.487262 159.313,-39.700157 C 159.23096,-39.913043 159.08545,-40.082965 158.87647,-40.209923 C 158.66748,-40.336871 158.42237,-40.400347 158.14112,-40.400352 C 157.6919,-40.400347 157.3042,-40.257769 156.97803,-39.972618 C 156.65186,-39.687457 156.48878,-39.146442 156.48878,-38.349571 L 156.48878,-34.951134 L 155.43409,-34.951134 z M 166.15089,-34.951134 L 166.15089,-35.73629 C 165.75635,-35.119102 165.17627,-34.810509 164.41065,-34.810509 C 163.91456,-34.810509 163.4585,-34.947227 163.04249,-35.220665 C 162.62647,-35.494102 162.30421,-35.875937 162.07569,-36.366173 C 161.84718,-36.856405 161.73292,-37.419881 161.73292,-38.056602 C 161.73292,-38.677693 161.83643,-39.241169 162.04346,-39.747032 C 162.25049,-40.252886 162.56104,-40.640581 162.97511,-40.910118 C 163.38917,-41.179643 163.85206,-41.314409 164.36378,-41.314415 C 164.73877,-41.314409 165.07276,-41.235307 165.36573,-41.07711 C 165.65869,-40.918901 165.89698,-40.712847 166.08057,-40.458946 L 166.08057,-43.540977 L 167.1294,-43.540977 L 167.1294,-34.951134 L 166.15089,-34.951134 z M 162.8169,-38.056602 C 162.8169,-37.259725 162.98487,-36.664023 163.32081,-36.269493 C 163.65674,-35.874961 164.05323,-35.677696 164.51026,-35.677696 C 164.9712,-35.677696 165.3628,-35.866172 165.68507,-36.243126 C 166.00733,-36.620077 166.16846,-37.195272 166.16846,-37.968712 C 166.16846,-38.82027 166.0044,-39.44527 165.67628,-39.843712 C 165.34815,-40.242144 164.94385,-40.441363 164.46339,-40.441368 C 163.99463,-40.441363 163.60303,-40.249957 163.28858,-39.867149 C 162.97413,-39.484332 162.8169,-38.880817 162.8169,-38.056602 L 162.8169,-38.056602 z M 168.78175,-34.951134 L 168.78175,-41.17379 L 169.73096,-41.17379 L 169.73096,-40.230431 C 169.97315,-40.671831 170.19678,-40.962846 170.40186,-41.103477 C 170.60694,-41.244096 170.83252,-41.314409 171.07862,-41.314415 C 171.43409,-41.314409 171.79541,-41.201127 172.16261,-40.974571 L 171.79932,-39.996056 C 171.54151,-40.148394 171.2837,-40.224566 171.02589,-40.224571 C 170.79541,-40.224566 170.58838,-40.15523 170.40479,-40.016563 C 170.2212,-39.877887 170.09034,-39.685504 170.01221,-39.439415 C 169.89503,-39.064411 169.83643,-38.654255 169.83643,-38.208946 L 169.83643,-34.951134 L 168.78175,-34.951134 z M 177.06104,-36.95504 L 178.15089,-36.820274 C 177.97901,-36.183554 177.66065,-35.689414 177.19581,-35.337852 C 176.73096,-34.98629 176.13721,-34.810509 175.41456,-34.810509 C 174.5044,-34.810509 173.78272,-35.090782 173.24952,-35.651329 C 172.71632,-36.211875 172.44971,-36.998007 172.44971,-38.009727 C 172.44971,-39.056598 172.71925,-39.869098 173.25831,-40.447227 C 173.79737,-41.025346 174.49659,-41.314409 175.35596,-41.314415 C 176.18799,-41.314409 176.86768,-41.031206 177.39503,-40.464806 C 177.92236,-39.898394 178.18604,-39.10152 178.18604,-38.074181 C 178.18604,-38.011678 178.18408,-37.917928 178.18018,-37.792931 L 173.53956,-37.792931 C 173.57862,-37.109335 173.77198,-36.585898 174.11964,-36.222618 C 174.46729,-35.859336 174.90088,-35.677696 175.42042,-35.677696 C 175.80713,-35.677696 176.13721,-35.779258 176.41065,-35.982384 C 176.68408,-36.185507 176.90088,-36.509726 177.06104,-36.95504 L 177.06104,-36.95504 z M 173.59815,-38.660118 L 177.07276,-38.660118 C 177.02588,-39.183551 176.89307,-39.576129 176.67432,-39.837852 C 176.33838,-40.244097 175.90284,-40.447222 175.36768,-40.447227 C 174.88331,-40.447222 174.47608,-40.285113 174.146,-39.960899 C 173.81592,-39.636676 173.63331,-39.203083 173.59815,-38.660118 L 173.59815,-38.660118 z M 180.6294,-34.951134 L 178.72511,-41.17379 L 179.81495,-41.17379 L 180.80518,-37.581993 L 181.17432,-36.246056 C 181.18995,-36.31246 181.29737,-36.740194 181.49659,-37.529259 L 182.48682,-41.17379 L 183.57081,-41.17379 L 184.50245,-37.564415 L 184.813,-36.374962 L 185.17042,-37.576134 L 186.23682,-41.17379 L 187.26221,-41.17379 L 185.3169,-34.951134 L 184.2212,-34.951134 L 183.23096,-38.677696 L 182.99073,-39.738243 L 181.73096,-34.951134 L 180.6294,-34.951134 z M 191.67432,-34.951134 L 191.67432,-43.540977 L 197.46925,-43.540977 L 197.46925,-42.527306 L 192.81104,-42.527306 L 192.81104,-39.867149 L 196.84229,-39.867149 L 196.84229,-38.853477 L 192.81104,-38.853477 L 192.81104,-34.951134 L 191.67432,-34.951134 z M 198.82276,-42.328087 L 198.82276,-43.540977 L 199.87745,-43.540977 L 199.87745,-42.328087 L 198.82276,-42.328087 z M 198.82276,-34.951134 L 198.82276,-41.17379 L 199.87745,-41.17379 L 199.87745,-34.951134 L 198.82276,-34.951134 z M 203.79151,-35.894493 L 203.94386,-34.962852 C 203.64698,-34.900353 203.38135,-34.869103 203.14698,-34.869102 C 202.76417,-34.869103 202.46729,-34.929649 202.25636,-35.050743 C 202.04542,-35.171837 201.89698,-35.331016 201.81104,-35.528282 C 201.7251,-35.725547 201.68214,-36.140586 201.68214,-36.773399 L 201.68214,-40.353477 L 200.9087,-40.353477 L 200.9087,-41.17379 L 201.68214,-41.17379 L 201.68214,-42.714806 L 202.73096,-43.347618 L 202.73096,-41.17379 L 203.79151,-41.17379 L 203.79151,-40.353477 L 202.73096,-40.353477 L 202.73096,-36.714806 C 202.73096,-36.414023 202.74952,-36.220664 202.78663,-36.134727 C 202.82374,-36.048789 202.88428,-35.98043 202.96827,-35.929649 C 203.05225,-35.878867 203.17237,-35.853477 203.32862,-35.853477 C 203.44581,-35.853477 203.6001,-35.867148 203.79151,-35.894493 L 203.79151,-35.894493 z M 204.26026,-34.951134 L 204.26026,-35.806602 L 208.2212,-40.353477 C 207.77198,-40.330035 207.37549,-40.318316 207.03175,-40.318321 L 204.49464,-40.318321 L 204.49464,-41.17379 L 209.58057,-41.17379 L 209.58057,-40.476524 L 206.21143,-36.527306 L 205.56104,-35.806602 C 206.0337,-35.841758 206.47706,-35.859336 206.89112,-35.859337 L 209.76807,-35.859337 L 209.76807,-34.951134 L 204.26026,-34.951134 z M 210.39503,-36.808556 L 211.438,-36.972618 C 211.49659,-36.554648 211.65967,-36.234336 211.92725,-36.011681 C 212.19483,-35.789024 212.56885,-35.677696 213.04932,-35.677696 C 213.5337,-35.677696 213.89307,-35.776328 214.12745,-35.973595 C 214.36182,-36.170859 214.47901,-36.402304 214.47901,-36.667931 C 214.47901,-36.90621 214.37549,-37.09371 214.16846,-37.230431 C 214.02393,-37.324178 213.66455,-37.443319 213.09034,-37.587852 C 212.3169,-37.783162 211.78077,-37.952107 211.48194,-38.094688 C 211.18311,-38.237263 210.95655,-38.434529 210.80225,-38.686485 C 210.64796,-38.938434 210.57081,-39.216754 210.57081,-39.521446 C 210.57081,-39.798785 210.63428,-40.055621 210.76124,-40.291954 C 210.88819,-40.528277 211.06104,-40.724565 211.27979,-40.880821 C 211.44385,-41.001909 211.66749,-41.104448 211.95069,-41.188438 C 212.23389,-41.272416 212.5376,-41.314409 212.86182,-41.314415 C 213.3501,-41.314409 213.77881,-41.244096 214.14796,-41.103477 C 214.51709,-40.962846 214.78955,-40.772417 214.96534,-40.532188 C 215.14112,-40.291949 215.26221,-39.97066 215.32862,-39.568321 L 214.29737,-39.427696 C 214.25049,-39.748004 214.11475,-39.998004 213.89014,-40.177696 C 213.66553,-40.357378 213.34815,-40.447222 212.938,-40.447227 C 212.45362,-40.447222 212.10792,-40.367144 211.90089,-40.206993 C 211.69385,-40.046832 211.59034,-39.859332 211.59034,-39.644493 C 211.59034,-39.50777 211.63331,-39.384723 211.71925,-39.275352 C 211.80518,-39.162067 211.93995,-39.068317 212.12354,-38.994102 C 212.22901,-38.955036 212.53956,-38.865192 213.05518,-38.724571 C 213.80127,-38.525349 214.32178,-38.362263 214.61671,-38.235313 C 214.91162,-38.108357 215.14307,-37.923787 215.31104,-37.681602 C 215.47901,-37.439412 215.56299,-37.138632 215.563,-36.779259 C 215.56299,-36.427695 215.46045,-36.09664 215.25538,-35.786095 C 215.0503,-35.475547 214.7544,-35.235313 214.36768,-35.065392 C 213.98096,-34.89547 213.54346,-34.810509 213.05518,-34.810509 C 212.24659,-34.810509 211.63038,-34.978477 211.20655,-35.314415 C 210.78272,-35.650352 210.51221,-36.148398 210.39503,-36.808556 L 210.39503,-36.808556 z M 216.82276,-42.328087 L 216.82276,-43.540977 L 217.87745,-43.540977 L 217.87745,-42.328087 L 216.82276,-42.328087 z M 216.82276,-34.951134 L 216.82276,-41.17379 L 217.87745,-41.17379 L 217.87745,-34.951134 L 216.82276,-34.951134 z M 219.48878,-34.951134 L 219.48878,-41.17379 L 220.43214,-41.17379 L 220.43214,-40.300743 C 220.62745,-40.605425 220.88721,-40.850542 221.21143,-41.036095 C 221.53565,-41.221635 221.90479,-41.314409 222.31886,-41.314415 C 222.77979,-41.314409 223.15772,-41.218706 223.45264,-41.027306 C 223.74756,-40.835893 223.95557,-40.568316 224.07667,-40.224571 C 224.56885,-40.951128 225.20947,-41.314409 225.99854,-41.314415 C 226.61572,-41.314409 227.09033,-41.14351 227.42237,-40.80172 C 227.75439,-40.459917 227.92041,-39.933551 227.92042,-39.222618 L 227.92042,-34.951134 L 226.87159,-34.951134 L 226.87159,-38.871056 C 226.87158,-39.292926 226.8374,-39.596637 226.76905,-39.782188 C 226.70068,-39.96773 226.57666,-40.117144 226.39698,-40.230431 C 226.21729,-40.343706 226.00635,-40.400347 225.76417,-40.400352 C 225.32666,-40.400347 224.96338,-40.254839 224.67432,-39.963829 C 224.38526,-39.672809 224.24072,-39.206989 224.24073,-38.566368 L 224.24073,-34.951134 L 223.18604,-34.951134 L 223.18604,-38.994102 C 223.18604,-39.462848 223.1001,-39.81441 222.92823,-40.04879 C 222.75635,-40.28316 222.4751,-40.400347 222.08448,-40.400352 C 221.7876,-40.400347 221.51319,-40.322222 221.26124,-40.165977 C 221.00928,-40.009722 220.82667,-39.781207 220.71339,-39.480431 C 220.6001,-39.179645 220.54346,-38.746052 220.54346,-38.179649 L 220.54346,-34.951134 L 219.48878,-34.951134 z M 229.10401,-38.062462 C 229.10401,-39.214801 229.42432,-40.068316 230.06495,-40.623009 C 230.6001,-41.08394 231.25245,-41.314409 232.02198,-41.314415 C 232.87744,-41.314409 233.57666,-41.034135 234.11964,-40.473595 C 234.6626,-39.913043 234.93408,-39.13863 234.93409,-38.150352 C 234.93408,-37.349569 234.81397,-36.719687 234.57374,-36.260704 C 234.3335,-35.801719 233.98389,-35.445274 233.52491,-35.191368 C 233.06592,-34.937462 232.56495,-34.810509 232.02198,-34.810509 C 231.15088,-34.810509 230.44678,-35.089805 229.90968,-35.648399 C 229.37257,-36.206992 229.10401,-37.011679 229.10401,-38.062462 L 229.10401,-38.062462 z M 230.188,-38.062462 C 230.18799,-37.265585 230.36182,-36.668905 230.70948,-36.272423 C 231.05713,-35.875937 231.49463,-35.677696 232.02198,-35.677696 C 232.54541,-35.677696 232.98096,-35.876914 233.32862,-36.275352 C 233.67627,-36.673788 233.8501,-37.28121 233.85011,-38.097618 C 233.8501,-38.867145 233.6753,-39.450153 233.32569,-39.846642 C 232.97608,-40.243121 232.54151,-40.441363 232.02198,-40.441368 C 231.49463,-40.441363 231.05713,-40.244097 230.70948,-39.849571 C 230.36182,-39.455035 230.18799,-38.859333 230.188,-38.062462 L 230.188,-38.062462 z M 236.17628,-34.951134 L 236.17628,-41.17379 L 237.1255,-41.17379 L 237.1255,-40.289024 C 237.58252,-40.972612 238.24268,-41.314409 239.10596,-41.314415 C 239.48096,-41.314409 239.82569,-41.247026 240.14014,-41.112267 C 240.45459,-40.977495 240.68994,-40.800737 240.8462,-40.581993 C 241.00244,-40.363238 241.11182,-40.103472 241.17432,-39.802696 C 241.21338,-39.607379 241.23291,-39.265583 241.23292,-38.777306 L 241.23292,-34.951134 L 240.17823,-34.951134 L 240.17823,-38.73629 C 240.17823,-39.165973 240.13721,-39.487262 240.05518,-39.700157 C 239.97315,-39.913043 239.82764,-40.082965 239.61866,-40.209923 C 239.40967,-40.336871 239.16455,-40.400347 238.88331,-40.400352 C 238.43409,-40.400347 238.04639,-40.257769 237.72022,-39.972618 C 237.39405,-39.687457 237.23096,-39.146442 237.23096,-38.349571 L 237.23096,-34.951134 L 236.17628,-34.951134 z" + id="text1235" /> + <g + id="g2852" + transform="matrix(1.018857,0.000000,0.000000,1.018857,-4.481650,2.131177)"> + <rect + height="8.3153667" + id="rect1866" + style="fill:url(#linearGradient1156);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient1157);stroke-width:1.4473482pt;" + transform="matrix(1.150066,0.000000,0.000000,1.150066,38.98882,26.86863)" + width="57.567924" + x="33.326111" + y="78.658051" /> + <rect + height="60.126495" + id="rect1867" + rx="5.4369707" + ry="5.4369707" + style="fill:url(#linearGradient905);fill-opacity:1;fill-rule:evenodd;stroke-width:1.6282668;" + transform="matrix(1.150066,0.000000,0.000000,1.150066,38.98882,26.86863)" + width="72.279724" + x="26.015469" + y="22.413721" /> + <rect + height="38.044163" + id="rect1868" + style="fill:url(#radialGradient1132);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient891);stroke-width:1.4649456pt;" + transform="matrix(1.150066,0.000000,0.000000,1.150066,38.98882,26.86863)" + width="58.178177" + x="33.386066" + y="31.695871" /> + <path + d="M 27.690431,52.841444 L 27.370609,74.749236 C 27.319624,78.241665 29.310209,80.477938 32.807578,80.506029 L 72.625393,80.825852 L 76.463254,71.870840 L 32.008024,71.551020 L 31.688202,52.681533 L 27.690431,52.841444 z " + id="path1869" + sodipodi:nodetypes="czzccccc" + style="fill:url(#linearGradient1146);fill-opacity:1;fill-rule:evenodd;stroke-width:1.0000000pt;" + transform="matrix(1.150066,0.000000,0.000000,1.150066,38.98882,26.86863)" /> + <g + id="g1870" + transform="matrix(1.150066,0.000000,0.000000,1.150066,38.98882,26.86863)"> + <path + d="M 42.062098,33.460351 L 77.341205,33.008055 C 82.787126,32.938235 89.553204,38.416797 89.553204,43.863165 L 89.553204,60.145830 L 41.609801,59.693534 L 42.062098,33.460351 z " + id="path1871" + sodipodi:nodetypes="czzccc" + style="fill:url(#linearGradient1148);fill-opacity:1;fill-rule:evenodd;stroke-width:1.0000000pt;" /> + <path + d="M 78.337784,67.629235 L 46.723745,67.724544 C 41.843589,67.739257 35.829319,62.771024 35.877168,57.891081 L 36.020221,43.301821 L 78.973514,44.128288 L 78.337784,67.629235 z " + id="path1872" + sodipodi:nodetypes="czzccc" + style="fill:url(#linearGradient1150);fill-opacity:1;fill-rule:evenodd;stroke-width:1.0000000pt;" /> + </g> + <rect + height="26.147448" + id="rect1888" + rx="7.4449978" + ry="7.4449978" + style="fill:url(#linearGradient901);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient1157);stroke-width:2.3625000;" + transform="matrix(0.917809,0.000000,0.000000,0.917809,-65.63305,158.5521)" + width="104.09673" + x="140.62315" + y="-34.316952" /> + <rect + height="15.829688" + id="rect1889" + rx="3.7576280" + ry="3.7576280" + style="fill:url(#linearGradient901);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient1157);stroke-width:1.3591428;" + transform="matrix(0.917809,0.000000,0.000000,0.917809,-65.63305,158.5521)" + width="56.908955" + x="184.04552" + y="-28.539845" /> + <rect + height="15.829688" + id="rect1890" + rx="2.9970589" + ry="2.9970589" + style="fill:url(#linearGradient1141);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient1157);stroke-width:0.96249998;" + transform="matrix(0.917809,0.000000,0.000000,0.917809,-65.63305,158.5521)" + width="28.796961" + x="145.28902" + y="-28.227346" /> + <rect + height="3.3627598" + id="rect1891" + rx="1.6813799" + ry="1.6813799" + style="fill-opacity:0.16981132;fill-rule:evenodd;stroke-width:0.46326005;" + transform="matrix(0.917809,0.000000,0.000000,0.917809,-65.63305,158.5521)" + width="49.231453" + x="187.88426" + y="-21.681381" /> + </g> +</svg> diff --git a/wpa_supplicant/wpa_gui-qt4/icons/invitation.svg b/wpa_supplicant/wpa_gui-qt4/icons/invitation.svg new file mode 100644 index 0000000000000..1a02d1327eec2 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/icons/invitation.svg @@ -0,0 +1,374 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://web.resource.org/cc/" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://inkscape.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="64.000000px" + height="64.000000px" + id="svg2" + sodipodi:version="0.32" + inkscape:version="0.42" + sodipodi:docbase="G:\Projs\Cliparts Stocker\released" + sodipodi:docname="unknown_green.svg" + inkscape:export-filename="/datas/wiki/unknown_green.png" + inkscape:export-xdpi="90.000000" + inkscape:export-ydpi="90.000000"> + <defs + id="defs4"> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2842" + id="linearGradient1363" + x1="25.403513" + y1="19.175573" + x2="35.541985" + y2="49.068703" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(-2.402975,4.759656e-3)" /> + <linearGradient + id="linearGradient2900"> + <stop + id="stop2902" + offset="0.0000000" + style="stop-color:#ffffff;stop-opacity:1.0000000;" /> + <stop + id="stop2904" + offset="1.0000000" + style="stop-color:#ffffff;stop-opacity:1.0000000;" /> + </linearGradient> + <linearGradient + id="linearGradient2842"> + <stop + style="stop-color:#ffffff;stop-opacity:1.0000000;" + offset="0.0000000" + id="stop2844" /> + <stop + style="stop-color:#c8c8c8;stop-opacity:1.0000000;" + offset="1.0000000" + id="stop2846" /> + </linearGradient> + <linearGradient + id="linearGradient2814"> + <stop + id="stop2816" + offset="0.0000000" + style="stop-color:#e6e6e6;stop-opacity:1.0000000;" /> + <stop + id="stop2818" + offset="1.0000000" + style="stop-color:#11661d;stop-opacity:0.0000000;" /> + </linearGradient> + <linearGradient + id="linearGradient2171"> + <stop + style="stop-color:#ffffff;stop-opacity:1.0000000;" + offset="0.0000000" + id="stop2173" /> + <stop + style="stop-color:#a3a5ee;stop-opacity:0.0000000;" + offset="1.0000000" + id="stop2175" /> + </linearGradient> + <linearGradient + id="linearGradient2160"> + <stop + id="stop2162" + offset="0.0000000" + style="stop-color:#d3cece;stop-opacity:1.0000000;" /> + <stop + id="stop2164" + offset="1.0000000" + style="stop-color:#474240;stop-opacity:1.0000000;" /> + </linearGradient> + <linearGradient + id="linearGradient1367"> + <stop + id="stop1369" + offset="0.0000000" + style="stop-color:#f67e36;stop-opacity:1.0000000;" /> + <stop + id="stop1371" + offset="1.0000000" + style="stop-color:#602604;stop-opacity:1.0000000;" /> + </linearGradient> + <linearGradient + id="linearGradient1347"> + <stop + style="stop-color:#f0da27;stop-opacity:1.0000000;" + offset="0.0000000" + id="stop1349" /> + <stop + style="stop-color:#bf4d09;stop-opacity:1.0000000;" + offset="1.0000000" + id="stop1351" /> + </linearGradient> + <linearGradient + id="linearGradient1315"> + <stop + style="stop-color:#97ff82;stop-opacity:1.0000000;" + offset="0.0000000" + id="stop1317" /> + <stop + style="stop-color:#ceff24;stop-opacity:0.0000000;" + offset="1.0000000" + id="stop1319" /> + </linearGradient> + <linearGradient + id="linearGradient2122"> + <stop + style="stop-color:#2edc32;stop-opacity:1.0000000;" + offset="0.0000000" + id="stop2124" /> + <stop + style="stop-color:#11661d;stop-opacity:1.0000000;" + offset="1.0000000" + id="stop2126" /> + </linearGradient> + <linearGradient + id="linearGradient1364"> + <stop + style="stop-color:#236b0d;stop-opacity:1.0000000;" + offset="0.00000000" + id="stop1366" /> + <stop + style="stop-color:#0a2205;stop-opacity:1.0000000;" + offset="1.0000000" + id="stop1368" /> + </linearGradient> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient1367" + id="radialGradient1402" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.211118e-16,1.330643,-1.347411,2.027373e-5,44.09678,-13.39507)" + cx="21.959658" + cy="14.921703" + fx="21.959658" + fy="14.921703" + r="27.500000" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient2122" + id="radialGradient1404" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.211118e-16,1.330643,-1.347411,2.027373e-5,44.09678,-13.39507)" + cx="21.959658" + cy="14.921703" + fx="21.959658" + fy="14.921703" + r="27.500000" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient1364" + id="linearGradient1419" + gradientUnits="userSpaceOnUse" + x1="74.910713" + y1="32.362179" + x2="84.910713" + y2="47.451466" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2122" + id="linearGradient1421" + gradientUnits="userSpaceOnUse" + x1="73.839287" + y1="34.428566" + x2="76.875000" + y2="43.714283" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient1315" + id="linearGradient1423" + gradientUnits="userSpaceOnUse" + x1="72.946426" + y1="35.589287" + x2="85.000000" + y2="47.375000" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2171" + id="linearGradient2177" + x1="24.916031" + y1="28.824427" + x2="39.816792" + y2="49.099239" + gradientUnits="userSpaceOnUse" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient2122" + id="radialGradient2184" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(9.909149e-17,1.088708,-1.102427,1.658760e-5,41.48828,-4.732338)" + cx="21.959658" + cy="14.921703" + fx="21.959658" + fy="14.921703" + r="27.500000" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient1364" + id="linearGradient2189" + x1="10.018247" + y1="8.6306763" + x2="63.487556" + y2="63.660282" + gradientUnits="userSpaceOnUse" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2171" + id="linearGradient1339" + gradientUnits="userSpaceOnUse" + x1="24.916031" + y1="28.824427" + x2="39.816792" + y2="49.099239" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient2122" + id="radialGradient1343" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(2.521415e-2,1.026125,-0.978137,2.404729e-2,38.83024,-3.575704)" + cx="24.764277" + cy="16.361967" + fx="24.764277" + fy="16.361967" + r="27.500000" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient1364" + id="linearGradient1346" + gradientUnits="userSpaceOnUse" + x1="10.018247" + y1="8.6306763" + x2="63.487556" + y2="63.660282" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient2814" + id="radialGradient2812" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.142398e-2,1.098850,-1.843995,1.878760e-2,52.15051,-5.667446)" + cx="18.387238" + cy="14.046815" + fx="18.387238" + fy="14.046815" + r="27.500000" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient1364" + id="linearGradient2832" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(-2.841000e-3,-2.841000e-3)" + x1="10.018247" + y1="8.6306763" + x2="63.487556" + y2="63.660282" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2842" + id="linearGradient2848" + x1="-0.56685609" + y1="22.651009" + x2="-0.33713850" + y2="23.858734" + gradientUnits="userSpaceOnUse" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2842" + id="linearGradient2864" + gradientUnits="userSpaceOnUse" + x1="-0.82287467" + y1="22.444542" + x2="-0.33713850" + y2="23.858734" /> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="8.2031250" + inkscape:cx="32.000000" + inkscape:cy="32.000000" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="false" + inkscape:grid-bbox="true" + inkscape:grid-points="true" + inkscape:window-width="1156" + inkscape:window-height="693" + inkscape:window-x="0" + inkscape:window-y="25" + showguides="false" /> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title>Green Unknown</dc:title> + <dc:date>2005-11-01</dc:date> + <dc:creator> + <cc:Agent> + <dc:title>Jean-Victor Balin</dc:title> + </cc:Agent> + </dc:creator> + <dc:description>jean.victor.balin@gmail.com</dc:description> + <cc:license + rdf:resource="http://web.resource.org/cc/PublicDomain" /> + <dc:subject> + <rdf:Bag> + <rdf:li>icon</rdf:li> + </rdf:Bag> + </dc:subject> + </cc:Work> + <cc:License + rdf:about="http://web.resource.org/cc/PublicDomain"> + <cc:permits + rdf:resource="http://web.resource.org/cc/Reproduction" /> + <cc:permits + rdf:resource="http://web.resource.org/cc/Distribution" /> + <cc:permits + rdf:resource="http://web.resource.org/cc/DerivativeWorks" /> + </cc:License> + </rdf:RDF> + </metadata> + <g + inkscape:label="Calque 1" + inkscape:groupmode="layer" + id="layer1"> + <g + id="g1354"> + <path + id="path1373" + d="M 32.000000,8.6306766 C 19.113097,8.6306766 8.6306766,19.113097 8.6306766,32.000000 C 8.6306766,44.886903 19.113097,55.369323 32.000000,55.369323 C 44.886903,55.369323 55.369323,44.886903 55.369323,32.000000 C 55.369323,19.113097 44.886903,8.6306766 32.000000,8.6306766 z " + style="fill:url(#linearGradient1346);fill-opacity:1.0000000;stroke:none;stroke-width:2.0000000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000" /> + <path + id="path1339" + d="M 54.500005,32.000000 C 54.500005,44.420003 44.420003,54.500005 32.000000,54.500005 C 19.579997,54.500005 9.4999950,44.420003 9.4999950,32.000000 C 9.4999950,19.579997 19.579997,9.4999950 32.000000,9.4999950 C 44.420003,9.4999950 54.500005,19.579997 54.500005,32.000000 z " + style="fill:url(#radialGradient1343);fill-opacity:1.0000000;stroke:none;stroke-width:2.0000000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000" /> + <path + id="path1341" + d="M 32.016991,9.1562500 C 22.574792,9.1562500 14.505423,14.865048 11.062500,22.968750 C 16.006322,25.801817 21.393258,27.855853 27.181339,27.593750 C 32.755311,27.279922 37.553510,23.530916 43.236968,23.812500 C 47.451058,23.716455 52.244330,25.294372 54.488550,29.000000 C 53.142630,17.846718 43.657640,9.1562500 32.016991,9.1562500 z " + style="fill:url(#radialGradient2812);fill-opacity:1.0000000;stroke:none;stroke-width:2.0000000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000" /> + <path + id="path2827" + d="M 32.000000,8.6250000 C 19.113098,8.6250000 8.6250000,19.113097 8.6250000,32.000000 C 8.6250000,44.886904 19.113097,55.375000 32.000000,55.375000 C 44.886904,55.375000 55.375000,44.886903 55.375000,32.000000 C 55.375000,19.113098 44.886903,8.6250000 32.000000,8.6250000 z M 32.000000,9.5000000 C 44.420004,9.4999998 54.500000,19.579997 54.500000,32.000000 C 54.499998,44.420004 44.420003,54.500000 32.000000,54.500000 C 19.579998,54.499998 9.5000000,44.420003 9.5000000,32.000000 C 9.5000000,19.579998 19.579997,9.5000000 32.000000,9.5000000 z " + style="fill:url(#linearGradient2832);fill-opacity:1.0000000;stroke:none;stroke-width:2.0000000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000" /> + <path + id="text1353" + d="M 32.556888,39.006317 C 32.692760,35.835967 33.100380,35.066018 35.908404,32.892064 C 39.395790,30.219911 39.803410,29.902873 40.120445,29.631129 C 41.705621,28.272407 42.611437,26.189029 42.611437,24.015074 C 42.611437,19.078386 38.625844,15.953318 32.285143,15.953318 C 26.306768,15.953318 22.094721,18.851931 22.094721,23.018677 C 22.094721,25.464376 23.906354,27.230718 26.397344,27.230718 C 28.707171,27.230718 30.292350,25.736121 30.292350,23.607457 C 30.292350,22.384608 29.794150,21.388209 28.843045,20.663558 C 28.027812,20.029488 27.982521,19.984196 27.982521,19.667161 C 27.982521,19.033091 28.978919,18.534892 30.382931,18.534892 C 33.100374,18.534892 34.640263,20.346525 34.640263,23.516876 C 34.640263,25.373795 33.960900,27.683628 32.828632,29.721710 C 30.337643,34.160201 29.975314,35.066023 29.975314,37.104105 C 29.975314,37.557012 30.020605,38.281665 30.111187,39.006317 L 32.556888,39.006317 M 31.424619,41.497309 C 29.069501,41.497309 27.167287,43.399523 27.167287,45.754641 C 27.167287,48.064467 29.069501,50.011973 31.379328,50.011973 C 33.779736,50.011973 35.681951,48.109758 35.681951,45.754641 C 35.681951,43.399523 33.779736,41.497309 31.424619,41.497309" + style="font-size:45.290764px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125.00000%;writing-mode:lr-tb;text-anchor:start;fill:url(#linearGradient1363);fill-opacity:1.0000000;stroke:none;stroke-width:1.0000000px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;font-family:Century Schoolbook L" /> + </g> + </g> +</svg> diff --git a/wpa_supplicant/wpa_gui-qt4/icons/laptop.svg b/wpa_supplicant/wpa_gui-qt4/icons/laptop.svg new file mode 100644 index 0000000000000..06235f02d5a38 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/icons/laptop.svg @@ -0,0 +1,1568 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://web.resource.org/cc/" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="400" + height="400" + id="svg2" + sodipodi:version="0.32" + inkscape:version="0.45" + version="1.0" + sodipodi:docbase="C:\Documents and Settings\Mete İslam\Desktop" + sodipodi:docname="MyLaptop.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape" + inkscape:export-filename="C:\Documents and Settings\Mete İslam\Desktop\MyLaptop.png" + inkscape:export-xdpi="98" + inkscape:export-ydpi="98" + sodipodi:modified="true"> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + gridtolerance="10000" + guidetolerance="10" + objecttolerance="10" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="1" + inkscape:cx="319.93339" + inkscape:cy="202.90098" + inkscape:document-units="px" + inkscape:current-layer="layer1" + width="400px" + height="400px" + inkscape:window-width="1277" + inkscape:window-height="751" + inkscape:window-x="0" + inkscape:window-y="22" + showguides="true" + inkscape:guide-bbox="true" /> + <defs + id="defs4"> + <linearGradient + id="linearGradient3757"> + <stop + style="stop-color:#70ffea;stop-opacity:1;" + offset="0" + id="stop3759" /> + <stop + style="stop-color:#0055f6;stop-opacity:1;" + offset="1" + id="stop3761" /> + </linearGradient> + <linearGradient + id="linearGradient3460"> + <stop + style="stop-color:#f1ff00;stop-opacity:1;" + offset="0" + id="stop3462" /> + <stop + style="stop-color:#8bff00;stop-opacity:0;" + offset="1" + id="stop3464" /> + </linearGradient> + <linearGradient + id="linearGradient26774"> + <stop + style="stop-color:#ffffff;stop-opacity:0.1122449;" + offset="0" + id="stop26776" /> + <stop + style="stop-color:#ffffff;stop-opacity:0.89795917;" + offset="1" + id="stop26778" /> + </linearGradient> + <linearGradient + id="linearGradient17245"> + <stop + offset="0" + style="stop-color:#ffefef;stop-opacity:0.58163267;" + id="stop17247" /> + <stop + offset="1" + style="stop-color:#ffefef;stop-opacity:0.14285715;" + id="stop17249" /> + </linearGradient> + <pattern + patternTransform="matrix(0.9848362,0,0,0.9848362,-402.92422,36.839002)" + id="pattern13296" + xlink:href="#pattern13289" + inkscape:collect="always" /> + <pattern + patternTransform="matrix(0.6565232,0,0,0.6651903,-8.1640579,-22.602821)" + id="pattern13287" + xlink:href="#pattern12311" + inkscape:collect="always" /> + <pattern + patternTransform="translate(-88.774232,-72.100299)" + id="pattern12309" + xlink:href="#pattern11335" + inkscape:collect="always" /> + <pattern + patternTransform="translate(-5.8654428,10.456268)" + id="pattern9368" + xlink:href="#pattern8394" + inkscape:collect="always" /> + <linearGradient + id="linearGradient4451"> + <stop + offset="0" + style="stop-color:#fffbfb;stop-opacity:0.82653064;" + id="stop4453" /> + <stop + offset="1" + style="stop-color:#000000;stop-opacity:0;" + id="stop4455" /> + </linearGradient> + <pattern + height="253" + id="pattern8394" + patternUnits="userSpaceOnUse" + patternTransform="translate(404.25649,166.01976)" + width="337"> + <image + id="image4466" + width="337" + y="0" + xlink:href="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/4QCURXhpZgAASUkqAAgAAAADADEBAgAcAAAAMgAAADIBAgAU AAAATgAAAGmHBAABAAAAYgAAAAAAAABBZG9iZSBQaG90b3Nob3AgQ1MyIFdpbmRvd3MAMjAwNjow NjoxMyAxMzozNDoyNAADAAGgAwABAAAAAQAAAAKgBAABAAAAAAQAAAOgBAABAAAAAAMAAAAAAAD/ 4gxYSUNDX1BST0ZJTEUAAQEAAAxITGlubwIQAABtbnRyUkdCIFhZWiAHzgACAAkABgAxAABhY3Nw TVNGVAAAAABJRUMgc1JHQgAAAAAAAAAAAAAAAQAA9tYAAQAAAADTLUhQICAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABFjcHJ0AAABUAAAADNkZXNjAAABhAAA AGx3dHB0AAAB8AAAABRia3B0AAACBAAAABRyWFlaAAACGAAAABRnWFlaAAACLAAAABRiWFlaAAAC QAAAABRkbW5kAAACVAAAAHBkbWRkAAACxAAAAIh2dWVkAAADTAAAAIZ2aWV3AAAD1AAAACRsdW1p AAAD+AAAABRtZWFzAAAEDAAAACR0ZWNoAAAEMAAAAAxyVFJDAAAEPAAACAxnVFJDAAAEPAAACAxi VFJDAAAEPAAACAx0ZXh0AAAAAENvcHlyaWdodCAoYykgMTk5OCBIZXdsZXR0LVBhY2thcmQgQ29t cGFueQAAZGVzYwAAAAAAAAASc1JHQiBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAABJzUkdCIElFQzYx OTY2LTIuMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA WFlaIAAAAAAAAPNRAAEAAAABFsxYWVogAAAAAAAAAAAAAAAAAAAAAFhZWiAAAAAAAABvogAAOPUA AAOQWFlaIAAAAAAAAGKZAAC3hQAAGNpYWVogAAAAAAAAJKAAAA+EAAC2z2Rlc2MAAAAAAAAAFklF QyBodHRwOi8vd3d3LmllYy5jaAAAAAAAAAAAAAAAFklFQyBodHRwOi8vd3d3LmllYy5jaAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABkZXNjAAAAAAAAAC5JRUMg NjE5NjYtMi4xIERlZmF1bHQgUkdCIGNvbG91ciBzcGFjZSAtIHNSR0IAAAAAAAAAAAAAAC5JRUMg NjE5NjYtMi4xIERlZmF1bHQgUkdCIGNvbG91ciBzcGFjZSAtIHNSR0IAAAAAAAAAAAAAAAAAAAAA AAAAAAAAZGVzYwAAAAAAAAAsUmVmZXJlbmNlIFZpZXdpbmcgQ29uZGl0aW9uIGluIElFQzYxOTY2 LTIuMQAAAAAAAAAAAAAALFJlZmVyZW5jZSBWaWV3aW5nIENvbmRpdGlvbiBpbiBJRUM2MTk2Ni0y LjEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHZpZXcAAAAAABOk/gAUXy4AEM8UAAPtzAAEEwsA A1yeAAAAAVhZWiAAAAAAAEwJVgBQAAAAVx/nbWVhcwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAA Ao8AAAACc2lnIAAAAABDUlQgY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAoAC0AMgA3ADsA QABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8AMEAxgDL ANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFuAXUB fAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YD ogOuA7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUc BSsFOgVJBVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG 9QcHBxkHKwc9B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQ CSUJOglPCWQJeQmPCaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4AL mAuwC8gL4Qv5DBIMKgxDDFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5k Dn8Omw62DtIO7g8JDyUPQQ9eD3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwR qhHJEegSBxImEkUSZBKEEqMSwxLjEwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0 FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbWFvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZ RRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2Z HcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAVIEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUi giKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVoJZclxyX3JicmVyaHJrcm6CcYJ0kneier J9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2K2krnSvRLAUsOSxuLKIs1y0MLUEt di2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGCMbox8jIqMmMymzLUMw0zRjN/ M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQOIw4yDkFOUI5fzm8Ofk6 Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+iP+JAI0BkQKZA50Ep QWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7R8BIBUhLSJFI 10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/dUCdQcVC7 UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjLWRpZ aVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr /2xXbK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXh dj52m3b4d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeA qIEKgWuBzYIwgpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuW i/yMY4zKjTGNmI3/jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqX dZfgmEyYuJkkmZCZ/JpomtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2 o+akVqTHpTilqaYapoum/adup+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACw dbDqsWCx1rJLssKzOLOutCW0nLUTtYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2P vgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NYw9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbL tsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx 2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6Lzp RunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio +Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t////2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB AQEBAQEBAQEBAQICAQECAQEBAgICAgICAgICAQICAgICAgICAgL/2wBDAQEBAQEBAQEBAQECAQEB AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgL/wAARCAD9 AVEDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIE AwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJico KSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZ 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 AJ+x/wDAn/8AKj//2Q== " + x="0" + height="253" /> + </pattern> + <filter + id="filter16257" + inkscape:collect="always"> + <feGaussianBlur + stdDeviation="0.41431294" + id="feGaussianBlur16259" + inkscape:collect="always" /> + </filter> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient17245" + id="linearGradient27074" + gradientUnits="userSpaceOnUse" + x1="136.5" + y1="161.5" + x2="313.74622" + y2="285.25275" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient4451" + id="linearGradient27076" + gradientUnits="userSpaceOnUse" + x1="155.34465" + y1="112.46042" + x2="136.51547" + y2="2.1517708" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient26774" + id="linearGradient27078" + gradientUnits="userSpaceOnUse" + x1="280.27875" + y1="261.40704" + x2="322.26389" + y2="275.19568" /> + <filter + inkscape:collect="always" + x="-0.086395349" + width="1.1727907" + y="-0.11145" + height="1.2229" + id="filter3497"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="11.145" + id="feGaussianBlur3499" /> + </filter> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3460" + id="radialGradient3466" + cx="167.48819" + cy="192.38739" + fx="167.48819" + fy="192.38739" + r="105.62836" + gradientTransform="matrix(0.8393229,-0.4383343,0.2966517,0.5680291,-30.16055,165.27307)" + gradientUnits="userSpaceOnUse" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient26774" + id="linearGradient2490" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.0228007,0,0,1.0228007,-6.1273429,-1.9735657)" + x1="280.27875" + y1="261.40704" + x2="322.26389" + y2="275.19568" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient4451" + id="linearGradient2500" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.0228007,0,0,1.0228007,-6.1273429,-1.9735657)" + x1="159.38791" + y1="126.94874" + x2="138.87404" + y2="12.596838" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient17245" + id="linearGradient2777" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.0228007,0,0,1.0228007,-6.1273429,-1.9735657)" + x1="136.5" + y1="161.5" + x2="313.74622" + y2="285.25275" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3757" + id="radialGradient3763" + cx="170.31175" + cy="209.16652" + fx="170.31175" + fy="209.16652" + r="104.54334" + gradientTransform="matrix(1.0743517,-0.8811517,0.8948667,1.0910737,-193.89727,134.77199)" + gradientUnits="userSpaceOnUse" /> + </defs> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + id="layer1" + inkscape:groupmode="layer"> + <g + id="g2491"> + <path + transform="matrix(0.9747328,0,0,0.9747328,8.1232897,4.8258519)" + inkscape:export-ydpi="98" + inkscape:export-xdpi="98" + sodipodi:nodetypes="cccccc" + id="path2486" + d="M 183,323 L 53,296 L 0.99999999,150 L 196,96 L 255,237 L 183,323 z " + style="fill:#1a1a1a;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3497)" /> + <path + id="path5140" + sodipodi:nodetypes="ccccc" + d="M 103.94986,273.26347 L 112.82679,278.13224 L 130.15293,266.67465 L 121.4575,262.08933 L 103.94986,273.26347 z " + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path5128" + sodipodi:nodetypes="ccccc" + d="M 156.01098,239.64164 L 164.60219,243.86025 L 180.14702,233.7166 L 171.54021,229.64474 L 156.01098,239.64164 z " + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path5132" + sodipodi:nodetypes="ccccc" + d="M 171.60139,229.6086 L 180.08668,233.62387 L 195.07741,223.67266 L 186.68482,219.84353 L 171.60139,229.6086 z " + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path5130" + sodipodi:nodetypes="ccccc" + d="M 186.53932,219.8472 L 195.14618,223.77328 L 207.96039,215.29699 L 199.53214,211.5709 L 186.53932,219.8472 z " + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path5134" + sodipodi:nodetypes="ccccc" + d="M 199.53942,211.55443 L 208.00833,215.29699 L 219.61158,207.48873 L 211.43624,203.97858 L 199.53942,211.55443 z " + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path5136" + sodipodi:nodetypes="ccccc" + d="M 211.48298,203.99346 L 219.48498,207.43045 L 231.12889,200.02493 L 223.45622,196.26063 L 211.48298,203.99346 z " + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2172" + sodipodi:nodetypes="ccccc" + d="M 68.307573,278.59889 L 233.3461,375.01483 L 393.09703,219.55258 L 250.10479,166.11978 L 68.307573,278.59889 z " + style="fill:#000000;fill-rule:evenodd;stroke:#333333;stroke-width:0.81824058;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + sodipodi:nodetypes="cccccc" + id="path16273" + d="M 277.69986,176.50516 C 177.30035,235.42715 239.60255,221.94517 101.71421,297.5792 L 189.47945,349.38523 C 273.26264,323.79193 366.41421,274.97725 355.07543,205.22753 L 295.56691,183.1214 L 277.69986,176.50516 z " + style="fill:url(#linearGradient2777);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + id="path2174" + sodipodi:nodetypes="ccccc" + d="M 68.171626,278.57262 L 68.303363,291.78988 L 232.57786,391.18452 C 235.83055,389.43142 237.23238,379.0975 233.63174,375.00319 L 68.171626,278.57262 z " + style="fill:#000000;fill-rule:evenodd;stroke:#333333;stroke-width:0.81824058;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + id="path2176" + sodipodi:nodetypes="ccccc" + d="M 393.07972,219.62716 C 394.063,222.66073 395.10956,229.26471 392.24335,231.97585 L 232.84133,390.92104 C 237.0608,386.09241 235.76133,380.47536 233.63174,374.73973 L 393.07972,219.62716 z " + style="fill:#000000;fill-rule:evenodd;stroke:#333333;stroke-width:0.81824058;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + d="M 34.974214,77.099241 C 34.974214,77.099241 63.429139,279.82741 65.53691,277.66987 C 67.644683,275.51232 249.44002,164.63325 249.44002,164.63325 L 249.96698,8.789858 L 34.974214,77.099241 z " + sodipodi:nodetypes="csccc" + id="path2178" + style="fill:#1a1a1a;fill-rule:evenodd;stroke:#333333;stroke-width:0.81824058;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + inkscape:export-ydpi="98" + inkscape:export-xdpi="98" + d="M 243.62988,7.3609944 C 248.64901,5.9522606 248.53121,7.8674281 249.93941,8.795189 L 34.974214,76.572297 C 33.041161,74.848362 32.232239,75.552395 27.898382,75.685479 L 243.62988,7.3609944 z " + sodipodi:nodetypes="ccccc" + id="path2182" + style="fill:#1a1a1a;fill-rule:evenodd;stroke:#333333;stroke-width:0.81824058;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + id="path3155" + sodipodi:nodetypes="ccccc" + d="M 41.127572,89.515883 L 247.04644,21.857141 L 245.56959,155.03645 L 66.892268,262.94003 L 41.127572,89.515883 z " + style="fill:url(#radialGradient3763);fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" /> + <path + id="path4130" + sodipodi:nodetypes="ccccc" + d="M 270.30146,173.89938 L 92.059548,291.88393 L 83.064049,286.84829 L 262.53913,170.82327 L 270.30146,173.89938 z " + style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path5126" + sodipodi:nodetypes="ccc" + d="M 113.88343,277.34844 L 133.41939,264.57042 L 113.88343,277.34844 z " + style="opacity:0.3;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 260.55944,282.16245 C 260.55944,282.16245 299.34305,249.72696 299.34305,249.72696 C 301.56567,247.86815 306.24993,247.63883 309.88553,249.20565 C 309.88553,249.20565 329.40032,257.90584 329.40032,257.90584 C 333.27561,259.57597 334.75677,262.55505 332.67995,264.59388 C 332.67995,264.59388 296.81833,299.27831 296.81833,299.27831 C 294.33747,301.71377 288.97351,302.06916 284.843,300.06406 C 284.843,300.06406 262.71041,289.52115 262.71041,289.52115 C 258.86144,287.6527 257.92393,284.36659 260.55944,282.16245 C 260.55944,282.16245 260.55944,282.16245 260.55944,282.16245" + sodipodi:nodetypes="cccccccccc" + id="rect2179" + style="opacity:0.88999999;fill:#999999;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.97445869;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + d="M 156.31199,239.61556 C 158.84932,237.6711 165.25317,241.39833 164.88374,243.74344 L 156.31199,239.61556 z " + sodipodi:nodetypes="ccc" + id="path5322" + style="opacity:0.99720004;fill:#666666;fill-rule:evenodd;stroke:#000000;stroke-width:0.20456015;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + d="M 112.5495,278.27384 L 130.07045,266.61299 C 128.6036,263.60624 125.86497,261.45071 121.46704,261.8451 L 104.04747,273.28768 C 107.08494,271.21271 112.98604,275.81472 112.5495,278.27384 z " + sodipodi:nodetypes="ccccc" + id="path6299" + style="fill:#808080;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.20456015;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + d="M 104.03121,273.29774 C 107.49791,271.05672 113.43311,276.6129 112.52259,278.36122 L 104.03121,273.29774 z " + sodipodi:nodetypes="ccc" + id="path4349" + style="opacity:0.68999999;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.20456015;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + id="path8239" + sodipodi:nodetypes="ccccc" + d="M 164.84019,243.7543 L 179.63884,233.97378 C 178.89388,231.15559 176.668,229.33065 171.42447,229.78669 L 156.27423,239.63114 C 158.95009,237.51097 165.57347,241.76179 164.84019,243.7543 z " + style="fill:#4d4d4d;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.20456015;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + d="M 179.54294,233.98976 L 194.50141,224.24118 C 193.46879,221.32711 190.97123,219.43825 185.96742,220.29382 L 170.84915,230.10631 C 173.88125,228.78365 179.05461,230.45474 179.54294,233.98976 z " + sodipodi:nodetypes="ccccc" + id="path8241" + style="fill:#4d4d4d;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.20456015;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + id="path8243" + sodipodi:nodetypes="ccccc" + d="M 194.43749,224.22521 L 207.70194,215.40355 C 205.96614,212.71322 203.80418,211.59145 199.45559,211.72786 L 185.90349,220.3098 C 189.09541,219.56246 193.62952,220.65822 194.43749,224.22521 z " + style="fill:#4d4d4d;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.20456015;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + d="M 207.74987,215.38757 L 219.33629,207.66862 C 217.34479,205.17006 215.05499,204.11222 211.28174,204.12078 L 199.40765,211.74385 C 202.7434,211.44397 205.80725,212.3959 207.74987,215.38757 z " + sodipodi:nodetypes="ccccc" + id="path8245" + style="fill:#4d4d4d;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.20456015;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + id="path8247" + sodipodi:nodetypes="ccccc" + d="M 219.30433,207.70059 L 231.03457,199.96565 C 229.55448,196.50822 226.62543,195.89785 223.29965,196.19408 L 211.04202,204.2646 C 214.01019,203.93277 216.67451,204.42126 219.30433,207.70059 z " + style="fill:#4d4d4d;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.20456015;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + id="rect2202" + sodipodi:nodetypes="cccccccccc" + d="M 221.68659,286.68216 C 221.68659,286.68216 271.7827,246.38199 271.7827,246.38199 C 272.38533,245.90267 273.62888,245.89275 274.57389,246.35926 C 274.57389,246.35926 282.22513,250.1218 282.22513,250.1218 C 283.18438,250.59535 283.48058,251.37156 282.88624,251.86264 C 282.88624,251.86264 233.55382,293.3224 233.55382,293.3224 C 232.81433,293.93337 231.39356,293.96431 230.37209,293.3911 C 230.37209,293.3911 222.1464,288.7742 222.1464,288.7742 C 221.14187,288.21048 220.9385,287.27719 221.68659,286.68216 C 221.68659,286.68216 221.68659,286.68216 221.68659,286.68216" + style="fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.69999999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path2449" + d="M 219.03426,288.29491 L 230.39646,294.74997 L 222.03124,301.79784 L 210.47874,295.2849 L 219.03426,288.29491 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2451" + d="M 209.4152,296.07932 L 221.07617,302.55531 L 212.38161,309.86664 L 201.01939,302.85172 L 209.4152,296.07932 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2453" + d="M 200.12349,303.5516 L 211.48907,310.55261 L 202.5404,317.99996 L 190.60096,311.18361 L 200.12349,303.5516 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2455" + d="M 189.70952,311.95745 L 201.6931,318.78476 L 190.71106,328.04619 L 178.53214,321.0788 L 189.70952,311.95745 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2457" + sodipodi:nodetypes="ccccc" + d="M 273.68393,244.42789 L 284.19406,249.63047 L 292.16408,243.02723 L 281.60172,238.10854 L 273.68393,244.42789 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2459" + sodipodi:nodetypes="ccccc" + d="M 282.58552,237.35878 L 290.13787,231.26634 L 300.94177,235.89096 L 293.0886,242.26102 L 282.58552,237.35878 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2461" + sodipodi:nodetypes="ccccc" + d="M 291.16226,230.48888 L 301.87328,235.11565 L 309.36795,228.87453 L 299.02555,224.08989 L 291.16226,230.48888 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2463" + sodipodi:nodetypes="ccccc" + d="M 289.93538,230.01549 L 280.93975,225.55672 L 289.03737,219.25932 L 297.95995,223.58772 L 289.93538,230.01549 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2465" + d="M 177.42321,320.41889 L 166.48786,314.26225 L 188.72682,297.2375 L 199.03429,302.90648 L 177.42321,320.41889 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2467" + sodipodi:nodetypes="ccccc" + d="M 290.08046,218.43641 L 298.85735,222.73969 L 319.87746,205.75759 L 310.78391,202.19572 L 290.08046,218.43641 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2469" + sodipodi:nodetypes="ccccccc" + d="M 298.08535,210.7646 L 309.62549,201.69751 L 288.03973,193.31937 L 273.6384,203.95987 L 283.46625,207.8962 L 286.18927,205.81393 L 298.08535,210.7646 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2471" + sodipodi:nodetypes="ccccc" + d="M 286.62375,192.75442 L 266.91147,207.38925 L 257.59069,203.41102 L 277.74109,189.32694 L 286.62375,192.75442 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2473" + d="M 165.36066,313.63832 L 184.46965,298.98505 L 172.33203,292.59392 L 153.19109,306.83736 L 165.36066,313.63832 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2475" + d="M 151.95557,306.23713 L 167.0892,294.93513 L 156.09726,289.01503 L 140.88977,299.91382 L 151.95557,306.23713 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2477" + d="M 139.74507,299.3323 L 150.85746,291.335 L 140.35153,285.53863 L 129.36288,293.43149 L 139.74507,299.3323 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2497" + d="M 141.44916,284.84419 L 150.48582,278.42628 L 160.93153,283.91261 L 151.79607,290.59399 L 141.44916,284.84419 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2499" + d="M 200.06639,302.0818 L 208.31119,295.49502 L 197.64102,290.05561 L 189.67659,296.43646 L 200.06639,302.0818 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2501" + d="M 185.44374,298.19559 L 193.86354,291.59284 L 181.96116,285.25056 L 173.47309,291.62812 L 185.44374,298.19559 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2503" + d="M 168.25222,294.17765 L 176.70261,287.76919 L 165.64522,281.91859 L 157.20372,288.20299 L 168.25222,294.17765 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 151.55607,277.71812 L 160.35985,271.62622 L 170.55671,277.01143 L 161.94955,283.18847 L 151.55607,277.71812 z " + sodipodi:nodetypes="ccccc" + id="path2505" + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2507" + d="M 177.75365,287.00502 L 186.23698,280.4648 L 175.54186,274.93789 L 166.63928,281.22229 L 177.75365,287.00502 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2509" + d="M 161.45339,270.89479 L 170.04758,264.82618 L 180.19785,270.07166 L 171.68385,276.24869 L 161.45339,270.89479 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2511" + d="M 194.72496,290.89785 L 203.34236,284.16335 L 191.50585,278.08454 L 183.01779,284.4621 L 194.72496,290.89785 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2513" + d="M 209.44739,294.73981 L 218.00506,287.74135 L 207.50073,282.4405 L 198.6953,289.16034 L 209.44739,294.73981 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2515" + d="M 219.03117,287.00034 L 227.95111,279.8372 L 217.57851,274.66809 L 208.54255,281.61848 L 219.03117,287.00034 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2517" + d="M 229.01016,279.06327 L 236.63632,273.08946 L 226.18986,268.0248 L 218.58741,273.879 L 229.01016,279.06327 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2519" + d="M 237.61157,272.25159 L 246.12267,265.48601 L 235.81592,260.50649 L 227.24338,267.18776 L 237.61157,272.25159 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 246.92622,264.70405 L 255.31922,258.03494 L 245.2101,253.19516 L 236.79987,259.71739 L 246.92622,264.70405 z " + sodipodi:nodetypes="ccccc" + id="path2521" + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2523" + sodipodi:nodetypes="ccccc" + d="M 256.24944,257.30562 L 264.00138,251.14415 L 253.91623,246.49536 L 246.24255,252.47136 L 256.24944,257.30562 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 264.94343,250.38346 L 271.79281,244.57197 L 262.14241,240.07284 L 254.8806,245.75341 L 264.94343,250.38346 z " + sodipodi:nodetypes="ccccc" + id="path2525" + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2527" + sodipodi:nodetypes="ccccc" + d="M 272.6829,243.79667 L 280.45445,237.62292 L 270.90284,233.32138 L 263.17995,239.29836 L 272.6829,243.79667 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 281.41039,236.84761 L 288.91847,230.83852 L 279.86087,226.40525 L 272.03919,232.4481 L 281.41039,236.84761 z " + sodipodi:nodetypes="ccccc" + id="path2529" + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2531" + d="M 171.18769,264.04817 L 180.52708,257.51381 L 190.25819,262.57298 L 181.32501,269.3555 L 171.18769,264.04817 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2533" + sodipodi:nodetypes="ccccc" + d="M 181.56334,256.75316 L 190.15752,250.68454 L 199.60916,255.83687 L 191.23489,261.73446 L 181.56334,256.75316 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 191.13838,249.97224 L 200.12848,243.67074 L 209.58148,248.76519 L 200.57843,255.12855 L 191.13838,249.97224 z " + sodipodi:nodetypes="ccccc" + id="path2535" + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2537" + sodipodi:nodetypes="ccccc" + d="M 201.14148,242.94942 L 208.96784,237.50889 L 218.76812,242.01382 L 210.62107,248.07043 L 201.14148,242.94942 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 209.93861,236.70829 L 218.48624,230.77939 L 228.17074,235.18651 L 219.79647,241.31698 L 209.93861,236.70829 z " + sodipodi:nodetypes="ccccc" + id="path2539" + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2541" + sodipodi:nodetypes="ccccc" + d="M 219.48896,230.08656 L 227.94108,224.18895 L 237.62559,228.38647 L 229.19014,234.46609 L 219.48896,230.08656 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 229.04981,223.34939 L 237.64399,217.28078 L 247.18879,221.5016 L 238.67479,227.67863 L 229.04981,223.34939 z " + sodipodi:nodetypes="ccccc" + id="path2543" + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2545" + sodipodi:nodetypes="ccccc" + d="M 238.75966,216.51376 L 246.46891,211.02733 L 256.03698,215.155 L 248.29149,220.74984 L 238.75966,216.51376 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 247.52515,210.2999 L 256.52687,204.13813 L 265.95523,208.14936 L 257.24328,214.48941 L 247.52515,210.2999 z " + sodipodi:nodetypes="ccccc" + id="path2547" + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2242" + d="M 192.42707,277.38276 L 201.04243,270.93376 L 212.81727,276.9437 L 204.25518,283.46405 L 192.42707,277.38276 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2244" + d="M 202.01582,270.12726 L 210.21341,264.10219 L 221.92044,269.93134 L 213.74806,276.20857 L 202.01582,270.12726 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2246" + d="M 211.22102,263.35122 L 219.52192,257.1363 L 231.06172,262.8037 L 222.85738,269.20877 L 211.22102,263.35122 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2248" + sodipodi:nodetypes="ccccc" + d="M 220.48694,256.41147 L 231.99345,262.05286 L 239.51447,256.25393 L 228.07804,250.72214 C 228.07804,250.72214 220.51891,256.44343 220.48694,256.41147 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 228.98488,249.90241 L 240.4914,255.5438 L 248.01241,249.74486 L 236.57599,244.21308 C 236.57599,244.21308 229.01684,249.93436 228.98488,249.90241 z " + sodipodi:nodetypes="ccccc" + id="path2250" + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2252" + sodipodi:nodetypes="ccccc" + d="M 237.56868,243.44725 L 248.97931,248.99274 L 257.07404,242.57877 L 246.02277,237.37437 L 237.56868,243.44725 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 247.06561,236.56786 L 258.0749,241.84765 L 265.97947,235.69713 L 254.71266,230.82259 L 247.06561,236.56786 z " + sodipodi:nodetypes="ccccc" + id="path2254" + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2256" + sodipodi:nodetypes="ccccc" + d="M 255.89713,229.9869 L 266.95163,234.90506 L 274.38224,229.19653 L 263.30742,224.43317 L 255.89713,229.9869 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 264.32207,223.68092 L 275.3101,228.44997 L 282.89504,222.58711 L 271.91318,217.99159 L 264.32207,223.68092 z " + sodipodi:nodetypes="ccccc" + id="path2258" + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2260" + sodipodi:nodetypes="ccccc" + d="M 272.9276,217.22814 L 283.86907,221.82988 L 290.30525,216.88979 L 279.45645,212.28463 L 272.9276,217.22814 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 280.51495,211.48086 L 291.20783,216.17301 L 297.14678,211.54933 L 286.38838,207.05719 L 280.51495,211.48086 z " + sodipodi:nodetypes="ccccc" + id="path2262" + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2264" + d="M 187.21456,279.65363 L 195.69788,273.11343 L 185.00277,267.58651 L 176.10019,273.87091 L 187.21456,279.65363 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2266" + d="M 196.93117,272.23833 L 205.68571,265.78852 L 194.71938,260.1712 L 185.8168,266.4556 L 196.93117,272.23833 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2268" + sodipodi:nodetypes="ccccc" + d="M 206.76011,265.04128 L 215.60505,258.63667 L 204.90993,253.42617 L 196.23337,259.52977 L 206.76011,265.04128 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 216.55615,257.79127 L 225.72667,251.12321 L 215.30323,245.97983 L 205.95318,252.6478 L 216.55615,257.79127 z " + sodipodi:nodetypes="ccccc" + id="path2270" + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2272" + sodipodi:nodetypes="ccccc" + d="M 226.62217,250.28446 L 234.42747,244.46748 L 224.14692,239.49074 L 216.41185,245.13456 L 226.62217,250.28446 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 235.3202,243.66108 L 242.92208,238.20571 L 232.8824,233.22122 L 225.20027,238.78239 L 235.3202,243.66108 z " + sodipodi:nodetypes="ccccc" + id="path2274" + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2276" + sodipodi:nodetypes="ccccc" + d="M 244.04269,237.40717 L 251.47219,231.70252 L 241.73406,226.77098 L 234.01317,232.34768 L 244.04269,237.40717 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 252.6522,231.11226 L 260.3045,225.27523 L 250.58643,220.64138 L 242.89843,226.09476 L 252.6522,231.11226 z " + sodipodi:nodetypes="ccccc" + id="path2278" + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2280" + sodipodi:nodetypes="ccccc" + d="M 261.23976,224.42239 L 268.45876,219.21044 L 258.80517,214.83417 L 251.72352,219.79033 L 261.23976,224.42239 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 269.52519,218.43638 L 275.83883,213.61385 L 266.36416,209.30737 L 259.8121,214.10012 L 269.52519,218.43638 z " + sodipodi:nodetypes="ccccc" + id="path2282" + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path2284" + sodipodi:nodetypes="ccccc" + d="M 276.80269,212.87655 L 282.2575,208.64164 L 272.64721,204.83239 L 267.406,208.63069 L 276.80269,212.87655 z " + style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="rect3291" + d="M 302.87759,234.27933 C 302.87759,234.27933 308.38212,229.69548 308.38212,229.69548 C 308.92862,229.24038 308.77424,228.59985 308.03355,228.25718 C 308.03355,228.25718 300.43832,224.74347 300.43832,224.74347 C 299.65719,224.38211 298.56475,224.46487 297.99153,224.93134 C 297.99153,224.93134 292.2163,229.63113 292.2163,229.63113 C 291.63275,230.10601 291.81664,230.77156 292.62585,231.12111 C 292.62585,231.12111 300.49174,234.51889 300.49174,234.51889 C 301.25859,234.85015 302.32155,234.74235 302.87759,234.27933 C 302.87759,234.27933 302.87759,234.27933 302.87759,234.27933" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3293" + d="M 235.3731,272.47703 C 235.3731,272.47703 227.42329,268.6228 227.42329,268.6228 C 226.73948,268.29128 225.78761,268.33455 225.2875,268.71966 C 225.2875,268.71966 219.50192,273.17479 219.50192,273.17479 C 218.99549,273.56477 219.13572,274.15174 219.81793,274.49107 C 219.81793,274.49107 227.7497,278.43631 227.7497,278.43631 C 228.44749,278.78339 229.41954,278.74258 229.9276,278.34461 C 229.9276,278.34461 235.73123,273.79845 235.73123,273.79845 C 236.23285,273.4055 236.07241,272.81607 235.3731,272.47703 C 235.3731,272.47703 235.3731,272.47703 235.3731,272.47703" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3299" + d="M 247.43674,211.44485 C 247.43674,211.44485 255.04475,214.72695 255.04475,214.72695 C 255.59412,214.96395 255.68572,215.40873 255.24904,215.72416 C 255.24904,215.72416 249.09015,220.17295 249.09015,220.17295 C 248.64784,220.49244 247.85024,220.55375 247.30291,220.31051 C 247.30291,220.31051 239.72373,216.94221 239.72373,216.94221 C 239.18929,216.7047 239.11431,216.26136 239.55452,215.94809 C 239.55452,215.94809 245.68459,211.58552 245.68459,211.58552 C 246.11924,211.27618 246.90023,211.21341 247.43674,211.44485 C 247.43674,211.44485 247.43674,211.44485 247.43674,211.44485" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3301" + d="M 211.38791,264.687 C 211.38791,264.687 220.69642,269.32187 220.69642,269.32187 C 221.37384,269.65917 221.55369,270.21304 221.09728,270.56361 C 221.09728,270.56361 214.59913,275.55485 214.59913,275.55485 C 214.12804,275.9167 213.20005,275.92451 212.52093,275.57249 C 212.52093,275.57249 203.19239,270.73714 203.19239,270.73714 C 202.53988,270.39891 202.39682,269.84723 202.86919,269.50005 C 202.86919,269.50005 209.38739,264.7093 209.38739,264.7093 C 209.84537,264.37269 210.73656,264.36268 211.38791,264.687 C 211.38791,264.687 211.38791,264.687 211.38791,264.687" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3305" + d="M 190.80124,251.03544 C 190.80124,251.03544 198.99762,255.5035 198.99762,255.5035 C 199.33669,255.68835 199.3609,256.01171 199.05255,256.22886 C 199.05255,256.22886 191.79012,261.34344 191.79012,261.34344 C 191.48252,261.56007 190.95608,261.59085 190.60912,261.41215 C 190.60912,261.41215 182.222,257.0924 182.222,257.0924 C 181.85741,256.90462 181.81745,256.57372 182.13313,256.35082 C 182.13313,256.35082 189.58629,251.08792 189.58629,251.08792 C 189.90275,250.86444 190.44492,250.8412 190.80124,251.03544 C 190.80124,251.03544 190.80124,251.03544 190.80124,251.03544" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3309" + d="M 200.86922,319.47957 C 200.86922,319.47957 191.5626,327.32806 191.5626,327.32806 C 191.09111,327.72566 190.29604,327.80875 189.78059,327.51386 C 189.78059,327.51386 179.45944,321.6093 179.45944,321.6093 C 178.94568,321.31539 178.91894,320.76315 179.3988,320.37157 C 179.3988,320.37157 188.87097,312.64175 188.87097,312.64175 C 189.33581,312.26242 190.11645,312.18928 190.62199,312.47729 C 190.62199,312.47729 200.77757,318.26317 200.77757,318.26317 C 201.28475,318.55212 201.32593,319.09442 200.86922,319.47957 C 200.86922,319.47957 200.86922,319.47957 200.86922,319.47957" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3311" + d="M 227.03328,279.37981 C 227.03328,279.37981 218.4746,275.11465 218.4746,275.11465 C 217.9779,274.86712 217.23368,274.93335 216.80449,275.26346 C 216.80449,275.26346 209.3489,280.99824 209.3489,280.99824 C 208.90258,281.34154 208.94628,281.82562 209.44841,282.08328 C 209.44841,282.08328 218.10282,286.52398 218.10282,286.52398 C 218.61686,286.78775 219.38667,286.71486 219.82736,286.36095 C 219.82736,286.36095 227.18725,280.45063 227.18725,280.45063 C 227.61081,280.1105 227.5415,279.63307 227.03328,279.37981 C 227.03328,279.37981 227.03328,279.37981 227.03328,279.37981" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3313" + d="M 200.87727,244.07429 C 200.87727,244.07429 208.8242,248.35707 208.8242,248.35707 C 209.24365,248.58313 209.26299,248.99029 208.86725,249.27001 C 208.86725,249.27001 201.29858,254.61953 201.29858,254.61953 C 200.89969,254.90148 200.24107,254.94426 199.82216,254.71545 C 199.82216,254.71545 191.88613,250.38067 191.88613,250.38067 C 191.47181,250.15435 191.45917,249.7474 191.8575,249.46819 C 191.8575,249.46819 199.41526,244.17067 199.41526,244.17067 C 199.81044,243.89367 200.46234,243.85067 200.87727,244.07429 C 200.87727,244.07429 200.87727,244.07429 200.87727,244.07429" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3317" + d="M 168.98052,312.35404 C 168.98052,312.35404 186.52228,298.92516 186.52228,298.92516 C 187.74742,297.98727 189.20942,297.50291 189.80916,297.83277 C 189.80916,297.83277 197.94313,302.30636 197.94313,302.30636 C 198.54751,302.63875 198.08283,303.67748 196.89261,304.64197 C 196.89261,304.64197 179.8462,318.45547 179.8462,318.45547 C 178.50836,319.53957 176.90663,320.12806 176.26526,319.76696 C 176.26526,319.76696 167.6358,314.90855 167.6358,314.90855 C 166.99971,314.55042 167.60418,313.40767 168.98052,312.35404 C 168.98052,312.35404 168.98052,312.35404 168.98052,312.35404" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3319" + d="M 192.5101,290.87165 C 192.5101,290.87165 183.27464,285.95046 183.27464,285.95046 C 182.54633,285.56237 181.54412,285.5639 181.0251,285.95388 C 181.0251,285.95388 174.43893,290.90244 174.43893,290.90244 C 173.90432,291.3041 174.06146,291.95091 174.79375,292.35267 C 174.79375,292.35267 184.08217,297.44861 184.08217,297.44861 C 184.83581,297.86207 185.87162,297.86005 186.40206,297.44408 C 186.40206,297.44408 192.93525,292.32079 192.93525,292.32079 C 193.44997,291.91714 193.25925,291.27084 192.5101,290.87165 C 192.5101,290.87165 192.5101,290.87165 192.5101,290.87165" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3321" + d="M 185.1382,279.89699 C 185.1382,279.89699 176.63387,275.50221 176.63387,275.50221 C 176.02877,275.18952 175.15088,275.21388 174.66322,275.55813 C 174.66322,275.55813 167.58535,280.55447 167.58535,280.55447 C 167.06223,280.92372 167.14522,281.48553 167.77396,281.81266 C 167.77396,281.81266 176.61165,286.41084 176.61165,286.41084 C 177.2442,286.73996 178.15674,286.69425 178.65527,286.3099 C 178.65527,286.3099 185.39983,281.11021 185.39983,281.11021 C 185.86446,280.752 185.74682,280.2115 185.1382,279.89699 C 185.1382,279.89699 185.1382,279.89699 185.1382,279.89699" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3323" + d="M 161.12258,272.02905 C 161.12258,272.02905 169.7964,276.60989 169.7964,276.60989 C 170.21763,276.83236 170.27487,277.2137 169.92432,277.46528 C 169.92432,277.46528 162.60291,282.7196 162.60291,282.7196 C 162.24117,282.9792 161.60395,283.00656 161.17461,282.7806 C 161.17461,282.7806 152.33353,278.12732 152.33353,278.12732 C 151.90283,277.90063 151.85435,277.51172 152.22438,277.25569 C 152.22438,277.25569 159.71303,272.0738 159.71303,272.0738 C 160.07158,271.82571 160.70003,271.80589 161.12258,272.02905 C 161.12258,272.02905 161.12258,272.02905 161.12258,272.02905" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3325" + d="M 220.2904,303.21609 C 220.2904,303.21609 213.19436,309.1832 213.19436,309.1832 C 212.74444,309.56153 211.88835,309.56212 211.27912,309.18598 C 211.27912,309.18598 202.00839,303.46232 202.00839,303.46232 C 201.45919,303.12325 201.36941,302.5694 201.80347,302.21926 C 201.80347,302.21926 208.65568,296.69198 208.65568,296.69198 C 209.07675,296.35232 209.86703,296.33025 210.43121,296.64355 C 210.43121,296.64355 219.94579,301.92755 219.94579,301.92755 C 220.57046,302.27448 220.72603,302.84974 220.2904,303.21609 C 220.2904,303.21609 220.2904,303.21609 220.2904,303.21609" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3327" + d="M 250.18155,231.04893 C 250.18155,231.04893 243.02922,227.42687 243.02922,227.42687 C 242.31181,227.06355 241.28875,227.09263 240.73358,227.49361 C 240.73358,227.49361 235.06335,231.58914 235.06335,231.58914 C 234.48252,232.00866 234.60822,232.64785 235.34714,233.02061 C 235.34714,233.02061 242.71349,236.73664 242.71349,236.73664 C 243.44994,237.10815 244.49431,237.0604 245.05318,236.63127 C 245.05318,236.63127 250.50942,232.44177 250.50942,232.44177 C 251.04367,232.03155 250.89663,231.41106 250.18155,231.04893 C 250.18155,231.04893 250.18155,231.04893 250.18155,231.04893" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3329" + d="M 241.73896,237.6183 C 241.73896,237.6183 234.09875,233.82511 234.09875,233.82511 C 233.42547,233.49084 232.47146,233.51871 231.96125,233.88804 C 231.96125,233.88804 226.11501,238.1202 226.11501,238.1202 C 225.60814,238.48713 225.74764,239.04628 226.42628,239.37343 C 226.42628,239.37343 234.12755,243.08611 234.12755,243.08611 C 234.78882,243.40491 235.72382,243.37142 236.22543,243.01146 C 236.22543,243.01146 242.01062,238.85982 242.01062,238.85982 C 242.51547,238.49751 242.39495,237.94399 241.73896,237.6183 C 241.73896,237.6183 241.73896,237.6183 241.73896,237.6183" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3331" + d="M 291.06923,231.66501 C 291.06923,231.66501 299.93537,235.46017 299.93537,235.46017 C 300.49195,235.69842 300.62867,236.14494 300.23967,236.46046 C 300.23967,236.46046 293.79405,241.68879 293.79405,241.68879 C 293.40327,242.00578 292.65126,242.0569 292.11015,241.80432 C 292.11015,241.80432 283.49087,237.78134 283.49087,237.78134 C 282.98847,237.54685 282.88811,237.11468 283.26389,236.81155 C 283.26389,236.81155 289.4626,231.81108 289.4626,231.81108 C 289.83674,231.50927 290.5524,231.44378 291.06923,231.66501 C 291.06923,231.66501 291.06923,231.66501 291.06923,231.66501" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3333" + d="M 214.6618,258.17714 C 214.6618,258.17714 205.79323,253.8565 205.79323,253.8565 C 205.30323,253.61778 204.58399,253.65545 204.17864,253.94061 C 204.17864,253.94061 196.98327,259.00223 196.98327,259.00223 C 196.56804,259.29434 196.62028,259.73235 197.10235,259.98475 C 197.10235,259.98475 205.83129,264.55497 205.83129,264.55497 C 206.34512,264.82401 207.10144,264.79412 207.5249,264.48748 C 207.5249,264.48748 214.85991,259.17623 214.85991,259.17623 C 215.27294,258.87715 215.18363,258.43136 214.6618,258.17714 C 214.6618,258.17714 214.6618,258.17714 214.6618,258.17714" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3335" + d="M 238.61522,217.71027 C 238.61522,217.71027 246.20482,221.06648 246.20482,221.06648 C 246.74975,221.30745 246.80537,221.77975 246.32841,222.12581 C 246.32841,222.12581 239.55856,227.03746 239.55856,227.03746 C 239.06928,227.39242 238.23201,227.47949 237.68245,227.2323 C 237.68245,227.2323 230.0291,223.78987 230.0291,223.78987 C 229.48641,223.54578 229.44798,223.06823 229.94181,222.71952 C 229.94181,222.71952 236.77542,217.89409 236.77542,217.89409 C 237.25693,217.55409 238.07699,217.47225 238.61522,217.71027 C 238.61522,217.71027 238.61522,217.71027 238.61522,217.71027" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3337" + d="M 228.91723,224.61205 C 228.91723,224.61205 236.61775,227.94965 236.61775,227.94965 C 237.17563,228.19144 237.24665,228.65958 236.77513,228.99941 C 236.77513,228.99941 230.06777,233.83356 230.06777,233.83356 C 229.58195,234.18369 228.73926,234.26255 228.18027,234.01019 C 228.18027,234.01019 220.46652,230.52787 220.46652,230.52787 C 219.92451,230.28318 219.88144,229.8127 220.36809,229.47313 C 220.36809,229.47313 227.0887,224.78372 227.0887,224.78372 C 227.56129,224.45396 228.37602,224.37747 228.91723,224.61205 C 228.91723,224.61205 228.91723,224.61205 228.91723,224.61205" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3339" + d="M 257.14967,204.40311 C 257.14967,204.40311 265.32623,207.88176 265.32623,207.88176 C 265.67464,208.02998 265.706,208.33073 265.3957,208.55655 C 265.3957,208.55655 257.84099,214.05443 257.84099,214.05443 C 257.51021,214.29515 256.95402,214.3647 256.59485,214.20987 C 256.59485,214.20987 248.16698,210.57659 248.16698,210.57659 C 247.81135,210.42328 247.8009,210.11114 248.14264,209.87723 C 248.14264,209.87723 255.94863,204.53395 255.94863,204.53395 C 256.2693,204.31446 256.80459,204.25629 257.14967,204.40311 C 257.14967,204.40311 257.14967,204.40311 257.14967,204.40311" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3341" + d="M 201.8511,271.34651 C 201.8511,271.34651 212.00249,276.52784 212.00249,276.52784 C 212.45381,276.7582 212.55592,277.14272 212.23112,277.39008 C 212.23112,277.39008 204.84954,283.01143 204.84954,283.01143 C 204.52035,283.26212 203.89008,283.27634 203.43671,283.04324 C 203.43671,283.04324 193.23937,277.8004 193.23937,277.8004 C 192.78929,277.56899 192.69387,277.18305 193.0251,276.9351 C 193.0251,276.9351 200.45262,271.37525 200.45262,271.37525 C 200.77944,271.1306 201.40305,271.11781 201.8511,271.34651 C 201.8511,271.34651 201.8511,271.34651 201.8511,271.34651" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3343" + d="M 219.21667,231.1118 C 219.21667,231.1118 227.42383,234.84661 227.42383,234.84661 C 227.83744,235.03484 227.89363,235.38938 227.54865,235.64192 C 227.54865,235.64192 220.45221,240.83693 220.45221,240.83693 C 220.08929,241.10262 219.45708,241.15831 219.03594,240.96141 C 219.03594,240.96141 210.6819,237.05577 210.6819,237.05577 C 210.26995,236.86319 210.23739,236.50104 210.60771,236.24418 C 210.60771,236.24418 217.85107,231.21996 217.85107,231.21996 C 218.20329,230.97566 218.81185,230.92758 219.21667,231.1118 C 219.21667,231.1118 219.21667,231.1118 219.21667,231.1118" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3345" + d="M 166.11203,294.40885 C 166.11203,294.40885 157.04241,289.52408 157.04241,289.52408 C 156.51843,289.24186 155.52217,289.42717 154.80588,289.94053 C 154.80588,289.94053 142.25863,298.93279 142.25863,298.93279 C 141.50124,299.47559 141.31352,300.15596 141.84075,300.45723 C 141.84075,300.45723 150.97129,305.67469 150.97129,305.67469 C 151.51617,305.98604 152.56442,305.78244 153.31853,305.21926 C 153.31853,305.21926 165.80481,295.89435 165.80481,295.89435 C 166.51725,295.36228 166.65299,294.70021 166.11203,294.40885 C 166.11203,294.40885 166.11203,294.40885 166.11203,294.40885" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3349" + d="M 151.01455,278.70397 C 151.01455,278.70397 160.37455,283.62006 160.37455,283.62006 C 160.68292,283.78202 160.7229,284.06518 160.46357,284.25485 C 160.46357,284.25485 152.27741,290.24195 152.27741,290.24195 C 152.01084,290.43692 151.5497,290.45709 151.24414,290.28729 C 151.24414,290.28729 141.97268,285.13512 141.97268,285.13512 C 141.68247,284.97384 141.66152,284.69337 141.92511,284.50616 C 141.92511,284.50616 150.02275,278.75515 150.02275,278.75515 C 150.27937,278.57289 150.72146,278.55003 151.01455,278.70397 C 151.01455,278.70397 151.01455,278.70397 151.01455,278.70397" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3351" + d="M 175.52443,287.1458 C 175.52443,287.1458 166.79876,282.52893 166.79876,282.52893 C 166.15936,282.19062 165.25137,282.21179 164.76171,282.57631 C 164.76171,282.57631 158.10028,287.53553 158.10028,287.53553 C 157.60379,287.90516 157.7174,288.48077 158.35625,288.82623 C 158.35625,288.82623 167.07489,293.541 167.07489,293.541 C 167.72676,293.8935 168.65274,293.87392 169.1498,293.49696 C 169.1498,293.49696 175.81827,288.43985 175.81827,288.43985 C 176.30838,288.06815 176.17677,287.49096 175.52443,287.1458 C 175.52443,287.1458 175.52443,287.1458 175.52443,287.1458" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3353" + d="M 201.97097,283.45905 C 201.97097,283.45905 192.78768,278.74285 192.78768,278.74285 C 192.07622,278.37747 191.08839,278.3982 190.5689,278.78852 C 190.5689,278.78852 183.98269,283.7371 183.98269,283.7371 C 183.44857,284.1384 183.58158,284.77203 184.28486,285.15865 C 184.28486,285.15865 193.36778,290.15177 193.36778,290.15177 C 194.11817,290.56428 195.16258,290.55584 195.70514,290.13183 C 195.70514,290.13183 202.39168,284.90631 202.39168,284.90631 C 202.9188,284.49436 202.72924,283.84847 201.97097,283.45905 C 201.97097,283.45905 201.97097,283.45905 201.97097,283.45905" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3355" + d="M 183.209,298.32124 C 183.209,298.32124 173.55788,293.23939 173.55788,293.23939 C 172.87828,292.88156 171.48018,293.22782 170.41912,294.01738 C 170.41912,294.01738 155.20003,305.34244 155.20003,305.34244 C 154.08849,306.16958 153.73855,307.14332 154.41967,307.52396 C 154.41967,307.52396 164.0962,312.93167 164.0962,312.93167 C 164.79619,313.32286 166.25692,312.95103 167.36704,312.09976 C 167.36704,312.09976 182.56068,300.44889 182.56068,300.44889 C 183.61957,299.63691 183.90689,298.68872 183.209,298.32124 C 183.209,298.32124 183.209,298.32124 183.209,298.32124" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3357" + d="M 181.25173,257.89055 C 181.25173,257.89055 189.52937,262.19408 189.52937,262.19408 C 189.93309,262.40396 189.973,262.78951 189.61763,263.05932 C 189.61763,263.05932 182.01974,268.82803 182.01974,268.82803 C 181.63543,269.11981 180.98628,269.17817 180.56566,268.95795 C 180.56566,268.95795 171.94248,264.44335 171.94248,264.44335 C 171.5243,264.2244 171.5122,263.82114 171.91393,263.54006 C 171.91393,263.54006 179.85732,257.98241 179.85732,257.98241 C 180.2289,257.72244 180.85025,257.68182 181.25173,257.89055 C 181.25173,257.89055 181.25173,257.89055 181.25173,257.89055" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3359" + d="M 207.30074,294.9799 C 207.30074,294.9799 198.59287,290.54083 198.59287,290.54083 C 198.06485,290.27167 197.31412,290.31751 196.90812,290.6428 C 196.90812,290.6428 190.40778,295.85065 190.40778,295.85065 C 190.00268,296.17521 190.08929,296.66071 190.60345,296.94008 C 190.60345,296.94008 199.08253,301.54721 199.08253,301.54721 C 199.62684,301.84297 200.40395,301.81213 200.82331,301.47711 C 200.82331,301.47711 207.55245,296.10117 207.55245,296.10117 C 207.97277,295.76537 207.85976,295.26488 207.30074,294.9799 C 207.30074,294.9799 207.30074,294.9799 207.30074,294.9799" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3361" + d="M 210.31834,311.52693 C 210.31834,311.52693 203.74605,316.99659 203.74605,316.99659 C 203.0788,317.55189 201.86,317.61152 201.01035,317.12644 C 201.01035,317.12644 192.24289,312.121 192.24289,312.121 C 191.3355,311.60296 191.17426,310.72413 191.88471,310.15474 C 191.88471,310.15474 198.87844,304.54948 198.87844,304.54948 C 199.56891,303.99608 200.82226,303.98204 201.68556,304.51382 C 201.68556,304.51382 210.03168,309.65489 210.03168,309.65489 C 210.84096,310.15338 210.96757,310.98662 210.31834,311.52693 C 210.31834,311.52693 210.31834,311.52693 210.31834,311.52693" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3363" + d="M 229.40869,295.58217 C 229.40869,295.58217 223.04271,300.94565 223.04271,300.94565 C 222.48275,301.41742 221.41805,301.45214 220.65599,301.0225 C 220.65599,301.0225 211.86431,296.06605 211.86431,296.06605 C 211.09687,295.63339 210.94055,294.9076 211.51326,294.43967 C 211.51326,294.43967 218.02408,289.12024 218.02408,289.12024 C 218.58411,288.66268 219.64217,288.64028 220.39694,289.06907 C 220.39694,289.06907 229.04379,293.98149 229.04379,293.98149 C 229.79334,294.40733 229.95631,295.12081 229.40869,295.58217 C 229.40869,295.58217 229.40869,295.58217 229.40869,295.58217" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3365" + d="M 217.05231,287.26056 C 217.05231,287.26056 208.43084,282.90987 208.43084,282.90987 C 207.9153,282.6497 207.16011,282.70044 206.73573,283.02431 C 206.73573,283.02431 199.50923,288.5392 199.50923,288.5392 C 199.05894,288.88283 199.11946,289.38044 199.64696,289.65417 C 199.64696,289.65417 208.47177,294.23353 208.47177,294.23353 C 209.01197,294.51386 209.80095,294.45066 210.23875,294.09263 C 210.23875,294.09263 217.26191,288.3491 217.26191,288.3491 C 217.67418,288.01194 217.57986,287.52677 217.05231,287.26056 C 217.05231,287.26056 217.05231,287.26056 217.05231,287.26056" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3367" + d="M 170.90895,265.27133 C 170.90895,265.27133 179.32655,269.62137 179.32655,269.62137 C 179.80913,269.87078 179.87784,270.30382 179.47992,270.59253 C 179.47992,270.59253 172.41934,275.71508 172.41934,275.71508 C 172.01209,276.01056 171.29204,276.04365 170.8056,275.78907 C 170.8056,275.78907 162.3215,271.34911 162.3215,271.34911 C 161.84045,271.09734 161.7847,270.66085 162.19576,270.37059 C 162.19576,270.37059 169.32283,265.33795 169.32283,265.33795 C 169.72453,265.05428 170.43163,265.02465 170.90895,265.27133 C 170.90895,265.27133 170.90895,265.27133 170.90895,265.27133" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3369" + d="M 204.55438,265.20903 C 204.55438,265.20903 195.83444,260.74237 195.83444,260.74237 C 195.21646,260.42583 194.32033,260.45289 193.82368,260.80347 C 193.82368,260.80347 186.74496,265.80041 186.74496,265.80041 C 186.23123,266.16305 186.32053,266.71769 186.94675,267.04351 C 186.94675,267.04351 195.78441,271.64167 195.78441,271.64167 C 196.41947,271.97209 197.33877,271.93803 197.84403,271.56578 C 197.84403,271.56578 204.80505,266.43735 204.80505,266.43735 C 205.29337,266.07757 205.18091,265.52994 204.55438,265.20903 C 204.55438,265.20903 204.55438,265.20903 204.55438,265.20903" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3371" + d="M 194.5991,272.5456 C 194.5991,272.5456 186.09478,268.15083 186.09478,268.15083 C 185.48971,267.83814 184.61179,267.8625 184.12412,268.20675 C 184.12412,268.20675 177.04625,273.20308 177.04625,273.20308 C 176.52314,273.57235 176.60616,274.13416 177.23487,274.46127 C 177.23487,274.46127 186.07255,279.05946 186.07255,279.05946 C 186.70512,279.38857 187.61765,279.34288 188.11618,278.95853 C 188.11618,278.95853 194.86074,273.75884 194.86074,273.75884 C 195.32537,273.40062 195.20773,272.86013 194.5991,272.5456 C 194.5991,272.5456 194.5991,272.5456 194.5991,272.5456" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3373" + d="M 284.73208,249.18471 C 284.73208,249.18471 291.64382,243.45826 291.64382,243.45826 C 291.9322,243.21935 291.84218,242.87732 291.44316,242.6915 C 291.44316,242.6915 282.28362,238.42609 282.28362,238.42609 C 281.90552,238.25002 281.37123,238.2925 281.08461,238.52125 C 281.08461,238.52125 274.21817,244.0015 274.21817,244.0015 C 273.92236,244.23759 273.98607,244.57745 274.36212,244.76359 C 274.36212,244.76359 283.47634,249.2752 283.47634,249.2752 C 283.87359,249.47184 284.43418,249.43153 284.73208,249.18471 C 284.73208,249.18471 284.73208,249.18471 284.73208,249.18471" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3375" + d="M 266.25849,207.11055 C 266.25849,207.11055 258.22296,203.68087 258.22296,203.68087 C 257.8725,203.5313 258.26258,202.94141 259.09217,202.36158 C 259.09217,202.36158 276.45653,190.22478 276.45653,190.22478 C 277.16993,189.72614 278.01001,189.4307 278.34442,189.55973 C 278.34442,189.55973 286.00228,192.51461 286.00228,192.51461 C 286.34641,192.64741 286.06579,193.16868 285.36877,193.68615 C 285.36877,193.68615 268.38219,206.29734 268.38219,206.29734 C 267.56963,206.9006 266.62006,207.26486 266.25849,207.11055 C 266.25849,207.11055 266.25849,207.11055 266.25849,207.11055" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3377" + d="M 150.04703,290.88787 C 150.04703,290.88787 141.14376,285.97573 141.14376,285.97573 C 140.70469,285.73348 139.97814,285.80683 139.51431,286.13998 C 139.51431,286.13998 130.20189,292.82886 130.20189,292.82886 C 129.7371,293.1627 129.71188,293.62984 130.14579,293.87645 C 130.14579,293.87645 138.94417,298.87711 138.94417,298.87711 C 139.38768,299.12918 140.1235,299.05996 140.59351,298.72169 C 140.59351,298.72169 150.01083,291.94429 150.01083,291.94429 C 150.47987,291.60674 150.49581,291.13546 150.04703,290.88787 C 150.04703,290.88787 150.04703,290.88787 150.04703,290.88787" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3379" + sodipodi:nodetypes="cccccssssccccc" + d="M 308.02082,201.07469 C 308.02082,201.07469 290.25388,194.17877 290.25388,194.17877 C 289.03643,193.70622 287.49217,193.72395 286.81047,194.22762 C 286.81047,194.22762 274.92805,203.00701 274.92805,203.00701 C 274.21432,203.53434 274.06866,203.98769 275.45256,204.3729 L 282.20799,207.13372 C 283.30081,207.58033 283.29864,207.60345 284.24763,206.91193 L 285.51228,205.99038 C 285.78882,205.78886 286.61757,205.71073 287.50613,206.09359 L 296.65875,210.03701 C 297.66812,210.31796 298.54496,210.40349 299.11475,209.9558 C 299.11475,209.9558 308.63657,202.4745 308.63657,202.4745 C 309.1849,202.04368 308.91548,201.42193 308.02082,201.07469 C 308.02082,201.07469 308.02082,201.07469 308.02082,201.07469" + style="fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3381" + d="M 263.00018,250.94679 C 263.00018,250.94679 254.5221,247.0388 254.5221,247.0388 C 254.08593,246.83775 253.46532,246.88652 253.12956,247.148 C 253.12956,247.148 246.6785,252.17186 246.6785,252.17186 C 246.33694,252.43788 246.40946,252.82015 246.84217,253.0292 C 246.84217,253.0292 255.25442,257.09309 255.25442,257.09309 C 255.70512,257.31082 256.34668,257.26542 256.6918,256.99111 C 256.6918,256.99111 263.20867,251.81133 263.20867,251.81133 C 263.54778,251.54179 263.45431,251.15613 263.00018,250.94679 C 263.00018,250.94679 263.00018,250.94679 263.00018,250.94679" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3385" + d="M 224.63535,250.5847 C 224.63535,250.5847 216.40989,246.52591 216.40989,246.52591 C 215.79702,246.2235 214.86783,246.29035 214.3264,246.67645 C 214.3264,246.67645 206.94803,251.93833 206.94803,251.93833 C 206.39716,252.33118 206.45553,252.89148 207.07898,253.19391 C 207.07898,253.19391 215.44611,257.2528 215.44611,257.2528 C 216.0613,257.55121 216.99156,257.47468 217.53182,257.08184 C 217.53182,257.08184 224.76854,251.81989 224.76854,251.81989 C 225.29959,251.43374 225.24017,250.88315 224.63535,250.5847 C 224.63535,250.5847 224.63535,250.5847 224.63535,250.5847" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3387" + d="M 220.79919,257.76358 C 220.79919,257.76358 229.7534,262.16116 229.7534,262.16116 C 230.47771,262.51688 230.65884,263.11821 230.15739,263.5097 C 230.15739,263.5097 223.79138,268.4796 223.79138,268.4796 C 223.27442,268.8832 222.26833,268.91225 221.53782,268.54453 C 221.53782,268.54453 212.50868,263.9994 212.50868,263.9994 C 211.79484,263.64005 211.64287,263.03537 212.16581,262.64384 C 212.16581,262.64384 218.60675,257.8215 218.60675,257.8215 C 219.11421,257.44156 220.09111,257.41585 220.79919,257.76358 C 220.79919,257.76358 220.79919,257.76358 220.79919,257.76358" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3389" + d="M 209.90305,237.93879 C 209.90305,237.93879 217.69144,241.5189 217.69144,241.5189 C 218.2857,241.79206 218.40792,242.28161 217.95894,242.61539 C 217.95894,242.61539 211.48141,247.43084 211.48141,247.43084 C 211.00555,247.78462 210.15328,247.81772 209.57718,247.50651 C 209.57718,247.50651 202.04394,243.43694 202.04394,243.43694 C 201.54218,243.16588 201.50989,242.69331 201.96601,242.37623 C 201.96601,242.37623 208.18868,238.05054 208.18868,238.05054 C 208.62095,237.75004 209.38314,237.69979 209.90305,237.93879 C 209.90305,237.93879 209.90305,237.93879 209.90305,237.93879" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="rect3391" + d="M 300.49388,221.41753 C 300.49388,221.41753 318.37188,206.97394 318.37188,206.97394 C 319.2072,206.29909 319.56463,205.63506 319.17704,205.48324 C 319.17704,205.48324 311.44233,202.45361 311.44233,202.45361 C 311.07717,202.31058 310.12278,202.71434 309.29892,203.3606 C 309.29892,203.3606 291.6901,217.17374 291.6901,217.17374 C 290.79969,217.87223 290.3631,218.57498 290.71507,218.74754 C 290.71507,218.74754 298.1804,222.40777 298.1804,222.40777 C 298.55498,222.59143 299.58864,222.1489 300.49388,221.41753 C 300.49388,221.41753 300.49388,221.41753 300.49388,221.41753" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + d="M 258.97104,224.63939 C 258.97104,224.63939 251.83463,221.23656 251.83463,221.23656 C 251.14159,220.90611 250.14534,220.95426 249.59505,221.34461 C 249.59505,221.34461 243.94919,225.34942 243.94919,225.34942 C 243.36824,225.76151 243.45494,226.38105 244.14978,226.73848 C 244.14978,226.73848 251.31231,230.42301 251.31231,230.42301 C 252.05284,230.80395 253.12041,230.75512 253.69927,230.31357 C 253.69927,230.31357 259.31886,226.02706 259.31886,226.02706 C 259.86601,225.60971 259.70806,224.99085 258.97104,224.63939 C 258.97104,224.63939 258.97104,224.63939 258.97104,224.63939" + id="path13125" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + d="M 245.03115,264.95865 C 245.03115,264.95865 236.89775,261.02915 236.89775,261.02915 C 236.29828,260.73954 235.41666,260.81767 234.92021,261.2046 C 234.92021,261.2046 228.15537,266.47697 228.15537,266.47697 C 227.65036,266.87057 227.72859,267.42473 228.33161,267.71924 C 228.33161,267.71924 236.5135,271.71529 236.5135,271.71529 C 237.12168,272.01232 238.01565,271.93037 238.51705,271.53179 C 238.51705,271.53179 245.23339,266.19289 245.23339,266.19289 C 245.7263,265.80109 245.63571,265.25074 245.03115,264.95865 C 245.03115,264.95865 245.03115,264.95865 245.03115,264.95865" + id="path13127" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + d="M 241.3425,254.88757 C 241.3425,254.88757 247.17841,250.38791 247.17841,250.38791 C 247.6407,250.03146 247.43669,249.46639 246.72219,249.12079 C 246.72219,249.12079 237.84809,244.82838 237.84809,244.82838 C 237.14306,244.48737 236.20078,244.49428 235.73414,244.84402 C 235.73414,244.84402 229.84385,249.25863 229.84385,249.25863 C 229.36826,249.61507 229.55538,250.18212 230.26467,250.52985 C 230.26467,250.52985 239.19316,254.90731 239.19316,254.90731 C 239.9121,255.25978 240.87126,255.2509 241.3425,254.88757 C 241.3425,254.88757 241.3425,254.88757 241.3425,254.88757" + id="path13129" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path13131" + d="M 250.09625,248.10772 C 250.09625,248.10772 256.04021,243.39795 256.04021,243.39795 C 256.61443,242.94295 256.40961,242.26587 255.58606,241.87803 C 255.58606,241.87803 247.4695,238.05567 247.4695,238.05567 C 246.66725,237.67787 245.54243,237.71942 244.94236,238.15046 C 244.94236,238.15046 238.73449,242.60981 238.73449,242.60981 C 238.09034,243.07251 238.23366,243.77042 239.06151,244.17275 C 239.06151,244.17275 247.44196,248.2456 247.44196,248.2456 C 248.29278,248.65909 249.47914,248.5967 250.09625,248.10772 C 250.09625,248.10772 250.09625,248.10772 250.09625,248.10772" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path13133" + d="M 270.78129,244.1004 C 270.78129,244.1004 263.16585,240.54998 263.16585,240.54998 C 262.59905,240.28573 261.81284,240.33065 261.40189,240.65211 C 261.40189,240.65211 255.67209,245.13428 255.67209,245.13428 C 255.23437,245.47666 255.35686,245.97255 255.94801,246.24454 C 255.94801,246.24454 263.88891,249.89826 263.88891,249.89826 C 264.4733,250.16714 265.27704,250.1004 265.6898,249.75019 C 265.6898,249.75019 271.09419,245.16472 271.09419,245.16472 C 271.48189,244.83578 271.34185,244.36173 270.78129,244.1004 C 270.78129,244.1004 270.78129,244.1004 270.78129,244.1004" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path13135" + d="M 279.36283,237.13131 C 279.36283,237.13131 271.95158,233.79368 271.95158,233.79368 C 271.36993,233.53174 270.52181,233.61628 270.04782,233.98311 C 270.04782,233.98311 264.05529,238.62092 264.05529,238.62092 C 263.57067,238.99597 263.64455,239.51827 264.22306,239.79213 C 264.22306,239.79213 271.59655,243.28243 271.59655,243.28243 C 272.19767,243.56699 273.0762,243.48424 273.56398,243.09674 C 273.56398,243.09674 279.59427,238.30625 279.59427,238.30625 C 280.07112,237.92743 279.96688,237.40335 279.36283,237.13131 C 279.36283,237.13131 279.36283,237.13131 279.36283,237.13131" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + d="M 259.13878,241.01984 C 259.13878,241.01984 264.94427,236.50262 264.94427,236.50262 C 265.5183,236.05597 265.27718,235.39329 264.41129,235.01866 C 264.41129,235.01866 256.13903,231.43971 256.13903,231.43971 C 255.34632,231.09675 254.26614,231.15807 253.71044,231.57558 C 253.71044,231.57558 248.09408,235.79517 248.09408,235.79517 C 247.52482,236.22286 247.6843,236.86458 248.45835,237.23578 C 248.45835,237.23578 256.54144,241.11225 256.54144,241.11225 C 257.38813,241.51828 258.54995,241.47801 259.13878,241.01984 C 259.13878,241.01984 259.13878,241.01984 259.13878,241.01984" + id="path14377" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + d="M 232.79455,261.43519 C 232.79455,261.43519 238.72958,256.85909 238.72958,256.85909 C 239.16465,256.52365 238.9726,255.99183 238.3001,255.66653 C 238.3001,255.66653 229.27525,251.30123 229.27525,251.30123 C 228.61176,250.98029 227.72493,250.98679 227.28579,251.31591 C 227.28579,251.31591 221.29544,255.80552 221.29544,255.80552 C 220.84778,256.14104 221.02387,256.67471 221.69138,257.00198 C 221.69138,257.00198 230.77154,261.45377 230.77154,261.45377 C 231.44822,261.78553 232.35098,261.77719 232.79455,261.43519 C 232.79455,261.43519 232.79455,261.43519 232.79455,261.43519" + id="path14379" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + d="M 254.17375,257.48652 C 254.17375,257.48652 246.32963,253.73115 246.32963,253.73115 C 245.70901,253.43402 244.79483,253.51721 244.27828,253.91779 C 244.27828,253.91779 237.75243,258.97869 237.75243,258.97869 C 237.22504,259.38768 237.29958,259.96347 237.92117,260.26957 C 237.92117,260.26957 245.77862,264.13893 245.77862,264.13893 C 246.41397,264.45181 247.35058,264.36687 247.87695,263.94859 C 247.87695,263.94859 254.38946,258.77374 254.38946,258.77374 C 254.90487,258.36419 254.80792,257.79015 254.17375,257.48652 C 254.17375,257.48652 254.17375,257.48652 254.17375,257.48652" + id="path14381" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path14383" + d="M 233.03678,243.79425 C 233.03678,243.79425 225.48646,240.13918 225.48646,240.13918 C 224.74342,239.77949 223.69299,239.82194 223.12841,240.23388 C 223.12841,240.23388 217.44731,244.37905 217.44731,244.37905 C 216.874,244.79736 217.00415,245.4333 217.742,245.80545 C 217.742,245.80545 225.24071,249.58767 225.24071,249.58767 C 226.00503,249.97318 227.0886,249.93684 227.66722,249.50562 C 227.66722,249.50562 233.39989,245.23329 233.39989,245.23329 C 233.96951,244.80878 233.80621,244.16674 233.03678,243.79425 C 233.03678,243.79425 233.03678,243.79425 233.03678,243.79425" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + d="M 282.03015,224.70872 C 282.03015,224.70872 287.9774,220.08364 287.9774,220.08364 C 288.56519,219.62653 289.56121,219.51344 290.21268,219.82947 C 290.21268,219.82947 296.76597,223.00851 296.76597,223.00851 C 297.42707,223.32921 297.49209,223.96249 296.90969,224.429 C 296.90969,224.429 291.01609,229.14982 291.01609,229.14982 C 290.41797,229.62893 289.39806,229.74916 288.73146,229.41875 C 288.73146,229.41875 282.12452,226.14398 282.12452,226.14398 C 281.4678,225.81845 281.42665,225.17805 282.03015,224.70872 C 282.03015,224.70872 282.03015,224.70872 282.03015,224.70872" + id="path14385" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path20314" + d="M 274.59176,213.04701 C 274.59176,213.04701 267.63301,209.8841 267.63301,209.8841 C 266.9305,209.56479 265.98277,209.58635 265.5079,209.93372 C 265.5079,209.93372 260.69581,213.4537 260.69581,213.4537 C 260.20677,213.81142 260.39277,214.35936 261.11308,214.68093 C 261.11308,214.68093 268.24694,217.86572 268.24694,217.86572 C 268.95551,218.18206 269.90543,218.14595 270.37662,217.78604 C 270.37662,217.78604 275.0136,214.24417 275.0136,214.24417 C 275.47126,213.89461 275.28303,213.36122 274.59176,213.04701 C 274.59176,213.04701 274.59176,213.04701 274.59176,213.04701" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + d="M 292.00448,215.55279 C 292.00448,215.55279 296.3664,212.1569 296.3664,212.1569 C 296.79902,211.82008 296.4902,211.27517 295.67813,210.9361 C 295.67813,210.9361 287.77738,207.63715 287.77738,207.63715 C 287.00644,207.31524 286.04433,207.31631 285.61635,207.63866 C 285.61635,207.63866 281.30257,210.88766 281.30257,210.88766 C 280.86653,211.21606 281.12901,211.75032 281.89503,212.08646 C 281.89503,212.08646 289.74764,215.53227 289.74764,215.53227 C 290.55499,215.88655 291.56345,215.89614 292.00448,215.55279 C 292.00448,215.55279 292.00448,215.55279 292.00448,215.55279" + id="path20316" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path20318" + d="M 284.72744,221.17105 C 284.72744,221.17105 289.45459,217.54272 289.45459,217.54272 C 289.926,217.18089 289.66619,216.61851 288.87179,216.28131 C 288.87179,216.28131 280.90373,212.899 280.90373,212.899 C 280.10221,212.55875 279.07175,212.57593 278.59356,212.93801 C 278.59356,212.93801 273.79834,216.56885 273.79834,216.56885 C 273.31611,216.93397 273.57888,217.50206 274.38726,217.84205 C 274.38726,217.84205 282.42339,221.22188 282.42339,221.22188 C 283.22457,221.55883 284.25207,221.53592 284.72744,221.17105 C 284.72744,221.17105 284.72744,221.17105 284.72744,221.17105" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + d="M 280.70619,208.18882 C 280.70619,208.18882 273.65334,205.39325 273.65334,205.39325 C 272.99267,205.13139 272.16292,205.14273 271.78475,205.41678 C 271.78475,205.41678 267.9355,208.20633 267.9355,208.20633 C 267.54254,208.4911 267.73876,208.95315 268.38364,209.24454 C 268.38364,209.24454 275.27945,212.36039 275.27945,212.36039 C 276.01926,212.69466 276.95281,212.71002 277.36247,212.39198 C 277.36247,212.39198 281.36853,209.28183 281.36853,209.28183 C 281.76145,208.97678 281.46158,208.48822 280.70619,208.18882 C 280.70619,208.18882 280.70619,208.18882 280.70619,208.18882" + id="path20320" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path20322" + d="M 267.13462,218.61017 C 267.13462,218.61017 260.04553,215.39646 260.04553,215.39646 C 259.35688,215.08427 258.39121,215.12387 257.87608,215.4844 C 257.87608,215.4844 252.67496,219.12445 252.67496,219.12445 C 252.14829,219.49305 252.26711,220.05491 252.94569,220.38521 C 252.94569,220.38521 259.93387,223.78674 259.93387,223.78674 C 260.65566,224.13807 261.67299,224.1096 262.21011,223.72182 C 262.21011,223.72182 267.5121,219.8939 267.5121,219.8939 C 268.03699,219.51494 267.86652,218.94196 267.13462,218.61017 C 267.13462,218.61017 267.13462,218.61017 267.13462,218.61017" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + d="M 287.7354,230.25946 C 287.7354,230.25946 281.08316,227.00351 281.08316,227.00351 C 280.40678,226.67245 279.40568,226.75692 278.83891,227.19481 C 278.83891,227.19481 273.09436,231.63291 273.09436,231.63291 C 272.51043,232.08403 272.60418,232.71336 273.30418,233.04198 C 273.30418,233.04198 280.18673,236.27314 280.18673,236.27314 C 280.86539,236.59174 281.86259,236.48569 282.42295,236.03722 C 282.42295,236.03722 287.93718,231.62391 287.93718,231.62391 C 288.48138,231.18834 288.39153,230.58062 287.7354,230.25946 C 287.7354,230.25946 287.7354,230.25946 287.7354,230.25946" + id="path20324" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path20326" + d="M 267.94729,234.14015 C 267.94729,234.14015 273.40476,229.94747 273.40476,229.94747 C 273.94662,229.5312 273.71505,228.90956 272.88832,228.55398 C 272.88832,228.55398 264.75456,225.0556 264.75456,225.0556 C 263.95198,224.71041 262.87291,224.75882 262.33246,225.16387 C 262.33246,225.16387 256.88991,229.24286 256.88991,229.24286 C 256.34026,229.6548 256.54039,230.27309 257.34135,230.62945 C 257.34135,230.62945 265.46021,234.24152 265.46021,234.24152 C 266.28553,234.60871 267.39604,234.56364 267.94729,234.14015 C 267.94729,234.14015 267.94729,234.14015 267.94729,234.14015" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path21282" + d="M 276.32923,227.66222 C 276.32923,227.66222 281.90001,223.35623 281.90001,223.35623 C 282.4517,222.9298 282.23314,222.31013 281.41303,221.96694 C 281.41303,221.96694 273.34754,218.59182 273.34754,218.59182 C 272.55203,218.25892 271.46937,218.32422 270.91711,218.73813 C 270.91711,218.73813 265.34181,222.91665 265.34181,222.91665 C 264.77733,223.33971 264.96112,223.95828 265.75691,224.30368 C 265.75691,224.30368 273.82693,227.80624 273.82693,227.80624 C 274.64767,228.16246 275.7651,228.09826 276.32923,227.66222 C 276.32923,227.66222 276.32923,227.66222 276.32923,227.66222" + style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <g + transform="matrix(1.0228007,0,0,1.0228007,-6.1273429,-1.9735657)" + id="g5349"> + <path + id="path3281" + d="M 200.53013,313.0503 C 200.53013,313.0503 193.03458,319.37147 193.03458,319.37147 C 192.65485,319.6917 192.0145,319.75862 191.59935,319.52112 C 191.59935,319.52112 183.2867,314.76558 183.2867,314.76558 C 182.87292,314.52886 182.85139,314.08409 183.23786,313.76871 C 183.23786,313.76871 190.86675,307.54312 190.86675,307.54312 C 191.24112,307.23761 191.86986,307.1787 192.27701,307.41066 C 192.27701,307.41066 200.45632,312.07061 200.45632,312.07061 C 200.86479,312.30333 200.89796,312.7401 200.53013,313.0503 C 200.53013,313.0503 200.53013,313.0503 200.53013,313.0503" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path3283" + d="M 172.41739,306.03622 C 172.41739,306.03622 188.0277,294.08595 188.0277,294.08595 C 189.11795,293.25133 190.41897,292.82031 190.95268,293.11384 C 190.95268,293.11384 198.19106,297.09485 198.19106,297.09485 C 198.72888,297.39065 198.31537,298.31501 197.2562,299.17331 C 197.2562,299.17331 182.08669,311.46584 182.08669,311.46584 C 180.89616,312.43058 179.47079,312.95427 178.90005,312.63295 C 178.90005,312.63295 171.22074,308.30947 171.22074,308.30947 C 170.65468,307.99077 171.1926,306.97384 172.41739,306.03622 C 172.41739,306.03622 172.41739,306.03622 172.41739,306.03622" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path3285" + d="M 183.71685,293.09242 C 183.71685,293.09242 175.21974,288.61823 175.21974,288.61823 C 174.62141,288.30317 173.39049,288.60803 172.45631,289.30319 C 172.45631,289.30319 159.05702,299.27405 159.05702,299.27405 C 158.07838,300.0023 157.77029,300.8596 158.36997,301.19473 C 158.36997,301.19473 166.88944,305.95582 166.88944,305.95582 C 167.50572,306.30023 168.79179,305.97286 169.76917,305.22339 C 169.76917,305.22339 183.14605,294.96565 183.14605,294.96565 C 184.07833,294.25076 184.33129,293.41596 183.71685,293.09242 C 183.71685,293.09242 183.71685,293.09242 183.71685,293.09242" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path3287" + d="M 166.83475,289.12754 C 166.83475,289.12754 159.13844,284.98242 159.13844,284.98242 C 158.69381,284.74294 157.84841,284.90019 157.24056,285.33581 C 157.24056,285.33581 146.5932,292.96648 146.5932,292.96648 C 145.95049,293.4271 145.7912,294.00445 146.23859,294.2601 C 146.23859,294.2601 153.9866,298.68753 153.9866,298.68753 C 154.44898,298.95174 155.33851,298.77896 155.97843,298.30106 C 155.97843,298.30106 166.57405,290.38811 166.57405,290.38811 C 167.17862,289.93661 167.2938,289.37478 166.83475,289.12754 C 166.83475,289.12754 166.83475,289.12754 166.83475,289.12754" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path3289" + d="M 151.36095,285.65566 C 151.36095,285.65566 143.84889,281.51108 143.84889,281.51108 C 143.47842,281.30668 142.86541,281.36856 142.47405,281.64966 C 142.47405,281.64966 134.61675,287.29335 134.61675,287.29335 C 134.22459,287.57503 134.20331,287.96918 134.56941,288.17726 C 134.56941,288.17726 141.993,292.39653 141.993,292.39653 C 142.36721,292.60921 142.98804,292.55081 143.38462,292.2654 C 143.38462,292.2654 151.33041,286.54701 151.33041,286.54701 C 151.72616,286.2622 151.73961,285.86457 151.36095,285.65566 C 151.36095,285.65566 151.36095,285.65566 151.36095,285.65566" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path3291" + d="M 210.70247,305.77557 C 210.70247,305.77557 204.93036,310.57929 204.93036,310.57929 C 204.34435,311.06699 203.27394,311.11934 202.52773,310.69333 C 202.52773,310.69333 194.82772,306.29731 194.82772,306.29731 C 194.03081,305.84234 193.88921,305.07052 194.51315,304.57044 C 194.51315,304.57044 200.65538,299.64763 200.65538,299.64763 C 201.26179,299.16161 202.36255,299.14928 203.12074,299.61631 C 203.12074,299.61631 210.45071,304.13144 210.45071,304.13144 C 211.16146,304.56926 211.27265,305.30104 210.70247,305.77557 C 210.70247,305.77557 210.70247,305.77557 210.70247,305.77557" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path3293" + d="M 220.36679,297.64972 C 220.36679,297.64972 214.19821,302.83693 214.19821,302.83693 C 213.8071,303.16581 213.0629,303.16632 212.53329,302.83934 C 212.53329,302.83934 204.47426,297.86377 204.47426,297.86377 C 203.99682,297.56901 203.91878,297.08754 204.2961,296.78318 C 204.2961,296.78318 210.25274,291.97831 210.25274,291.97831 C 210.61877,291.68306 211.30575,291.66386 211.7962,291.93623 C 211.7962,291.93623 220.06723,296.5296 220.06723,296.5296 C 220.61026,296.83118 220.74549,297.33126 220.36679,297.64972 C 220.36679,297.64972 220.36679,297.64972 220.36679,297.64972" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path3295" + d="M 229.27683,290.15464 C 229.27683,290.15464 223.76946,294.79471 223.76946,294.79471 C 223.28503,295.20286 222.36392,295.23289 221.70465,294.86121 C 221.70465,294.86121 214.09874,290.57325 214.09874,290.57325 C 213.43481,290.19895 213.29956,289.57104 213.79504,289.16623 C 213.79504,289.16623 219.4277,284.56425 219.4277,284.56425 C 219.91221,284.1684 220.82757,284.14902 221.48054,284.51999 C 221.48054,284.51999 228.96116,288.76985 228.96116,288.76985 C 229.60961,289.13825 229.75059,289.75549 229.27683,290.15464 C 229.27683,290.15464 229.27683,290.15464 229.27683,290.15464" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path3297" + d="M 207.6366,289.76045 C 207.6366,289.76045 200.20453,285.97175 200.20453,285.97175 C 199.75388,285.74202 199.11315,285.78115 198.76662,286.05878 C 198.76662,286.05878 193.21867,290.50362 193.21867,290.50362 C 192.87292,290.78062 192.94685,291.19499 193.38567,291.43343 C 193.38567,291.43343 200.62246,295.36556 200.62246,295.36556 C 201.08703,295.61798 201.75028,295.59166 202.10819,295.30573 C 202.10819,295.30573 207.85144,290.71744 207.85144,290.71744 C 208.21018,290.43084 208.11372,290.00367 207.6366,289.76045 C 207.6366,289.76045 207.6366,289.76045 207.6366,289.76045" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path3299" + d="M 193.30538,285.7839 C 193.30538,285.7839 185.22052,281.47582 185.22052,281.47582 C 184.58295,281.13609 183.70561,281.13742 183.25124,281.47882 C 183.25124,281.47882 177.48561,285.81085 177.48561,285.81085 C 177.01761,286.16248 177.15516,286.7287 177.79623,287.08041 C 177.79623,287.08041 185.92745,291.54147 185.92745,291.54147 C 186.5872,291.90343 187.49396,291.90165 187.95831,291.53752 C 187.95831,291.53752 193.67756,287.05251 193.67756,287.05251 C 194.12816,286.69915 193.9612,286.13336 193.30538,285.7839 C 193.30538,285.7839 193.30538,285.7839 193.30538,285.7839" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path3301" + d="M 153.65825,274.40909 C 153.65825,274.40909 161.75797,278.66326 161.75797,278.66326 C 162.02482,278.80341 162.05943,279.04844 161.83501,279.21257 C 161.83501,279.21257 154.75107,284.39353 154.75107,284.39353 C 154.52039,284.56224 154.12135,284.5797 153.85694,284.43276 C 153.85694,284.43276 145.83383,279.97431 145.83383,279.97431 C 145.58269,279.83475 145.56457,279.59204 145.79266,279.43005 C 145.79266,279.43005 152.79999,274.45338 152.79999,274.45338 C 153.02206,274.29566 153.40462,274.27588 153.65825,274.40909 C 153.65825,274.40909 153.65825,274.40909 153.65825,274.40909" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path3303" + d="M 176.68036,282.15173 C 176.68036,282.15173 169.08774,278.13438 169.08774,278.13438 C 168.53136,277.83999 167.74128,277.85841 167.31522,278.1756 C 167.31522,278.1756 161.51878,282.49085 161.51878,282.49085 C 161.08675,282.81248 161.18562,283.31335 161.7415,283.61396 C 161.7415,283.61396 169.32801,287.71649 169.32801,287.71649 C 169.89523,288.02323 170.70097,288.00619 171.1335,287.67818 C 171.1335,287.67818 176.93604,283.27774 176.93604,283.27774 C 177.36252,282.95432 177.24799,282.45207 176.68036,282.15173 C 176.68036,282.15173 176.68036,282.15173 176.68036,282.15173" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path3305" + d="M 186.05575,275.08382 C 186.05575,275.08382 178.66837,271.26624 178.66837,271.26624 C 178.14275,270.99462 177.38016,271.01579 176.95654,271.31482 C 176.95654,271.31482 170.80827,275.65494 170.80827,275.65494 C 170.35386,275.97571 170.42595,276.46372 170.9721,276.74789 C 170.9721,276.74789 178.64907,280.74215 178.64907,280.74215 C 179.19855,281.02804 179.99123,280.98835 180.42429,280.65448 C 180.42429,280.65448 186.28302,276.1377 186.28302,276.1377 C 186.68663,275.82653 186.58444,275.35703 186.05575,275.08382 C 186.05575,275.08382 186.05575,275.08382 186.05575,275.08382" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path3307" + d="M 163.51597,267.87954 C 163.51597,267.87954 171.1646,271.91896 171.1646,271.91896 C 171.53604,272.11513 171.58651,272.4514 171.2774,272.67324 C 171.2774,272.67324 164.82134,277.30652 164.82134,277.30652 C 164.50235,277.53544 163.94045,277.55958 163.56185,277.36032 C 163.56185,277.36032 155.76574,273.25703 155.76574,273.25703 C 155.38595,273.05714 155.34321,272.7142 155.66949,272.48842 C 155.66949,272.48842 162.27303,267.91901 162.27303,267.91901 C 162.5892,267.70024 163.14337,267.68276 163.51597,267.87954 C 163.51597,267.87954 163.51597,267.87954 163.51597,267.87954" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path3309" + d="M 173.08132,261.26965 C 173.08132,261.26965 180.42033,265.06231 180.42033,265.06231 C 180.84108,265.27975 180.901,265.65731 180.55405,265.90902 C 180.55405,265.90902 174.39819,270.3752 174.39819,270.3752 C 174.04312,270.63281 173.41533,270.66166 172.99122,270.43971 C 172.99122,270.43971 165.59423,266.56865 165.59423,266.56865 C 165.17482,266.34916 165.12621,265.96859 165.4846,265.71552 C 165.4846,265.71552 171.69844,261.32774 171.69844,261.32774 C 172.04868,261.08043 172.66517,261.05459 173.08132,261.26965 C 173.08132,261.26965 173.08132,261.26965 173.08132,261.26965" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path3311" + d="M 183.15808,254.05968 C 183.15808,254.05968 190.5411,257.89809 190.5411,257.89809 C 190.90119,258.0853 190.9368,258.42917 190.61983,258.66983 C 190.61983,258.66983 183.84308,263.81508 183.84308,263.81508 C 183.50031,264.07534 182.92132,264.12739 182.54615,263.93097 C 182.54615,263.93097 174.85492,259.90428 174.85492,259.90428 C 174.48192,259.709 174.47113,259.34932 174.82945,259.09862 C 174.82945,259.09862 181.91436,254.14161 181.91436,254.14161 C 182.24579,253.90973 182.79998,253.8735 183.15808,254.05968 C 183.15808,254.05968 183.15808,254.05968 183.15808,254.05968" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path3313" + d="M 195.50846,268.00414 C 195.50846,268.00414 187.92237,264.08387 187.92237,264.08387 C 187.38263,263.80495 186.5995,263.82667 186.16449,264.13375 C 186.16449,264.13375 179.85083,268.59062 179.85083,268.59062 C 179.38419,268.92002 179.45825,269.42118 180.01907,269.71297 C 180.01907,269.71297 187.90254,273.81468 187.90254,273.81468 C 188.4668,274.10826 189.28081,274.0675 189.72552,273.72465 C 189.72552,273.72465 195.74185,269.08637 195.74185,269.08637 C 196.15632,268.76683 196.05137,268.2847 195.50846,268.00414 C 195.50846,268.00414 195.50846,268.00414 195.50846,268.00414" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path3315" + d="M 202.55006,278.55525 C 202.55006,278.55525 194.52053,274.43157 194.52053,274.43157 C 193.89847,274.1121 193.03474,274.13023 192.58051,274.47152 C 192.58051,274.47152 186.82178,278.79836 186.82178,278.79836 C 186.35477,279.14926 186.47106,279.70327 187.086,280.04131 C 187.086,280.04131 195.02775,284.40711 195.02775,284.40711 C 195.68386,284.76779 196.59706,284.76041 197.07144,284.38968 C 197.07144,284.38968 202.9179,279.82067 202.9179,279.82067 C 203.3788,279.46048 203.21306,278.89574 202.55006,278.55525 C 202.55006,278.55525 202.55006,278.55525 202.55006,278.55525" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path3317" + d="M 217.28268,282.30723 C 217.28268,282.30723 209.76798,278.51506 209.76798,278.51506 C 209.31861,278.28829 208.66038,278.33252 208.29049,278.6148 C 208.29049,278.6148 201.99168,283.42172 201.99168,283.42172 C 201.5992,283.72125 201.65194,284.15497 202.11173,284.39356 C 202.11173,284.39356 209.80365,288.38505 209.80365,288.38505 C 210.27451,288.62939 210.96221,288.57431 211.3438,288.26224 C 211.3438,288.26224 217.46536,283.25603 217.46536,283.25603 C 217.82471,282.96216 217.7425,282.53927 217.28268,282.30723 C 217.28268,282.30723 217.28268,282.30723 217.28268,282.30723" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5259" + sodipodi:nodetypes="cccccccccc" + d="M 223.6315,280.9685 C 223.6315,280.9685 271.20382,242.80623 271.20382,242.80623 C 271.77183,242.35446 272.94396,242.34509 273.8347,242.78481 C 273.8347,242.78481 280.16259,245.8893 280.16259,245.8893 C 281.06675,246.33566 281.34594,247.06729 280.78574,247.53016 C 280.78574,247.53016 233.93323,286.78539 233.93323,286.78539 C 233.23622,287.36129 231.89705,287.39044 230.93425,286.85016 C 230.93425,286.85016 224.06489,282.94036 224.06489,282.94036 C 223.11807,282.40904 222.92637,281.52934 223.6315,280.9685 C 223.6315,280.9685 223.6315,280.9685 223.6315,280.9685" + style="opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.69999999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5261" + d="M 227.03565,274.61509 C 227.03565,274.61509 219.57159,270.89543 219.57159,270.89543 C 219.13842,270.67957 218.48938,270.73732 218.1151,271.02521 C 218.1151,271.02521 211.61304,276.02653 211.61304,276.02653 C 211.22381,276.32593 211.26193,276.7481 211.69984,276.97279 C 211.69984,276.97279 219.24737,280.84554 219.24737,280.84554 C 219.69565,281.07556 220.36702,281.012 220.75135,280.70336 C 220.75135,280.70336 227.16991,275.54895 227.16991,275.54895 C 227.53929,275.25232 227.47886,274.83596 227.03565,274.61509 C 227.03565,274.61509 227.03565,274.61509 227.03565,274.61509" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5263" + d="M 192.51964,247.35677 C 192.51964,247.35677 199.78325,251.31635 199.78325,251.31635 C 200.08373,251.48015 200.10518,251.76671 199.83193,251.95915 C 199.83193,251.95915 193.39598,256.49168 193.39598,256.49168 C 193.12338,256.68366 192.65687,256.71094 192.34938,256.55257 C 192.34938,256.55257 184.91676,252.72442 184.91676,252.72442 C 184.59365,252.55801 184.55824,252.26477 184.83799,252.06723 C 184.83799,252.06723 191.44296,247.40326 191.44296,247.40326 C 191.7234,247.20524 192.20387,247.18464 192.51964,247.35677 C 192.51964,247.35677 192.51964,247.35677 192.51964,247.35677" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5265" + d="M 204.95758,260.67876 C 204.95758,260.67876 197.45088,256.83357 197.45088,256.83357 C 196.91888,256.56107 196.14744,256.58436 195.71989,256.88617 C 195.71989,256.88617 189.62606,261.18786 189.62606,261.18786 C 189.18381,261.50005 189.26067,261.97752 189.79977,262.25801 C 189.79977,262.25801 197.4078,266.21641 197.4078,266.21641 C 197.95451,266.50085 198.7459,266.47153 199.18087,266.15108 C 199.18087,266.15108 205.17336,261.73618 205.17336,261.73618 C 205.59375,261.42646 205.49693,260.95503 204.95758,260.67876 C 204.95758,260.67876 204.95758,260.67876 204.95758,260.67876" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5267" + d="M 203.42735,267.20908 C 203.42735,267.20908 212.2333,271.70369 212.2333,271.70369 C 212.6248,271.90351 212.71338,272.23708 212.43163,272.45164 C 212.43163,272.45164 206.02839,277.32796 206.02839,277.32796 C 205.74282,277.54543 205.19609,277.55776 204.8028,277.35556 C 204.8028,277.35556 195.957,272.80759 195.957,272.80759 C 195.56658,272.60686 195.48379,272.27205 195.77113,272.05697 C 195.77113,272.05697 202.21422,267.23402 202.21422,267.23402 C 202.49773,267.02179 203.03868,267.0107 203.42735,267.20908 C 203.42735,267.20908 203.42735,267.20908 203.42735,267.20908" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5269" + d="M 212.72962,260.6903 C 212.72962,260.6903 220.81856,264.71792 220.81856,264.71792 C 221.40722,265.01103 221.56351,265.49233 221.1669,265.79697 C 221.1669,265.79697 215.52012,270.13427 215.52012,270.13427 C 215.11075,270.44871 214.30434,270.45549 213.7142,270.1496 C 213.7142,270.1496 205.60787,265.94776 205.60787,265.94776 C 205.04085,265.65385 204.91653,265.17445 205.32701,264.87276 C 205.32701,264.87276 210.99121,260.70967 210.99121,260.70967 C 211.38919,260.41716 212.16362,260.40847 212.72962,260.6903 C 212.72962,260.6903 212.72962,260.6903 212.72962,260.6903" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5271" + d="M 235.38208,267.94652 C 235.38208,267.94652 228.36195,264.54302 228.36195,264.54302 C 227.7581,264.25027 226.91755,264.28848 226.47592,264.62855 C 226.47592,264.62855 221.36693,268.56268 221.36693,268.56268 C 220.91972,268.90705 221.04356,269.42537 221.64599,269.72502 C 221.64599,269.72502 228.65018,273.2089 228.65018,273.2089 C 229.26637,273.51538 230.12476,273.47935 230.5734,273.12791 C 230.5734,273.12791 235.69833,269.1134 235.69833,269.1134 C 236.14129,268.76642 235.99961,268.24591 235.38208,267.94652 C 235.38208,267.94652 235.38208,267.94652 235.38208,267.94652" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5273" + d="M 202.3439,240.55011 C 202.3439,240.55011 209.40685,244.3565 209.40685,244.3565 C 209.77966,244.55741 209.79685,244.91929 209.44513,245.16789 C 209.44513,245.16789 202.71836,249.92236 202.71836,249.92236 C 202.36384,250.17294 201.77847,250.21096 201.40617,250.00761 C 201.40617,250.00761 194.3529,246.15501 194.3529,246.15501 C 193.98466,245.95386 193.97344,245.59217 194.32745,245.34403 C 194.32745,245.34403 201.04453,240.63577 201.04453,240.63577 C 201.39575,240.38958 201.97514,240.35138 202.3439,240.55011 C 202.3439,240.55011 202.3439,240.55011 202.3439,240.55011" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5275" + d="M 214.95641,253.88523 C 214.95641,253.88523 207.20865,250.11064 207.20865,250.11064 C 206.78058,249.90209 206.15223,249.93501 205.79811,250.18412 C 205.79811,250.18412 199.5121,254.60606 199.5121,254.60606 C 199.14935,254.86124 199.19499,255.24389 199.61614,255.46439 C 199.61614,255.46439 207.24189,259.45703 207.24189,259.45703 C 207.69079,259.69206 208.35153,259.66596 208.72148,259.39808 C 208.72148,259.39808 215.12948,254.75806 215.12948,254.75806 C 215.49031,254.49678 215.41229,254.10733 214.95641,253.88523 C 214.95641,253.88523 214.95641,253.88523 214.95641,253.88523" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5277" + d="M 221.9033,253.92205 C 221.9033,253.92205 229.74607,257.77376 229.74607,257.77376 C 230.38047,258.08533 230.53912,258.61203 230.09991,258.95492 C 230.09991,258.95492 224.52409,263.30793 224.52409,263.30793 C 224.07129,263.66142 223.1901,263.68688 222.55025,263.36479 C 222.55025,263.36479 214.64187,259.38384 214.64187,259.38384 C 214.01663,259.0691 213.88352,258.53947 214.34155,258.19654 C 214.34155,258.19654 219.983,253.97277 219.983,253.97277 C 220.42747,253.64 221.28312,253.61747 221.9033,253.92205 C 221.9033,253.92205 221.9033,253.92205 221.9033,253.92205" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + d="M 244.75785,260.58731 C 244.75785,260.58731 237.5872,257.12295 237.5872,257.12295 C 237.05869,256.86761 236.28144,256.9365 235.84374,257.27763 C 235.84374,257.27763 229.87966,261.92591 229.87966,261.92591 C 229.43444,262.2729 229.5034,262.76148 230.03504,263.02113 C 230.03504,263.02113 237.24843,266.54415 237.24843,266.54415 C 237.78463,266.80603 238.57278,266.73378 239.01483,266.38238 C 239.01483,266.38238 244.93616,261.67544 244.93616,261.67544 C 245.3707,261.33002 245.29084,260.84482 244.75785,260.58731 C 244.75785,260.58731 244.75785,260.58731 244.75785,260.58731" + id="path5279" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + d="M 233.2943,256.416 C 233.2943,256.416 238.45564,252.43646 238.45564,252.43646 C 238.83398,252.14474 238.66697,251.68224 238.08213,251.39936 C 238.08213,251.39936 230.23378,247.60312 230.23378,247.60312 C 229.65678,247.32402 228.88557,247.32967 228.50367,247.6159 C 228.50367,247.6159 223.29424,251.52023 223.29424,251.52023 C 222.90493,251.812 223.05807,252.27611 223.63856,252.56071 C 223.63856,252.56071 231.535,256.43217 231.535,256.43217 C 232.12348,256.72068 232.90856,256.71342 233.2943,256.416 C 233.2943,256.416 233.2943,256.416 233.2943,256.416" + id="path5281" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5283" + d="M 224.84883,246.55597 C 224.84883,246.55597 217.52734,242.94323 217.52734,242.94323 C 216.98183,242.67405 216.15475,242.73355 215.67283,243.07723 C 215.67283,243.07723 209.10533,247.76083 209.10533,247.76083 C 208.61499,248.11051 208.66694,248.60923 209.22188,248.87843 C 209.22188,248.87843 216.66948,252.49124 216.66948,252.49124 C 217.21707,252.75687 218.04508,252.68874 218.52598,252.33907 C 218.52598,252.33907 224.96738,247.65541 224.96738,247.65541 C 225.44008,247.3117 225.38717,246.82161 224.84883,246.55597 C 224.84883,246.55597 224.84883,246.55597 224.84883,246.55597" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5285" + d="M 211.2176,234.55068 C 211.2176,234.55068 218.13924,237.73238 218.13924,237.73238 C 218.66737,237.97515 218.77598,238.41021 218.37697,238.70684 C 218.37697,238.70684 212.62031,242.9864 212.62031,242.9864 C 212.1974,243.3008 211.43997,243.33022 210.928,243.05365 C 210.928,243.05365 204.23311,239.43697 204.23311,239.43697 C 203.78717,239.19607 203.75848,238.77609 204.16385,238.4943 C 204.16385,238.4943 209.694,234.65 209.694,234.65 C 210.07817,234.38294 210.75554,234.33829 211.2176,234.55068 C 211.2176,234.55068 211.2176,234.55068 211.2176,234.55068" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5287" + d="M 233.15615,239.94299 C 233.15615,239.94299 226.44421,236.69379 226.44421,236.69379 C 225.78369,236.37403 224.84989,236.41177 224.348,236.77797 C 224.348,236.77797 219.29773,240.46286 219.29773,240.46286 C 218.78807,240.83472 218.90378,241.40005 219.55968,241.73088 C 219.55968,241.73088 226.22576,245.09312 226.22576,245.09312 C 226.90521,245.43582 227.86846,245.40352 228.38283,245.02018 C 228.38283,245.02018 233.47896,241.22224 233.47896,241.22224 C 233.98531,240.84488 233.84015,240.27412 233.15615,239.94299 C 233.15615,239.94299 233.15615,239.94299 233.15615,239.94299" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5289" + d="M 220.30432,227.87754 C 220.30432,227.87754 227.46486,231.13608 227.46486,231.13608 C 227.82573,231.3003 227.87475,231.60963 227.57378,231.82996 C 227.57378,231.82996 221.38229,236.3625 221.38229,236.3625 C 221.06565,236.59429 220.51407,236.64288 220.14663,236.4711 C 220.14663,236.4711 212.85793,233.06353 212.85793,233.06353 C 212.49851,232.8955 212.47011,232.57953 212.7932,232.35543 C 212.7932,232.35543 219.11286,227.97192 219.11286,227.97192 C 219.42016,227.75877 219.95112,227.71681 220.30432,227.87754 C 220.30432,227.87754 220.30432,227.87754 220.30432,227.87754" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + d="M 253.8328,253.35343 C 253.8328,253.35343 246.80705,249.98982 246.80705,249.98982 C 246.25118,249.7237 245.43236,249.7982 244.96971,250.15699 C 244.96971,250.15699 239.12467,254.6899 239.12467,254.6899 C 238.65231,255.05622 238.71907,255.57195 239.27581,255.84611 C 239.27581,255.84611 246.31353,259.3118 246.31353,259.3118 C 246.88258,259.59203 247.72148,259.51595 248.19294,259.14132 C 248.19294,259.14132 254.02601,254.50633 254.02601,254.50633 C 254.48765,254.13951 254.40082,253.62536 253.8328,253.35343 C 253.8328,253.35343 253.8328,253.35343 253.8328,253.35343" + id="path5291" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + d="M 241.69245,250.18836 C 241.69245,250.18836 246.86241,246.20217 246.86241,246.20217 C 247.27195,245.8864 247.09122,245.38581 246.45826,245.07965 C 246.45826,245.07965 238.59679,241.27706 238.59679,241.27706 C 237.97222,240.97496 237.13747,240.98108 236.72408,241.29091 C 236.72408,241.29091 231.50595,245.20176 231.50595,245.20176 C 231.08462,245.51753 231.25039,246.01986 231.87874,246.32792 C 231.87874,246.32792 239.78839,250.20585 239.78839,250.20585 C 240.42527,250.5181 241.27499,250.51024 241.69245,250.18836 C 241.69245,250.18836 241.69245,250.18836 241.69245,250.18836" + id="path5293" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5295" + d="M 262.29906,246.8676 C 262.29906,246.8676 254.88168,243.44853 254.88168,243.44853 C 254.50008,243.27263 253.95712,243.3153 253.66336,243.54406 C 253.66336,243.54406 248.0194,247.9394 248.0194,247.9394 C 247.72057,248.17212 247.78403,248.50657 248.1626,248.68946 C 248.1626,248.68946 255.52239,252.24492 255.52239,252.24492 C 255.9167,252.43541 256.47799,252.39569 256.77993,252.1557 C 256.77993,252.1557 262.48146,247.62397 262.48146,247.62397 C 262.77816,247.38815 262.69638,247.05074 262.29906,246.8676 C 262.29906,246.8676 262.29906,246.8676 262.29906,246.8676" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5297" + d="M 269.92313,240.18072 C 269.92313,240.18072 263.31143,237.09826 263.31143,237.09826 C 262.81933,236.86884 262.13676,236.90784 261.77997,237.18694 C 261.77997,237.18694 256.80537,241.07833 256.80537,241.07833 C 256.42536,241.3756 256.53169,241.80611 257.04493,242.04226 C 257.04493,242.04226 263.93919,245.21441 263.93919,245.21441 C 264.44655,245.44785 265.14436,245.3899 265.50271,245.08585 C 265.50271,245.08585 270.19479,241.10477 270.19479,241.10477 C 270.53139,240.81918 270.4098,240.40761 269.92313,240.18072 C 269.92313,240.18072 269.92313,240.18072 269.92313,240.18072" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5299" + d="M 284.15897,244.25926 C 284.15897,244.25926 290.08975,239.34554 290.08975,239.34554 C 290.3372,239.14053 290.25996,238.84705 289.91756,238.6876 C 289.91756,238.6876 282.05801,235.02758 282.05801,235.02758 C 281.73357,234.87649 281.27511,234.91294 281.02917,235.10924 C 281.02917,235.10924 275.13725,239.81168 275.13725,239.81168 C 274.88342,240.01427 274.9381,240.30589 275.26078,240.46561 C 275.26078,240.46561 283.08145,244.3369 283.08145,244.3369 C 283.42232,244.50564 283.90334,244.47105 284.15897,244.25926 C 284.15897,244.25926 284.15897,244.25926 284.15897,244.25926" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5301" + d="M 290.6347,228.41799 C 290.6347,228.41799 298.47677,231.7748 298.47677,231.7748 C 298.96904,231.98552 299.08998,232.38046 298.7459,232.65955 C 298.7459,232.65955 293.04479,237.28397 293.04479,237.28397 C 292.69915,237.56434 292.03399,237.60956 291.55538,237.38617 C 291.55538,237.38617 283.93166,233.82786 283.93166,233.82786 C 283.4873,233.62045 283.39853,233.2382 283.73091,232.97007 C 283.73091,232.97007 289.21363,228.54719 289.21363,228.54719 C 289.54456,228.28023 290.17756,228.22231 290.6347,228.41799 C 290.6347,228.41799 290.6347,228.41799 290.6347,228.41799" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5303" + d="M 301.86982,230.05516 C 301.86982,230.05516 306.72655,226.01075 306.72655,226.01075 C 307.20873,225.60921 307.07252,225.04406 306.41901,224.74173 C 306.41901,224.74173 299.71761,221.64153 299.71761,221.64153 C 299.02841,221.32268 298.06453,221.39571 297.55878,221.80728 C 297.55878,221.80728 292.46319,225.95398 292.46319,225.95398 C 291.94831,226.37298 292.11057,226.9602 292.82454,227.2686 C 292.82454,227.2686 299.76474,230.26652 299.76474,230.26652 C 300.44135,230.5588 301.37922,230.4637 301.86982,230.05516 C 301.86982,230.05516 301.86982,230.05516 301.86982,230.05516" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5305" + d="M 229.75901,221.51708 C 229.75901,221.51708 236.30365,224.3537 236.30365,224.3537 C 236.77779,224.55921 236.83816,224.95708 236.4374,225.2459 C 236.4374,225.2459 230.73685,229.35443 230.73685,229.35443 C 230.32395,229.65201 229.60775,229.71902 229.13267,229.50455 C 229.13267,229.50455 222.57677,226.54493 222.57677,226.54493 C 222.11612,226.33697 222.07952,225.93711 222.49312,225.64852 C 222.49312,225.64852 228.20494,221.66299 228.20494,221.66299 C 228.60659,221.38273 229.29903,221.31772 229.75901,221.51708 C 229.75901,221.51708 229.75901,221.51708 229.75901,221.51708" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5307" + d="M 241.4331,233.77444 C 241.4331,233.77444 234.86702,230.51453 234.86702,230.51453 C 234.2884,230.22725 233.46852,230.2512 233.03004,230.56861 C 233.03004,230.56861 228.00571,234.20578 228.00571,234.20578 C 227.5701,234.52112 227.69,235.00165 228.27321,235.28282 C 228.27321,235.28282 234.89177,238.47354 234.89177,238.47354 C 235.46008,238.74751 236.26364,238.71874 236.69472,238.40938 C 236.69472,238.40938 241.66657,234.84141 241.66657,234.84141 C 242.10045,234.53004 241.99687,234.05434 241.4331,233.77444 C 241.4331,233.77444 241.4331,233.77444 241.4331,233.77444" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5309" + d="M 250.21733,243.40142 C 250.21733,243.40142 255.39176,239.30139 255.39176,239.30139 C 255.89164,238.9053 255.71333,238.31587 254.9964,237.97824 C 254.9964,237.97824 247.93065,234.65074 247.93065,234.65074 C 247.23226,234.32185 246.25306,234.35802 245.73067,234.73327 C 245.73067,234.73327 240.32649,238.61528 240.32649,238.61528 C 239.76575,239.01809 239.8905,239.62564 240.61117,239.97588 C 240.61117,239.97588 247.90666,243.52145 247.90666,243.52145 C 248.64734,243.88142 249.68011,243.82709 250.21733,243.40142 C 250.21733,243.40142 250.21733,243.40142 250.21733,243.40142" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + d="M 259.03235,236.38432 C 259.03235,236.38432 264.00233,232.5172 264.00233,232.5172 C 264.49375,232.13483 264.28734,231.56751 263.54605,231.2468 C 263.54605,231.2468 256.4643,228.18292 256.4643,228.18292 C 255.78568,227.88932 254.86097,227.94181 254.38524,228.29923 C 254.38524,228.29923 249.57716,231.91156 249.57716,231.91156 C 249.08982,232.2777 249.22635,232.82706 249.889,233.14485 C 249.889,233.14485 256.80881,236.46342 256.80881,236.46342 C 257.53364,236.81102 258.52826,236.77654 259.03235,236.38432 C 259.03235,236.38432 259.03235,236.38432 259.03235,236.38432" + id="path5311" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5313" + d="M 249.92718,227.47874 C 249.92718,227.47874 243.57843,224.26363 243.57843,224.26363 C 242.94161,223.94113 242.03351,223.96693 241.54071,224.32288 C 241.54071,224.32288 236.50753,227.95826 236.50753,227.95826 C 235.99196,228.33065 236.10354,228.89802 236.75945,229.2289 C 236.75945,229.2289 243.29817,232.52743 243.29817,232.52743 C 243.95188,232.8572 244.8789,232.81481 245.37499,232.4339 C 245.37499,232.4339 250.21821,228.7151 250.21821,228.7151 C 250.69243,228.35098 250.56193,227.80019 249.92718,227.47874 C 249.92718,227.47874 249.92718,227.47874 249.92718,227.47874" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5315" + d="M 278.37779,233.42877 C 278.37779,233.42877 271.86628,230.49633 271.86628,230.49633 C 271.35525,230.26619 270.61009,230.34047 270.19363,230.66278 C 270.19363,230.66278 264.92861,234.73754 264.92861,234.73754 C 264.50283,235.06706 264.56772,235.52596 265.07602,235.76657 C 265.07602,235.76657 271.55435,238.83315 271.55435,238.83315 C 272.08251,239.08315 272.85436,239.01044 273.28293,238.66998 C 273.28293,238.66998 278.58112,234.46107 278.58112,234.46107 C 279.00009,234.12824 278.90852,233.66778 278.37779,233.42877 C 278.37779,233.42877 278.37779,233.42877 278.37779,233.42877" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + d="M 286.64967,226.73945 C 286.64967,226.73945 280.75469,223.85413 280.75469,223.85413 C 280.1553,223.56076 279.26816,223.63562 278.7659,224.02366 C 278.7659,224.02366 273.67528,227.95655 273.67528,227.95655 C 273.15783,228.35631 273.24091,228.914 273.86123,229.20522 C 273.86123,229.20522 279.9603,232.06855 279.9603,232.06855 C 280.56171,232.3509 281.4454,232.25691 281.94196,231.85949 C 281.94196,231.85949 286.82847,227.94856 286.82847,227.94856 C 287.31074,227.56258 287.23111,227.02404 286.64967,226.73945 C 286.64967,226.73945 286.64967,226.73945 286.64967,226.73945" + id="path5317" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5319" + d="M 267.6823,229.91307 C 267.6823,229.91307 272.46908,226.23566 272.46908,226.23566 C 272.94434,225.87054 272.74124,225.3253 272.01611,225.01342 C 272.01611,225.01342 264.88193,221.94496 264.88193,221.94496 C 264.17799,221.64219 263.23154,221.68466 262.7575,222.03994 C 262.7575,222.03994 257.98381,225.61763 257.98381,225.61763 C 257.50171,225.97894 257.67724,226.52125 258.37978,226.83381 C 258.37978,226.83381 265.50086,230.00199 265.50086,230.00199 C 266.22477,230.32406 267.1988,230.28451 267.6823,229.91307 C 267.6823,229.91307 267.6823,229.91307 267.6823,229.91307" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + d="M 258.51488,221.22986 C 258.51488,221.22986 252.19037,218.21416 252.19037,218.21416 C 251.57618,217.9213 250.69327,217.96397 250.20557,218.30991 C 250.20557,218.30991 245.20204,221.8591 245.20204,221.8591 C 244.68719,222.22431 244.76403,222.77336 245.37981,223.09013 C 245.37981,223.09013 251.72747,226.35547 251.72747,226.35547 C 252.38375,226.69307 253.32987,226.6498 253.84288,226.25849 C 253.84288,226.25849 258.82312,222.45965 258.82312,222.45965 C 259.30803,222.08977 259.16806,221.54132 258.51488,221.22986 C 258.51488,221.22986 258.51488,221.22986 258.51488,221.22986" + id="path5321" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5323" + d="M 239.22906,214.77058 C 239.22906,214.77058 245.75948,217.65839 245.75948,217.65839 C 246.22836,217.86574 246.27624,218.27214 245.86583,218.5699 C 245.86583,218.5699 240.04074,222.79608 240.04074,222.79608 C 239.61975,223.10153 238.89933,223.17644 238.42646,222.96374 C 238.42646,222.96374 231.84118,220.00174 231.84118,220.00174 C 231.37421,219.79169 231.34116,219.38081 231.76608,219.08076 C 231.76608,219.08076 237.64602,214.92874 237.64602,214.92874 C 238.06034,214.63619 238.76594,214.56577 239.22906,214.77058 C 239.22906,214.77058 239.22906,214.77058 239.22906,214.77058" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5325" + d="M 247.90724,208.64597 C 247.90724,208.64597 254.53759,211.50631 254.53759,211.50631 C 255.01636,211.71285 255.09618,212.10048 254.71562,212.37537 C 254.71562,212.37537 249.34818,216.25246 249.34818,216.25246 C 248.96271,216.5309 248.26761,216.58434 247.79061,216.37235 C 247.79061,216.37235 241.18538,213.43689 241.18538,213.43689 C 240.71963,213.22991 240.65428,212.84354 241.03791,212.57052 C 241.03791,212.57052 246.38024,208.76857 246.38024,208.76857 C 246.75905,208.49898 247.43968,208.44427 247.90724,208.64597 C 247.90724,208.64597 247.90724,208.64597 247.90724,208.64597" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5327" + d="M 266.53609,215.35847 C 266.53609,215.35847 260.2425,212.50539 260.2425,212.50539 C 259.63111,212.22823 258.77381,212.26339 258.31648,212.58345 C 258.31648,212.58345 253.69899,215.81504 253.69899,215.81504 C 253.23142,216.14228 253.3369,216.6411 253.93933,216.93434 C 253.93933,216.93434 260.14335,219.95417 260.14335,219.95417 C 260.78415,220.26608 261.68732,220.24081 262.16416,219.89654 C 262.16416,219.89654 266.87121,216.49816 266.87121,216.49816 C 267.3372,216.16172 267.18586,215.65303 266.53609,215.35847 C 266.53609,215.35847 266.53609,215.35847 266.53609,215.35847" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5329" + d="M 275.86123,223.49708 C 275.86123,223.49708 280.69597,219.76003 280.69597,219.76003 C 281.17477,219.38993 280.98509,218.85214 280.27334,218.55429 C 280.27334,218.55429 273.2735,215.62511 273.2735,215.62511 C 272.58309,215.3362 271.64347,215.39287 271.16418,215.75208 C 271.16418,215.75208 266.32552,219.37853 266.32552,219.37853 C 265.83563,219.74569 265.99513,220.28253 266.68578,220.58229 C 266.68578,220.58229 273.68954,223.62208 273.68954,223.62208 C 274.40184,223.93123 275.37164,223.87552 275.86123,223.49708 C 275.86123,223.49708 275.86123,223.49708 275.86123,223.49708" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + d="M 282.59083,221.04513 C 282.59083,221.04513 287.71903,217.05699 287.71903,217.05699 C 288.22587,216.66285 289.08472,216.56532 289.64647,216.83783 C 289.64647,216.83783 295.29725,219.57907 295.29725,219.57907 C 295.86731,219.85561 295.92338,220.40166 295.42118,220.80393 C 295.42118,220.80393 290.33924,224.87461 290.33924,224.87461 C 289.82349,225.28773 288.94404,225.3914 288.36924,225.1065 C 288.36924,225.1065 282.67221,222.28271 282.67221,222.28271 C 282.10592,222.00203 282.07045,221.44982 282.59083,221.04513 C 282.59083,221.04513 282.59083,221.04513 282.59083,221.04513" + id="path5331" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5333" + d="M 273.82385,209.9072 C 273.82385,209.9072 267.67742,207.1135 267.67742,207.1135 C 267.05691,206.83146 266.21983,206.85051 265.80038,207.15733 C 265.80038,207.15733 261.55003,210.26641 261.55003,210.26641 C 261.11807,210.58238 261.28238,211.06634 261.91859,211.35037 C 261.91859,211.35037 268.21969,214.1634 268.21969,214.1634 C 268.84554,214.4428 269.68458,214.41091 270.10075,214.09302 C 270.10075,214.09302 274.19645,210.96462 274.19645,210.96462 C 274.60069,210.65585 274.43443,210.18473 273.82385,209.9072 C 273.82385,209.9072 273.82385,209.9072 273.82385,209.9072" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5335" + d="M 257.3726,201.76885 C 257.3726,201.76885 264.64202,204.86157 264.64202,204.86157 C 264.95177,204.99335 264.97965,205.26073 264.70378,205.46149 C 264.70378,205.46149 257.98723,210.34941 257.98723,210.34941 C 257.69315,210.56343 257.19866,210.62527 256.87934,210.48761 C 256.87934,210.48761 249.38649,207.25742 249.38649,207.25742 C 249.07032,207.12112 249.06104,206.84361 249.36486,206.63564 C 249.36486,206.63564 256.30482,201.88518 256.30482,201.88518 C 256.58991,201.69003 257.06581,201.63832 257.3726,201.76885 C 257.3726,201.76885 257.3726,201.76885 257.3726,201.76885" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5337" + d="M 284.0905,217.40082 C 284.0905,217.40082 288.28521,214.18116 288.28521,214.18116 C 288.70353,213.86008 288.47298,213.36105 287.76806,213.06182 C 287.76806,213.06182 280.69745,210.06045 280.69745,210.06045 C 279.9862,209.75854 279.07181,209.77379 278.64748,210.09508 C 278.64748,210.09508 274.39236,213.31696 274.39236,213.31696 C 273.96443,213.64097 274.19761,214.14507 274.91495,214.44677 C 274.91495,214.44677 282.04596,217.44592 282.04596,217.44592 C 282.75689,217.74492 283.66866,217.7246 284.0905,217.40082 C 284.0905,217.40082 284.0905,217.40082 284.0905,217.40082" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5339" + d="M 300.10154,217.06983 C 300.10154,217.06983 316.31837,203.96829 316.31837,203.96829 C 317.07607,203.35614 317.4003,202.7538 317.04873,202.61609 C 317.04873,202.61609 310.03269,199.86796 310.03269,199.86796 C 309.70146,199.73822 308.83575,200.10447 308.08844,200.69068 C 308.08844,200.69068 292.11577,213.22035 292.11577,213.22035 C 291.30809,213.85393 290.91207,214.49139 291.23134,214.64793 C 291.23134,214.64793 298.003,217.96805 298.003,217.96805 C 298.34279,218.13465 299.2804,217.73322 300.10154,217.06983 C 300.10154,217.06983 300.10154,217.06983 300.10154,217.06983" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + d="M 291.15172,211.81971 C 291.15172,211.81971 294.95784,208.85652 294.95784,208.85652 C 295.33535,208.56261 295.06588,208.08714 294.35728,207.79127 C 294.35728,207.79127 287.46322,204.91266 287.46322,204.91266 C 286.7905,204.63177 285.951,204.6327 285.57753,204.91398 C 285.57753,204.91398 281.81341,207.749 281.81341,207.749 C 281.43293,208.03556 281.66197,208.50175 282.33038,208.79505 C 282.33038,208.79505 289.18243,211.80181 289.18243,211.80181 C 289.88692,212.11095 290.76688,212.11932 291.15172,211.81971 C 291.15172,211.81971 291.15172,211.81971 291.15172,211.81971" + id="path5341" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + d="M 279.78845,205.15721 C 279.78845,205.15721 273.64923,202.7238 273.64923,202.7238 C 273.07415,202.49585 272.35188,202.50572 272.02271,202.74427 C 272.02271,202.74427 268.6721,205.17246 268.6721,205.17246 C 268.33004,205.42034 268.50084,205.82253 269.06217,206.07617 C 269.06217,206.07617 275.0647,208.78839 275.0647,208.78839 C 275.70867,209.07937 276.52129,209.09274 276.87788,208.8159 C 276.87788,208.8159 280.365,206.10863 280.365,206.10863 C 280.70701,205.84311 280.44598,205.41784 279.78845,205.15721 C 279.78845,205.15721 279.78845,205.15721 279.78845,205.15721" + id="path5343" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5345" + d="M 266.63729,203.4528 C 266.63729,203.4528 259.22437,200.28887 259.22437,200.28887 C 258.90107,200.15088 259.26093,199.6067 260.02623,199.07179 C 260.02623,199.07179 276.04515,187.8754 276.04515,187.8754 C 276.70328,187.4154 277.47826,187.14286 277.78675,187.26189 C 277.78675,187.26189 284.85126,189.98782 284.85126,189.98782 C 285.16872,190.11032 284.90984,190.59119 284.26684,191.06857 C 284.26684,191.06857 268.59644,202.70261 268.59644,202.70261 C 267.84684,203.25912 266.97083,203.59516 266.63729,203.4528 C 266.63729,203.4528 266.63729,203.4528 266.63729,203.4528" + style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + <path + id="path5347" + sodipodi:nodetypes="cccccssssccccc" + d="M 306.08894,198.05464 C 306.08894,198.05464 289.86483,191.75754 289.86483,191.75754 C 288.75309,191.32603 287.34294,191.34222 286.72043,191.80216 C 286.72043,191.80216 275.86985,199.81916 275.86985,199.81916 C 275.21811,200.30071 275.08509,200.71469 276.34881,201.06644 L 282.51762,203.58752 C 283.51554,203.99535 283.51356,204.01646 284.38014,203.38499 L 285.53498,202.54346 C 285.7875,202.35945 286.54428,202.2881 287.35569,202.6377 L 295.71352,206.2387 C 296.63524,206.49525 297.43593,206.57335 297.95625,206.16455 C 297.95625,206.16455 306.65122,199.3329 306.65122,199.3329 C 307.15193,198.93949 306.90591,198.37173 306.08894,198.05464 C 306.08894,198.05464 306.08894,198.05464 306.08894,198.05464" + style="opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + </g> + <path + d="M 195.84384,316.18138 C 198.01729,317.52381 197.72963,318.48268 195.26851,320.68809 C 192.78733,322.9115 196.00365,319.69726 196.67486,318.67446 C 197.37566,317.60657 193.70779,314.86206 195.84384,316.18138 z " + sodipodi:nodetypes="czzz" + id="path2379" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3350" + sodipodi:nodetypes="czzz" + d="M 216.172,300.16816 C 218.34546,301.51059 218.79294,302.1818 216.33181,304.38722 C 213.85064,306.61061 216.81125,303.42834 217.61032,302.34161 C 218.38754,301.28458 214.03595,298.84884 216.172,300.16816 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 206.4554,308.31861 C 208.62885,309.66103 209.20418,310.14046 206.74306,312.34587 C 204.26188,314.56927 207.35035,311.1313 207.82978,310.49205 C 208.39381,309.74001 204.31935,306.99928 206.4554,308.31861 z " + sodipodi:nodetypes="czzz" + id="path3352" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3354" + sodipodi:nodetypes="czzz" + d="M 225.50506,292.46519 C 227.67851,293.80762 228.25383,294.28706 225.79272,296.49247 C 223.31154,298.71586 227.0073,295.62948 226.78356,294.4149 C 226.56079,293.20564 223.36901,291.14588 225.50506,292.46519 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3356" + sodipodi:nodetypes="czzz" + d="M 203.49886,293.36015 C 205.63491,294.67947 206.0239,295.13407 203.48288,297.25957 C 200.9308,299.39433 204.5367,295.76662 204.79335,295.2779 C 205.04344,294.80165 201.36281,292.04083 203.49886,293.36015 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 189.00386,289.3009 C 191.59281,290.73922 191.94897,291.213 189.29153,293.32818 C 186.61856,295.45568 190.37824,292.20949 190.37824,291.21865 C 190.37824,290.19585 186.42834,287.87006 189.00386,289.3009 z " + sodipodi:nodetypes="czzz" + id="path3358" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3360" + sodipodi:nodetypes="czzz" + d="M 171.16877,285.43344 C 173.47007,286.64801 174.17325,287.38316 171.45643,289.46072 C 168.74413,291.53484 172.83082,288.34203 172.60709,287.38316 C 172.36917,286.36356 168.8898,284.23064 171.16877,285.43344 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 155.9546,281.50205 C 158.12806,282.78055 158.83124,283.54764 156.24227,285.52933 C 153.64201,287.51964 157.42488,284.66634 157.20114,283.38784 C 156.97829,282.11439 153.75272,280.20681 155.9546,281.50205 z " + sodipodi:nodetypes="czzz" + id="path3362" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 161.10057,292.56108 C 163.27403,293.9035 163.96914,294.4897 161.38823,296.58835 C 158.81117,298.6839 157.15517,299.80076 160.6531,296.71621 C 164.16898,293.61583 158.96453,291.24176 161.10057,292.56108 z " + sodipodi:nodetypes="czzz" + id="path3364" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 178.74389,296.62031 C 182.35566,298.53807 181.81229,298.31433 176.12296,302.85301 C 170.44917,307.37928 178.76168,300.08938 179.60688,299.14536 C 180.42197,298.23497 175.15702,294.71579 178.74389,296.62031 z " + sodipodi:nodetypes="czzz" + id="path3366" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 193.22292,300.42386 C 195.39636,301.76628 195.49224,303.36442 189.3874,308.28664 C 183.26317,313.22451 195.05807,303.89438 193.51058,301.89414 C 192.00596,299.94933 191.08687,299.10454 193.22292,300.42386 z " + sodipodi:nodetypes="czzz" + id="path3368" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3370" + sodipodi:nodetypes="czzz" + d="M 145.18324,288.69362 C 147.80416,290.22781 147.8681,290.89903 145.18324,292.91266 C 142.52875,294.90353 146.62155,291.63417 146.39782,290.67529 C 146.18115,289.74671 142.58609,287.17332 145.18324,288.69362 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3372" + sodipodi:nodetypes="czzz" + d="M 165.73514,274.56618 C 167.9086,275.9086 168.61177,276.57982 166.0228,278.59347 C 163.40256,280.63143 166.7899,277.25103 167.20542,276.73963 C 167.59048,276.2657 163.5991,273.24685 165.73514,274.56618 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 181.26892,278.14598 C 183.44237,279.48841 184.0177,279.96784 181.55659,282.17326 C 179.07541,284.39665 182.5794,281.34223 182.35566,280.06373 C 182.13279,278.79029 179.13287,276.82666 181.26892,278.14598 z " + sodipodi:nodetypes="czzz" + id="path3374" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3376" + sodipodi:nodetypes="czzz" + d="M 198.2659,281.884 C 200.43936,283.22641 201.01468,283.70585 198.55356,285.91126 C 196.07238,288.13466 199.57637,285.08024 199.35263,283.80174 C 199.12976,282.52829 196.12986,280.56467 198.2659,281.884 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 213.08942,285.57681 C 215.8957,286.96443 216.29022,287.35347 213.37708,289.60408 C 210.50713,291.82133 214.72017,288.27028 214.62817,287.40415 C 214.54091,286.58272 210.2463,284.17095 213.08942,285.57681 z " + sodipodi:nodetypes="czzz" + id="path3378" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3380" + sodipodi:nodetypes="czzz" + d="M 175.13212,267.66227 C 177.30557,269.00471 177.99635,269.63282 175.41979,271.68956 C 172.87468,273.72117 176.44259,270.7946 176.41062,269.64395 C 176.37866,268.4933 172.99607,266.34296 175.13212,267.66227 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3382" + sodipodi:nodetypes="czzz" + d="M 190.72984,270.85853 C 192.96721,272.0731 193.60646,272.74431 191.01751,274.88581 C 188.47105,276.99212 192.23208,274.11871 192.00834,272.84021 C 191.78548,271.56676 188.48472,269.63975 190.72984,270.85853 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 207.55763,274.67852 C 209.73109,276.02094 210.30641,276.50038 207.8453,278.70579 C 205.36412,280.92919 208.8681,277.87477 208.64437,276.59627 C 208.4215,275.32282 205.42158,273.35919 207.55763,274.67852 z " + sodipodi:nodetypes="czzz" + id="path3384" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3386" + sodipodi:nodetypes="czzz" + d="M 223.20376,277.63457 C 226.23604,279.29342 225.95254,279.45644 223.49142,281.66186 C 221.01023,283.88525 224.74999,280.23696 224.6521,279.41673 C 224.56485,278.6857 220.20621,275.99474 223.20376,277.63457 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 277.38117,248.70302 C 281.95031,251.26589 281.48626,250.13465 270.88855,258.92295 C 260.38024,267.63712 274.33964,254.68517 277.12282,252.1444 C 279.88337,249.62427 272.7431,246.10148 277.38117,248.70302 z " + sodipodi:nodetypes="czzz" + id="path3388" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3390" + sodipodi:nodetypes="czzz" + d="M 231.57794,270.34712 C 233.75139,271.68956 234.32672,272.16899 231.86561,274.37441 C 229.38442,276.5978 232.88839,273.54338 232.66467,272.26488 C 232.4418,270.99143 229.44189,269.02781 231.57794,270.34712 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 241.29454,263.69892 C 243.46799,265.04135 244.04332,265.52079 241.58221,267.72621 C 239.10103,269.9496 242.60501,266.89517 242.38128,265.61667 C 242.15841,264.34322 239.15849,262.3796 241.29454,263.69892 z " + sodipodi:nodetypes="czzz" + id="path3392" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3394" + sodipodi:nodetypes="czzz" + d="M 250.24405,256.53932 C 252.4175,257.88175 252.99283,258.36119 250.53172,260.5666 C 248.05053,262.78999 251.55451,259.73557 251.33077,258.45707 C 251.10792,257.18362 248.10801,255.21999 250.24405,256.53932 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 258.68216,249.37971 C 260.85561,250.72214 261.43093,251.20158 258.96982,253.40699 C 256.48864,255.63038 259.99262,252.57596 259.76888,251.29747 C 259.54603,250.02402 256.54611,248.06039 258.68216,249.37971 z " + sodipodi:nodetypes="czzz" + id="path3396" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3398" + sodipodi:nodetypes="czzz" + d="M 215.08528,267.40658 C 217.25872,268.749 217.83405,269.22845 215.37294,271.43386 C 212.89175,273.65725 216.39574,270.60283 216.172,269.32432 C 215.94914,268.05088 212.94923,266.08725 215.08528,267.40658 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 200.54781,263.45098 C 204.31939,265.43265 203.68014,265.40068 200.83547,267.47825 C 198.05046,269.51225 201.27066,266.64722 202.04136,265.54954 C 202.78342,264.49262 196.80222,261.48295 200.54781,263.45098 z " + sodipodi:nodetypes="czzz" + id="path3400" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3402" + sodipodi:nodetypes="czzz" + d="M 185.48798,260.50267 C 188.30069,262.16472 188.68423,262.2606 185.77564,264.52995 C 182.90663,266.76843 187.05414,263.76284 186.83041,262.48434 C 186.60755,261.21089 182.72253,258.86853 185.48798,260.50267 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 194.50141,253.47091 C 196.67486,254.81334 197.25018,255.29277 194.78907,257.49819 C 192.30789,259.72159 195.81187,256.66717 195.58814,255.38867 C 195.36528,254.11522 192.36537,252.15159 194.50141,253.47091 z " + sodipodi:nodetypes="czzz" + id="path3404" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3406" + sodipodi:nodetypes="czzz" + d="M 204.79335,246.88664 C 206.96679,248.22906 207.54211,248.7085 205.081,250.91391 C 202.59982,253.13731 206.10381,250.08289 205.88007,248.80439 C 205.65721,247.53094 202.6573,245.56731 204.79335,246.88664 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 209.97127,256.34754 C 212.14473,257.68997 212.72004,258.16941 210.25893,260.37482 C 207.77775,262.59821 211.28174,259.54379 211.058,258.26529 C 210.83513,256.99184 207.83522,255.02823 209.97127,256.34754 z " + sodipodi:nodetypes="czzz" + id="path3408" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3410" + sodipodi:nodetypes="czzz" + d="M 219.94358,248.99617 C 222.11703,250.33858 222.69236,250.81803 220.23124,253.02345 C 217.75006,255.24684 221.25405,252.19241 221.03031,250.91391 C 220.80745,249.64046 217.80754,247.67683 219.94358,248.99617 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 226.08039,260.5666 C 228.25383,261.90901 228.82916,262.38845 226.36805,264.59388 C 223.88687,266.81727 227.39085,263.76284 227.16711,262.48434 C 226.94426,261.21089 223.94434,259.24727 226.08039,260.5666 z " + sodipodi:nodetypes="czzz" + id="path3412" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3414" + sodipodi:nodetypes="czzz" + d="M 234.39063,254.11016 C 236.56408,255.4526 237.13941,255.93204 234.6783,258.13745 C 232.19712,260.36084 235.70111,257.30642 235.47737,256.02792 C 235.25451,254.75447 232.25458,252.79084 234.39063,254.11016 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 229.27663,242.2201 C 231.45009,243.56254 232.02541,244.04197 229.5643,246.24738 C 227.08312,248.47078 230.58711,245.41636 230.36337,244.13786 C 230.1405,242.86442 227.14058,240.90079 229.27663,242.2201 z " + sodipodi:nodetypes="czzz" + id="path3416" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3418" + sodipodi:nodetypes="czzz" + d="M 243.0141,247.59302 C 245.39457,248.78588 245.85328,249.41488 243.39217,251.6203 C 240.91099,253.84369 244.68618,250.11125 244.59804,249.42037 C 244.51044,248.73372 240.56163,246.3641 243.0141,247.59302 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 213.8707,239.98274 C 216.04415,241.32515 216.61948,241.8046 214.15837,244.01001 C 211.67717,246.23341 215.18116,243.17898 214.95742,241.90048 C 214.73456,240.62703 211.73465,238.66341 213.8707,239.98274 z " + sodipodi:nodetypes="czzz" + id="path3420" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3422" + sodipodi:nodetypes="czzz" + d="M 222.91837,233.04137 C 225.31782,234.20299 225.93836,234.99885 223.20602,237.06866 C 220.48021,239.13354 224.63565,235.424 224.54751,234.77832 C 224.44726,234.04379 220.53635,231.8882 222.91837,233.04137 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 246.64686,229.49127 C 249.00111,230.74329 249.89286,231.08713 246.93452,233.51855 C 243.99824,235.93184 248.36413,232.59712 248.14039,231.31861 C 247.91754,230.04517 244.31107,228.24907 246.64686,229.49127 z " + sodipodi:nodetypes="czzz" + id="path3424" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3426" + sodipodi:nodetypes="czzz" + d="M 237.71153,235.84633 C 240.02059,237.03055 240.64112,237.84899 237.9992,239.87362 C 235.36048,241.89576 239.38361,238.50015 239.25027,237.67368 C 239.10149,236.75149 235.41158,234.66678 237.71153,235.84633 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 284.38244,228.84004 C 286.66889,230.04686 287.0481,230.84805 284.67009,232.86732 C 282.27897,234.89772 285.51201,231.73741 285.85337,231.07421 C 286.18578,230.42841 282.11363,227.64254 284.38244,228.84004 z " + sodipodi:nodetypes="czzz" + id="path3428" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3430" + sodipodi:nodetypes="czzz" + d="M 232.52396,226.41189 C 234.78783,227.52831 235.40834,228.14335 232.81163,230.16796 C 230.19284,232.20977 234.24125,228.7945 234.06271,228.10364 C 233.88666,227.4224 230.2964,225.31338 232.52396,226.41189 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 242.49949,219.69334 C 245.873,221.52582 244.75103,221.244 242.19951,223.3138 C 239.67516,225.36158 243.76147,221.8425 243.54101,221.11387 C 243.32053,220.38523 239.12596,217.86086 242.49949,219.69334 z " + sodipodi:nodetypes="czzz" + id="path3432" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3434" + sodipodi:nodetypes="czzz" + d="M 255.29092,223.29442 C 257.75818,224.59164 258.28831,225.25187 255.57858,227.32169 C 252.87211,229.38902 256.89519,225.88045 256.80705,225.21217 C 256.71558,224.51847 252.84135,222.00649 255.29092,223.29442 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 269.10194,227.3895 C 271.27539,228.73192 271.85071,229.21136 269.3896,231.41677 C 266.90842,233.64017 270.41241,230.58576 270.18867,229.30725 C 269.96581,228.0338 266.96589,226.07017 269.10194,227.3895 z " + sodipodi:nodetypes="czzz" + id="path3436" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3438" + sodipodi:nodetypes="czzz" + d="M 260.47206,233.782 C 262.64551,235.12443 263.22084,235.60386 260.75972,237.80927 C 258.27854,240.03267 261.78252,236.97826 261.55878,235.69975 C 261.33593,234.42631 258.33601,232.46268 260.47206,233.782 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 252.00426,240.41924 C 254.76534,241.85208 254.88865,242.2863 252.29193,244.44653 C 249.69924,246.60338 253.58595,242.98267 253.49781,242.15619 C 253.40345,241.27142 249.33359,239.0316 252.00426,240.41924 z " + sodipodi:nodetypes="czzz" + id="path3440" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3442" + sodipodi:nodetypes="czzz" + d="M 266.73671,242.2201 C 268.91017,243.56254 269.4855,244.04197 267.02438,246.24738 C 264.54319,248.47078 268.04717,245.41636 267.82343,244.13786 C 267.60058,242.86442 264.60067,240.90079 266.73671,242.2201 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 275.77863,235.80888 C 278.29108,237.0157 278.6856,237.56294 276.08889,239.70055 C 273.50776,241.82532 277.52012,238.29917 277.43037,237.50061 C 277.34312,236.7244 273.32091,234.62837 275.77863,235.80888 z " + sodipodi:nodetypes="czzz" + id="path3444" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3446" + sodipodi:nodetypes="czzz" + d="M 286.95302,240.94161 C 289.63786,242.37993 289.92552,242.74749 287.24067,244.96888 C 284.57146,247.17734 287.44366,244.39791 288.24749,243.37076 C 289.03146,242.369 284.28786,239.51385 286.95302,240.94161 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 295.77487,234.08611 C 298.28732,235.22513 298.70444,235.86277 296.06253,238.11339 C 293.44279,240.34511 296.0466,237.62948 297.1102,236.38809 C 298.1513,235.17297 293.28962,232.95944 295.77487,234.08611 z " + sodipodi:nodetypes="czzz" + id="path3448" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3450" + sodipodi:nodetypes="czzz" + d="M 304.36663,227.14477 C 306.83388,228.44201 307.1154,228.96663 304.65428,231.17204 C 302.17311,233.39544 304.72941,230.49547 305.36295,229.31112 C 306.04412,228.03769 301.92637,225.86174 304.36663,227.14477 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 293.12178,221.54036 C 295.47102,222.691 295.98242,223.42615 293.40945,225.56763 C 290.83798,227.70786 294.60852,224.14154 294.54411,223.33025 C 294.48107,222.53624 290.77257,220.38972 293.12178,221.54036 z " + sodipodi:nodetypes="czzz" + id="path3452" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3454" + sodipodi:nodetypes="czzz" + d="M 277.5149,220.70385 C 280.36638,222.31747 280.39928,222.70652 277.80256,224.73113 C 275.18377,226.77294 276.77322,224.86768 278.55642,223.48044 C 280.36765,222.07138 274.77049,219.15079 277.5149,220.70385 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 263.22084,217.41719 C 265.39428,218.75961 265.96961,219.23905 263.5085,221.44447 C 261.02732,223.66786 264.5313,220.61343 264.30756,219.33494 C 264.08471,218.06149 261.08479,216.09787 263.22084,217.41719 z " + sodipodi:nodetypes="czzz" + id="path3456" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3458" + sodipodi:nodetypes="czzz" + d="M 251.15585,213.31501 C 254.39256,214.77625 254.35665,214.86567 251.3531,216.98068 C 248.32814,219.11078 252.86671,215.85366 252.60418,214.91636 C 252.33613,213.95933 247.95664,211.87072 251.15585,213.31501 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 260.85561,206.10245 C 263.02906,207.44489 263.60438,207.92432 261.14327,210.12974 C 258.66209,212.35313 262.16607,209.29871 261.94234,208.02022 C 261.71947,206.74677 258.71956,204.78313 260.85561,206.10245 z " + sodipodi:nodetypes="czzz" + id="path3460" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3462" + sodipodi:nodetypes="czzz" + d="M 271.01969,211.72786 C 273.19314,213.07028 273.76847,213.54972 271.30736,215.75513 C 268.82618,217.97853 272.33015,214.92411 272.10641,213.64561 C 271.88356,212.37216 268.88364,210.40854 271.01969,211.72786 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 284.99091,214.99901 C 287.70679,216.25102 288.10131,216.82086 285.27858,219.02628 C 282.51164,221.18811 285.03572,219.0993 286.34886,217.27838 C 287.55847,215.60098 282.34938,213.78125 284.99091,214.99901 z " + sodipodi:nodetypes="czzz" + id="path3464" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3466" + sodipodi:nodetypes="czzz" + d="M 276.90079,206.80563 C 279.07425,208.14806 279.64957,208.6275 277.18845,210.83291 C 274.70727,213.05631 278.21126,210.00188 277.98752,208.72339 C 277.76466,207.44994 274.76474,205.48631 276.90079,206.80563 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 292.17888,209.68226 C 294.35233,211.02468 294.92766,211.50412 292.46654,213.70954 C 289.98536,215.93294 293.48934,212.87852 293.2656,211.60001 C 293.04275,210.32657 290.04283,208.36293 292.17888,209.68226 z " + sodipodi:nodetypes="czzz" + id="path3468" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3470" + sodipodi:nodetypes="czzz" + d="M 314.68049,204.24863 C 317.7489,205.39928 317.87645,205.56214 311.38835,210.80095 C 304.93164,216.01443 312.98405,209.0753 315.15994,207.02937 C 317.35285,204.96744 311.62248,203.10188 314.68049,204.24863 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + d="M 303.62147,199.96565 C 305.79492,201.30808 306.37025,201.78752 303.90913,203.99293 C 301.42794,206.21632 304.93192,203.16191 304.70818,201.8834 C 304.48533,200.60995 301.48542,198.64633 303.62147,199.96565 z " + sodipodi:nodetypes="czzz" + id="path3472" + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3474" + sodipodi:nodetypes="czzz" + d="M 281.07787,191.30607 C 284.96899,192.8745 284.82107,192.72112 278.69861,197.41265 C 272.5826,202.09922 281.39387,194.86394 282.66181,193.63065 C 283.93119,192.39593 277.15462,189.7247 281.07787,191.30607 z " + style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + id="path3477" + d="M 247.0478,21.870476 L 41.113266,89.503174 L 48.496608,139.14097 C 116.54867,104.13919 192.87556,126.61352 246.15284,103.7265 L 247.0478,21.870476 z " + style="fill:url(#linearGradient2500);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + inkscape:transform-center-x="21.926591" + inkscape:transform-center-y="-98.60823" + d="M 27.882376,75.746856 C 32.065415,74.206702 33.389047,74.781014 34.949531,76.633878 L 65.088465,277.78657 L 56.641394,273.5449 L 27.882376,75.746856 z " + sodipodi:nodetypes="ccccc" + id="path2180" + style="fill:#1a1a1a;fill-rule:evenodd;stroke:#333333;stroke-width:0.81824058;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + id="path13298" + sodipodi:nodetypes="ccccc" + d="M 92.828627,259.35202 L 230.90672,174.71526 C 232.64824,175.78414 234.17161,176.98389 234.23083,179.06216 L 96.66413,263.82678 C 96.420949,261.86067 95.117349,260.38059 92.828627,259.35202 z " + style="fill:#808080;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + transform="matrix(1.0228007,0,0,1.0228007,-6.1273429,-1.9735657)" + id="path14269" + sodipodi:nodetypes="ccccc" + d="M 166.43526,236.83511 L 228.92582,195.64614 L 229.45615,196.53002 L 168.02625,238.24933 L 166.43526,236.83511 z " + style="opacity:0.67000002;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter16257)" /> + <path + transform="matrix(1.0228007,0,0,1.0228007,-6.1273429,-1.9735657)" + d="M 114.5513,270.06913 L 130.10765,259.99286 L 130.63798,260.87674 L 116.14229,271.48335 L 114.5513,270.06913 z " + sodipodi:nodetypes="ccccc" + id="path16261" + style="opacity:0.67000002;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter16257)" /> + <path + transform="matrix(1.0228007,0,0,1.0228007,-6.1273429,-1.9735657)" + d="M 114.5513,270.06913 L 130.10765,259.99286 L 130.63798,260.87674 L 116.14229,271.48335 L 114.5513,270.06913 z " + sodipodi:nodetypes="ccccc" + id="path16263" + style="opacity:0.67000002;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter16257)" /> + <path + transform="matrix(1.0228007,0,0,1.0228007,-6.1273429,-1.9735657)" + d="M 166.43526,236.83511 L 228.92582,195.64614 L 229.45615,196.53002 L 168.02625,238.24933 L 166.43526,236.83511 z " + sodipodi:nodetypes="ccccc" + id="path16267" + style="opacity:0.67000002;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter16257)" /> + <path + d="M 104.05869,273.38134 C 106.59601,271.03005 113.13546,275.43531 112.49482,278.36804 L 104.05869,273.38134 z " + sodipodi:nodetypes="ccc" + id="path16269" + style="opacity:0.99720004;fill:#666666;fill-rule:evenodd;stroke:#000000;stroke-width:0.20456015;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + sodipodi:nodetypes="ccsccccc" + id="path25800" + d="M 304.06894,248.1971 C 302.1656,248.29093 300.4498,248.80189 299.33849,249.7313 C 299.33849,249.7313 260.56796,282.17326 260.56795,282.17326 C 257.93245,284.37739 258.86046,287.65621 262.70943,289.52464 C 262.70943,289.52464 267.73895,291.93253 272.68174,294.28706 C 306.66914,288.15442 283.9982,253.02759 313.94536,251.0098 C 312.61326,250.41591 309.88612,249.2199 309.88612,249.2199 C 308.06831,248.43649 305.97228,248.10326 304.06894,248.1971 z " + style="fill:url(#linearGradient2490);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.9527356;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" /> + </g> + </g> +</svg> diff --git a/wpa_supplicant/wpa_gui-qt4/icons/wpa_gui.svg b/wpa_supplicant/wpa_gui-qt4/icons/wpa_gui.svg new file mode 100644 index 0000000000000..b3abf0a288d89 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/icons/wpa_gui.svg @@ -0,0 +1,256 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + version="1.0" + width="128" + height="128" + id="svg2" + sodipodi:version="0.32" + inkscape:version="0.46" + sodipodi:docname="wpa_gui.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape"> + <metadata + id="metadata47"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <sodipodi:namedview + inkscape:window-height="771" + inkscape:window-width="640" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + guidetolerance="10.0" + gridtolerance="10.0" + objecttolerance="10.0" + borderopacity="1.0" + bordercolor="#666666" + pagecolor="#ffffff" + id="base" + showgrid="false" + inkscape:zoom="4.2421875" + inkscape:cx="64" + inkscape:cy="64" + inkscape:window-x="634" + inkscape:window-y="0" + inkscape:current-layer="svg2" /> + <defs + id="defs4"> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="0 : 64 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="128 : 64 : 1" + inkscape:persp3d-origin="64 : 42.666667 : 1" + id="perspective49" /> + <linearGradient + id="linearGradient39133"> + <stop + id="stop39135" + style="stop-color:#252525;stop-opacity:1" + offset="0" /> + <stop + id="stop39137" + style="stop-color:#515151;stop-opacity:1" + offset="0" /> + <stop + id="stop39139" + style="stop-color:#878787;stop-opacity:1" + offset="0.28677997" /> + <stop + id="stop39141" + style="stop-color:#000000;stop-opacity:1" + offset="0.92151743" /> + <stop + id="stop39143" + style="stop-color:#ffffff;stop-opacity:0.73786408" + offset="1" /> + </linearGradient> + <linearGradient + id="linearGradient39119"> + <stop + id="stop39121" + style="stop-color:#ffffff;stop-opacity:0.82905984" + offset="0" /> + <stop + id="stop39123" + style="stop-color:#ffffff;stop-opacity:0" + offset="1" /> + </linearGradient> + <linearGradient + id="linearGradient39106"> + <stop + id="stop39108" + style="stop-color:#ffffff;stop-opacity:1" + offset="0" /> + <stop + id="stop39110" + style="stop-color:#a8a8a8;stop-opacity:0" + offset="1" /> + </linearGradient> + <linearGradient + id="linearGradient39094"> + <stop + id="stop39096" + style="stop-color:#000000;stop-opacity:1" + offset="0" /> + <stop + id="stop39098" + style="stop-color:#333333;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + id="linearGradient39062"> + <stop + id="stop39064" + style="stop-color:#252525;stop-opacity:1" + offset="0" /> + <stop + id="stop39086" + style="stop-color:#515151;stop-opacity:1" + offset="0.21101321" /> + <stop + id="stop39088" + style="stop-color:#878787;stop-opacity:1" + offset="0.75" /> + <stop + id="stop39090" + style="stop-color:#6c6c6c;stop-opacity:1" + offset="0.875" /> + <stop + id="stop39066" + style="stop-color:#1e1e1e;stop-opacity:1" + offset="1" /> + </linearGradient> + <linearGradient + x1="4" + y1="40" + x2="124" + y2="60" + id="linearGradient39068" + xlink:href="#linearGradient39062" + gradientUnits="userSpaceOnUse" /> + <radialGradient + cx="100.70589" + cy="96" + r="60" + fx="158.07428" + fy="95.718063" + id="radialGradient39100" + xlink:href="#linearGradient39094" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(2.7837903e-8,-1,0.99999999,-2.1864248e-6,-32.000004,164.7061)" /> + <radialGradient + cx="100.44444" + cy="34.363636" + r="32" + fx="83.18" + fy="34.228985" + id="radialGradient39104" + xlink:href="#linearGradient39106" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(3.1472435e-6,1.0227273,-0.87499999,-9.5061964e-8,94.067865,-4.7272712)" /> + <radialGradient + cx="75.999977" + cy="-2.7730541" + r="48" + fx="55.266491" + fy="-2.5338216" + id="radialGradient39125" + xlink:href="#linearGradient39119" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0,0.83333324,-1.6666667,2.518705e-6,59.378243,-35.333302)" /> + <radialGradient + cx="64.066589" + cy="63.713329" + r="60" + fx="64.066589" + fy="63.713329" + id="radialGradient39131" + xlink:href="#linearGradient39133" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.1333333,5.1768857e-8,5.2556881e-6,1.1666667,-8.6091298,-10.332226)" /> + <filter + id="filter39153"> + <feGaussianBlur + id="feGaussianBlur39155" + stdDeviation="2.28" + inkscape:collect="always" /> + </filter> + <filter + id="filter39159"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="1.68" + id="feGaussianBlur39161" /> + </filter> + </defs> + <g + id="layer1" + style="display:inline"> + <path + d="M 29,4 C 15.147058,4 4,15.14706 4,29 l 0,70 c 0,13.85294 11.147058,25 25,25 l 70,0 c 13.85294,0 25,-11.14706 25,-25 l 0,-70 C 124,15.14706 112.85294,4 99,4 L 29,4 z" + id="path39151" + style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;filter:url(#filter39153)" /> + <path + d="M 29,4 C 15.147058,4 4,15.14706 4,29 l 0,70 c 0,13.85294 11.147058,25 25,25 l 70,0 c 13.85294,0 25,-11.14706 25,-25 l 0,-70 C 124,15.14706 112.85294,4 99,4 L 29,4 z" + id="path39157" + style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;filter:url(#filter39159)" /> + <rect + width="120" + height="120" + ry="25.00531" + x="4" + y="0" + id="rect2573" + style="opacity:1;fill:url(#radialGradient39100);fill-opacity:1;stroke:none" /> + <path + d="M 29,0 C 15.147058,0 4,11.14706 4,25 l 0,70 c 0,13.85294 11.147058,25 25,25 l 70,0 c 13.85294,0 25,-11.14706 25,-25 l 0,-70 C 124,11.14706 112.85294,0 99,0 L 29,0 z" + id="path39127" + style="opacity:0.20512821;fill:url(#radialGradient39131);fill-opacity:1;stroke:none" /> + <path + d="m 44,68 40,0 12,40 c -20,7.27273 -44,7.27273 -64,0 L 44,68 z" + id="path39102" + style="opacity:0.53418801;fill:url(#radialGradient39104);fill-opacity:1;stroke:none" /> + <path + d="M 25.339207,12 C 52,8 76,8 102.66079,12 107.83471,12 112,16.165286 112,21.339207 L 116,52 C 100,73.339207 28,73.339207 12,52 L 16,21.339207 C 16,16.165286 20.165286,12 25.339207,12 z" + id="rect39116" + style="opacity:0.92307691;fill:url(#radialGradient39125);fill-opacity:1;stroke:none" /> + <path + d="M 29,8 C 15.147058,8 4,19.14706 4,33 l 0,70 c 0,13.85294 11.147058,25 25,25 l 70,0 c 13.85294,0 25,-11.14706 25,-25 l 0,-70 C 124,19.14706 112.85294,8 99,8 L 29,8 z" + id="path39147" + style="opacity:0.20512821;fill:#000000;fill-opacity:1;stroke:none" /> + <path + d="M 29,0 C 15.147058,0 4,11.147058 4,25 l 0,70 c 0,13.85294 11.147058,25 25,25 l 70,0 c 13.85294,0 25,-11.14706 25,-25 l 0,-70 C 124,11.147058 112.85294,0 99,0 L 29,0 z m 0,4 70,0 c 11.70613,0 21,9.293869 21,21 l 0,70 c 0,11.70613 -9.29387,21 -21,21 l -70,0 C 17.293869,116 8,106.70613 8,95 L 8,25 C 8,13.293869 17.293869,4 29,4 z" + id="rect39029" + style="opacity:1;fill:url(#linearGradient39068);fill-opacity:1;stroke:none" /> + <path + d="M 66.35081,74.771345 A 36,36 0 1 1 54.34964,35.777782" + transform="matrix(-0.16680323,0.53082142,-0.53082142,-0.16680323,103.31027,53.117897)" + id="path3351" + style="opacity:1;fill:none;stroke:#ffffff;stroke-width:21.56673813;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /> + <path + d="m 36,56 a 4,4 0 1 1 -8,0 4,4 0 1 1 8,0 z" + transform="matrix(1.4851301,0,0,1.4851301,16.475837,-23.948973)" + id="path3353" + style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none" /> + <path + d="M 66.35081,74.771345 A 36,36 0 1 1 54.34964,35.777782" + transform="matrix(-0.35033273,1.1148712,-1.1148712,-0.35033273,146.5624,46.88078)" + id="path2622" + style="opacity:1;fill:none;stroke:#ffffff;stroke-width:10.26852894;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /> + </g> +</svg> diff --git a/wpa_supplicant/wpa_gui-qt4/icons_png.qrc b/wpa_supplicant/wpa_gui-qt4/icons_png.qrc new file mode 100644 index 0000000000000..9a30b7f560bab --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/icons_png.qrc @@ -0,0 +1,9 @@ +<RCC> + <qresource prefix="/icons" > + <file alias="wpa_gui.png">icons/hicolor/16x16/apps/wpa_gui.png</file> + <file alias="ap.png">icons/hicolor/32x32/apps/ap.png</file> + <file alias="laptop.png">icons/hicolor/32x32/apps/laptop.png</file> + <file alias="group.png">icons/hicolor/32x32/apps/group.png</file> + <file alias="invitation.png">icons/hicolor/32x32/apps/invitation.png</file> + </qresource> +</RCC> diff --git a/wpa_supplicant/wpa_gui-qt4/lang/.gitignore b/wpa_supplicant/wpa_gui-qt4/lang/.gitignore new file mode 100644 index 0000000000000..8df47d550c7d4 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/lang/.gitignore @@ -0,0 +1 @@ +*.qm diff --git a/wpa_supplicant/wpa_gui-qt4/lang/wpa_gui_de.ts b/wpa_supplicant/wpa_gui-qt4/lang/wpa_gui_de.ts new file mode 100644 index 0000000000000..d7a9c89fa18a1 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/lang/wpa_gui_de.ts @@ -0,0 +1,1262 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.0" language="de_DE" sourcelanguage="en_US"> +<context> + <name>AddInterface</name> + <message> + <location filename="../addinterface.cpp" line="38"/> + <source>Select network interface to add</source> + <translation>Wähle die Netzwerkschnittstelle zum hinzufügen aus</translation> + </message> + <message> + <location filename="../addinterface.cpp" line="47"/> + <source>driver</source> + <translation>Treiber</translation> + </message> + <message> + <location filename="../addinterface.cpp" line="48"/> + <source>interface</source> + <translation>Schnittstelle</translation> + </message> + <message> + <location filename="../addinterface.cpp" line="49"/> + <source>description</source> + <translation>Beschreibung</translation> + </message> + <message> + <location filename="../addinterface.cpp" line="221"/> + <source>Add interface command could not be completed.</source> + <translation>Das Schnittstellen hinzufügen Kommando konnte nicht abgeschlossen werden.</translation> + </message> + <message> + <location filename="../addinterface.cpp" line="229"/> + <source>Failed to add the interface.</source> + <translation>Fehler beim hinzufügen der Schnittstelle.</translation> + </message> + <message> + <location filename="../addinterface.cpp" line="238"/> + <source>Failed to add the interface into registry.</source> + <translation>Fehler beim hinzufügen der Schnittstelle in die Registry.</translation> + </message> +</context> +<context> + <name>ErrorMsg</name> + <message> + <location filename="../wpagui.cpp" line="1621"/> + <source>wpa_gui error</source> + <translation>wpa_gui Fehler</translation> + </message> +</context> +<context> + <name>EventHistory</name> + <message> + <location filename="../eventhistory.ui" line="13"/> + <source>Event history</source> + <translation>Ereignis Historie</translation> + </message> + <message> + <location filename="../eventhistory.ui" line="48"/> + <source>Close</source> + <translation>Schließen</translation> + </message> +</context> +<context> + <name>EventListModel</name> + <message> + <location filename="../eventhistory.cpp" line="62"/> + <source>Timestamp</source> + <translation>Zeit</translation> + </message> + <message> + <location filename="../eventhistory.cpp" line="64"/> + <source>Message</source> + <translation>Meldung</translation> + </message> +</context> +<context> + <name>NetworkConfig</name> + <message> + <location filename="../networkconfig.ui" line="13"/> + <source>NetworkConfig</source> + <translation></translation> + </message> + <message> + <location filename="../networkconfig.ui" line="19"/> + <source>Cancel</source> + <translation>Abbrechen</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="35"/> + <source>SSID</source> + <translation>SSID</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="42"/> + <source>Network name (Service Set IDentifier)</source> + <translation>Netzwerkname (Service Set IDentifier)</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="52"/> + <source>Authentication</source> + <translation>Authentifizierung</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="60"/> + <source>Plaintext (open / no authentication)</source> + <translation>Plaintext (offen / keine Authentifizierung)</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="65"/> + <source>Static WEP (no authentication)</source> + <translation>Static WEP (keine Authentifizierung)</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="70"/> + <source>Static WEP (Shared Key authentication)</source> + <translation></translation> + </message> + <message> + <location filename="../networkconfig.ui" line="75"/> + <source>IEEE 802.1X</source> + <translation></translation> + </message> + <message> + <location filename="../networkconfig.ui" line="80"/> + <source>WPA-Personal (PSK)</source> + <translation></translation> + </message> + <message> + <location filename="../networkconfig.ui" line="85"/> + <source>WPA-Enterprise (EAP)</source> + <translation></translation> + </message> + <message> + <location filename="../networkconfig.ui" line="90"/> + <source>WPA2-Personal (PSK)</source> + <translation></translation> + </message> + <message> + <location filename="../networkconfig.ui" line="95"/> + <source>WPA2-Enterprise (EAP)</source> + <translation></translation> + </message> + <message> + <location filename="../networkconfig.ui" line="103"/> + <source>Encryption</source> + <translation>Verschlüsselung</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="111"/> + <source>None</source> + <translation>Keine</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="116"/> + <source>WEP</source> + <translation></translation> + </message> + <message> + <location filename="../networkconfig.ui" line="121"/> + <source>TKIP</source> + <translation></translation> + </message> + <message> + <location filename="../networkconfig.ui" line="126"/> + <source>CCMP</source> + <translation></translation> + </message> + <message> + <location filename="../networkconfig.ui" line="134"/> + <source>PSK</source> + <translation></translation> + </message> + <message> + <location filename="../networkconfig.ui" line="144"/> + <source>WPA/WPA2 pre-shared key or passphrase</source> + <translation>WPA/WPA2 Pre-Shared Key oder Passphrase</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="157"/> + <source>EAP method</source> + <translation>EAP Verfahren</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="171"/> + <source>Identity</source> + <translation>Identität</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="181"/> + <source>Username/Identity for EAP methods</source> + <translation>Nutzername/Identitär für die EAP Verfahren</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="188"/> + <source>Password</source> + <translation>Passwort</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="198"/> + <source>Password for EAP methods</source> + <translation>Passwort für die EAP Verfahren</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="208"/> + <source>CA certificate</source> + <translation>CA Zertifikat</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="225"/> + <source>WEP keys</source> + <translation>WEP Schlüssel</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="234"/> + <source>key 0</source> + <translation>Schlüssel 0</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="244"/> + <source>key 1</source> + <translation>Schlüssel 1</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="254"/> + <source>key 3</source> + <translation>Schlüssel 3</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="264"/> + <source>key 2</source> + <translation>Schlüssel 2</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="305"/> + <source>Optional Settings</source> + <translation>Optionale Einstellungen</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="311"/> + <source>Network Identification String</source> + <translation>Netzwerk Indentifikations Zeichenfolge</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="318"/> + <source>Network Priority</source> + <translation>Netzwerk Priorität</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="331"/> + <source>IDString</source> + <translation></translation> + </message> + <message> + <location filename="../networkconfig.ui" line="338"/> + <source>Priority</source> + <translation>Priorität</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="345"/> + <source>Inner auth</source> + <translation>Geheime Auth</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="365"/> + <source>Add</source> + <translation>Hinzufügen</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="375"/> + <source>Remove</source> + <translation>Entfernen</translation> + </message> + <message> + <location filename="../networkconfig.ui" line="398"/> + <source>WPS</source> + <translation></translation> + </message> + <message> + <location filename="../networkconfig.cpp" line="202"/> + <source>WPA Pre-Shared Key Error</source> + <translation>WPA Pre Shared Key Fehler</translation> + </message> + <message> + <location filename="../networkconfig.cpp" line="203"/> + <source>WPA-PSK requires a passphrase of 8 to 63 characters +or 64 hex digit PSK</source> + <translation>WPA PSK benötigt ein Passphrase mit 8 bis 63 Zeichen +oder 64 hexadezimal stelligen PSK</translation> + </message> + <message> + <location filename="../networkconfig.cpp" line="215"/> + <source>Network ID Error</source> + <translation>Netzwerk ID Fehler</translation> + </message> + <message> + <location filename="../networkconfig.cpp" line="216"/> + <source>Network ID String contains non-word characters. +It must be a simple string, without spaces, containing +only characters in this range: [A-Za-z0-9_-] +</source> + <translation>Netzwerk ID Zeichnfolge beinhaltet ungültige Zeichen. +Es muss eine einfache Zeichnfolge aus [A-Za-z0-9_] ohne +Leerzeichen sein +</translation> + </message> + <message> + <location filename="../networkconfig.cpp" line="237"/> + <source>Failed to add network to wpa_supplicant +configuration.</source> + <translation>Hinzufügen des Netzwerks in die wpa_supplicant +Konfiguration fehlgeschlagen.</translation> + </message> + <message> + <location filename="../networkconfig.cpp" line="414"/> + <source>Failed to enable network in wpa_supplicant +configuration.</source> + <translation>Aktivieren des Netzwerks in der wpa_supplicant +Konfiguration fehlgeschlagen.</translation> + </message> + <message> + <location filename="../networkconfig.cpp" line="802"/> + <source>This will permanently remove the network +from the configuration. Do you really want +to remove this network?</source> + <translation>Dies wird das Netzwerk permanent aus +der Konfiguration entfernen. Möchtest du +das Netzwerk wirklich entfernen?</translation> + </message> + <message> + <location filename="../networkconfig.cpp" line="805"/> + <source>Yes</source> + <translation>Ja</translation> + </message> + <message> + <location filename="../networkconfig.cpp" line="805"/> + <source>No</source> + <translation>Nein</translation> + </message> + <message> + <location filename="../networkconfig.cpp" line="813"/> + <source>Failed to remove network from wpa_supplicant +configuration.</source> + <translation>Entfernen des Netzwerks aus der wpa_supplicant +Konfiguration fehlgeschlagen.</translation> + </message> +</context> +<context> + <name>Peers</name> + <message> + <location filename="../peers.ui" line="14"/> + <source>Peers</source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="107"/> + <source>Associated station</source> + <translation>Verbundene Stationen</translation> + </message> + <message> + <location filename="../peers.cpp" line="110"/> + <source>AP</source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="113"/> + <source>WPS AP</source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="116"/> + <source>WPS PIN needed</source> + <translation>WPS PIN wird benötigt</translation> + </message> + <message> + <location filename="../peers.cpp" line="119"/> + <source>ER: WPS AP</source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="122"/> + <source>ER: WPS AP (Unconfigured)</source> + <translation>ER: WPS AP (nicht konfiguriert)</translation> + </message> + <message> + <location filename="../peers.cpp" line="125"/> + <source>ER: WPS Enrollee</source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="128"/> + <source>WPS Enrollee</source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="159"/> + <source>Enter WPS PIN</source> + <translation>WPS PIN Eingabe</translation> + </message> + <message> + <location filename="../peers.cpp" line="164"/> + <source>Connect (PBC)</source> + <translation>Verbinden (PBC)</translation> + </message> + <message> + <location filename="../peers.cpp" line="172"/> + <source>Enroll (PBC)</source> + <translation>Anmelden (PBC)</translation> + </message> + <message> + <location filename="../peers.cpp" line="177"/> + <source>Learn Configuration</source> + <translation>Konfiguration lernen</translation> + </message> + <message> + <location filename="../peers.cpp" line="181"/> + <source>Properties</source> + <translation>Eigenschaften</translation> + </message> + <message> + <location filename="../peers.cpp" line="184"/> + <source>Refresh</source> + <translation>Aktualisieren</translation> + </message> + <message> + <location filename="../peers.cpp" line="205"/> + <source>PIN:</source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="206"/> + <source>PIN for </source> + <translation>Pin für </translation> + </message> + <message> + <location filename="../peers.cpp" line="227"/> + <source>Failed to set the WPS PIN.</source> + <translation>Setzten des WPS PIN fehlgeschlagen.</translation> + </message> + <message> + <location filename="../peers.cpp" line="815"/> + <source>Peer Properties</source> + <translation>Peer Eigenschaften</translation> + </message> + <message> + <location filename="../peers.cpp" line="820"/> + <source>Name: </source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="827"/> + <source>Address: </source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="831"/> + <source>UUID: </source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="835"/> + <source>Primary Device Type: </source> + <translation>Primärer Geräte Typ: </translation> + </message> + <message> + <location filename="../peers.cpp" line="840"/> + <source>SSID: </source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="845"/> + <source>Configuration Methods: </source> + <translation>Konfigurationsverfahren: </translation> + </message> + <message> + <location filename="../peers.cpp" line="847"/> + <source>[USBA]</source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="849"/> + <source>[Ethernet]</source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="851"/> + <source>[Label]</source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="853"/> + <source>[Display]</source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="855"/> + <source>[Ext. NFC Token]</source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="857"/> + <source>[Int. NFC Token]</source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="859"/> + <source>[NFC Interface]</source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="861"/> + <source>[Push Button]</source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="863"/> + <source>[Keypad]</source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="869"/> + <source>Device Password ID: </source> + <translation>Geräte Passwort ID: </translation> + </message> + <message> + <location filename="../peers.cpp" line="872"/> + <source> (Default PIN)</source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="875"/> + <source> (User-specified PIN)</source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="878"/> + <source> (Machine-specified PIN)</source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="881"/> + <source> (Rekey)</source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="884"/> + <source> (Push Button)</source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="887"/> + <source> (Registrar-specified)</source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="924"/> + <source>Failed to start WPS PBC.</source> + <translation>Starten von WPS PBC fehlgeschlagen.</translation> + </message> + <message> + <location filename="../peers.cpp" line="937"/> + <source>AP PIN:</source> + <translation></translation> + </message> + <message> + <location filename="../peers.cpp" line="938"/> + <source>AP PIN for </source> + <translation>AP PIN für </translation> + </message> + <message> + <location filename="../peers.cpp" line="953"/> + <source>Failed to start learning AP configuration.</source> + <translation>Fehler beim erkennen der AP Konfiguration.</translation> + </message> +</context> +<context> + <name>ScanResults</name> + <message> + <location filename="../scanresults.ui" line="13"/> + <source>Scan results</source> + <translation>Scan Ergebnisse</translation> + </message> + <message> + <location filename="../scanresults.ui" line="32"/> + <source>SSID</source> + <translation></translation> + </message> + <message> + <location filename="../scanresults.ui" line="37"/> + <source>BSSID</source> + <translation></translation> + </message> + <message> + <location filename="../scanresults.ui" line="42"/> + <source>frequency</source> + <translation>Frequenz</translation> + </message> + <message> + <location filename="../scanresults.ui" line="47"/> + <source>signal</source> + <translation>Signal</translation> + </message> + <message> + <location filename="../scanresults.ui" line="52"/> + <source>flags</source> + <translation>Flags</translation> + </message> + <message> + <location filename="../scanresults.ui" line="75"/> + <source>Scan</source> + <translation>Scannen</translation> + </message> + <message> + <location filename="../scanresults.ui" line="82"/> + <source>Close</source> + <translation>Schließen</translation> + </message> +</context> +<context> + <name>UserDataRequest</name> + <message> + <location filename="../userdatarequest.ui" line="16"/> + <source>Authentication credentials required</source> + <translation>Authentifzierungs Beglaubigung nötig</translation> + </message> + <message> + <location filename="../userdatarequest.ui" line="77"/> + <source>&OK</source> + <translation></translation> + </message> + <message> + <location filename="../userdatarequest.ui" line="93"/> + <source>&Cancel</source> + <translation></translation> + </message> + <message> + <location filename="../userdatarequest.cpp" line="67"/> + <source>Password: </source> + <translation>Passwort: </translation> + </message> + <message> + <location filename="../userdatarequest.cpp" line="70"/> + <source>New password: </source> + <translation>Neues Passwort: </translation> + </message> + <message> + <location filename="../userdatarequest.cpp" line="73"/> + <source>Identity: </source> + <translation>Identität: </translation> + </message> + <message> + <location filename="../userdatarequest.cpp" line="75"/> + <source>Private key passphrase: </source> + <translation>Privater Key Passphrase: </translation> + </message> +</context> +<context> + <name>WpaGui</name> + <message> + <location filename="../wpagui.ui" line="13"/> + <source>wpa_gui</source> + <translation></translation> + </message> + <message> + <location filename="../wpagui.ui" line="24"/> + <source>Adapter:</source> + <translation></translation> + </message> + <message> + <location filename="../wpagui.ui" line="34"/> + <source>Network:</source> + <translation>Netzwerk:</translation> + </message> + <message> + <location filename="../wpagui.ui" line="48"/> + <source>Current Status</source> + <translation>Aktueller Status</translation> + </message> + <message> + <location filename="../wpagui.ui" line="63"/> + <location filename="../wpagui.ui" line="300"/> + <source>Status:</source> + <translation></translation> + </message> + <message> + <location filename="../wpagui.ui" line="70"/> + <source>Last message:</source> + <translation>Letzte Meldung:</translation> + </message> + <message> + <location filename="../wpagui.ui" line="77"/> + <source>Authentication:</source> + <translation>Authentifizierung:</translation> + </message> + <message> + <location filename="../wpagui.ui" line="84"/> + <source>Encryption:</source> + <translation>Verschlüsselung:</translation> + </message> + <message> + <location filename="../wpagui.ui" line="91"/> + <source>SSID:</source> + <translation></translation> + </message> + <message> + <location filename="../wpagui.ui" line="98"/> + <source>BSSID:</source> + <translation></translation> + </message> + <message> + <location filename="../wpagui.ui" line="105"/> + <source>IP address:</source> + <translation>IP Adresse:</translation> + </message> + <message> + <location filename="../wpagui.ui" line="177"/> + <source>Connect</source> + <translation>Verbinden</translation> + </message> + <message> + <location filename="../wpagui.ui" line="184"/> + <source>Disconnect</source> + <translation>Trennen</translation> + </message> + <message> + <location filename="../wpagui.ui" line="191"/> + <location filename="../wpagui.ui" line="286"/> + <source>Scan</source> + <translation>Scannen</translation> + </message> + <message> + <location filename="../wpagui.ui" line="212"/> + <source>Manage Networks</source> + <translation>Netzwerke verwalten</translation> + </message> + <message> + <location filename="../wpagui.ui" line="238"/> + <source>Enabled</source> + <translation>Aktiviert</translation> + </message> + <message> + <location filename="../wpagui.ui" line="245"/> + <source>Edit</source> + <translation>Bearbeiten</translation> + </message> + <message> + <location filename="../wpagui.ui" line="252"/> + <source>Remove</source> + <translation>Entfernen</translation> + </message> + <message> + <location filename="../wpagui.ui" line="272"/> + <source>Disabled</source> + <translation>Deaktiviert</translation> + </message> + <message> + <location filename="../wpagui.ui" line="279"/> + <source>Add</source> + <translation>Hinzufügen</translation> + </message> + <message> + <location filename="../wpagui.ui" line="294"/> + <source>WPS</source> + <translation></translation> + </message> + <message> + <location filename="../wpagui.ui" line="314"/> + <source>PBC - push button</source> + <translation>PBC - Taste</translation> + </message> + <message> + <location filename="../wpagui.ui" line="321"/> + <source>Generate PIN</source> + <translation>PIN erzeugen</translation> + </message> + <message> + <location filename="../wpagui.ui" line="328"/> + <source>PIN:</source> + <translation></translation> + </message> + <message> + <location filename="../wpagui.ui" line="348"/> + <source>Use AP PIN</source> + <translation>AP PIN verwenden</translation> + </message> + <message> + <location filename="../wpagui.ui" line="355"/> + <source>AP PIN:</source> + <translation></translation> + </message> + <message> + <location filename="../wpagui.ui" line="390"/> + <source>&File</source> + <translation>&Datei</translation> + </message> + <message> + <location filename="../wpagui.ui" line="401"/> + <source>&Network</source> + <translation>&Netzwerk</translation> + </message> + <message> + <location filename="../wpagui.ui" line="413"/> + <source>&Help</source> + <translation>&Hilfe</translation> + </message> + <message> + <location filename="../wpagui.ui" line="426"/> + <source>Event &History</source> + <translation>Ereignis &Historie</translation> + </message> + <message> + <location filename="../wpagui.ui" line="431"/> + <source>&Save Configuration</source> + <translation>Konfiguration &Speichern</translation> + </message> + <message> + <location filename="../wpagui.ui" line="434"/> + <source>Ctrl+S</source> + <translation></translation> + </message> + <message> + <location filename="../wpagui.ui" line="439"/> + <source>E&xit</source> + <translation>&Beenden</translation> + </message> + <message> + <location filename="../wpagui.ui" line="442"/> + <source>Ctrl+Q</source> + <translation></translation> + </message> + <message> + <location filename="../wpagui.ui" line="447"/> + <source>&Add</source> + <translation>&Hinzufügen</translation> + </message> + <message> + <location filename="../wpagui.ui" line="452"/> + <source>&Edit</source> + <translation>&Bearbeiten</translation> + </message> + <message> + <location filename="../wpagui.ui" line="457"/> + <source>&Remove</source> + <translation>&Entfernen</translation> + </message> + <message> + <location filename="../wpagui.ui" line="462"/> + <source>E&nable All</source> + <translation>Alle &aktivieren</translation> + </message> + <message> + <location filename="../wpagui.ui" line="467"/> + <source>&Disable All</source> + <translation>Alle &deaktivieren</translation> + </message> + <message> + <location filename="../wpagui.ui" line="472"/> + <source>Re&move All</source> + <translation>Alle &entfernen</translation> + </message> + <message> + <location filename="../wpagui.ui" line="480"/> + <source>&Contents...</source> + <translation>&Inhalt...</translation> + </message> + <message> + <location filename="../wpagui.ui" line="488"/> + <source>&Index...</source> + <translation></translation> + </message> + <message> + <location filename="../wpagui.ui" line="493"/> + <source>&About</source> + <translation>&Über</translation> + </message> + <message> + <location filename="../wpagui.ui" line="501"/> + <source>&Wi-Fi Protected Setup</source> + <translation></translation> + </message> + <message> + <location filename="../wpagui.ui" line="506"/> + <source>&Peers</source> + <translation></translation> + </message> + <message> + <location filename="../wpagui.cpp" line="53"/> + <source>Stop Service</source> + <translation>Dienst stoppen</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="58"/> + <source>Start Service</source> + <translation>Dienst starten</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="67"/> + <source>Add Interface</source> + <translation>Schnittstelle hinzufügen</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="167"/> + <source>connecting to wpa_supplicant</source> + <translation>Verbindungsaufbau zu wpa_supplicant</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="343"/> + <source>wpa_supplicant service is not running. +Do you want to start it?</source> + <translation>wpa_supplicant ist nicht gestartet. +Möchtest du ihn starten?</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="466"/> + <source>Disconnected</source> + <translation>Getrennt</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="468"/> + <source>Inactive</source> + <translation>Inaktiv</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="470"/> + <source>Scanning</source> + <translation>Scannen</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="472"/> + <source>Authenticating</source> + <translation>Authentifizieren</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="474"/> + <source>Associating</source> + <translation>Assoziieren</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="476"/> + <source>Associated</source> + <translation>Assoziiert</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="478"/> + <source>4-Way Handshake</source> + <translation>4-Wege Handshake</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="480"/> + <source>Group Handshake</source> + <translation>Gruppen Handshake</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="482"/> + <source>Completed</source> + <translation>Abgeschlossen</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="484"/> + <source>Unknown</source> + <translation>Unbekannt</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="497"/> + <source>Could not get status from wpa_supplicant</source> + <translation>Status konnte nicht von wpa_supplicant abgerufen werden</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="512"/> + <source>No network interfaces in use. +Would you like to add one?</source> + <translation>Es ist keine Netzwerkschnittstelle in verwendung. +Möchtest du eine hinzufügen?</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="682"/> + <location filename="../wpagui.cpp" line="974"/> + <location filename="../wpagui.cpp" line="1039"/> + <location filename="../wpagui.cpp" line="1117"/> + <source>Select any network</source> + <translation>Wähle beliebiges Netzwerk</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="883"/> + <source>Disconnected from network.</source> + <translation>Getrennt vom Netzwerk.</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="886"/> + <source>Connection to network established.</source> + <translation>Verbindung zum Netzwerk wurde aufgebaut.</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="890"/> + <location filename="../wpagui.cpp" line="1523"/> + <source>WPS AP in active PBC mode found</source> + <translation>WPS AP im aktiven PBC Modus gefunden</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="894"/> + <source>Press the PBC button on the screen to start registration</source> + <translation>Drücke den PBC Knopf auf dem Bildschirm um die Registrierung zu starten</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="897"/> + <source>WPS AP with recently selected registrar</source> + <translation>WPS AP mit kürzlich ausgewähltem Registrator</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="903"/> + <source>WPS AP detected</source> + <translation>WPS AP erkannt</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="905"/> + <source>PBC mode overlap detected</source> + <translation>PBC Modus Overlap erkannt</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="906"/> + <source>More than one AP is currently in active WPS PBC mode. Wait couple of minutes and try again</source> + <translation>Mehr als ein AP ist momentan im aktiven WPS PBC Modus. Versuch es in ein paar Minuten nochmal</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="911"/> + <source>Network configuration received</source> + <translation>Netzwerk Konfiguration empfangen</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="915"/> + <source>Registration started</source> + <translation>Registrierung gestartet</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="917"/> + <source>Registrar does not yet know PIN</source> + <translation>Registrator kennt den PIN noch nicht</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="919"/> + <source>Registration failed</source> + <translation>Registrierung fehlgeschlagen</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="921"/> + <source>Registration succeeded</source> + <translation>Registrierung erfolgreich</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1069"/> + <location filename="../wpagui.cpp" line="1138"/> + <source>No Networks</source> + <translation>Keine Netzwerke</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1070"/> + <source>There are no networks to edit. +</source> + <translation>Keine Netzwerke zum bearbeiten. +</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1081"/> + <location filename="../wpagui.cpp" line="1151"/> + <source>Select A Network</source> + <translation>Wähle ein Netzwerk</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1082"/> + <source>Select a network from the list to edit it. +</source> + <translation>Wähle ein Netzwerk aus der Liste zum bearbeiten. +</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1139"/> + <source>There are no networks to remove. +</source> + <translation>Es sind keine Netzwerke zum entfernen vorhanden. +</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1152"/> + <source>Select a network from the list to remove it. +</source> + <translation>Wähle ein Netzwerk aus der Liste zum entfernen. +</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1264"/> + <source>Failed to save configuration</source> + <translation>Speichern der Konfiguration fehlgeschlagen</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1265"/> + <source>The configuration could not be saved. + +The update_config=1 configuration option +must be used for configuration saving to +be permitted. +</source> + <translation>Die Konfiguration konnte nicht gespeichert werden. + +Die Einstellung update_config=1 muss gesetzt sein, +damit Konfigurationen gespeichert werden können. +</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1272"/> + <source>Saved configuration</source> + <translation>Konfiguration gespeichert</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1273"/> + <source>The current configuration was saved. +</source> + <translation>Die aktuelle Konfiguration wurde gespeichert. +</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1293"/> + <source> - wpa_supplicant user interface</source> + <translation> - wpa_supplicant Benutzerschnittstelle</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1307"/> + <source>&Disconnect</source> + <translation>&Trennen</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1308"/> + <source>Re&connect</source> + <translation>&Wiederverbinden</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1317"/> + <source>&Event History</source> + <translation>&Ereignis Historie</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1318"/> + <source>Scan &Results</source> + <translation>Scan E&rgebnisse</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1319"/> + <source>S&tatus</source> + <translation></translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1328"/> + <source>&Show Window</source> + <translation>&Fenster anzeigen</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1329"/> + <source>&Hide Window</source> + <translation>&Fenster ausblenden</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1330"/> + <source>&Quit</source> + <translation>&Beenden</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1462"/> + <source> will keep running in the system tray.</source> + <translation> wird weiterhin in der System Ablage laufen.</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1466"/> + <source> systray</source> + <translation>System Ablage</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1467"/> + <source>The program will keep running in the system tray.</source> + <translation>Das Programm wird weiterhin in der System Ablage laufen.</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1524"/> + <source>Press the push button on the AP to start the PBC mode.</source> + <translation>Drücke die Taste am AP um den PBC Modus zu starten.</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1527"/> + <source>If you have not yet done so, press the push button on the AP to start the PBC mode.</source> + <translation>Wenn Sie es noch nicht getan haben, so drücken Sie die Taste am AP um den PBC Modus zu starten.</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1531"/> + <location filename="../wpagui.cpp" line="1551"/> + <source>Waiting for Registrar</source> + <translation>Warte auf Registrator</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1548"/> + <source>Enter the generated PIN into the Registrar (either the internal one in the AP or an external one).</source> + <translation>Geben Sie den generierten PIN in der Registrierungsstelle ein (entweder der interne oder der externe im AP).</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1561"/> + <source>WPS AP selected from scan results</source> + <translation>WPS AP ausgewählt aus Scan Ergebnissen</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1562"/> + <source>If you want to use an AP device PIN, e.g., from a label in the device, enter the eight digit AP PIN and click Use AP PIN button.</source> + <translation>Wenn Sie einen AP Geräte PIN verwenden möchten, z.B.: von einem Aufkleber am Gerät, geben Sie denn acht stelligen AP PIN ein und klicken Sie auf den AP PIN Knopf.</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1583"/> + <source>Waiting for AP/Enrollee</source> + <translation>Warte auf AP/Bewerber</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1591"/> + <source>Connected to the network</source> + <translation>Verbunden zum Netzwerk</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1592"/> + <source>Stopped</source> + <translation>Gestoppt</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1651"/> + <location filename="../wpagui.cpp" line="1679"/> + <source>OpenSCManager failed</source> + <translation>OpenSCManager fehlgeschlagen</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1657"/> + <location filename="../wpagui.cpp" line="1685"/> + <source>OpenService failed</source> + <translation>OpenService fehlgeschlagen</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1663"/> + <source>Failed to start wpa_supplicant service</source> + <translation>Starten des wpa_supplicant Dienstes fehlgeschlagen</translation> + </message> + <message> + <location filename="../wpagui.cpp" line="1691"/> + <source>Failed to stop wpa_supplicant service</source> + <translation>Stoppen des wpa_supplicant Dienstes fehlgeschlagen</translation> + </message> + <message> + <source>OpenSCManager failed: %d +</source> + <translation type="obsolete">OpenSCManager fehlgeschlagen: %d +</translation> + </message> + <message> + <source>OpenService failed: %d + +</source> + <translation type="obsolete">OpenService fehlgeschlagen: %d + +</translation> + </message> +</context> +</TS> diff --git a/wpa_supplicant/wpa_gui-qt4/main.cpp b/wpa_supplicant/wpa_gui-qt4/main.cpp new file mode 100644 index 0000000000000..bbd45c6e1d28d --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/main.cpp @@ -0,0 +1,67 @@ +/* + * wpa_gui - Application startup + * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifdef CONFIG_NATIVE_WINDOWS +#include <winsock.h> +#endif /* CONFIG_NATIVE_WINDOWS */ +#include <QApplication> +#include <QtCore/QLibraryInfo> +#include <QtCore/QTranslator> +#include "wpagui.h" + +WpaGuiApp::WpaGuiApp(int &argc, char **argv) : + QApplication(argc, argv), + argc(argc), + argv(argv) +{ + w = NULL; +} + +#if !defined(QT_NO_SESSIONMANAGER) && QT_VERSION < 0x050000 +void WpaGuiApp::saveState(QSessionManager &manager) +{ + QApplication::saveState(manager); + w->saveState(); +} +#endif + + +int main(int argc, char *argv[]) +{ + WpaGuiApp app(argc, argv); + QTranslator translator; + QString locale; + QString resourceDir; + int ret; + + locale = QLocale::system().name(); + resourceDir = QLibraryInfo::location(QLibraryInfo::TranslationsPath); + if (!translator.load("wpa_gui_" + locale, resourceDir)) + translator.load("wpa_gui_" + locale, "lang"); + app.installTranslator(&translator); + + WpaGui w(&app); + +#ifdef CONFIG_NATIVE_WINDOWS + WSADATA wsaData; + if (WSAStartup(MAKEWORD(2, 0), &wsaData)) { + /* printf("Could not find a usable WinSock.dll\n"); */ + return -1; + } +#endif /* CONFIG_NATIVE_WINDOWS */ + + app.w = &w; + + ret = app.exec(); + +#ifdef CONFIG_NATIVE_WINDOWS + WSACleanup(); +#endif /* CONFIG_NATIVE_WINDOWS */ + + return ret; +} diff --git a/wpa_supplicant/wpa_gui-qt4/networkconfig.cpp b/wpa_supplicant/wpa_gui-qt4/networkconfig.cpp new file mode 100644 index 0000000000000..2727318bcd5c0 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/networkconfig.cpp @@ -0,0 +1,853 @@ +/* + * wpa_gui - NetworkConfig class + * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include <cstdio> +#include <QMessageBox> + +#include "networkconfig.h" +#include "wpagui.h" + +enum { + AUTH_NONE_OPEN, + AUTH_NONE_WEP, + AUTH_NONE_WEP_SHARED, + AUTH_IEEE8021X, + AUTH_WPA_PSK, + AUTH_WPA_EAP, + AUTH_WPA2_PSK, + AUTH_WPA2_EAP +}; + +#define WPA_GUI_KEY_DATA "[key is configured]" + + +NetworkConfig::NetworkConfig(QWidget *parent, const char *, bool, + Qt::WindowFlags) + : QDialog(parent) +{ + setupUi(this); + + encrSelect->setEnabled(false); + connect(authSelect, SIGNAL(activated(int)), this, + SLOT(authChanged(int))); + connect(cancelButton, SIGNAL(clicked()), this, SLOT(close())); + connect(addButton, SIGNAL(clicked()), this, SLOT(addNetwork())); + connect(encrSelect, SIGNAL(activated(const QString &)), this, + SLOT(encrChanged(const QString &))); + connect(removeButton, SIGNAL(clicked()), this, SLOT(removeNetwork())); + connect(eapSelect, SIGNAL(activated(int)), this, + SLOT(eapChanged(int))); + connect(useWpsButton, SIGNAL(clicked()), this, SLOT(useWps())); + + wpagui = NULL; + new_network = false; +} + + +NetworkConfig::~NetworkConfig() +{ +} + + +void NetworkConfig::languageChange() +{ + retranslateUi(this); +} + + +void NetworkConfig::paramsFromScanResults(QTreeWidgetItem *sel) +{ + new_network = true; + + /* SSID BSSID frequency signal flags */ + setWindowTitle(sel->text(0)); + ssidEdit->setText(sel->text(0)); + + QString flags = sel->text(4); + int auth, encr = 0; + if (flags.indexOf("[WPA2-EAP") >= 0) + auth = AUTH_WPA2_EAP; + else if (flags.indexOf("[WPA-EAP") >= 0) + auth = AUTH_WPA_EAP; + else if (flags.indexOf("[WPA2-PSK") >= 0) + auth = AUTH_WPA2_PSK; + else if (flags.indexOf("[WPA-PSK") >= 0) + auth = AUTH_WPA_PSK; + else + auth = AUTH_NONE_OPEN; + + if (flags.indexOf("-CCMP") >= 0) + encr = 1; + else if (flags.indexOf("-TKIP") >= 0) + encr = 0; + else if (flags.indexOf("WEP") >= 0) { + encr = 1; + if (auth == AUTH_NONE_OPEN) + auth = AUTH_NONE_WEP; + } else + encr = 0; + + authSelect->setCurrentIndex(auth); + authChanged(auth); + encrSelect->setCurrentIndex(encr); + + wepEnabled(auth == AUTH_NONE_WEP); + + getEapCapa(); + + if (flags.indexOf("[WPS") >= 0) + useWpsButton->setEnabled(true); + bssid = sel->text(1); +} + + +void NetworkConfig::authChanged(int sel) +{ + encrSelect->setEnabled(sel != AUTH_NONE_OPEN && sel != AUTH_NONE_WEP && + sel != AUTH_NONE_WEP_SHARED); + pskEdit->setEnabled(sel == AUTH_WPA_PSK || sel == AUTH_WPA2_PSK); + bool eap = sel == AUTH_IEEE8021X || sel == AUTH_WPA_EAP || + sel == AUTH_WPA2_EAP; + eapSelect->setEnabled(eap); + identityEdit->setEnabled(eap); + passwordEdit->setEnabled(eap); + cacertEdit->setEnabled(eap); + phase2Select->setEnabled(eap); + if (eap) + eapChanged(eapSelect->currentIndex()); + + while (encrSelect->count()) + encrSelect->removeItem(0); + + if (sel == AUTH_NONE_OPEN || sel == AUTH_NONE_WEP || + sel == AUTH_NONE_WEP_SHARED || sel == AUTH_IEEE8021X) { + encrSelect->addItem("None"); + encrSelect->addItem("WEP"); + encrSelect->setCurrentIndex(sel == AUTH_NONE_OPEN ? 0 : 1); + } else { + encrSelect->addItem("TKIP"); + encrSelect->addItem("CCMP"); + encrSelect->setCurrentIndex((sel == AUTH_WPA2_PSK || + sel == AUTH_WPA2_EAP) ? 1 : 0); + } + + wepEnabled(sel == AUTH_NONE_WEP || sel == AUTH_NONE_WEP_SHARED); +} + + +void NetworkConfig::eapChanged(int sel) +{ + QString prev_val = phase2Select->currentText(); + while (phase2Select->count()) + phase2Select->removeItem(0); + + QStringList inner; + inner << "PEAP" << "TTLS" << "FAST"; + if (!inner.contains(eapSelect->itemText(sel))) + return; + + phase2Select->addItem("[ any ]"); + + /* Add special cases based on outer method */ + if (eapSelect->currentText().compare("TTLS") == 0) { + phase2Select->addItem("PAP"); + phase2Select->addItem("CHAP"); + phase2Select->addItem("MSCHAP"); + phase2Select->addItem("MSCHAPv2"); + } else if (eapSelect->currentText().compare("FAST") == 0) + phase2Select->addItem("GTC(auth) + MSCHAPv2(prov)"); + + /* Add all enabled EAP methods that can be used in the tunnel */ + int i; + QStringList allowed; + allowed << "MSCHAPV2" << "MD5" << "GTC" << "TLS" << "OTP" << "SIM" + << "AKA"; + for (i = 0; i < eapSelect->count(); i++) { + if (allowed.contains(eapSelect->itemText(i))) { + phase2Select->addItem("EAP-" + eapSelect->itemText(i)); + } + } + + for (i = 0; i < phase2Select->count(); i++) { + if (phase2Select->itemText(i).compare(prev_val) == 0) { + phase2Select->setCurrentIndex(i); + break; + } + } +} + + +void NetworkConfig::addNetwork() +{ + char reply[10], cmd[256]; + size_t reply_len; + int id; + int psklen = pskEdit->text().length(); + int auth = authSelect->currentIndex(); + + if (auth == AUTH_WPA_PSK || auth == AUTH_WPA2_PSK) { + if (psklen < 8 || psklen > 64) { + QMessageBox::warning( + this, + tr("WPA Pre-Shared Key Error"), + tr("WPA-PSK requires a passphrase of 8 to 63 " + "characters\n" + "or 64 hex digit PSK")); + pskEdit->setFocus(); + return; + } + } + + if (idstrEdit->isEnabled() && !idstrEdit->text().isEmpty()) { + QRegExp rx("^(\\w|-)+$"); + if (rx.indexIn(idstrEdit->text()) < 0) { + QMessageBox::warning( + this, tr("Network ID Error"), + tr("Network ID String contains non-word " + "characters.\n" + "It must be a simple string, " + "without spaces, containing\n" + "only characters in this range: " + "[A-Za-z0-9_-]\n")); + idstrEdit->setFocus(); + return; + } + } + + if (wpagui == NULL) + return; + + memset(reply, 0, sizeof(reply)); + reply_len = sizeof(reply) - 1; + + if (new_network) { + wpagui->ctrlRequest("ADD_NETWORK", reply, &reply_len); + if (reply[0] == 'F') { + QMessageBox::warning(this, "wpa_gui", + tr("Failed to add " + "network to wpa_supplicant\n" + "configuration.")); + return; + } + id = atoi(reply); + } else + id = edit_network_id; + + setNetworkParam(id, "ssid", ssidEdit->text().toLocal8Bit().constData(), + true); + + const char *key_mgmt = NULL, *proto = NULL, *pairwise = NULL; + switch (auth) { + case AUTH_NONE_OPEN: + case AUTH_NONE_WEP: + case AUTH_NONE_WEP_SHARED: + key_mgmt = "NONE"; + break; + case AUTH_IEEE8021X: + key_mgmt = "IEEE8021X"; + break; + case AUTH_WPA_PSK: + key_mgmt = "WPA-PSK"; + proto = "WPA"; + break; + case AUTH_WPA_EAP: + key_mgmt = "WPA-EAP"; + proto = "WPA"; + break; + case AUTH_WPA2_PSK: + key_mgmt = "WPA-PSK"; + proto = "WPA2"; + break; + case AUTH_WPA2_EAP: + key_mgmt = "WPA-EAP"; + proto = "WPA2"; + break; + } + + if (auth == AUTH_NONE_WEP_SHARED) + setNetworkParam(id, "auth_alg", "SHARED", false); + else + setNetworkParam(id, "auth_alg", "OPEN", false); + + if (auth == AUTH_WPA_PSK || auth == AUTH_WPA_EAP || + auth == AUTH_WPA2_PSK || auth == AUTH_WPA2_EAP) { + int encr = encrSelect->currentIndex(); + if (encr == 0) + pairwise = "TKIP"; + else + pairwise = "CCMP"; + } + + if (proto) + setNetworkParam(id, "proto", proto, false); + if (key_mgmt) + setNetworkParam(id, "key_mgmt", key_mgmt, false); + if (pairwise) { + setNetworkParam(id, "pairwise", pairwise, false); + setNetworkParam(id, "group", "TKIP CCMP WEP104 WEP40", false); + } + if (pskEdit->isEnabled() && + strcmp(pskEdit->text().toLocal8Bit().constData(), + WPA_GUI_KEY_DATA) != 0) + setNetworkParam(id, "psk", + pskEdit->text().toLocal8Bit().constData(), + psklen != 64); + if (eapSelect->isEnabled()) { + const char *eap = + eapSelect->currentText().toLocal8Bit().constData(); + setNetworkParam(id, "eap", eap, false); + if (strcmp(eap, "SIM") == 0 || strcmp(eap, "AKA") == 0) + setNetworkParam(id, "pcsc", "", true); + else + setNetworkParam(id, "pcsc", "NULL", false); + } + if (phase2Select->isEnabled()) { + QString eap = eapSelect->currentText(); + QString inner = phase2Select->currentText(); + char phase2[32]; + phase2[0] = '\0'; + if (eap.compare("PEAP") == 0) { + if (inner.startsWith("EAP-")) + snprintf(phase2, sizeof(phase2), "auth=%s", + inner.right(inner.size() - 4). + toLocal8Bit().constData()); + } else if (eap.compare("TTLS") == 0) { + if (inner.startsWith("EAP-")) + snprintf(phase2, sizeof(phase2), "autheap=%s", + inner.right(inner.size() - 4). + toLocal8Bit().constData()); + else + snprintf(phase2, sizeof(phase2), "auth=%s", + inner.toLocal8Bit().constData()); + } else if (eap.compare("FAST") == 0) { + const char *provisioning = NULL; + if (inner.startsWith("EAP-")) { + snprintf(phase2, sizeof(phase2), "auth=%s", + inner.right(inner.size() - 4). + toLocal8Bit().constData()); + provisioning = "fast_provisioning=2"; + } else if (inner.compare("GTC(auth) + MSCHAPv2(prov)") + == 0) { + snprintf(phase2, sizeof(phase2), + "auth=GTC auth=MSCHAPV2"); + provisioning = "fast_provisioning=1"; + } else + provisioning = "fast_provisioning=3"; + if (provisioning) { + char blob[32]; + setNetworkParam(id, "phase1", provisioning, + true); + snprintf(blob, sizeof(blob), + "blob://fast-pac-%d", id); + setNetworkParam(id, "pac_file", blob, true); + } + } + if (phase2[0]) + setNetworkParam(id, "phase2", phase2, true); + else + setNetworkParam(id, "phase2", "NULL", false); + } else + setNetworkParam(id, "phase2", "NULL", false); + if (identityEdit->isEnabled() && identityEdit->text().length() > 0) + setNetworkParam(id, "identity", + identityEdit->text().toLocal8Bit().constData(), + true); + else + setNetworkParam(id, "identity", "NULL", false); + if (passwordEdit->isEnabled() && passwordEdit->text().length() > 0 && + strcmp(passwordEdit->text().toLocal8Bit().constData(), + WPA_GUI_KEY_DATA) != 0) + setNetworkParam(id, "password", + passwordEdit->text().toLocal8Bit().constData(), + true); + else if (passwordEdit->text().length() == 0) + setNetworkParam(id, "password", "NULL", false); + if (cacertEdit->isEnabled() && cacertEdit->text().length() > 0) + setNetworkParam(id, "ca_cert", + cacertEdit->text().toLocal8Bit().constData(), + true); + else + setNetworkParam(id, "ca_cert", "NULL", false); + writeWepKey(id, wep0Edit, 0); + writeWepKey(id, wep1Edit, 1); + writeWepKey(id, wep2Edit, 2); + writeWepKey(id, wep3Edit, 3); + + if (wep0Radio->isEnabled() && wep0Radio->isChecked()) + setNetworkParam(id, "wep_tx_keyidx", "0", false); + else if (wep1Radio->isEnabled() && wep1Radio->isChecked()) + setNetworkParam(id, "wep_tx_keyidx", "1", false); + else if (wep2Radio->isEnabled() && wep2Radio->isChecked()) + setNetworkParam(id, "wep_tx_keyidx", "2", false); + else if (wep3Radio->isEnabled() && wep3Radio->isChecked()) + setNetworkParam(id, "wep_tx_keyidx", "3", false); + + if (idstrEdit->isEnabled() && idstrEdit->text().length() > 0) + setNetworkParam(id, "id_str", + idstrEdit->text().toLocal8Bit().constData(), + true); + else + setNetworkParam(id, "id_str", "NULL", false); + + if (prioritySpinBox->isEnabled()) { + QString prio; + prio = prio.setNum(prioritySpinBox->value()); + setNetworkParam(id, "priority", prio.toLocal8Bit().constData(), + false); + } + + snprintf(cmd, sizeof(cmd), "ENABLE_NETWORK %d", id); + reply_len = sizeof(reply); + wpagui->ctrlRequest(cmd, reply, &reply_len); + if (strncmp(reply, "OK", 2) != 0) { + QMessageBox::warning(this, "wpa_gui", + tr("Failed to enable " + "network in wpa_supplicant\n" + "configuration.")); + /* Network was added, so continue anyway */ + } + wpagui->triggerUpdate(); + wpagui->ctrlRequest("SAVE_CONFIG", reply, &reply_len); + + close(); +} + + +void NetworkConfig::setWpaGui(WpaGui *_wpagui) +{ + wpagui = _wpagui; +} + + +int NetworkConfig::setNetworkParam(int id, const char *field, + const char *value, bool quote) +{ + char reply[10], cmd[256]; + size_t reply_len; + snprintf(cmd, sizeof(cmd), "SET_NETWORK %d %s %s%s%s", + id, field, quote ? "\"" : "", value, quote ? "\"" : ""); + reply_len = sizeof(reply); + wpagui->ctrlRequest(cmd, reply, &reply_len); + return strncmp(reply, "OK", 2) == 0 ? 0 : -1; +} + + +void NetworkConfig::encrChanged(const QString &) +{ +} + + +void NetworkConfig::wepEnabled(bool enabled) +{ + wep0Edit->setEnabled(enabled); + wep1Edit->setEnabled(enabled); + wep2Edit->setEnabled(enabled); + wep3Edit->setEnabled(enabled); + wep0Radio->setEnabled(enabled); + wep1Radio->setEnabled(enabled); + wep2Radio->setEnabled(enabled); + wep3Radio->setEnabled(enabled); +} + + +void NetworkConfig::writeWepKey(int network_id, QLineEdit *edit, int id) +{ + char buf[10]; + bool hex; + const char *txt, *pos; + size_t len; + + if (!edit->isEnabled() || edit->text().isEmpty()) + return; + + /* + * Assume hex key if only hex characters are present and length matches + * with 40, 104, or 128-bit key + */ + txt = edit->text().toLocal8Bit().constData(); + if (strcmp(txt, WPA_GUI_KEY_DATA) == 0) + return; + len = strlen(txt); + if (len == 0) + return; + pos = txt; + hex = true; + while (*pos) { + if (!((*pos >= '0' && *pos <= '9') || + (*pos >= 'a' && *pos <= 'f') || + (*pos >= 'A' && *pos <= 'F'))) { + hex = false; + break; + } + pos++; + } + if (hex && len != 10 && len != 26 && len != 32) + hex = false; + snprintf(buf, sizeof(buf), "wep_key%d", id); + setNetworkParam(network_id, buf, txt, !hex); +} + + +static int key_value_isset(const char *reply, size_t reply_len) +{ + return reply_len > 0 && (reply_len < 4 || memcmp(reply, "FAIL", 4) != 0); +} + + +void NetworkConfig::paramsFromConfig(int network_id) +{ + int i, res; + + edit_network_id = network_id; + getEapCapa(); + + char reply[1024], cmd[256], *pos; + size_t reply_len; + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d ssid", network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && + reply_len >= 2 && reply[0] == '"') { + reply[reply_len] = '\0'; + pos = strchr(reply + 1, '"'); + if (pos) + *pos = '\0'; + ssidEdit->setText(reply + 1); + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d proto", network_id); + reply_len = sizeof(reply) - 1; + int wpa = 0; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0) { + reply[reply_len] = '\0'; + if (strstr(reply, "RSN") || strstr(reply, "WPA2")) + wpa = 2; + else if (strstr(reply, "WPA")) + wpa = 1; + } + + int auth = AUTH_NONE_OPEN, encr = 0; + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d key_mgmt", network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0) { + reply[reply_len] = '\0'; + if (strstr(reply, "WPA-EAP")) + auth = wpa & 2 ? AUTH_WPA2_EAP : AUTH_WPA_EAP; + else if (strstr(reply, "WPA-PSK")) + auth = wpa & 2 ? AUTH_WPA2_PSK : AUTH_WPA_PSK; + else if (strstr(reply, "IEEE8021X")) { + auth = AUTH_IEEE8021X; + encr = 1; + } + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d pairwise", network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0) { + reply[reply_len] = '\0'; + if (strstr(reply, "CCMP") && auth != AUTH_NONE_OPEN && + auth != AUTH_NONE_WEP && auth != AUTH_NONE_WEP_SHARED) + encr = 1; + else if (strstr(reply, "TKIP")) + encr = 0; + else if (strstr(reply, "WEP")) + encr = 1; + else + encr = 0; + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d psk", network_id); + reply_len = sizeof(reply) - 1; + res = wpagui->ctrlRequest(cmd, reply, &reply_len); + if (res >= 0 && reply_len >= 2 && reply[0] == '"') { + reply[reply_len] = '\0'; + pos = strchr(reply + 1, '"'); + if (pos) + *pos = '\0'; + pskEdit->setText(reply + 1); + } else if (res >= 0 && key_value_isset(reply, reply_len)) { + pskEdit->setText(WPA_GUI_KEY_DATA); + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d identity", network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && + reply_len >= 2 && reply[0] == '"') { + reply[reply_len] = '\0'; + pos = strchr(reply + 1, '"'); + if (pos) + *pos = '\0'; + identityEdit->setText(reply + 1); + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d password", network_id); + reply_len = sizeof(reply) - 1; + res = wpagui->ctrlRequest(cmd, reply, &reply_len); + if (res >= 0 && reply_len >= 2 && reply[0] == '"') { + reply[reply_len] = '\0'; + pos = strchr(reply + 1, '"'); + if (pos) + *pos = '\0'; + passwordEdit->setText(reply + 1); + } else if (res >= 0 && key_value_isset(reply, reply_len)) { + passwordEdit->setText(WPA_GUI_KEY_DATA); + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d ca_cert", network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && + reply_len >= 2 && reply[0] == '"') { + reply[reply_len] = '\0'; + pos = strchr(reply + 1, '"'); + if (pos) + *pos = '\0'; + cacertEdit->setText(reply + 1); + } + + enum { NO_INNER, PEAP_INNER, TTLS_INNER, FAST_INNER } eap = NO_INNER; + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d eap", network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && + reply_len >= 1) { + reply[reply_len] = '\0'; + for (i = 0; i < eapSelect->count(); i++) { + if (eapSelect->itemText(i).compare(reply) == 0) { + eapSelect->setCurrentIndex(i); + if (strcmp(reply, "PEAP") == 0) + eap = PEAP_INNER; + else if (strcmp(reply, "TTLS") == 0) + eap = TTLS_INNER; + else if (strcmp(reply, "FAST") == 0) + eap = FAST_INNER; + break; + } + } + } + + if (eap != NO_INNER) { + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d phase2", + network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && + reply_len >= 1) { + reply[reply_len] = '\0'; + eapChanged(eapSelect->currentIndex()); + } else + eap = NO_INNER; + } + + char *val; + val = reply + 1; + while (*(val + 1)) + val++; + if (*val == '"') + *val = '\0'; + + switch (eap) { + case PEAP_INNER: + if (strncmp(reply, "\"auth=", 6)) + break; + val = reply + 2; + memcpy(val, "EAP-", 4); + break; + case TTLS_INNER: + if (strncmp(reply, "\"autheap=", 9) == 0) { + val = reply + 5; + memcpy(val, "EAP-", 4); + } else if (strncmp(reply, "\"auth=", 6) == 0) + val = reply + 6; + break; + case FAST_INNER: + if (strncmp(reply, "\"auth=", 6)) + break; + if (strcmp(reply + 6, "GTC auth=MSCHAPV2") == 0) { + val = (char *) "GTC(auth) + MSCHAPv2(prov)"; + break; + } + val = reply + 2; + memcpy(val, "EAP-", 4); + break; + case NO_INNER: + break; + } + + for (i = 0; i < phase2Select->count(); i++) { + if (phase2Select->itemText(i).compare(val) == 0) { + phase2Select->setCurrentIndex(i); + break; + } + } + + for (i = 0; i < 4; i++) { + QLineEdit *wepEdit; + switch (i) { + default: + case 0: + wepEdit = wep0Edit; + break; + case 1: + wepEdit = wep1Edit; + break; + case 2: + wepEdit = wep2Edit; + break; + case 3: + wepEdit = wep3Edit; + break; + } + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d wep_key%d", + network_id, i); + reply_len = sizeof(reply) - 1; + res = wpagui->ctrlRequest(cmd, reply, &reply_len); + if (res >= 0 && reply_len >= 2 && reply[0] == '"') { + reply[reply_len] = '\0'; + pos = strchr(reply + 1, '"'); + if (pos) + *pos = '\0'; + if (auth == AUTH_NONE_OPEN || auth == AUTH_IEEE8021X) { + if (auth == AUTH_NONE_OPEN) + auth = AUTH_NONE_WEP; + encr = 1; + } + + wepEdit->setText(reply + 1); + } else if (res >= 0 && key_value_isset(reply, reply_len)) { + if (auth == AUTH_NONE_OPEN || auth == AUTH_IEEE8021X) { + if (auth == AUTH_NONE_OPEN) + auth = AUTH_NONE_WEP; + encr = 1; + } + wepEdit->setText(WPA_GUI_KEY_DATA); + } + } + + if (auth == AUTH_NONE_WEP) { + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d auth_alg", + network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0) { + reply[reply_len] = '\0'; + if (strcmp(reply, "SHARED") == 0) + auth = AUTH_NONE_WEP_SHARED; + } + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d wep_tx_keyidx", network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && reply_len >= 1) + { + reply[reply_len] = '\0'; + switch (atoi(reply)) { + case 0: + wep0Radio->setChecked(true); + break; + case 1: + wep1Radio->setChecked(true); + break; + case 2: + wep2Radio->setChecked(true); + break; + case 3: + wep3Radio->setChecked(true); + break; + } + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d id_str", network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && + reply_len >= 2 && reply[0] == '"') { + reply[reply_len] = '\0'; + pos = strchr(reply + 1, '"'); + if (pos) + *pos = '\0'; + idstrEdit->setText(reply + 1); + } + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d priority", network_id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && reply_len >= 1) + { + reply[reply_len] = '\0'; + prioritySpinBox->setValue(atoi(reply)); + } + + authSelect->setCurrentIndex(auth); + authChanged(auth); + encrSelect->setCurrentIndex(encr); + wepEnabled(auth == AUTH_NONE_WEP || auth == AUTH_NONE_WEP_SHARED); + + removeButton->setEnabled(true); + addButton->setText("Save"); +} + + +void NetworkConfig::removeNetwork() +{ + char reply[10], cmd[256]; + size_t reply_len; + + if (QMessageBox::information( + this, "wpa_gui", + tr("This will permanently remove the network\n" + "from the configuration. Do you really want\n" + "to remove this network?"), + tr("Yes"), tr("No")) != 0) + return; + + snprintf(cmd, sizeof(cmd), "REMOVE_NETWORK %d", edit_network_id); + reply_len = sizeof(reply); + wpagui->ctrlRequest(cmd, reply, &reply_len); + if (strncmp(reply, "OK", 2) != 0) { + QMessageBox::warning(this, "wpa_gui", + tr("Failed to remove network from " + "wpa_supplicant\n" + "configuration.")); + } else { + wpagui->triggerUpdate(); + wpagui->ctrlRequest("SAVE_CONFIG", reply, &reply_len); + } + + close(); +} + + +void NetworkConfig::newNetwork() +{ + new_network = true; + getEapCapa(); +} + + +void NetworkConfig::getEapCapa() +{ + char reply[256]; + size_t reply_len; + + if (wpagui == NULL) + return; + + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest("GET_CAPABILITY eap", reply, &reply_len) < 0) + return; + reply[reply_len] = '\0'; + + QString res(reply); + QStringList types = res.split(QChar(' ')); + eapSelect->insertItems(-1, types); +} + + +void NetworkConfig::useWps() +{ + if (wpagui == NULL) + return; + wpagui->setBssFromScan(bssid); + wpagui->wpsDialog(); + close(); +} diff --git a/wpa_supplicant/wpa_gui-qt4/networkconfig.h b/wpa_supplicant/wpa_gui-qt4/networkconfig.h new file mode 100644 index 0000000000000..fd09dec54318f --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/networkconfig.h @@ -0,0 +1,55 @@ +/* + * wpa_gui - NetworkConfig class + * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef NETWORKCONFIG_H +#define NETWORKCONFIG_H + +#include <QObject> +#include "ui_networkconfig.h" + +class WpaGui; + +class NetworkConfig : public QDialog, public Ui::NetworkConfig +{ + Q_OBJECT + +public: + NetworkConfig(QWidget *parent = 0, const char *name = 0, + bool modal = false, Qt::WindowFlags fl = 0); + ~NetworkConfig(); + + virtual void paramsFromScanResults(QTreeWidgetItem *sel); + virtual void setWpaGui(WpaGui *_wpagui); + virtual int setNetworkParam(int id, const char *field, + const char *value, bool quote); + virtual void paramsFromConfig(int network_id); + virtual void newNetwork(); + +public slots: + virtual void authChanged(int sel); + virtual void addNetwork(); + virtual void encrChanged(const QString &sel); + virtual void writeWepKey(int network_id, QLineEdit *edit, int id); + virtual void removeNetwork(); + virtual void eapChanged(int sel); + virtual void useWps(); + +protected slots: + virtual void languageChange(); + +private: + WpaGui *wpagui; + int edit_network_id; + bool new_network; + QString bssid; + + virtual void wepEnabled(bool enabled); + virtual void getEapCapa(); +}; + +#endif /* NETWORKCONFIG_H */ diff --git a/wpa_supplicant/wpa_gui-qt4/networkconfig.ui b/wpa_supplicant/wpa_gui-qt4/networkconfig.ui new file mode 100644 index 0000000000000..217a8ff587046 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/networkconfig.ui @@ -0,0 +1,435 @@ +<ui version="4.0" > + <class>NetworkConfig</class> + <widget class="QDialog" name="NetworkConfig" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>410</width> + <height>534</height> + </rect> + </property> + <property name="windowTitle" > + <string>NetworkConfig</string> + </property> + <layout class="QGridLayout" > + <item row="1" column="3" > + <widget class="QPushButton" name="cancelButton" > + <property name="text" > + <string>Cancel</string> + </property> + </widget> + </item> + <item row="0" column="0" colspan="4" > + <widget class="QFrame" name="frame9" > + <property name="frameShape" > + <enum>QFrame::NoFrame</enum> + </property> + <property name="frameShadow" > + <enum>QFrame::Plain</enum> + </property> + <layout class="QGridLayout" > + <item row="0" column="0" > + <widget class="QLabel" name="ssidLabel" > + <property name="text" > + <string>SSID</string> + </property> + </widget> + </item> + <item row="0" column="1" > + <widget class="QLineEdit" name="ssidEdit" > + <property name="toolTip" > + <string>Network name (Service Set IDentifier)</string> + </property> + <property name="text" > + <string/> + </property> + </widget> + </item> + <item row="1" column="0" > + <widget class="QLabel" name="authLabel" > + <property name="text" > + <string>Authentication</string> + </property> + </widget> + </item> + <item row="1" column="1" > + <widget class="QComboBox" name="authSelect" > + <item> + <property name="text" > + <string>Plaintext (open / no authentication)</string> + </property> + </item> + <item> + <property name="text" > + <string>Static WEP (no authentication)</string> + </property> + </item> + <item> + <property name="text" > + <string>Static WEP (Shared Key authentication)</string> + </property> + </item> + <item> + <property name="text" > + <string>IEEE 802.1X</string> + </property> + </item> + <item> + <property name="text" > + <string>WPA-Personal (PSK)</string> + </property> + </item> + <item> + <property name="text" > + <string>WPA-Enterprise (EAP)</string> + </property> + </item> + <item> + <property name="text" > + <string>WPA2-Personal (PSK)</string> + </property> + </item> + <item> + <property name="text" > + <string>WPA2-Enterprise (EAP)</string> + </property> + </item> + </widget> + </item> + <item row="2" column="0" > + <widget class="QLabel" name="encrLabel" > + <property name="text" > + <string>Encryption</string> + </property> + </widget> + </item> + <item row="2" column="1" > + <widget class="QComboBox" name="encrSelect" > + <item> + <property name="text" > + <string>None</string> + </property> + </item> + <item> + <property name="text" > + <string>WEP</string> + </property> + </item> + <item> + <property name="text" > + <string>TKIP</string> + </property> + </item> + <item> + <property name="text" > + <string>CCMP</string> + </property> + </item> + </widget> + </item> + <item row="3" column="0" > + <widget class="QLabel" name="pskLabel" > + <property name="text" > + <string>PSK</string> + </property> + </widget> + </item> + <item row="3" column="1" > + <widget class="QLineEdit" name="pskEdit" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="toolTip" > + <string>WPA/WPA2 pre-shared key or passphrase</string> + </property> + <property name="whatsThis" > + <string/> + </property> + <property name="echoMode" > + <enum>QLineEdit::Password</enum> + </property> + </widget> + </item> + <item row="4" column="0" > + <widget class="QLabel" name="eapLabel" > + <property name="text" > + <string>EAP method</string> + </property> + </widget> + </item> + <item row="4" column="1" > + <widget class="QComboBox" name="eapSelect" > + <property name="enabled" > + <bool>false</bool> + </property> + </widget> + </item> + <item row="5" column="0" > + <widget class="QLabel" name="identityLabel" > + <property name="text" > + <string>Identity</string> + </property> + </widget> + </item> + <item row="5" column="1" > + <widget class="QLineEdit" name="identityEdit" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="toolTip" > + <string>Username/Identity for EAP methods</string> + </property> + </widget> + </item> + <item row="6" column="0" > + <widget class="QLabel" name="passwordLabel" > + <property name="text" > + <string>Password</string> + </property> + </widget> + </item> + <item row="6" column="1" > + <widget class="QLineEdit" name="passwordEdit" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="toolTip" > + <string>Password for EAP methods</string> + </property> + <property name="echoMode" > + <enum>QLineEdit::Password</enum> + </property> + </widget> + </item> + <item row="7" column="0" > + <widget class="QLabel" name="cacertLabel" > + <property name="text" > + <string>CA certificate</string> + </property> + </widget> + </item> + <item row="7" column="1" > + <widget class="QLineEdit" name="cacertEdit" > + <property name="enabled" > + <bool>false</bool> + </property> + </widget> + </item> + <item row="8" column="0" colspan="2" > + <widget class="QGroupBox" name="wepBox" > + <property name="enabled" > + <bool>true</bool> + </property> + <property name="title" > + <string>WEP keys</string> + </property> + <layout class="QGridLayout" > + <item row="0" column="0" > + <widget class="QRadioButton" name="wep0Radio" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="text" > + <string>key 0</string> + </property> + </widget> + </item> + <item row="1" column="0" > + <widget class="QRadioButton" name="wep1Radio" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="text" > + <string>key 1</string> + </property> + </widget> + </item> + <item row="3" column="0" > + <widget class="QRadioButton" name="wep3Radio" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="text" > + <string>key 3</string> + </property> + </widget> + </item> + <item row="2" column="0" > + <widget class="QRadioButton" name="wep2Radio" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="text" > + <string>key 2</string> + </property> + </widget> + </item> + <item row="0" column="1" > + <widget class="QLineEdit" name="wep0Edit" > + <property name="enabled" > + <bool>false</bool> + </property> + </widget> + </item> + <item row="1" column="1" > + <widget class="QLineEdit" name="wep1Edit" > + <property name="enabled" > + <bool>false</bool> + </property> + </widget> + </item> + <item row="2" column="1" > + <widget class="QLineEdit" name="wep2Edit" > + <property name="enabled" > + <bool>false</bool> + </property> + </widget> + </item> + <item row="3" column="1" > + <widget class="QLineEdit" name="wep3Edit" > + <property name="enabled" > + <bool>false</bool> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item row="9" column="0" colspan="2" > + <widget class="QGroupBox" name="optionalSettingsBox" > + <property name="enabled" > + <bool>true</bool> + </property> + <property name="title" > + <string>Optional Settings</string> + </property> + <layout class="QGridLayout" > + <item row="0" column="1" > + <widget class="QLineEdit" name="idstrEdit" > + <property name="toolTip" > + <string>Network Identification String</string> + </property> + </widget> + </item> + <item row="0" column="3" > + <widget class="QSpinBox" name="prioritySpinBox" > + <property name="toolTip" > + <string>Network Priority</string> + </property> + <property name="maximum" > + <number>10000</number> + </property> + <property name="singleStep" > + <number>10</number> + </property> + </widget> + </item> + <item row="0" column="0" > + <widget class="QLabel" name="idstrLabel" > + <property name="text" > + <string>IDString</string> + </property> + </widget> + </item> + <item row="0" column="2" > + <widget class="QLabel" name="priorityLabel" > + <property name="text" > + <string>Priority</string> + </property> + </widget> + </item> + <item row="1" column="0" > + <widget class="QLabel" name="phase2Label" > + <property name="text" > + <string>Inner auth</string> + </property> + </widget> + </item> + <item row="1" column="1" > + <widget class="QComboBox" name="phase2Select" > + <property name="enabled" > + <bool>false</bool> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + <item row="1" column="2" > + <widget class="QPushButton" name="addButton" > + <property name="text" > + <string>Add</string> + </property> + </widget> + </item> + <item row="1" column="3" > + <widget class="QPushButton" name="removeButton" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="text" > + <string>Remove</string> + </property> + </widget> + </item> + <item row="1" column="0" > + <spacer> + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" > + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="1" > + <widget class="QPushButton" name="useWpsButton" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="text" > + <string>WPS</string> + </property> + </widget> + </item> + </layout> + </widget> + <layoutdefault spacing="6" margin="11" /> + <pixmapfunction></pixmapfunction> + <tabstops> + <tabstop>ssidEdit</tabstop> + <tabstop>authSelect</tabstop> + <tabstop>encrSelect</tabstop> + <tabstop>pskEdit</tabstop> + <tabstop>eapSelect</tabstop> + <tabstop>identityEdit</tabstop> + <tabstop>passwordEdit</tabstop> + <tabstop>cacertEdit</tabstop> + <tabstop>wep0Radio</tabstop> + <tabstop>wep0Edit</tabstop> + <tabstop>wep1Radio</tabstop> + <tabstop>wep1Edit</tabstop> + <tabstop>wep2Radio</tabstop> + <tabstop>wep2Edit</tabstop> + <tabstop>wep3Radio</tabstop> + <tabstop>wep3Edit</tabstop> + <tabstop>idstrEdit</tabstop> + <tabstop>prioritySpinBox</tabstop> + <tabstop>phase2Select</tabstop> + <tabstop>addButton</tabstop> + <tabstop>removeButton</tabstop> + <tabstop>cancelButton</tabstop> + </tabstops> + <includes> + <include location="global" >qtreewidget.h</include> + </includes> + <resources/> + <connections/> +</ui> diff --git a/wpa_supplicant/wpa_gui-qt4/peers.cpp b/wpa_supplicant/wpa_gui-qt4/peers.cpp new file mode 100644 index 0000000000000..3bcf2f51d37fb --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/peers.cpp @@ -0,0 +1,1883 @@ +/* + * wpa_gui - Peers class + * Copyright (c) 2009-2010, Atheros Communications + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include <cstdio> +#include <QImageReader> +#include <QMessageBox> + +#include "common/wpa_ctrl.h" +#include "wpagui.h" +#include "stringquery.h" +#include "peers.h" + + +enum { + peer_role_address = Qt::UserRole + 1, + peer_role_type, + peer_role_uuid, + peer_role_details, + peer_role_ifname, + peer_role_pri_dev_type, + peer_role_ssid, + peer_role_config_methods, + peer_role_dev_passwd_id, + peer_role_bss_id, + peer_role_selected_method, + peer_role_selected_pin, + peer_role_requested_method, + peer_role_network_id +}; + +enum selected_method { + SEL_METHOD_NONE, + SEL_METHOD_PIN_PEER_DISPLAY, + SEL_METHOD_PIN_LOCAL_DISPLAY +}; + +/* + * TODO: + * - add current AP info (e.g., from WPS) in station mode + */ + +enum peer_type { + PEER_TYPE_ASSOCIATED_STATION, + PEER_TYPE_AP, + PEER_TYPE_AP_WPS, + PEER_TYPE_WPS_PIN_NEEDED, + PEER_TYPE_P2P, + PEER_TYPE_P2P_CLIENT, + PEER_TYPE_P2P_GROUP, + PEER_TYPE_P2P_PERSISTENT_GROUP_GO, + PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT, + PEER_TYPE_P2P_INVITATION, + PEER_TYPE_WPS_ER_AP, + PEER_TYPE_WPS_ER_AP_UNCONFIGURED, + PEER_TYPE_WPS_ER_ENROLLEE, + PEER_TYPE_WPS_ENROLLEE +}; + + +Peers::Peers(QWidget *parent, const char *, bool, Qt::WindowFlags) + : QDialog(parent) +{ + setupUi(this); + + if (QImageReader::supportedImageFormats().contains(QByteArray("svg"))) + { + default_icon = new QIcon(":/icons/wpa_gui.svg"); + ap_icon = new QIcon(":/icons/ap.svg"); + laptop_icon = new QIcon(":/icons/laptop.svg"); + group_icon = new QIcon(":/icons/group.svg"); + invitation_icon = new QIcon(":/icons/invitation.svg"); + } else { + default_icon = new QIcon(":/icons/wpa_gui.png"); + ap_icon = new QIcon(":/icons/ap.png"); + laptop_icon = new QIcon(":/icons/laptop.png"); + group_icon = new QIcon(":/icons/group.png"); + invitation_icon = new QIcon(":/icons/invitation.png"); + } + + peers->setModel(&model); + peers->setResizeMode(QListView::Adjust); + peers->setDragEnabled(false); + peers->setSelectionMode(QAbstractItemView::NoSelection); + + peers->setContextMenuPolicy(Qt::CustomContextMenu); + connect(peers, SIGNAL(customContextMenuRequested(const QPoint &)), + this, SLOT(context_menu(const QPoint &))); + + wpagui = NULL; + hide_ap = false; +} + + +void Peers::setWpaGui(WpaGui *_wpagui) +{ + wpagui = _wpagui; + update_peers(); +} + + +Peers::~Peers() +{ + delete default_icon; + delete ap_icon; + delete laptop_icon; + delete group_icon; + delete invitation_icon; +} + + +void Peers::languageChange() +{ + retranslateUi(this); +} + + +QString Peers::ItemType(int type) +{ + QString title; + switch (type) { + case PEER_TYPE_ASSOCIATED_STATION: + title = tr("Associated station"); + break; + case PEER_TYPE_AP: + title = tr("AP"); + break; + case PEER_TYPE_AP_WPS: + title = tr("WPS AP"); + break; + case PEER_TYPE_WPS_PIN_NEEDED: + title = tr("WPS PIN needed"); + break; + case PEER_TYPE_P2P: + title = tr("P2P Device"); + break; + case PEER_TYPE_P2P_CLIENT: + title = tr("P2P Device (group client)"); + break; + case PEER_TYPE_P2P_GROUP: + title = tr("P2P Group"); + break; + case PEER_TYPE_P2P_PERSISTENT_GROUP_GO: + title = tr("P2P Persistent Group (GO)"); + break; + case PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT: + title = tr("P2P Persistent Group (client)"); + break; + case PEER_TYPE_P2P_INVITATION: + title = tr("P2P Invitation"); + break; + case PEER_TYPE_WPS_ER_AP: + title = tr("ER: WPS AP"); + break; + case PEER_TYPE_WPS_ER_AP_UNCONFIGURED: + title = tr("ER: WPS AP (Unconfigured)"); + break; + case PEER_TYPE_WPS_ER_ENROLLEE: + title = tr("ER: WPS Enrollee"); + break; + case PEER_TYPE_WPS_ENROLLEE: + title = tr("WPS Enrollee"); + break; + } + return title; +} + + +void Peers::context_menu(const QPoint &pos) +{ + QMenu *menu = new QMenu; + if (menu == NULL) + return; + + QModelIndex idx = peers->indexAt(pos); + if (idx.isValid()) { + ctx_item = model.itemFromIndex(idx); + int type = ctx_item->data(peer_role_type).toInt(); + menu->addAction(Peers::ItemType(type))->setEnabled(false); + menu->addSeparator(); + + int config_methods = -1; + QVariant var = ctx_item->data(peer_role_config_methods); + if (var.isValid()) + config_methods = var.toInt(); + + enum selected_method method = SEL_METHOD_NONE; + var = ctx_item->data(peer_role_selected_method); + if (var.isValid()) + method = (enum selected_method) var.toInt(); + + if ((type == PEER_TYPE_ASSOCIATED_STATION || + type == PEER_TYPE_AP_WPS || + type == PEER_TYPE_WPS_PIN_NEEDED || + type == PEER_TYPE_WPS_ER_ENROLLEE || + type == PEER_TYPE_WPS_ENROLLEE) && + (config_methods == -1 || (config_methods & 0x010c))) { + menu->addAction(tr("Enter WPS PIN"), this, + SLOT(enter_pin())); + } + + if (type == PEER_TYPE_P2P || type == PEER_TYPE_P2P_CLIENT) { + menu->addAction(tr("P2P Connect"), this, + SLOT(ctx_p2p_connect())); + if (method == SEL_METHOD_NONE && + config_methods > -1 && + config_methods & 0x0080 /* PBC */ && + config_methods != 0x0080) + menu->addAction(tr("P2P Connect (PBC)"), this, + SLOT(connect_pbc())); + if (method == SEL_METHOD_NONE) { + menu->addAction(tr("P2P Request PIN"), this, + SLOT(ctx_p2p_req_pin())); + menu->addAction(tr("P2P Show PIN"), this, + SLOT(ctx_p2p_show_pin())); + } + + if (config_methods > -1 && (config_methods & 0x0100)) { + /* Peer has Keypad */ + menu->addAction(tr("P2P Display PIN"), this, + SLOT(ctx_p2p_display_pin())); + } + + if (config_methods > -1 && (config_methods & 0x000c)) { + /* Peer has Label or Display */ + menu->addAction(tr("P2P Enter PIN"), this, + SLOT(ctx_p2p_enter_pin())); + } + } + + if (type == PEER_TYPE_P2P_GROUP) { + menu->addAction(tr("Show passphrase"), this, + SLOT(ctx_p2p_show_passphrase())); + menu->addAction(tr("Remove P2P Group"), this, + SLOT(ctx_p2p_remove_group())); + } + + if (type == PEER_TYPE_P2P_PERSISTENT_GROUP_GO || + type == PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT || + type == PEER_TYPE_P2P_INVITATION) { + menu->addAction(tr("Start group"), this, + SLOT(ctx_p2p_start_persistent())); + } + + if (type == PEER_TYPE_P2P_PERSISTENT_GROUP_GO || + type == PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT) { + menu->addAction(tr("Invite"), this, + SLOT(ctx_p2p_invite())); + } + + if (type == PEER_TYPE_P2P_INVITATION) { + menu->addAction(tr("Ignore"), this, + SLOT(ctx_p2p_delete())); + } + + if (type == PEER_TYPE_AP_WPS) { + menu->addAction(tr("Connect (PBC)"), this, + SLOT(connect_pbc())); + } + + if ((type == PEER_TYPE_ASSOCIATED_STATION || + type == PEER_TYPE_WPS_ER_ENROLLEE || + type == PEER_TYPE_WPS_ENROLLEE) && + config_methods >= 0 && (config_methods & 0x0080)) { + menu->addAction(tr("Enroll (PBC)"), this, + SLOT(connect_pbc())); + } + + if (type == PEER_TYPE_WPS_ER_AP) { + menu->addAction(tr("Learn Configuration"), this, + SLOT(learn_ap_config())); + } + + menu->addAction(tr("Properties"), this, SLOT(properties())); + } else { + ctx_item = NULL; + menu->addAction(QString(tr("Refresh")), this, + SLOT(ctx_refresh())); + menu->addAction(tr("Start P2P discovery"), this, + SLOT(ctx_p2p_start())); + menu->addAction(tr("Stop P2P discovery"), this, + SLOT(ctx_p2p_stop())); + menu->addAction(tr("P2P listen only"), this, + SLOT(ctx_p2p_listen())); + menu->addAction(tr("Start P2P group"), this, + SLOT(ctx_p2p_start_group())); + if (hide_ap) + menu->addAction(tr("Show AP entries"), this, + SLOT(ctx_show_ap())); + else + menu->addAction(tr("Hide AP entries"), this, + SLOT(ctx_hide_ap())); + } + + menu->exec(peers->mapToGlobal(pos)); +} + + +void Peers::enter_pin() +{ + if (ctx_item == NULL) + return; + + int peer_type = ctx_item->data(peer_role_type).toInt(); + QString uuid; + QString addr; + addr = ctx_item->data(peer_role_address).toString(); + if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE) + uuid = ctx_item->data(peer_role_uuid).toString(); + + StringQuery input(tr("PIN:")); + input.setWindowTitle(tr("PIN for ") + ctx_item->text()); + if (input.exec() != QDialog::Accepted) + return; + + char cmd[100]; + char reply[100]; + size_t reply_len; + + if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE) { + snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s %s", + uuid.toLocal8Bit().constData(), + input.get_string().toLocal8Bit().constData(), + addr.toLocal8Bit().constData()); + } else { + snprintf(cmd, sizeof(cmd), "WPS_PIN %s %s", + addr.toLocal8Bit().constData(), + input.get_string().toLocal8Bit().constData()); + } + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText(tr("Failed to set the WPS PIN.")); + msg.exec(); + } +} + + +void Peers::ctx_refresh() +{ + update_peers(); +} + + +void Peers::ctx_p2p_start() +{ + char reply[20]; + size_t reply_len; + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest("P2P_FIND", reply, &reply_len) < 0 || + memcmp(reply, "FAIL", 4) == 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText("Failed to start P2P discovery."); + msg.exec(); + } +} + + +void Peers::ctx_p2p_stop() +{ + char reply[20]; + size_t reply_len; + reply_len = sizeof(reply) - 1; + wpagui->ctrlRequest("P2P_STOP_FIND", reply, &reply_len); +} + + +void Peers::ctx_p2p_listen() +{ + char reply[20]; + size_t reply_len; + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest("P2P_LISTEN 3600", reply, &reply_len) < 0 || + memcmp(reply, "FAIL", 4) == 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText("Failed to start P2P listen."); + msg.exec(); + } +} + + +void Peers::ctx_p2p_start_group() +{ + char reply[20]; + size_t reply_len; + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest("P2P_GROUP_ADD", reply, &reply_len) < 0 || + memcmp(reply, "FAIL", 4) == 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText("Failed to start P2P group."); + msg.exec(); + } +} + + +void Peers::add_station(QString info) +{ + QStringList lines = info.split(QRegExp("\\n")); + QString name; + + for (QStringList::Iterator it = lines.begin(); + it != lines.end(); it++) { + int pos = (*it).indexOf('=') + 1; + if (pos < 1) + continue; + + if ((*it).startsWith("wpsDeviceName=")) + name = (*it).mid(pos); + else if ((*it).startsWith("p2p_device_name=")) + name = (*it).mid(pos); + } + + if (name.isEmpty()) + name = lines[0]; + + QStandardItem *item = new QStandardItem(*laptop_icon, name); + if (item) { + /* Remove WPS enrollee entry if one is still pending */ + if (model.rowCount() > 0) { + QModelIndexList lst = model.match(model.index(0, 0), + peer_role_address, + lines[0]); + for (int i = 0; i < lst.size(); i++) { + QStandardItem *item; + item = model.itemFromIndex(lst[i]); + if (item == NULL) + continue; + int type = item->data(peer_role_type).toInt(); + if (type == PEER_TYPE_WPS_ENROLLEE) { + model.removeRow(lst[i].row()); + break; + } + } + } + + item->setData(lines[0], peer_role_address); + item->setData(PEER_TYPE_ASSOCIATED_STATION, + peer_role_type); + item->setData(info, peer_role_details); + item->setToolTip(ItemType(PEER_TYPE_ASSOCIATED_STATION)); + model.appendRow(item); + } +} + + +void Peers::add_stations() +{ + char reply[2048]; + size_t reply_len; + char cmd[30]; + int res; + + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest("STA-FIRST", reply, &reply_len) < 0) + return; + + do { + reply[reply_len] = '\0'; + QString info(reply); + char *txt = reply; + while (*txt != '\0' && *txt != '\n') + txt++; + *txt++ = '\0'; + if (strncmp(reply, "FAIL", 4) == 0 || + strncmp(reply, "UNKNOWN", 7) == 0) + break; + + add_station(info); + + reply_len = sizeof(reply) - 1; + snprintf(cmd, sizeof(cmd), "STA-NEXT %s", reply); + res = wpagui->ctrlRequest(cmd, reply, &reply_len); + } while (res >= 0); +} + + +void Peers::add_single_station(const char *addr) +{ + char reply[2048]; + size_t reply_len; + char cmd[30]; + + reply_len = sizeof(reply) - 1; + snprintf(cmd, sizeof(cmd), "STA %s", addr); + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) + return; + + reply[reply_len] = '\0'; + QString info(reply); + char *txt = reply; + while (*txt != '\0' && *txt != '\n') + txt++; + *txt++ = '\0'; + if (strncmp(reply, "FAIL", 4) == 0 || + strncmp(reply, "UNKNOWN", 7) == 0) + return; + + add_station(info); +} + + +void Peers::add_p2p_group_client(QStandardItem * /*parent*/, QString params) +{ + /* + * dev=02:b5:64:63:30:63 iface=02:b5:64:63:30:63 dev_capab=0x0 + * dev_type=1-0050f204-1 dev_name='Wireless Client' + * config_methods=0x8c + */ + + QStringList items = + params.split(QRegExp(" (?=[^']*('[^']*'[^']*)*$)")); + QString addr = ""; + QString name = ""; + int config_methods = 0; + QString dev_type; + + for (int i = 0; i < items.size(); i++) { + QString str = items.at(i); + int pos = str.indexOf('=') + 1; + if (str.startsWith("dev_name='")) + name = str.section('\'', 1, -2); + else if (str.startsWith("config_methods=")) + config_methods = + str.section('=', 1).toInt(0, 0); + else if (str.startsWith("dev=")) + addr = str.mid(pos); + else if (str.startsWith("dev_type=") && dev_type.isEmpty()) + dev_type = str.mid(pos); + } + + QStandardItem *item = find_addr(addr); + if (item) + return; + + item = new QStandardItem(*default_icon, name); + if (item) { + /* TODO: indicate somehow the relationship to the group owner + * (parent) */ + item->setData(addr, peer_role_address); + item->setData(config_methods, peer_role_config_methods); + item->setData(PEER_TYPE_P2P_CLIENT, peer_role_type); + if (!dev_type.isEmpty()) + item->setData(dev_type, peer_role_pri_dev_type); + item->setData(items.join(QString("\n")), peer_role_details); + item->setToolTip(ItemType(PEER_TYPE_P2P_CLIENT)); + model.appendRow(item); + } +} + + +void Peers::remove_bss(int id) +{ + if (model.rowCount() == 0) + return; + + QModelIndexList lst = model.match(model.index(0, 0), peer_role_bss_id, + id); + if (lst.size() == 0) + return; + model.removeRow(lst[0].row()); +} + + +bool Peers::add_bss(const char *cmd) +{ + char reply[2048]; + size_t reply_len; + + if (hide_ap) + return false; + + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) + return false; + reply[reply_len] = '\0'; + + QString bss(reply); + if (bss.isEmpty() || bss.startsWith("FAIL")) + return false; + + QString ssid, bssid, flags, wps_name, pri_dev_type; + int id = -1; + + QStringList lines = bss.split(QRegExp("\\n")); + for (QStringList::Iterator it = lines.begin(); + it != lines.end(); it++) { + int pos = (*it).indexOf('=') + 1; + if (pos < 1) + continue; + + if ((*it).startsWith("bssid=")) + bssid = (*it).mid(pos); + else if ((*it).startsWith("id=")) + id = (*it).mid(pos).toInt(); + else if ((*it).startsWith("flags=")) + flags = (*it).mid(pos); + else if ((*it).startsWith("ssid=")) + ssid = (*it).mid(pos); + else if ((*it).startsWith("wps_device_name=")) + wps_name = (*it).mid(pos); + else if ((*it).startsWith("wps_primary_device_type=")) + pri_dev_type = (*it).mid(pos); + } + + QString name = wps_name; + if (name.isEmpty()) + name = ssid + "\n" + bssid; + + QStandardItem *item = new QStandardItem(*ap_icon, name); + if (item) { + item->setData(bssid, peer_role_address); + if (id >= 0) + item->setData(id, peer_role_bss_id); + int type; + if (flags.contains("[WPS")) + type = PEER_TYPE_AP_WPS; + else + type = PEER_TYPE_AP; + item->setData(type, peer_role_type); + + for (int i = 0; i < lines.size(); i++) { + if (lines[i].length() > 60) { + lines[i].remove(60, lines[i].length()); + lines[i] += ".."; + } + } + item->setToolTip(ItemType(type)); + item->setData(lines.join("\n"), peer_role_details); + if (!pri_dev_type.isEmpty()) + item->setData(pri_dev_type, + peer_role_pri_dev_type); + if (!ssid.isEmpty()) + item->setData(ssid, peer_role_ssid); + model.appendRow(item); + + lines = bss.split(QRegExp("\\n")); + for (QStringList::Iterator it = lines.begin(); + it != lines.end(); it++) { + if ((*it).startsWith("p2p_group_client:")) + add_p2p_group_client(item, + (*it).mid(18)); + } + } + + return true; +} + + +void Peers::add_scan_results() +{ + int index; + char cmd[20]; + + index = 0; + while (wpagui) { + snprintf(cmd, sizeof(cmd), "BSS %d", index++); + if (index > 1000) + break; + + if (!add_bss(cmd)) + break; + } +} + + +void Peers::add_persistent(int id, const char *ssid, const char *bssid) +{ + char cmd[100]; + char reply[100]; + size_t reply_len; + int mode; + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d mode", id); + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) + return; + reply[reply_len] = '\0'; + mode = atoi(reply); + + QString name = ssid; + name = '[' + name + ']'; + + QStandardItem *item = new QStandardItem(*group_icon, name); + if (!item) + return; + + int type; + if (mode == 3) + type = PEER_TYPE_P2P_PERSISTENT_GROUP_GO; + else + type = PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT; + item->setData(type, peer_role_type); + item->setToolTip(ItemType(type)); + item->setData(ssid, peer_role_ssid); + if (bssid && strcmp(bssid, "any") == 0) + bssid = NULL; + if (bssid) + item->setData(bssid, peer_role_address); + item->setData(id, peer_role_network_id); + item->setBackground(Qt::BDiagPattern); + + model.appendRow(item); +} + + +void Peers::add_persistent_groups() +{ + char buf[2048], *start, *end, *id, *ssid, *bssid, *flags; + size_t len; + + len = sizeof(buf) - 1; + if (wpagui->ctrlRequest("LIST_NETWORKS", buf, &len) < 0) + return; + + buf[len] = '\0'; + start = strchr(buf, '\n'); + if (start == NULL) + return; + start++; + + while (*start) { + bool last = false; + end = strchr(start, '\n'); + if (end == NULL) { + last = true; + end = start; + while (end[0] && end[1]) + end++; + } + *end = '\0'; + + id = start; + ssid = strchr(id, '\t'); + if (ssid == NULL) + break; + *ssid++ = '\0'; + bssid = strchr(ssid, '\t'); + if (bssid == NULL) + break; + *bssid++ = '\0'; + flags = strchr(bssid, '\t'); + if (flags == NULL) + break; + *flags++ = '\0'; + + if (strstr(flags, "[DISABLED][P2P-PERSISTENT]")) + add_persistent(atoi(id), ssid, bssid); + + if (last) + break; + start = end + 1; + } +} + + +void Peers::update_peers() +{ + model.clear(); + if (wpagui == NULL) + return; + + char reply[20]; + size_t replylen = sizeof(reply) - 1; + wpagui->ctrlRequest("WPS_ER_START", reply, &replylen); + + add_stations(); + add_scan_results(); + add_persistent_groups(); +} + + +QStandardItem * Peers::find_addr(QString addr) +{ + if (model.rowCount() == 0) + return NULL; + + QModelIndexList lst = model.match(model.index(0, 0), peer_role_address, + addr); + if (lst.size() == 0) + return NULL; + return model.itemFromIndex(lst[0]); +} + + +QStandardItem * Peers::find_addr_type(QString addr, int type) +{ + if (model.rowCount() == 0) + return NULL; + + QModelIndexList lst = model.match(model.index(0, 0), peer_role_address, + addr); + for (int i = 0; i < lst.size(); i++) { + QStandardItem *item = model.itemFromIndex(lst[i]); + if (item->data(peer_role_type).toInt() == type) + return item; + } + return NULL; +} + + +QStandardItem * Peers::find_uuid(QString uuid) +{ + if (model.rowCount() == 0) + return NULL; + + QModelIndexList lst = model.match(model.index(0, 0), peer_role_uuid, + uuid); + if (lst.size() == 0) + return NULL; + return model.itemFromIndex(lst[0]); +} + + +void Peers::event_notify(WpaMsg msg) +{ + QString text = msg.getMsg(); + + if (text.startsWith(WPS_EVENT_PIN_NEEDED)) { + /* + * WPS-PIN-NEEDED 5a02a5fa-9199-5e7c-bc46-e183d3cb32f7 + * 02:2a:c4:18:5b:f3 + * [Wireless Client|Company|cmodel|123|12345|1-0050F204-1] + */ + QStringList items = text.split(' '); + QString uuid = items[1]; + QString addr = items[2]; + QString name = ""; + + QStandardItem *item = find_addr(addr); + if (item) + return; + + int pos = text.indexOf('['); + if (pos >= 0) { + int pos2 = text.lastIndexOf(']'); + if (pos2 >= pos) { + items = text.mid(pos + 1, pos2 - pos - 1). + split('|'); + name = items[0]; + items.append(addr); + } + } + + item = new QStandardItem(*laptop_icon, name); + if (item) { + item->setData(addr, peer_role_address); + item->setData(PEER_TYPE_WPS_PIN_NEEDED, + peer_role_type); + item->setToolTip(ItemType(PEER_TYPE_WPS_PIN_NEEDED)); + item->setData(items.join("\n"), peer_role_details); + item->setData(items[5], peer_role_pri_dev_type); + model.appendRow(item); + } + return; + } + + if (text.startsWith(AP_STA_CONNECTED)) { + /* AP-STA-CONNECTED 02:2a:c4:18:5b:f3 */ + QStringList items = text.split(' '); + QString addr = items[1]; + QStandardItem *item = find_addr(addr); + if (item == NULL || item->data(peer_role_type).toInt() != + PEER_TYPE_ASSOCIATED_STATION) + add_single_station(addr.toLocal8Bit().constData()); + return; + } + + if (text.startsWith(AP_STA_DISCONNECTED)) { + /* AP-STA-DISCONNECTED 02:2a:c4:18:5b:f3 */ + QStringList items = text.split(' '); + QString addr = items[1]; + + if (model.rowCount() == 0) + return; + + QModelIndexList lst = model.match(model.index(0, 0), + peer_role_address, addr, -1); + for (int i = 0; i < lst.size(); i++) { + QStandardItem *item = model.itemFromIndex(lst[i]); + if (item && item->data(peer_role_type).toInt() == + PEER_TYPE_ASSOCIATED_STATION) { + model.removeRow(lst[i].row()); + break; + } + } + return; + } + + if (text.startsWith(P2P_EVENT_DEVICE_FOUND)) { + /* + * P2P-DEVICE-FOUND 02:b5:64:63:30:63 + * p2p_dev_addr=02:b5:64:63:30:63 pri_dev_type=1-0050f204-1 + * name='Wireless Client' config_methods=0x84 dev_capab=0x21 + * group_capab=0x0 + */ + QStringList items = + text.split(QRegExp(" (?=[^']*('[^']*'[^']*)*$)")); + QString addr = items[1]; + QString name = ""; + QString pri_dev_type; + int config_methods = 0; + for (int i = 0; i < items.size(); i++) { + QString str = items.at(i); + if (str.startsWith("name='")) + name = str.section('\'', 1, -2); + else if (str.startsWith("config_methods=")) + config_methods = + str.section('=', 1).toInt(0, 0); + else if (str.startsWith("pri_dev_type=")) + pri_dev_type = str.section('=', 1); + } + + QStandardItem *item = find_addr(addr); + if (item) { + int type = item->data(peer_role_type).toInt(); + if (type == PEER_TYPE_P2P) + return; + } + + item = new QStandardItem(*default_icon, name); + if (item) { + item->setData(addr, peer_role_address); + item->setData(config_methods, + peer_role_config_methods); + item->setData(PEER_TYPE_P2P, peer_role_type); + if (!pri_dev_type.isEmpty()) + item->setData(pri_dev_type, + peer_role_pri_dev_type); + item->setData(items.join(QString("\n")), + peer_role_details); + item->setToolTip(ItemType(PEER_TYPE_P2P)); + model.appendRow(item); + } + + item = find_addr_type(addr, + PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT); + if (item) + item->setBackground(Qt::NoBrush); + } + + if (text.startsWith(P2P_EVENT_GROUP_STARTED)) { + /* P2P-GROUP-STARTED wlan0-p2p-0 GO ssid="DIRECT-3F" + * passphrase="YOyTkxID" go_dev_addr=02:40:61:c2:f3:b7 + * [PERSISTENT] */ + QStringList items = text.split(' '); + if (items.size() < 4) + return; + + int pos = text.indexOf(" ssid=\""); + if (pos < 0) + return; + QString ssid = text.mid(pos + 7); + pos = ssid.indexOf(" passphrase=\""); + if (pos < 0) + pos = ssid.indexOf(" psk="); + if (pos >= 0) + ssid.truncate(pos); + pos = ssid.lastIndexOf('"'); + if (pos >= 0) + ssid.truncate(pos); + + QStandardItem *item = new QStandardItem(*group_icon, ssid); + if (item) { + item->setData(PEER_TYPE_P2P_GROUP, peer_role_type); + item->setData(items[1], peer_role_ifname); + QString details; + if (items[2] == "GO") { + details = tr("P2P GO for interface ") + + items[1]; + } else { + details = tr("P2P client for interface ") + + items[1]; + } + if (text.contains(" [PERSISTENT]")) + details += "\nPersistent group"; + item->setData(details, peer_role_details); + item->setToolTip(ItemType(PEER_TYPE_P2P_GROUP)); + model.appendRow(item); + } + } + + if (text.startsWith(P2P_EVENT_GROUP_REMOVED)) { + /* P2P-GROUP-REMOVED wlan0-p2p-0 GO */ + QStringList items = text.split(' '); + if (items.size() < 2) + return; + + if (model.rowCount() == 0) + return; + + QModelIndexList lst = model.match(model.index(0, 0), + peer_role_ifname, items[1]); + for (int i = 0; i < lst.size(); i++) + model.removeRow(lst[i].row()); + return; + } + + if (text.startsWith(P2P_EVENT_PROV_DISC_SHOW_PIN)) { + /* P2P-PROV-DISC-SHOW-PIN 02:40:61:c2:f3:b7 12345670 */ + QStringList items = text.split(' '); + if (items.size() < 3) + return; + QString addr = items[1]; + QString pin = items[2]; + + QStandardItem *item = find_addr_type(addr, PEER_TYPE_P2P); + if (item == NULL) + return; + item->setData(SEL_METHOD_PIN_LOCAL_DISPLAY, + peer_role_selected_method); + item->setData(pin, peer_role_selected_pin); + QVariant var = item->data(peer_role_requested_method); + if (var.isValid() && + var.toInt() == SEL_METHOD_PIN_LOCAL_DISPLAY) { + ctx_item = item; + ctx_p2p_display_pin_pd(); + } + return; + } + + if (text.startsWith(P2P_EVENT_PROV_DISC_ENTER_PIN)) { + /* P2P-PROV-DISC-ENTER-PIN 02:40:61:c2:f3:b7 */ + QStringList items = text.split(' '); + if (items.size() < 2) + return; + QString addr = items[1]; + + QStandardItem *item = find_addr_type(addr, PEER_TYPE_P2P); + if (item == NULL) + return; + item->setData(SEL_METHOD_PIN_PEER_DISPLAY, + peer_role_selected_method); + QVariant var = item->data(peer_role_requested_method); + if (var.isValid() && + var.toInt() == SEL_METHOD_PIN_PEER_DISPLAY) { + ctx_item = item; + ctx_p2p_connect(); + } + return; + } + + if (text.startsWith(P2P_EVENT_INVITATION_RECEIVED)) { + /* P2P-INVITATION-RECEIVED sa=02:f0:bc:44:87:62 persistent=4 */ + QStringList items = text.split(' '); + if (items.size() < 3) + return; + if (!items[1].startsWith("sa=") || + !items[2].startsWith("persistent=")) + return; + QString addr = items[1].mid(3); + int id = items[2].mid(11).toInt(); + + char cmd[100]; + char reply[100]; + size_t reply_len; + + snprintf(cmd, sizeof(cmd), "GET_NETWORK %d ssid", id); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) + return; + reply[reply_len] = '\0'; + QString name; + char *pos = strrchr(reply, '"'); + if (pos && reply[0] == '"') { + *pos = '\0'; + name = reply + 1; + } else + name = reply; + + QStandardItem *item; + item = find_addr_type(addr, PEER_TYPE_P2P_INVITATION); + if (item) + model.removeRow(item->row()); + + item = new QStandardItem(*invitation_icon, name); + if (!item) + return; + item->setData(PEER_TYPE_P2P_INVITATION, peer_role_type); + item->setToolTip(ItemType(PEER_TYPE_P2P_INVITATION)); + item->setData(addr, peer_role_address); + item->setData(id, peer_role_network_id); + + model.appendRow(item); + + enable_persistent(id); + + return; + } + + if (text.startsWith(P2P_EVENT_INVITATION_RESULT)) { + /* P2P-INVITATION-RESULT status=1 */ + /* TODO */ + return; + } + + if (text.startsWith(WPS_EVENT_ER_AP_ADD)) { + /* + * WPS-ER-AP-ADD 87654321-9abc-def0-1234-56789abc0002 + * 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/ + */ + QStringList items = text.split(' '); + if (items.size() < 5) + return; + QString uuid = items[1]; + QString addr = items[2]; + QString pri_dev_type = items[3].mid(13); + int wps_state = items[4].mid(10).toInt(); + + int pos = text.indexOf('|'); + if (pos < 0) + return; + items = text.mid(pos + 1).split('|'); + if (items.size() < 1) + return; + + QStandardItem *item = find_uuid(uuid); + if (item) + return; + + item = new QStandardItem(*ap_icon, items[0]); + if (item) { + item->setData(uuid, peer_role_uuid); + item->setData(addr, peer_role_address); + int type = wps_state == 2 ? PEER_TYPE_WPS_ER_AP: + PEER_TYPE_WPS_ER_AP_UNCONFIGURED; + item->setData(type, peer_role_type); + item->setToolTip(ItemType(type)); + item->setData(pri_dev_type, peer_role_pri_dev_type); + item->setData(items.join(QString("\n")), + peer_role_details); + model.appendRow(item); + } + + return; + } + + if (text.startsWith(WPS_EVENT_ER_AP_REMOVE)) { + /* WPS-ER-AP-REMOVE 87654321-9abc-def0-1234-56789abc0002 */ + QStringList items = text.split(' '); + if (items.size() < 2) + return; + if (model.rowCount() == 0) + return; + + QModelIndexList lst = model.match(model.index(0, 0), + peer_role_uuid, items[1]); + for (int i = 0; i < lst.size(); i++) { + QStandardItem *item = model.itemFromIndex(lst[i]); + if (item && + (item->data(peer_role_type).toInt() == + PEER_TYPE_WPS_ER_AP || + item->data(peer_role_type).toInt() == + PEER_TYPE_WPS_ER_AP_UNCONFIGURED)) + model.removeRow(lst[i].row()); + } + return; + } + + if (text.startsWith(WPS_EVENT_ER_ENROLLEE_ADD)) { + /* + * 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| + */ + QStringList items = text.split(' '); + if (items.size() < 3) + return; + QString uuid = items[1]; + QString addr = items[2]; + QString pri_dev_type = items[6].mid(13); + int config_methods = -1; + int dev_passwd_id = -1; + + for (int i = 3; i < items.size(); i++) { + int pos = items[i].indexOf('=') + 1; + if (pos < 1) + continue; + QString val = items[i].mid(pos); + if (items[i].startsWith("config_methods=")) { + config_methods = val.toInt(0, 0); + } else if (items[i].startsWith("dev_passwd_id=")) { + dev_passwd_id = val.toInt(); + } + } + + int pos = text.indexOf('|'); + if (pos < 0) + return; + items = text.mid(pos + 1).split('|'); + if (items.size() < 1) + return; + QString name = items[0]; + if (name.length() == 0) + name = addr; + + remove_enrollee_uuid(uuid); + + QStandardItem *item; + item = new QStandardItem(*laptop_icon, name); + if (item) { + item->setData(uuid, peer_role_uuid); + item->setData(addr, peer_role_address); + item->setData(PEER_TYPE_WPS_ER_ENROLLEE, + peer_role_type); + item->setToolTip(ItemType(PEER_TYPE_WPS_ER_ENROLLEE)); + item->setData(items.join(QString("\n")), + peer_role_details); + item->setData(pri_dev_type, peer_role_pri_dev_type); + if (config_methods >= 0) + item->setData(config_methods, + peer_role_config_methods); + if (dev_passwd_id >= 0) + item->setData(dev_passwd_id, + peer_role_dev_passwd_id); + model.appendRow(item); + } + + return; + } + + if (text.startsWith(WPS_EVENT_ER_ENROLLEE_REMOVE)) { + /* + * WPS-ER-ENROLLEE-REMOVE 2b7093f1-d6fb-5108-adbb-bea66bb87333 + * 02:66:a0:ee:17:27 + */ + QStringList items = text.split(' '); + if (items.size() < 2) + return; + remove_enrollee_uuid(items[1]); + return; + } + + if (text.startsWith(WPS_EVENT_ENROLLEE_SEEN)) { + /* TODO: need to time out this somehow or remove on successful + * WPS run, etc. */ + /* + * WPS-ENROLLEE-SEEN 02:00:00:00:01:00 + * 572cf82f-c957-5653-9b16-b5cfb298abf1 1-0050F204-1 0x80 4 1 + * [Wireless Client] + * (MAC addr, UUID-E, pri dev type, config methods, + * dev passwd id, request type, [dev name]) + */ + QStringList items = text.split(' '); + if (items.size() < 7) + return; + QString addr = items[1]; + QString uuid = items[2]; + QString pri_dev_type = items[3]; + int config_methods = items[4].toInt(0, 0); + int dev_passwd_id = items[5].toInt(); + QString name; + + QStandardItem *item = find_addr(addr); + if (item) { + int type = item->data(peer_role_type).toInt(); + if (type == PEER_TYPE_ASSOCIATED_STATION) + return; /* already associated */ + } + + int pos = text.indexOf('['); + if (pos >= 0) { + int pos2 = text.lastIndexOf(']'); + if (pos2 >= pos) { + QStringList items2 = + text.mid(pos + 1, pos2 - pos - 1). + split('|'); + name = items2[0]; + } + } + if (name.isEmpty()) + name = addr; + + item = find_uuid(uuid); + if (item) { + QVariant var = item->data(peer_role_config_methods); + QVariant var2 = item->data(peer_role_dev_passwd_id); + if ((var.isValid() && config_methods != var.toInt()) || + (var2.isValid() && dev_passwd_id != var2.toInt())) + remove_enrollee_uuid(uuid); + else + return; + } + + item = new QStandardItem(*laptop_icon, name); + if (item) { + item->setData(uuid, peer_role_uuid); + item->setData(addr, peer_role_address); + item->setData(PEER_TYPE_WPS_ENROLLEE, + peer_role_type); + item->setToolTip(ItemType(PEER_TYPE_WPS_ENROLLEE)); + item->setData(items.join(QString("\n")), + peer_role_details); + item->setData(pri_dev_type, peer_role_pri_dev_type); + item->setData(config_methods, + peer_role_config_methods); + item->setData(dev_passwd_id, peer_role_dev_passwd_id); + model.appendRow(item); + } + + return; + } + + if (text.startsWith(WPA_EVENT_BSS_ADDED)) { + /* CTRL-EVENT-BSS-ADDED 34 00:11:22:33:44:55 */ + QStringList items = text.split(' '); + if (items.size() < 2) + return; + char cmd[20]; + snprintf(cmd, sizeof(cmd), "BSS ID-%d", items[1].toInt()); + add_bss(cmd); + return; + } + + if (text.startsWith(WPA_EVENT_BSS_REMOVED)) { + /* CTRL-EVENT-BSS-REMOVED 34 00:11:22:33:44:55 */ + QStringList items = text.split(' '); + if (items.size() < 2) + return; + remove_bss(items[1].toInt()); + return; + } +} + + +void Peers::ctx_p2p_connect() +{ + if (ctx_item == NULL) + return; + QString addr = ctx_item->data(peer_role_address).toString(); + QString arg; + int config_methods = + ctx_item->data(peer_role_config_methods).toInt(); + enum selected_method method = SEL_METHOD_NONE; + QVariant var = ctx_item->data(peer_role_selected_method); + if (var.isValid()) + method = (enum selected_method) var.toInt(); + if (method == SEL_METHOD_PIN_LOCAL_DISPLAY) { + arg = ctx_item->data(peer_role_selected_pin).toString(); + char cmd[100]; + char reply[100]; + size_t reply_len; + snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s display", + addr.toLocal8Bit().constData(), + arg.toLocal8Bit().constData()); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText("Failed to initiate P2P connect."); + msg.exec(); + return; + } + QMessageBox::information(this, + tr("PIN for ") + ctx_item->text(), + tr("Enter the following PIN on the\n" + "peer device: ") + arg); + } else if (method == SEL_METHOD_PIN_PEER_DISPLAY) { + StringQuery input(tr("PIN from peer display:")); + input.setWindowTitle(tr("PIN for ") + ctx_item->text()); + if (input.exec() != QDialog::Accepted) + return; + arg = input.get_string(); + } else if (config_methods == 0x0080 /* PBC */) { + arg = "pbc"; + } else { + StringQuery input(tr("PIN:")); + input.setWindowTitle(tr("PIN for ") + ctx_item->text()); + if (input.exec() != QDialog::Accepted) + return; + arg = input.get_string(); + } + + char cmd[100]; + char reply[100]; + size_t reply_len; + snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s", + addr.toLocal8Bit().constData(), + arg.toLocal8Bit().constData()); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText("Failed to initiate P2P connect."); + msg.exec(); + } +} + + +void Peers::ctx_p2p_req_pin() +{ + if (ctx_item == NULL) + return; + QString addr = ctx_item->data(peer_role_address).toString(); + ctx_item->setData(SEL_METHOD_PIN_PEER_DISPLAY, + peer_role_requested_method); + + char cmd[100]; + char reply[100]; + size_t reply_len; + snprintf(cmd, sizeof(cmd), "P2P_PROV_DISC %s display", + addr.toLocal8Bit().constData()); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText(tr("Failed to request PIN from peer.")); + msg.exec(); + } +} + + +void Peers::ctx_p2p_show_pin() +{ + if (ctx_item == NULL) + return; + QString addr = ctx_item->data(peer_role_address).toString(); + ctx_item->setData(SEL_METHOD_PIN_LOCAL_DISPLAY, + peer_role_requested_method); + + char cmd[100]; + char reply[100]; + size_t reply_len; + snprintf(cmd, sizeof(cmd), "P2P_PROV_DISC %s keypad", + addr.toLocal8Bit().constData()); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText(tr("Failed to request peer to enter PIN.")); + msg.exec(); + } +} + + +void Peers::ctx_p2p_display_pin() +{ + if (ctx_item == NULL) + return; + QString addr = ctx_item->data(peer_role_address).toString(); + + char cmd[100]; + char reply[100]; + size_t reply_len; + snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s pin", + addr.toLocal8Bit().constData()); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText("Failed to initiate P2P connect."); + msg.exec(); + return; + } + reply[reply_len] = '\0'; + QMessageBox::information(this, + tr("PIN for ") + ctx_item->text(), + tr("Enter the following PIN on the\n" + "peer device: ") + reply); +} + + +void Peers::ctx_p2p_display_pin_pd() +{ + if (ctx_item == NULL) + return; + QString addr = ctx_item->data(peer_role_address).toString(); + QString arg = ctx_item->data(peer_role_selected_pin).toString(); + + char cmd[100]; + char reply[100]; + size_t reply_len; + snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s display", + addr.toLocal8Bit().constData(), + arg.toLocal8Bit().constData()); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText("Failed to initiate P2P connect."); + msg.exec(); + return; + } + reply[reply_len] = '\0'; + QMessageBox::information(this, + tr("PIN for ") + ctx_item->text(), + tr("Enter the following PIN on the\n" + "peer device: ") + arg); +} + + +void Peers::ctx_p2p_enter_pin() +{ + if (ctx_item == NULL) + return; + QString addr = ctx_item->data(peer_role_address).toString(); + QString arg; + + StringQuery input(tr("PIN from peer:")); + input.setWindowTitle(tr("PIN for ") + ctx_item->text()); + if (input.exec() != QDialog::Accepted) + return; + arg = input.get_string(); + + char cmd[100]; + char reply[100]; + size_t reply_len; + snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s keypad", + addr.toLocal8Bit().constData(), + arg.toLocal8Bit().constData()); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText("Failed to initiate P2P connect."); + msg.exec(); + } +} + + +void Peers::ctx_p2p_remove_group() +{ + if (ctx_item == NULL) + return; + char cmd[100]; + char reply[100]; + size_t reply_len; + snprintf(cmd, sizeof(cmd), "P2P_GROUP_REMOVE %s", + ctx_item->data(peer_role_ifname).toString().toLocal8Bit(). + constData()); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText("Failed to remove P2P Group."); + msg.exec(); + } +} + + +void Peers::closeEvent(QCloseEvent *) +{ + if (wpagui) { + char reply[20]; + size_t replylen = sizeof(reply) - 1; + wpagui->ctrlRequest("WPS_ER_STOP", reply, &replylen); + } +} + + +void Peers::done(int r) +{ + QDialog::done(r); + close(); +} + + +void Peers::remove_enrollee_uuid(QString uuid) +{ + if (model.rowCount() == 0) + return; + + QModelIndexList lst = model.match(model.index(0, 0), + peer_role_uuid, uuid); + for (int i = 0; i < lst.size(); i++) { + QStandardItem *item = model.itemFromIndex(lst[i]); + if (item == NULL) + continue; + int type = item->data(peer_role_type).toInt(); + if (type == PEER_TYPE_WPS_ER_ENROLLEE || + type == PEER_TYPE_WPS_ENROLLEE) + model.removeRow(lst[i].row()); + } +} + + +void Peers::properties() +{ + if (ctx_item == NULL) + return; + + QMessageBox msg(this); + msg.setStandardButtons(QMessageBox::Ok); + msg.setDefaultButton(QMessageBox::Ok); + msg.setEscapeButton(QMessageBox::Ok); + msg.setWindowTitle(tr("Peer Properties")); + + int type = ctx_item->data(peer_role_type).toInt(); + QString title = Peers::ItemType(type); + + msg.setText(title + QString("\n") + tr("Name: ") + ctx_item->text()); + + QVariant var; + QString info; + + var = ctx_item->data(peer_role_address); + if (var.isValid()) + info += tr("Address: ") + var.toString() + QString("\n"); + + var = ctx_item->data(peer_role_uuid); + if (var.isValid()) + info += tr("UUID: ") + var.toString() + QString("\n"); + + var = ctx_item->data(peer_role_pri_dev_type); + if (var.isValid()) + info += tr("Primary Device Type: ") + var.toString() + + QString("\n"); + + var = ctx_item->data(peer_role_ssid); + if (var.isValid()) + info += tr("SSID: ") + var.toString() + QString("\n"); + + var = ctx_item->data(peer_role_config_methods); + if (var.isValid()) { + int methods = var.toInt(); + info += tr("Configuration Methods: "); + if (methods & 0x0001) + info += tr("[USBA]"); + if (methods & 0x0002) + info += tr("[Ethernet]"); + if (methods & 0x0004) + info += tr("[Label]"); + if (methods & 0x0008) + info += tr("[Display]"); + if (methods & 0x0010) + info += tr("[Ext. NFC Token]"); + if (methods & 0x0020) + info += tr("[Int. NFC Token]"); + if (methods & 0x0040) + info += tr("[NFC Interface]"); + if (methods & 0x0080) + info += tr("[Push Button]"); + if (methods & 0x0100) + info += tr("[Keypad]"); + info += "\n"; + } + + var = ctx_item->data(peer_role_selected_method); + if (var.isValid()) { + enum selected_method method = + (enum selected_method) var.toInt(); + switch (method) { + case SEL_METHOD_NONE: + break; + case SEL_METHOD_PIN_PEER_DISPLAY: + info += tr("Selected Method: PIN on peer display\n"); + break; + case SEL_METHOD_PIN_LOCAL_DISPLAY: + info += tr("Selected Method: PIN on local display\n"); + break; + } + } + + var = ctx_item->data(peer_role_selected_pin); + if (var.isValid()) { + info += tr("PIN to enter on peer: ") + var.toString() + "\n"; + } + + var = ctx_item->data(peer_role_dev_passwd_id); + if (var.isValid()) { + info += tr("Device Password ID: ") + var.toString(); + switch (var.toInt()) { + case 0: + info += tr(" (Default PIN)"); + break; + case 1: + info += tr(" (User-specified PIN)"); + break; + case 2: + info += tr(" (Machine-specified PIN)"); + break; + case 3: + info += tr(" (Rekey)"); + break; + case 4: + info += tr(" (Push Button)"); + break; + case 5: + info += tr(" (Registrar-specified)"); + break; + } + info += "\n"; + } + + msg.setInformativeText(info); + + var = ctx_item->data(peer_role_details); + if (var.isValid()) + msg.setDetailedText(var.toString()); + + msg.exec(); +} + + +void Peers::connect_pbc() +{ + if (ctx_item == NULL) + return; + + char cmd[100]; + char reply[100]; + size_t reply_len; + + int peer_type = ctx_item->data(peer_role_type).toInt(); + if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE) { + snprintf(cmd, sizeof(cmd), "WPS_ER_PBC %s", + ctx_item->data(peer_role_uuid).toString().toLocal8Bit(). + constData()); + } else if (peer_type == PEER_TYPE_P2P || + peer_type == PEER_TYPE_P2P_CLIENT) { + snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s pbc", + ctx_item->data(peer_role_address).toString(). + toLocal8Bit().constData()); + } else { + snprintf(cmd, sizeof(cmd), "WPS_PBC"); + } + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText(tr("Failed to start WPS PBC.")); + msg.exec(); + } +} + + +void Peers::learn_ap_config() +{ + if (ctx_item == NULL) + return; + + QString uuid = ctx_item->data(peer_role_uuid).toString(); + + StringQuery input(tr("AP PIN:")); + input.setWindowTitle(tr("AP PIN for ") + ctx_item->text()); + if (input.exec() != QDialog::Accepted) + return; + + char cmd[100]; + char reply[100]; + size_t reply_len; + + snprintf(cmd, sizeof(cmd), "WPS_ER_LEARN %s %s", + uuid.toLocal8Bit().constData(), + input.get_string().toLocal8Bit().constData()); + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText(tr("Failed to start learning AP configuration.")); + msg.exec(); + } +} + + +void Peers::ctx_hide_ap() +{ + hide_ap = true; + + if (model.rowCount() == 0) + return; + + do { + QModelIndexList lst; + lst = model.match(model.index(0, 0), + peer_role_type, PEER_TYPE_AP); + if (lst.size() == 0) { + lst = model.match(model.index(0, 0), + peer_role_type, PEER_TYPE_AP_WPS); + if (lst.size() == 0) + break; + } + + model.removeRow(lst[0].row()); + } while (1); +} + + +void Peers::ctx_show_ap() +{ + hide_ap = false; + add_scan_results(); +} + + +void Peers::ctx_p2p_show_passphrase() +{ + char reply[64]; + size_t reply_len; + + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest("P2P_GET_PASSPHRASE", reply, &reply_len) < 0 || + memcmp(reply, "FAIL", 4) == 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText("Failed to get P2P group passphrase."); + msg.exec(); + } else { + reply[reply_len] = '\0'; + QMessageBox::information(this, tr("Passphrase"), + tr("P2P group passphrase:\n") + + reply); + } +} + + +void Peers::ctx_p2p_start_persistent() +{ + if (ctx_item == NULL) + return; + + char cmd[100]; + char reply[100]; + size_t reply_len; + + snprintf(cmd, sizeof(cmd), "P2P_GROUP_ADD persistent=%d", + ctx_item->data(peer_role_network_id).toInt()); + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0 || + memcmp(reply, "FAIL", 4) == 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText(tr("Failed to start persistent P2P Group.")); + msg.exec(); + } else if (ctx_item->data(peer_role_type).toInt() == + PEER_TYPE_P2P_INVITATION) + model.removeRow(ctx_item->row()); +} + + +void Peers::ctx_p2p_invite() +{ + if (ctx_item == NULL) + return; + + char cmd[100]; + char reply[100]; + size_t reply_len; + + snprintf(cmd, sizeof(cmd), "P2P_INVITE persistent=%d", + ctx_item->data(peer_role_network_id).toInt()); + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0 || + memcmp(reply, "FAIL", 4) == 0) { + QMessageBox msg; + msg.setIcon(QMessageBox::Warning); + msg.setText(tr("Failed to invite peer to start persistent " + "P2P Group.")); + msg.exec(); + } +} + + +void Peers::ctx_p2p_delete() +{ + if (ctx_item == NULL) + return; + model.removeRow(ctx_item->row()); +} + + +void Peers::enable_persistent(int id) +{ + if (model.rowCount() == 0) + return; + + QModelIndexList lst = model.match(model.index(0, 0), + peer_role_network_id, id); + for (int i = 0; i < lst.size(); i++) { + QStandardItem *item = model.itemFromIndex(lst[i]); + int type = item->data(peer_role_type).toInt(); + if (type == PEER_TYPE_P2P_PERSISTENT_GROUP_GO || + type == PEER_TYPE_P2P_PERSISTENT_GROUP_CLIENT) + item->setBackground(Qt::NoBrush); + } +} diff --git a/wpa_supplicant/wpa_gui-qt4/peers.h b/wpa_supplicant/wpa_gui-qt4/peers.h new file mode 100644 index 0000000000000..bb7373749c2fc --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/peers.h @@ -0,0 +1,90 @@ +/* + * wpa_gui - Peers class + * Copyright (c) 2009-2010, Atheros Communications + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef PEERS_H +#define PEERS_H + +#include <QObject> +#include <QStandardItemModel> +#include "wpamsg.h" +#include "ui_peers.h" + +class WpaGui; + +class Peers : public QDialog, public Ui::Peers +{ + Q_OBJECT + +public: + Peers(QWidget *parent = 0, const char *name = 0, + bool modal = false, Qt::WindowFlags fl = 0); + ~Peers(); + void setWpaGui(WpaGui *_wpagui); + void event_notify(WpaMsg msg); + +public slots: + virtual void context_menu(const QPoint &pos); + virtual void enter_pin(); + virtual void connect_pbc(); + virtual void learn_ap_config(); + virtual void ctx_refresh(); + virtual void ctx_p2p_start(); + virtual void ctx_p2p_stop(); + virtual void ctx_p2p_listen(); + virtual void ctx_p2p_start_group(); + virtual void ctx_p2p_remove_group(); + virtual void ctx_p2p_connect(); + virtual void ctx_p2p_req_pin(); + virtual void ctx_p2p_show_pin(); + virtual void ctx_p2p_display_pin(); + virtual void ctx_p2p_display_pin_pd(); + virtual void ctx_p2p_enter_pin(); + virtual void properties(); + virtual void ctx_hide_ap(); + virtual void ctx_show_ap(); + virtual void ctx_p2p_show_passphrase(); + virtual void ctx_p2p_start_persistent(); + virtual void ctx_p2p_invite(); + virtual void ctx_p2p_delete(); + +protected slots: + virtual void languageChange(); + virtual void closeEvent(QCloseEvent *event); + +private: + void add_station(QString info); + void add_stations(); + void add_single_station(const char *addr); + bool add_bss(const char *cmd); + void remove_bss(int id); + void add_scan_results(); + void add_persistent(int id, const char *ssid, const char *bssid); + void add_persistent_groups(); + void update_peers(); + QStandardItem * find_addr(QString addr); + QStandardItem * find_addr_type(QString addr, int type); + void add_p2p_group_client(QStandardItem *parent, QString params); + QStandardItem * find_uuid(QString uuid); + void done(int r); + void remove_enrollee_uuid(QString uuid); + QString ItemType(int type); + void enable_persistent(int id); + + WpaGui *wpagui; + QStandardItemModel model; + QIcon *default_icon; + QIcon *ap_icon; + QIcon *laptop_icon; + QIcon *group_icon; + QIcon *invitation_icon; + QStandardItem *ctx_item; + + bool hide_ap; +}; + +#endif /* PEERS_H */ diff --git a/wpa_supplicant/wpa_gui-qt4/peers.ui b/wpa_supplicant/wpa_gui-qt4/peers.ui new file mode 100644 index 0000000000000..9508c254b70e3 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/peers.ui @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Peers</class> + <widget class="QDialog" name="Peers"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Peers</string> + </property> + <layout class="QGridLayout"> + <item row="0" column="0"> + <widget class="QListView" name="peers"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="mouseTracking"> + <bool>true</bool> + </property> + <property name="editTriggers"> + <set>QAbstractItemView::NoEditTriggers</set> + </property> + <property name="viewMode"> + <enum>QListView::IconMode</enum> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/wpa_supplicant/wpa_gui-qt4/scanresults.cpp b/wpa_supplicant/wpa_gui-qt4/scanresults.cpp new file mode 100644 index 0000000000000..a2e3072fb6e1e --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/scanresults.cpp @@ -0,0 +1,141 @@ +/* + * wpa_gui - ScanResults class + * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include <cstdio> + +#include "scanresults.h" +#include "signalbar.h" +#include "wpagui.h" +#include "networkconfig.h" +#include "scanresultsitem.h" + + +ScanResults::ScanResults(QWidget *parent, const char *, bool, Qt::WindowFlags) + : QDialog(parent) +{ + setupUi(this); + + connect(closeButton, SIGNAL(clicked()), this, SLOT(close())); + connect(scanButton, SIGNAL(clicked()), this, SLOT(scanRequest())); + connect(scanResultsWidget, + SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)), this, + SLOT(bssSelected(QTreeWidgetItem *))); + + wpagui = NULL; + scanResultsWidget->setItemsExpandable(false); + scanResultsWidget->setRootIsDecorated(false); + scanResultsWidget->setItemDelegate(new SignalBar(scanResultsWidget)); +} + + +ScanResults::~ScanResults() +{ +} + + +void ScanResults::languageChange() +{ + retranslateUi(this); +} + + +void ScanResults::setWpaGui(WpaGui *_wpagui) +{ + wpagui = _wpagui; + updateResults(); +} + + +void ScanResults::updateResults() +{ + char reply[2048]; + size_t reply_len; + int index; + char cmd[20]; + + scanResultsWidget->clear(); + + index = 0; + while (wpagui) { + snprintf(cmd, sizeof(cmd), "BSS %d", index++); + if (index > 1000) + break; + + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) + break; + reply[reply_len] = '\0'; + + QString bss(reply); + if (bss.isEmpty() || bss.startsWith("FAIL")) + break; + + QString ssid, bssid, freq, signal, flags; + + QStringList lines = bss.split(QRegExp("\\n")); + for (QStringList::Iterator it = lines.begin(); + it != lines.end(); it++) { + int pos = (*it).indexOf('=') + 1; + if (pos < 1) + continue; + + if ((*it).startsWith("bssid=")) + bssid = (*it).mid(pos); + else if ((*it).startsWith("freq=")) + freq = (*it).mid(pos); + else if ((*it).startsWith("level=")) + signal = (*it).mid(pos); + else if ((*it).startsWith("flags=")) + flags = (*it).mid(pos); + else if ((*it).startsWith("ssid=")) + ssid = (*it).mid(pos); + } + + ScanResultsItem *item = new ScanResultsItem(scanResultsWidget); + if (item) { + item->setText(0, ssid); + item->setText(1, bssid); + item->setText(2, freq); + item->setText(3, signal); + item->setText(4, flags); + } + + if (bssid.isEmpty()) + break; + } +} + + +void ScanResults::scanRequest() +{ + char reply[10]; + size_t reply_len = sizeof(reply); + + if (wpagui == NULL) + return; + + wpagui->ctrlRequest("SCAN", reply, &reply_len); +} + + +void ScanResults::getResults() +{ + updateResults(); +} + + +void ScanResults::bssSelected(QTreeWidgetItem *sel) +{ + NetworkConfig *nc = new NetworkConfig(); + if (nc == NULL) + return; + nc->setWpaGui(wpagui); + nc->paramsFromScanResults(sel); + nc->show(); + nc->exec(); +} diff --git a/wpa_supplicant/wpa_gui-qt4/scanresults.h b/wpa_supplicant/wpa_gui-qt4/scanresults.h new file mode 100644 index 0000000000000..2cddd133fe2b7 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/scanresults.h @@ -0,0 +1,40 @@ +/* + * wpa_gui - ScanResults class + * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef SCANRESULTS_H +#define SCANRESULTS_H + +#include <QObject> +#include "ui_scanresults.h" + +class WpaGui; + +class ScanResults : public QDialog, public Ui::ScanResults +{ + Q_OBJECT + +public: + ScanResults(QWidget *parent = 0, const char *name = 0, + bool modal = false, Qt::WindowFlags fl = 0); + ~ScanResults(); + +public slots: + virtual void setWpaGui(WpaGui *_wpagui); + virtual void updateResults(); + virtual void scanRequest(); + virtual void getResults(); + virtual void bssSelected(QTreeWidgetItem *sel); + +protected slots: + virtual void languageChange(); + +private: + WpaGui *wpagui; +}; + +#endif /* SCANRESULTS_H */ diff --git a/wpa_supplicant/wpa_gui-qt4/scanresults.ui b/wpa_supplicant/wpa_gui-qt4/scanresults.ui new file mode 100644 index 0000000000000..81e405efc3198 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/scanresults.ui @@ -0,0 +1,94 @@ +<ui version="4.0" > + <class>ScanResults</class> + <widget class="QDialog" name="ScanResults" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>452</width> + <height>244</height> + </rect> + </property> + <property name="windowTitle" > + <string>Scan results</string> + </property> + <layout class="QVBoxLayout" > + <item> + <widget class="QTreeWidget" name="scanResultsWidget" > + <property name="editTriggers" > + <set>QAbstractItemView::NoEditTriggers</set> + </property> + <property name="uniformRowHeights" > + <bool>true</bool> + </property> + <property name="sortingEnabled" > + <bool>true</bool> + </property> + <property name="columnCount" > + <number>5</number> + </property> + <column> + <property name="text" > + <string>SSID</string> + </property> + </column> + <column> + <property name="text" > + <string>BSSID</string> + </property> + </column> + <column> + <property name="text" > + <string>frequency</string> + </property> + </column> + <column> + <property name="text" > + <string>signal</string> + </property> + </column> + <column> + <property name="text" > + <string>flags</string> + </property> + </column> + </widget> + </item> + <item> + <layout class="QHBoxLayout" > + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" > + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="scanButton" > + <property name="text" > + <string>Scan</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="closeButton" > + <property name="text" > + <string>Close</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <layoutdefault spacing="6" margin="11" /> + <pixmapfunction></pixmapfunction> + <resources/> + <connections/> +</ui> diff --git a/wpa_supplicant/wpa_gui-qt4/scanresultsitem.cpp b/wpa_supplicant/wpa_gui-qt4/scanresultsitem.cpp new file mode 100644 index 0000000000000..9cd937cd6e24b --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/scanresultsitem.cpp @@ -0,0 +1,18 @@ +/* + * wpa_gui - ScanResultsItem class + * Copyright (c) 2015, Adrian Nowicki <adinowicki@gmail.com> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "scanresultsitem.h" + +bool ScanResultsItem::operator< (const QTreeWidgetItem &other) const +{ + int sortCol = treeWidget()->sortColumn(); + if (sortCol == 2 || sortCol == 3) { + return text(sortCol).toInt() < other.text(sortCol).toInt(); + } + return text(sortCol) < other.text(sortCol); +} diff --git a/wpa_supplicant/wpa_gui-qt4/scanresultsitem.h b/wpa_supplicant/wpa_gui-qt4/scanresultsitem.h new file mode 100644 index 0000000000000..74887eefb59c6 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/scanresultsitem.h @@ -0,0 +1,21 @@ +/* + * wpa_gui - ScanResultsItem class + * Copyright (c) 2015, Adrian Nowicki <adinowicki@gmail.com> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef SCANRESULTSITEM_H +#define SCANRESULTSITEM_H + +#include <QTreeWidgetItem> + +class ScanResultsItem : public QTreeWidgetItem +{ +public: + ScanResultsItem(QTreeWidget *tree) : QTreeWidgetItem(tree) {} + bool operator< (const QTreeWidgetItem &other) const; +}; + +#endif /* SCANRESULTSITEM_H */ diff --git a/wpa_supplicant/wpa_gui-qt4/signalbar.cpp b/wpa_supplicant/wpa_gui-qt4/signalbar.cpp new file mode 100644 index 0000000000000..2bba582175e53 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/signalbar.cpp @@ -0,0 +1,58 @@ +/* + * wpa_gui - SignalBar class + * Copyright (c) 2011, Kel Modderman <kel@otaku42.de> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include <cstdio> +#include <qapplication.h> + +#include "signalbar.h" + + +SignalBar::SignalBar(QObject *parent) + : QStyledItemDelegate(parent) +{ +} + + +SignalBar::~SignalBar() +{ +} + + +void SignalBar::paint(QPainter *painter, + const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + QStyleOptionProgressBar opts; + int signal; + + if (index.column() != 3) { + QStyledItemDelegate::paint(painter, option, index); + return; + } + + if (index.data().toInt() > 0) + signal = 0 - (256 - index.data().toInt()); + else + signal = index.data().toInt(); + + opts.minimum = -95; + opts.maximum = -35; + if (signal < opts.minimum) + opts.progress = opts.minimum; + else if (signal > opts.maximum) + opts.progress = opts.maximum; + else + opts.progress = signal; + + opts.text = QString::number(signal) + " dBm"; + opts.textVisible = true; + opts.rect = option.rect; + + QApplication::style()->drawControl(QStyle::CE_ProgressBar, + &opts, painter); +} diff --git a/wpa_supplicant/wpa_gui-qt4/signalbar.h b/wpa_supplicant/wpa_gui-qt4/signalbar.h new file mode 100644 index 0000000000000..37da5dd2ce941 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/signalbar.h @@ -0,0 +1,28 @@ +/* + * wpa_gui - SignalBar class + * Copyright (c) 2011, Kel Modderman <kel@otaku42.de> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef SIGNALBAR_H +#define SIGNALBAR_H + +#include <QObject> +#include <QStyledItemDelegate> + +class SignalBar : public QStyledItemDelegate +{ + Q_OBJECT + +public: + SignalBar(QObject *parent = 0); + ~SignalBar(); + + virtual void paint(QPainter *painter, + const QStyleOptionViewItem &option, + const QModelIndex &index) const ; +}; + +#endif /* SIGNALBAR_H */ diff --git a/wpa_supplicant/wpa_gui-qt4/stringquery.cpp b/wpa_supplicant/wpa_gui-qt4/stringquery.cpp new file mode 100644 index 0000000000000..420e0bec4d04e --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/stringquery.cpp @@ -0,0 +1,31 @@ +/* + * wpa_gui - StringQuery class + * Copyright (c) 2009, Atheros Communications + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include <cstdio> +#include <QLabel> + +#include "stringquery.h" + + +StringQuery::StringQuery(QString label) +{ + edit = new QLineEdit; + edit->setFocus(); + QGridLayout *layout = new QGridLayout; + layout->addWidget(new QLabel(label), 0, 0); + layout->addWidget(edit, 0, 1); + setLayout(layout); + + connect(edit, SIGNAL(returnPressed()), this, SLOT(accept())); +} + + +QString StringQuery::get_string() +{ + return edit->text(); +} diff --git a/wpa_supplicant/wpa_gui-qt4/stringquery.h b/wpa_supplicant/wpa_gui-qt4/stringquery.h new file mode 100644 index 0000000000000..9d6bffd3e7b62 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/stringquery.h @@ -0,0 +1,28 @@ +/* + * wpa_gui - StringQuery class + * Copyright (c) 2009, Atheros Communications + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef STRINGQUERY_H +#define STRINGQUERY_H + +#include <QDialog> +#include <QLineEdit> +#include <QGridLayout> + +class StringQuery : public QDialog +{ + Q_OBJECT + +public: + StringQuery(QString label); + QString get_string(); + +private: + QLineEdit *edit; +}; + +#endif /* STRINGQUERY_H */ diff --git a/wpa_supplicant/wpa_gui-qt4/userdatarequest.cpp b/wpa_supplicant/wpa_gui-qt4/userdatarequest.cpp new file mode 100644 index 0000000000000..9d933b012053d --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/userdatarequest.cpp @@ -0,0 +1,94 @@ +/* + * wpa_gui - UserDataRequest class + * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "userdatarequest.h" +#include "wpagui.h" +#include "common/wpa_ctrl.h" + + +UserDataRequest::UserDataRequest(QWidget *parent, const char *, bool, + Qt::WindowFlags) + : QDialog(parent) +{ + setupUi(this); + + connect(buttonOk, SIGNAL(clicked()), this, SLOT(sendReply())); + connect(buttonCancel, SIGNAL(clicked()), this, SLOT(reject())); + connect(queryEdit, SIGNAL(returnPressed()), this, SLOT(sendReply())); +} + + +UserDataRequest::~UserDataRequest() +{ +} + + +void UserDataRequest::languageChange() +{ + retranslateUi(this); +} + + +int UserDataRequest::setParams(WpaGui *_wpagui, const char *reqMsg) +{ + char *tmp, *pos, *pos2; + wpagui = _wpagui; + tmp = strdup(reqMsg); + if (tmp == NULL) + return -1; + pos = strchr(tmp, '-'); + if (pos == NULL) { + free(tmp); + return -1; + } + *pos++ = '\0'; + field = tmp; + pos2 = strchr(pos, ':'); + if (pos2 == NULL) { + free(tmp); + return -1; + } + *pos2++ = '\0'; + + networkid = atoi(pos); + queryInfo->setText(pos2); + if (strcmp(tmp, "PASSWORD") == 0) { + queryField->setText(tr("Password: ")); + queryEdit->setEchoMode(QLineEdit::Password); + } else if (strcmp(tmp, "NEW_PASSWORD") == 0) { + queryField->setText(tr("New password: ")); + queryEdit->setEchoMode(QLineEdit::Password); + } else if (strcmp(tmp, "IDENTITY") == 0) + queryField->setText(tr("Identity: ")); + else if (strcmp(tmp, "PASSPHRASE") == 0) { + queryField->setText(tr("Private key passphrase: ")); + queryEdit->setEchoMode(QLineEdit::Password); + } else + queryField->setText(field + ":"); + free(tmp); + + return 0; +} + + +void UserDataRequest::sendReply() +{ + char reply[10]; + size_t reply_len = sizeof(reply); + + if (wpagui == NULL) { + reject(); + return; + } + + QString cmd = QString(WPA_CTRL_RSP) + field + '-' + + QString::number(networkid) + ':' + + queryEdit->text(); + wpagui->ctrlRequest(cmd.toLocal8Bit().constData(), reply, &reply_len); + accept(); +} diff --git a/wpa_supplicant/wpa_gui-qt4/userdatarequest.h b/wpa_supplicant/wpa_gui-qt4/userdatarequest.h new file mode 100644 index 0000000000000..b6d1ad2f4f1eb --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/userdatarequest.h @@ -0,0 +1,40 @@ +/* + * wpa_gui - UserDataRequest class + * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef USERDATAREQUEST_H +#define USERDATAREQUEST_H + +#include <QObject> +#include "ui_userdatarequest.h" + +class WpaGui; + +class UserDataRequest : public QDialog, public Ui::UserDataRequest +{ + Q_OBJECT + +public: + UserDataRequest(QWidget *parent = 0, const char *name = 0, + bool modal = false, Qt::WindowFlags fl = 0); + ~UserDataRequest(); + + int setParams(WpaGui *_wpagui, const char *reqMsg); + +public slots: + virtual void sendReply(); + +protected slots: + virtual void languageChange(); + +private: + WpaGui *wpagui; + int networkid; + QString field; +}; + +#endif /* USERDATAREQUEST_H */ diff --git a/wpa_supplicant/wpa_gui-qt4/userdatarequest.ui b/wpa_supplicant/wpa_gui-qt4/userdatarequest.ui new file mode 100644 index 0000000000000..1de2a26da1cd0 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/userdatarequest.ui @@ -0,0 +1,109 @@ +<ui version="4.0" stdsetdef="1" > + <author></author> + <comment></comment> + <exportmacro></exportmacro> + <class>UserDataRequest</class> + <widget class="QDialog" name="UserDataRequest" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>216</width> + <height>103</height> + </rect> + </property> + <property name="windowTitle" > + <string>Authentication credentials required</string> + </property> + <property name="sizeGripEnabled" > + <bool>true</bool> + </property> + <layout class="QVBoxLayout" > + <item> + <widget class="QLabel" name="queryInfo" > + <property name="text" > + <string/> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <item> + <widget class="QLabel" name="queryField" > + <property name="text" > + <string/> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="queryEdit" > + <property name="enabled" > + <bool>true</bool> + </property> + <property name="echoMode" > + <enum>QLineEdit::Password</enum> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <item> + <spacer name="spacer4" > + <property name="sizeHint" > + <size> + <width>20</width> + <height>20</height> + </size> + </property> + <property name="sizeType" > + <enum>Expanding</enum> + </property> + <property name="orientation" > + <enum>Horizontal</enum> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="buttonOk" > + <property name="text" > + <string>&OK</string> + </property> + <property name="shortcut" > + <string/> + </property> + <property name="autoDefault" > + <bool>true</bool> + </property> + <property name="default" > + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="buttonCancel" > + <property name="text" > + <string>&Cancel</string> + </property> + <property name="shortcut" > + <string/> + </property> + <property name="autoDefault" > + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <layoutdefault spacing="6" margin="11" /> + <pixmapfunction></pixmapfunction> +</ui> diff --git a/wpa_supplicant/wpa_gui-qt4/wpa_gui.desktop b/wpa_supplicant/wpa_gui-qt4/wpa_gui.desktop new file mode 100644 index 0000000000000..ccc7d8741d02b --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/wpa_gui.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Version=1.0 +Name=wpa_gui +Comment=Graphical user interface for wpa_supplicant +Exec=wpa_gui +Icon=wpa_gui +GenericName=wpa_supplicant user interface +Terminal=false +Type=Application +Categories=Qt;Network; diff --git a/wpa_supplicant/wpa_gui-qt4/wpa_gui.pro b/wpa_supplicant/wpa_gui-qt4/wpa_gui.pro new file mode 100644 index 0000000000000..3fa734b577585 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/wpa_gui.pro @@ -0,0 +1,73 @@ +TEMPLATE = app +LANGUAGE = C++ +TRANSLATIONS = lang/wpa_gui_de.ts +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += qt warn_on release + +DEFINES += CONFIG_CTRL_IFACE + +win32 { + LIBS += -lws2_32 -static + DEFINES += CONFIG_NATIVE_WINDOWS CONFIG_CTRL_IFACE_NAMED_PIPE + SOURCES += ../../src/utils/os_win32.c +} else:win32-g++ { + # cross compilation to win32 + LIBS += -lws2_32 -static -mwindows + DEFINES += CONFIG_NATIVE_WINDOWS CONFIG_CTRL_IFACE_NAMED_PIPE + SOURCES += ../../src/utils/os_win32.c + RESOURCES += icons_png.qrc +} else:win32-x-g++ { + # cross compilation to win32 + LIBS += -lws2_32 -static -mwindows + DEFINES += CONFIG_NATIVE_WINDOWS CONFIG_CTRL_IFACE_NAMED_PIPE + DEFINES += _X86_ + SOURCES += ../../src/utils/os_win32.c + RESOURCES += icons_png.qrc +} else { + DEFINES += CONFIG_CTRL_IFACE_UNIX + SOURCES += ../../src/utils/os_unix.c +} + +INCLUDEPATH += . .. ../../src ../../src/utils + +HEADERS += wpamsg.h \ + wpagui.h \ + eventhistory.h \ + scanresults.h \ + scanresultsitem.h \ + signalbar.h \ + userdatarequest.h \ + networkconfig.h \ + addinterface.h \ + peers.h \ + stringquery.h + +SOURCES += main.cpp \ + wpagui.cpp \ + eventhistory.cpp \ + scanresults.cpp \ + scanresultsitem.cpp \ + signalbar.cpp \ + userdatarequest.cpp \ + networkconfig.cpp \ + addinterface.cpp \ + peers.cpp \ + stringquery.cpp \ + ../../src/common/wpa_ctrl.c + +RESOURCES += icons.qrc + +FORMS = wpagui.ui \ + eventhistory.ui \ + scanresults.ui \ + userdatarequest.ui \ + networkconfig.ui \ + peers.ui + + +unix { + UI_DIR = .ui + MOC_DIR = .moc + OBJECTS_DIR = .obj +} diff --git a/wpa_supplicant/wpa_gui-qt4/wpagui.cpp b/wpa_supplicant/wpa_gui-qt4/wpagui.cpp new file mode 100644 index 0000000000000..a0aa05ed37362 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/wpagui.cpp @@ -0,0 +1,1898 @@ +/* + * wpa_gui - WpaGui class + * Copyright (c) 2005-2011, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifdef CONFIG_NATIVE_WINDOWS +#include <windows.h> +#endif /* CONFIG_NATIVE_WINDOWS */ + +#include <cstdio> +#include <unistd.h> +#include <QMessageBox> +#include <QCloseEvent> +#include <QImageReader> +#include <QSettings> + +#include "wpagui.h" +#include "dirent.h" +#include "common/wpa_ctrl.h" +#include "userdatarequest.h" +#include "networkconfig.h" + + +#ifndef QT_NO_DEBUG +#define debug(M, ...) qDebug("DEBUG %d: " M, __LINE__, ##__VA_ARGS__) +#else +#define debug(M, ...) do {} while (0) +#endif + + +WpaGui::WpaGui(QApplication *_app, QWidget *parent, const char *, + Qt::WindowFlags) + : QMainWindow(parent), app(_app) +{ + setupUi(this); + this->setWindowFlags(Qt::Dialog); + +#ifdef CONFIG_NATIVE_WINDOWS + fileStopServiceAction = new QAction(this); + fileStopServiceAction->setObjectName("Stop Service"); + fileStopServiceAction->setIconText(tr("Stop Service")); + fileMenu->insertAction(actionWPS, fileStopServiceAction); + + fileStartServiceAction = new QAction(this); + fileStartServiceAction->setObjectName("Start Service"); + fileStartServiceAction->setIconText(tr("Start Service")); + fileMenu->insertAction(fileStopServiceAction, fileStartServiceAction); + + connect(fileStartServiceAction, SIGNAL(triggered()), this, + SLOT(startService())); + connect(fileStopServiceAction, SIGNAL(triggered()), this, + SLOT(stopService())); + + addInterfaceAction = new QAction(this); + addInterfaceAction->setIconText(tr("Add Interface")); + fileMenu->insertAction(fileStartServiceAction, addInterfaceAction); + + connect(addInterfaceAction, SIGNAL(triggered()), this, + SLOT(addInterface())); +#endif /* CONFIG_NATIVE_WINDOWS */ + + (void) statusBar(); + + /* + * Disable WPS tab by default; it will be enabled if wpa_supplicant is + * built with WPS support. + */ + wpsTab->setEnabled(false); + wpaguiTab->setTabEnabled(wpaguiTab->indexOf(wpsTab), false); + + connect(fileEventHistoryAction, SIGNAL(triggered()), this, + SLOT(eventHistory())); + connect(fileSaveConfigAction, SIGNAL(triggered()), this, + SLOT(saveConfig())); + connect(actionWPS, SIGNAL(triggered()), this, SLOT(wpsDialog())); + connect(actionPeers, SIGNAL(triggered()), this, SLOT(peersDialog())); + connect(fileExitAction, SIGNAL(triggered()), qApp, SLOT(quit())); + connect(networkAddAction, SIGNAL(triggered()), this, + SLOT(addNetwork())); + connect(networkEditAction, SIGNAL(triggered()), this, + SLOT(editSelectedNetwork())); + connect(networkRemoveAction, SIGNAL(triggered()), this, + SLOT(removeSelectedNetwork())); + connect(networkEnableAllAction, SIGNAL(triggered()), this, + SLOT(enableAllNetworks())); + connect(networkDisableAllAction, SIGNAL(triggered()), this, + SLOT(disableAllNetworks())); + connect(networkRemoveAllAction, SIGNAL(triggered()), this, + SLOT(removeAllNetworks())); + connect(helpIndexAction, SIGNAL(triggered()), this, SLOT(helpIndex())); + connect(helpContentsAction, SIGNAL(triggered()), this, + SLOT(helpContents())); + connect(helpAboutAction, SIGNAL(triggered()), this, SLOT(helpAbout())); + connect(disconnectButton, SIGNAL(clicked()), this, SLOT(disconnect())); + connect(scanButton, SIGNAL(clicked()), this, SLOT(scan())); + connect(connectButton, SIGNAL(clicked()), this, SLOT(connectB())); + connect(adapterSelect, SIGNAL(activated(const QString&)), this, + SLOT(selectAdapter(const QString&))); + connect(networkSelect, SIGNAL(activated(const QString&)), this, + SLOT(selectNetwork(const QString&))); + connect(addNetworkButton, SIGNAL(clicked()), this, SLOT(addNetwork())); + connect(editNetworkButton, SIGNAL(clicked()), this, + SLOT(editListedNetwork())); + connect(removeNetworkButton, SIGNAL(clicked()), this, + SLOT(removeListedNetwork())); + connect(networkList, SIGNAL(itemSelectionChanged()), this, + SLOT(updateNetworkDisabledStatus())); + connect(enableRadioButton, SIGNAL(toggled(bool)), this, + SLOT(enableListedNetwork(bool))); + connect(disableRadioButton, SIGNAL(toggled(bool)), this, + SLOT(disableListedNetwork(bool))); + connect(scanNetworkButton, SIGNAL(clicked()), this, SLOT(scan())); + connect(networkList, SIGNAL(itemDoubleClicked(QListWidgetItem *)), + this, SLOT(editListedNetwork())); + connect(wpaguiTab, SIGNAL(currentChanged(int)), this, + SLOT(tabChanged(int))); + connect(wpsPbcButton, SIGNAL(clicked()), this, SLOT(wpsPbc())); + connect(wpsPinButton, SIGNAL(clicked()), this, SLOT(wpsGeneratePin())); + connect(wpsApPinEdit, SIGNAL(textChanged(const QString &)), this, + SLOT(wpsApPinChanged(const QString &))); + connect(wpsApPinButton, SIGNAL(clicked()), this, SLOT(wpsApPin())); + + eh = NULL; + scanres = NULL; + peers = NULL; + add_iface = NULL; + udr = NULL; + tray_icon = NULL; + startInTray = false; + quietMode = false; + ctrl_iface = NULL; + ctrl_conn = NULL; + monitor_conn = NULL; + msgNotifier = NULL; + ctrl_iface_dir = strdup("/var/run/wpa_supplicant"); + signalMeterInterval = 0; + + parse_argv(); + +#ifndef QT_NO_SESSIONMANAGER + if (app->isSessionRestored()) { + QSettings settings("wpa_supplicant", "wpa_gui"); + settings.beginGroup("state"); + if (app->sessionId().compare(settings.value("session_id"). + toString()) == 0) + startInTray = settings.value("in_tray").toBool(); + settings.endGroup(); + } +#endif + + if (QSystemTrayIcon::isSystemTrayAvailable()) + createTrayIcon(startInTray); + else + show(); + + connectedToService = false; + textStatus->setText(tr("connecting to wpa_supplicant")); + timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), SLOT(ping())); + timer->setSingleShot(false); + timer->start(1000); + + signalMeterTimer = new QTimer(this); + signalMeterTimer->setInterval(signalMeterInterval); + connect(signalMeterTimer, SIGNAL(timeout()), SLOT(signalMeterUpdate())); + + if (openCtrlConnection(ctrl_iface) < 0) { + debug("Failed to open control connection to " + "wpa_supplicant."); + } + + updateStatus(); + networkMayHaveChanged = true; + updateNetworks(); +} + + +WpaGui::~WpaGui() +{ + delete msgNotifier; + + if (monitor_conn) { + wpa_ctrl_detach(monitor_conn); + wpa_ctrl_close(monitor_conn); + monitor_conn = NULL; + } + if (ctrl_conn) { + wpa_ctrl_close(ctrl_conn); + ctrl_conn = NULL; + } + + if (eh) { + eh->close(); + delete eh; + eh = NULL; + } + + if (scanres) { + scanres->close(); + delete scanres; + scanres = NULL; + } + + if (peers) { + peers->close(); + delete peers; + peers = NULL; + } + + if (add_iface) { + add_iface->close(); + delete add_iface; + add_iface = NULL; + } + + if (udr) { + udr->close(); + delete udr; + udr = NULL; + } + + free(ctrl_iface); + ctrl_iface = NULL; + + free(ctrl_iface_dir); + ctrl_iface_dir = NULL; +} + + +void WpaGui::languageChange() +{ + retranslateUi(this); +} + + +void WpaGui::parse_argv() +{ + int c; + WpaGuiApp *app = qobject_cast<WpaGuiApp*>(qApp); + for (;;) { + c = getopt(app->argc, app->argv, "i:m:p:tq"); + if (c < 0) + break; + switch (c) { + case 'i': + free(ctrl_iface); + ctrl_iface = strdup(optarg); + break; + case 'm': + signalMeterInterval = atoi(optarg) * 1000; + break; + case 'p': + free(ctrl_iface_dir); + ctrl_iface_dir = strdup(optarg); + break; + case 't': + startInTray = true; + break; + case 'q': + quietMode = true; + break; + } + } +} + + +int WpaGui::openCtrlConnection(const char *ifname) +{ + char *cfile; + int flen; + char buf[2048], *pos, *pos2; + size_t len; + + if (ifname) { + if (ifname != ctrl_iface) { + free(ctrl_iface); + ctrl_iface = strdup(ifname); + } + } else { +#ifdef CONFIG_CTRL_IFACE_UDP + free(ctrl_iface); + ctrl_iface = strdup("udp"); +#endif /* CONFIG_CTRL_IFACE_UDP */ +#ifdef CONFIG_CTRL_IFACE_UNIX + struct dirent *dent; + DIR *dir = opendir(ctrl_iface_dir); + free(ctrl_iface); + ctrl_iface = NULL; + if (dir) { + while ((dent = readdir(dir))) { +#ifdef _DIRENT_HAVE_D_TYPE + /* Skip the file if it is not a socket. + * Also accept DT_UNKNOWN (0) in case + * the C library or underlying file + * system does not support d_type. */ + if (dent->d_type != DT_SOCK && + dent->d_type != DT_UNKNOWN) + continue; +#endif /* _DIRENT_HAVE_D_TYPE */ + + if (strcmp(dent->d_name, ".") == 0 || + strcmp(dent->d_name, "..") == 0) + continue; + debug("Selected interface '%s'", + dent->d_name); + ctrl_iface = strdup(dent->d_name); + break; + } + closedir(dir); + } +#endif /* CONFIG_CTRL_IFACE_UNIX */ +#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE + struct wpa_ctrl *ctrl; + int ret; + + free(ctrl_iface); + ctrl_iface = NULL; + + ctrl = wpa_ctrl_open(NULL); + if (ctrl) { + len = sizeof(buf) - 1; + ret = wpa_ctrl_request(ctrl, "INTERFACES", 10, buf, + &len, NULL); + if (ret >= 0) { + connectedToService = true; + buf[len] = '\0'; + pos = strchr(buf, '\n'); + if (pos) + *pos = '\0'; + ctrl_iface = strdup(buf); + } + wpa_ctrl_close(ctrl); + } +#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */ + } + + if (ctrl_iface == NULL) { +#ifdef CONFIG_NATIVE_WINDOWS + static bool first = true; + if (first && !serviceRunning()) { + first = false; + if (QMessageBox::warning( + this, qAppName(), + tr("wpa_supplicant service is not " + "running.\n" + "Do you want to start it?"), + QMessageBox::Yes | QMessageBox::No) == + QMessageBox::Yes) + startService(); + } +#endif /* CONFIG_NATIVE_WINDOWS */ + return -1; + } + +#ifdef CONFIG_CTRL_IFACE_UNIX + flen = strlen(ctrl_iface_dir) + strlen(ctrl_iface) + 2; + cfile = (char *) malloc(flen); + if (cfile == NULL) + return -1; + snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ctrl_iface); +#else /* CONFIG_CTRL_IFACE_UNIX */ + flen = strlen(ctrl_iface) + 1; + cfile = (char *) malloc(flen); + if (cfile == NULL) + return -1; + snprintf(cfile, flen, "%s", ctrl_iface); +#endif /* CONFIG_CTRL_IFACE_UNIX */ + + if (ctrl_conn) { + wpa_ctrl_close(ctrl_conn); + ctrl_conn = NULL; + } + + if (monitor_conn) { + delete msgNotifier; + msgNotifier = NULL; + wpa_ctrl_detach(monitor_conn); + wpa_ctrl_close(monitor_conn); + monitor_conn = NULL; + } + + debug("Trying to connect to '%s'", cfile); + ctrl_conn = wpa_ctrl_open(cfile); + if (ctrl_conn == NULL) { + free(cfile); + return -1; + } + monitor_conn = wpa_ctrl_open(cfile); + free(cfile); + if (monitor_conn == NULL) { + wpa_ctrl_close(ctrl_conn); + return -1; + } + if (wpa_ctrl_attach(monitor_conn)) { + debug("Failed to attach to wpa_supplicant"); + wpa_ctrl_close(monitor_conn); + monitor_conn = NULL; + wpa_ctrl_close(ctrl_conn); + ctrl_conn = NULL; + return -1; + } + +#if defined(CONFIG_CTRL_IFACE_UNIX) || defined(CONFIG_CTRL_IFACE_UDP) + msgNotifier = new QSocketNotifier(wpa_ctrl_get_fd(monitor_conn), + QSocketNotifier::Read, this); + connect(msgNotifier, SIGNAL(activated(int)), SLOT(receiveMsgs())); +#endif + + adapterSelect->clear(); + adapterSelect->addItem(ctrl_iface); + adapterSelect->setCurrentIndex(0); + + len = sizeof(buf) - 1; + if (wpa_ctrl_request(ctrl_conn, "INTERFACES", 10, buf, &len, NULL) >= + 0) { + buf[len] = '\0'; + pos = buf; + while (*pos) { + pos2 = strchr(pos, '\n'); + if (pos2) + *pos2 = '\0'; + if (strcmp(pos, ctrl_iface) != 0) + adapterSelect->addItem(pos); + if (pos2) + pos = pos2 + 1; + else + break; + } + } + + len = sizeof(buf) - 1; + if (wpa_ctrl_request(ctrl_conn, "GET_CAPABILITY eap", 18, buf, &len, + NULL) >= 0) { + buf[len] = '\0'; + + QString res(buf); + QStringList types = res.split(QChar(' ')); + bool wps = types.contains("WSC"); + actionWPS->setEnabled(wps); + wpsTab->setEnabled(wps); + wpaguiTab->setTabEnabled(wpaguiTab->indexOf(wpsTab), wps); + } + + return 0; +} + + +int WpaGui::ctrlRequest(const char *cmd, char *buf, size_t *buflen) +{ + int ret; + + if (ctrl_conn == NULL) + return -3; + ret = wpa_ctrl_request(ctrl_conn, cmd, strlen(cmd), buf, buflen, NULL); + if (ret == -2) + debug("'%s' command timed out.", cmd); + else if (ret < 0) + debug("'%s' command failed.", cmd); + + return ret; +} + + +QString WpaGui::wpaStateTranslate(char *state) +{ + if (!strcmp(state, "DISCONNECTED")) + return tr("Disconnected"); + else if (!strcmp(state, "INACTIVE")) + return tr("Inactive"); + else if (!strcmp(state, "SCANNING")) + return tr("Scanning"); + else if (!strcmp(state, "AUTHENTICATING")) + return tr("Authenticating"); + else if (!strcmp(state, "ASSOCIATING")) + return tr("Associating"); + else if (!strcmp(state, "ASSOCIATED")) + return tr("Associated"); + else if (!strcmp(state, "4WAY_HANDSHAKE")) + return tr("4-Way Handshake"); + else if (!strcmp(state, "GROUP_HANDSHAKE")) + return tr("Group Handshake"); + else if (!strcmp(state, "COMPLETED")) + return tr("Completed"); + else + return tr("Unknown"); +} + + +void WpaGui::updateStatus() +{ + char buf[2048], *start, *end, *pos; + size_t len; + + pingsToStatusUpdate = 10; + + len = sizeof(buf) - 1; + if (ctrl_conn == NULL || ctrlRequest("STATUS", buf, &len) < 0) { + textStatus->setText(tr("Could not get status from " + "wpa_supplicant")); + textAuthentication->clear(); + textEncryption->clear(); + textSsid->clear(); + textBssid->clear(); + textIpAddress->clear(); + updateTrayToolTip(tr("no status information")); + updateTrayIcon(TrayIconOffline); + signalMeterTimer->stop(); + +#ifdef CONFIG_NATIVE_WINDOWS + static bool first = true; + if (first && connectedToService && + (ctrl_iface == NULL || *ctrl_iface == '\0')) { + first = false; + if (QMessageBox::information( + this, qAppName(), + tr("No network interfaces in use.\n" + "Would you like to add one?"), + QMessageBox::Yes | QMessageBox::No) == + QMessageBox::Yes) + addInterface(); + } +#endif /* CONFIG_NATIVE_WINDOWS */ + return; + } + + buf[len] = '\0'; + + bool auth_updated = false, ssid_updated = false; + bool bssid_updated = false, ipaddr_updated = false; + bool status_updated = false; + char *pairwise_cipher = NULL, *group_cipher = NULL; + char *mode = NULL; + + start = buf; + while (*start) { + bool last = false; + end = strchr(start, '\n'); + if (end == NULL) { + last = true; + end = start; + while (end[0] && end[1]) + end++; + } + *end = '\0'; + + pos = strchr(start, '='); + if (pos) { + *pos++ = '\0'; + if (strcmp(start, "bssid") == 0) { + bssid_updated = true; + textBssid->setText(pos); + } else if (strcmp(start, "ssid") == 0) { + ssid_updated = true; + textSsid->setText(pos); + updateTrayToolTip(pos + tr(" (associated)")); + if (!signalMeterInterval) { + /* if signal meter is not enabled show + * full signal strength */ + updateTrayIcon(TrayIconSignalExcellent); + } + } else if (strcmp(start, "ip_address") == 0) { + ipaddr_updated = true; + textIpAddress->setText(pos); + } else if (strcmp(start, "wpa_state") == 0) { + status_updated = true; + textStatus->setText(wpaStateTranslate(pos)); + } else if (strcmp(start, "key_mgmt") == 0) { + auth_updated = true; + textAuthentication->setText(pos); + /* TODO: could add EAP status to this */ + } else if (strcmp(start, "pairwise_cipher") == 0) { + pairwise_cipher = pos; + } else if (strcmp(start, "group_cipher") == 0) { + group_cipher = pos; + } else if (strcmp(start, "mode") == 0) { + mode = pos; + } + } + + if (last) + break; + start = end + 1; + } + if (status_updated && mode) + textStatus->setText(textStatus->text() + " (" + mode + ")"); + + if (pairwise_cipher || group_cipher) { + QString encr; + if (pairwise_cipher && group_cipher && + strcmp(pairwise_cipher, group_cipher) != 0) { + encr.append(pairwise_cipher); + encr.append(" + "); + encr.append(group_cipher); + } else if (pairwise_cipher) { + encr.append(pairwise_cipher); + } else { + encr.append(group_cipher); + encr.append(" [group key only]"); + } + textEncryption->setText(encr); + } else + textEncryption->clear(); + + if (signalMeterInterval) { + /* + * Handle signal meter service. When network is not associated, + * deactivate timer, otherwise keep it going. Tray icon has to + * be initialized here, because of the initial delay of the + * timer. + */ + if (ssid_updated) { + if (!signalMeterTimer->isActive()) { + updateTrayIcon(TrayIconConnected); + signalMeterTimer->start(); + } + } else { + signalMeterTimer->stop(); + } + } + + if (!status_updated) + textStatus->clear(); + if (!auth_updated) + textAuthentication->clear(); + if (!ssid_updated) { + textSsid->clear(); + updateTrayToolTip(tr("(not-associated)")); + updateTrayIcon(TrayIconOffline); + } + if (!bssid_updated) + textBssid->clear(); + if (!ipaddr_updated) + textIpAddress->clear(); +} + + +void WpaGui::updateNetworks() +{ + char buf[4096], *start, *end, *id, *ssid, *bssid, *flags; + size_t len; + int first_active = -1; + int was_selected = -1; + bool current = false; + + if (!networkMayHaveChanged) + return; + + if (networkList->currentRow() >= 0) + was_selected = networkList->currentRow(); + + networkSelect->clear(); + networkList->clear(); + + if (ctrl_conn == NULL) + return; + + len = sizeof(buf) - 1; + if (ctrlRequest("LIST_NETWORKS", buf, &len) < 0) + return; + + buf[len] = '\0'; + start = strchr(buf, '\n'); + if (start == NULL) + return; + start++; + + while (*start) { + bool last = false; + end = strchr(start, '\n'); + if (end == NULL) { + last = true; + end = start; + while (end[0] && end[1]) + end++; + } + *end = '\0'; + + id = start; + ssid = strchr(id, '\t'); + if (ssid == NULL) + break; + *ssid++ = '\0'; + bssid = strchr(ssid, '\t'); + if (bssid == NULL) + break; + *bssid++ = '\0'; + flags = strchr(bssid, '\t'); + if (flags == NULL) + break; + *flags++ = '\0'; + + if (strstr(flags, "[DISABLED][P2P-PERSISTENT]")) { + if (last) + break; + start = end + 1; + continue; + } + + QString network(id); + network.append(": "); + network.append(ssid); + networkSelect->addItem(network); + networkList->addItem(network); + + if (strstr(flags, "[CURRENT]")) { + networkSelect->setCurrentIndex(networkSelect->count() - + 1); + current = true; + } else if (first_active < 0 && + strstr(flags, "[DISABLED]") == NULL) + first_active = networkSelect->count() - 1; + + if (last) + break; + start = end + 1; + } + + if (networkSelect->count() > 1) + networkSelect->addItem(tr("Select any network")); + + if (!current && first_active >= 0) + networkSelect->setCurrentIndex(first_active); + + if (was_selected >= 0 && networkList->count() > 0) { + if (was_selected < networkList->count()) + networkList->setCurrentRow(was_selected); + else + networkList->setCurrentRow(networkList->count() - 1); + } + else + networkList->setCurrentRow(networkSelect->currentIndex()); + + networkMayHaveChanged = false; +} + + +void WpaGui::helpIndex() +{ + debug("helpIndex"); +} + + +void WpaGui::helpContents() +{ + debug("helpContents"); +} + + +void WpaGui::helpAbout() +{ + QMessageBox::about(this, "wpa_gui for wpa_supplicant", + "Copyright (c) 2003-2015,\n" + "Jouni Malinen <j@w1.fi>\n" + "and contributors.\n" + "\n" + "This software may be distributed under\n" + "the terms of the BSD license.\n" + "See README for more details.\n" + "\n" + "This product includes software developed\n" + "by the OpenSSL Project for use in the\n" + "OpenSSL Toolkit (http://www.openssl.org/)\n"); +} + + +void WpaGui::disconnect() +{ + char reply[10]; + size_t reply_len = sizeof(reply); + ctrlRequest("DISCONNECT", reply, &reply_len); + stopWpsRun(false); +} + + +void WpaGui::scan() +{ + if (scanres) { + scanres->close(); + delete scanres; + } + + scanres = new ScanResults(); + if (scanres == NULL) + return; + scanres->setWpaGui(this); + scanres->show(); + scanres->exec(); +} + + +void WpaGui::eventHistory() +{ + if (eh) { + eh->close(); + delete eh; + } + + eh = new EventHistory(); + if (eh == NULL) + return; + eh->addEvents(msgs); + eh->show(); + eh->exec(); +} + + +void WpaGui::ping() +{ + char buf[10]; + size_t len; + +#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE + /* + * QSocketNotifier cannot be used with Windows named pipes, so use a + * timer to check for received messages for now. This could be + * optimized be doing something specific to named pipes or Windows + * events, but it is not clear what would be the best way of doing that + * in Qt. + */ + receiveMsgs(); +#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */ + + if (scanres && !scanres->isVisible()) { + delete scanres; + scanres = NULL; + } + + if (eh && !eh->isVisible()) { + delete eh; + eh = NULL; + } + + if (udr && !udr->isVisible()) { + delete udr; + udr = NULL; + } + + len = sizeof(buf) - 1; + if (ctrlRequest("PING", buf, &len) < 0) { + debug("PING failed - trying to reconnect"); + if (openCtrlConnection(ctrl_iface) >= 0) { + debug("Reconnected successfully"); + pingsToStatusUpdate = 0; + } + } + + pingsToStatusUpdate--; + if (pingsToStatusUpdate <= 0) { + updateStatus(); + updateNetworks(); + } + +#ifndef CONFIG_CTRL_IFACE_NAMED_PIPE + /* Use less frequent pings and status updates when the main window is + * hidden (running in taskbar). */ + int interval = isHidden() ? 5000 : 1000; + if (timer->interval() != interval) + timer->setInterval(interval); +#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */ +} + + +void WpaGui::signalMeterUpdate() +{ + char reply[128]; + size_t reply_len = sizeof(reply); + char *rssi; + int rssi_value; + + ctrlRequest("SIGNAL_POLL", reply, &reply_len); + + /* In order to eliminate signal strength fluctuations, try + * to obtain averaged RSSI value in the first place. */ + if ((rssi = strstr(reply, "AVG_RSSI=")) != NULL) + rssi_value = atoi(&rssi[sizeof("AVG_RSSI")]); + else if ((rssi = strstr(reply, "RSSI=")) != NULL) + rssi_value = atoi(&rssi[sizeof("RSSI")]); + else { + debug("Failed to get RSSI value"); + updateTrayIcon(TrayIconSignalNone); + return; + } + + debug("RSSI value: %d", rssi_value); + + /* + * NOTE: The code below assumes, that the unit of the value returned + * by the SIGNAL POLL request is dBm. It might not be true for all + * wpa_supplicant drivers. + */ + + /* + * Calibration is based on "various Internet sources". Nonetheless, + * it seems to be compatible with the Windows 8.1 strength meter - + * tested on Intel Centrino Advanced-N 6235. + */ + if (rssi_value >= -60) + updateTrayIcon(TrayIconSignalExcellent); + else if (rssi_value >= -68) + updateTrayIcon(TrayIconSignalGood); + else if (rssi_value >= -76) + updateTrayIcon(TrayIconSignalOk); + else if (rssi_value >= -84) + updateTrayIcon(TrayIconSignalWeak); + else + updateTrayIcon(TrayIconSignalNone); +} + + +static int str_match(const char *a, const char *b) +{ + return strncmp(a, b, strlen(b)) == 0; +} + + +void WpaGui::processMsg(char *msg) +{ + char *pos = msg, *pos2; + int priority = 2; + + if (*pos == '<') { + /* skip priority */ + pos++; + priority = atoi(pos); + pos = strchr(pos, '>'); + if (pos) + pos++; + else + pos = msg; + } + + WpaMsg wm(pos, priority); + if (eh) + eh->addEvent(wm); + if (peers) + peers->event_notify(wm); + msgs.append(wm); + while (msgs.count() > 100) + msgs.pop_front(); + + /* Update last message with truncated version of the event */ + if (strncmp(pos, "CTRL-", 5) == 0) { + pos2 = strchr(pos, str_match(pos, WPA_CTRL_REQ) ? ':' : ' '); + if (pos2) + pos2++; + else + pos2 = pos; + } else + pos2 = pos; + QString lastmsg = pos2; + lastmsg.truncate(40); + textLastMessage->setText(lastmsg); + + pingsToStatusUpdate = 0; + networkMayHaveChanged = true; + + if (str_match(pos, WPA_CTRL_REQ)) + processCtrlReq(pos + strlen(WPA_CTRL_REQ)); + else if (str_match(pos, WPA_EVENT_SCAN_RESULTS) && scanres) + scanres->updateResults(); + else if (str_match(pos, WPA_EVENT_DISCONNECTED)) + showTrayMessage(QSystemTrayIcon::Information, 3, + tr("Disconnected from network.")); + else if (str_match(pos, WPA_EVENT_CONNECTED)) { + showTrayMessage(QSystemTrayIcon::Information, 3, + tr("Connection to network established.")); + QTimer::singleShot(5 * 1000, this, SLOT(showTrayStatus())); + stopWpsRun(true); + } else if (str_match(pos, WPS_EVENT_AP_AVAILABLE_PBC)) { + wpsStatusText->setText(tr("WPS AP in active PBC mode found")); + if (textStatus->text() == "INACTIVE" || + textStatus->text() == "DISCONNECTED") + wpaguiTab->setCurrentWidget(wpsTab); + wpsInstructions->setText(tr("Press the PBC button on the " + "screen to start registration")); + } else if (str_match(pos, WPS_EVENT_AP_AVAILABLE_PIN)) { + wpsStatusText->setText(tr("WPS AP with recently selected " + "registrar")); + if (textStatus->text() == "INACTIVE" || + textStatus->text() == "DISCONNECTED") + wpaguiTab->setCurrentWidget(wpsTab); + } else if (str_match(pos, WPS_EVENT_AP_AVAILABLE_AUTH)) { + showTrayMessage(QSystemTrayIcon::Information, 3, + "Wi-Fi Protected Setup (WPS) AP\n" + "indicating this client is authorized."); + wpsStatusText->setText("WPS AP indicating this client is " + "authorized"); + if (textStatus->text() == "INACTIVE" || + textStatus->text() == "DISCONNECTED") + wpaguiTab->setCurrentWidget(wpsTab); + } else if (str_match(pos, WPS_EVENT_AP_AVAILABLE)) { + wpsStatusText->setText(tr("WPS AP detected")); + } else if (str_match(pos, WPS_EVENT_OVERLAP)) { + wpsStatusText->setText(tr("PBC mode overlap detected")); + wpsInstructions->setText(tr("More than one AP is currently in " + "active WPS PBC mode. Wait couple " + "of minutes and try again")); + wpaguiTab->setCurrentWidget(wpsTab); + } else if (str_match(pos, WPS_EVENT_CRED_RECEIVED)) { + wpsStatusText->setText(tr("Network configuration received")); + wpaguiTab->setCurrentWidget(wpsTab); + } else if (str_match(pos, WPA_EVENT_EAP_METHOD)) { + if (strstr(pos, "(WSC)")) + wpsStatusText->setText(tr("Registration started")); + } else if (str_match(pos, WPS_EVENT_M2D)) { + wpsStatusText->setText(tr("Registrar does not yet know PIN")); + } else if (str_match(pos, WPS_EVENT_FAIL)) { + wpsStatusText->setText(tr("Registration failed")); + } else if (str_match(pos, WPS_EVENT_SUCCESS)) { + wpsStatusText->setText(tr("Registration succeeded")); + } +} + + +void WpaGui::processCtrlReq(const char *req) +{ + if (udr) { + udr->close(); + delete udr; + } + udr = new UserDataRequest(); + if (udr == NULL) + return; + if (udr->setParams(this, req) < 0) { + delete udr; + udr = NULL; + return; + } + udr->show(); + udr->exec(); +} + + +void WpaGui::receiveMsgs() +{ + char buf[256]; + size_t len; + + while (monitor_conn && wpa_ctrl_pending(monitor_conn) > 0) { + len = sizeof(buf) - 1; + if (wpa_ctrl_recv(monitor_conn, buf, &len) == 0) { + buf[len] = '\0'; + processMsg(buf); + } + } +} + + +void WpaGui::connectB() +{ + char reply[10]; + size_t reply_len = sizeof(reply); + ctrlRequest("REASSOCIATE", reply, &reply_len); +} + + +void WpaGui::selectNetwork( const QString &sel ) +{ + QString cmd(sel); + char reply[10]; + size_t reply_len = sizeof(reply); + + if (cmd.contains(QRegExp("^\\d+:"))) + cmd.truncate(cmd.indexOf(':')); + else + cmd = "any"; + cmd.prepend("SELECT_NETWORK "); + ctrlRequest(cmd.toLocal8Bit().constData(), reply, &reply_len); + triggerUpdate(); + stopWpsRun(false); +} + + +void WpaGui::enableNetwork(const QString &sel) +{ + QString cmd(sel); + char reply[10]; + size_t reply_len = sizeof(reply); + + if (cmd.contains(QRegExp("^\\d+:"))) + cmd.truncate(cmd.indexOf(':')); + else if (!cmd.startsWith("all")) { + debug("Invalid editNetwork '%s'", + cmd.toLocal8Bit().constData()); + return; + } + cmd.prepend("ENABLE_NETWORK "); + ctrlRequest(cmd.toLocal8Bit().constData(), reply, &reply_len); + triggerUpdate(); +} + + +void WpaGui::disableNetwork(const QString &sel) +{ + QString cmd(sel); + char reply[10]; + size_t reply_len = sizeof(reply); + + if (cmd.contains(QRegExp("^\\d+:"))) + cmd.truncate(cmd.indexOf(':')); + else if (!cmd.startsWith("all")) { + debug("Invalid editNetwork '%s'", + cmd.toLocal8Bit().constData()); + return; + } + cmd.prepend("DISABLE_NETWORK "); + ctrlRequest(cmd.toLocal8Bit().constData(), reply, &reply_len); + triggerUpdate(); +} + + +void WpaGui::editNetwork(const QString &sel) +{ + QString cmd(sel); + int id = -1; + + if (cmd.contains(QRegExp("^\\d+:"))) { + cmd.truncate(cmd.indexOf(':')); + id = cmd.toInt(); + } + + NetworkConfig *nc = new NetworkConfig(); + if (nc == NULL) + return; + nc->setWpaGui(this); + + if (id >= 0) + nc->paramsFromConfig(id); + else + nc->newNetwork(); + + nc->show(); + nc->exec(); +} + + +void WpaGui::editSelectedNetwork() +{ + if (networkSelect->count() < 1) { + QMessageBox::information( + this, tr("No Networks"), + tr("There are no networks to edit.\n")); + return; + } + QString sel(networkSelect->currentText()); + editNetwork(sel); +} + + +void WpaGui::editListedNetwork() +{ + if (networkList->currentRow() < 0) { + QMessageBox::information(this, tr("Select A Network"), + tr("Select a network from the list to" + " edit it.\n")); + return; + } + QString sel(networkList->currentItem()->text()); + editNetwork(sel); +} + + +void WpaGui::triggerUpdate() +{ + updateStatus(); + networkMayHaveChanged = true; + updateNetworks(); +} + + +void WpaGui::addNetwork() +{ + NetworkConfig *nc = new NetworkConfig(); + if (nc == NULL) + return; + nc->setWpaGui(this); + nc->newNetwork(); + nc->show(); + nc->exec(); +} + + +void WpaGui::removeNetwork(const QString &sel) +{ + QString cmd(sel); + char reply[10]; + size_t reply_len = sizeof(reply); + + if (cmd.contains(QRegExp("^\\d+:"))) + cmd.truncate(cmd.indexOf(':')); + else if (!cmd.startsWith("all")) { + debug("Invalid editNetwork '%s'", + cmd.toLocal8Bit().constData()); + return; + } + cmd.prepend("REMOVE_NETWORK "); + ctrlRequest(cmd.toLocal8Bit().constData(), reply, &reply_len); + triggerUpdate(); +} + + +void WpaGui::removeSelectedNetwork() +{ + if (networkSelect->count() < 1) { + QMessageBox::information(this, tr("No Networks"), + tr("There are no networks to remove." + "\n")); + return; + } + QString sel(networkSelect->currentText()); + removeNetwork(sel); +} + + +void WpaGui::removeListedNetwork() +{ + if (networkList->currentRow() < 0) { + QMessageBox::information(this, tr("Select A Network"), + tr("Select a network from the list " + "to remove it.\n")); + return; + } + QString sel(networkList->currentItem()->text()); + removeNetwork(sel); +} + + +void WpaGui::enableAllNetworks() +{ + QString sel("all"); + enableNetwork(sel); +} + + +void WpaGui::disableAllNetworks() +{ + QString sel("all"); + disableNetwork(sel); +} + + +void WpaGui::removeAllNetworks() +{ + QString sel("all"); + removeNetwork(sel); +} + + +int WpaGui::getNetworkDisabled(const QString &sel) +{ + QString cmd(sel); + char reply[10]; + size_t reply_len = sizeof(reply) - 1; + int pos = cmd.indexOf(':'); + if (pos < 0) { + debug("Invalid getNetworkDisabled '%s'", + cmd.toLocal8Bit().constData()); + return -1; + } + cmd.truncate(pos); + cmd.prepend("GET_NETWORK "); + cmd.append(" disabled"); + + if (ctrlRequest(cmd.toLocal8Bit().constData(), reply, &reply_len) >= 0 + && reply_len >= 1) { + reply[reply_len] = '\0'; + if (!str_match(reply, "FAIL")) + return atoi(reply); + } + + return -1; +} + + +void WpaGui::updateNetworkDisabledStatus() +{ + if (networkList->currentRow() < 0) + return; + + QString sel(networkList->currentItem()->text()); + + switch (getNetworkDisabled(sel)) { + case 0: + if (!enableRadioButton->isChecked()) + enableRadioButton->setChecked(true); + return; + case 1: + if (!disableRadioButton->isChecked()) + disableRadioButton->setChecked(true); + return; + } +} + + +void WpaGui::enableListedNetwork(bool enabled) +{ + if (networkList->currentRow() < 0 || !enabled) + return; + + QString sel(networkList->currentItem()->text()); + + if (getNetworkDisabled(sel) == 1) + enableNetwork(sel); +} + + +void WpaGui::disableListedNetwork(bool disabled) +{ + if (networkList->currentRow() < 0 || !disabled) + return; + + QString sel(networkList->currentItem()->text()); + + if (getNetworkDisabled(sel) == 0) + disableNetwork(sel); +} + + +void WpaGui::saveConfig() +{ + char buf[10]; + size_t len; + + len = sizeof(buf) - 1; + ctrlRequest("SAVE_CONFIG", buf, &len); + + buf[len] = '\0'; + + if (str_match(buf, "FAIL")) + QMessageBox::warning( + this, tr("Failed to save configuration"), + tr("The configuration could not be saved.\n" + "\n" + "The update_config=1 configuration option\n" + "must be used for configuration saving to\n" + "be permitted.\n")); + else + QMessageBox::information( + this, tr("Saved configuration"), + tr("The current configuration was saved." + "\n")); +} + + +void WpaGui::selectAdapter( const QString & sel ) +{ + if (openCtrlConnection(sel.toLocal8Bit().constData()) < 0) + debug("Failed to open control connection to " + "wpa_supplicant."); + updateStatus(); + updateNetworks(); +} + + +void WpaGui::createTrayIcon(bool trayOnly) +{ + QApplication::setQuitOnLastWindowClosed(false); + + tray_icon = new QSystemTrayIcon(this); + updateTrayIcon(TrayIconOffline); + + connect(tray_icon, + SIGNAL(activated(QSystemTrayIcon::ActivationReason)), + this, SLOT(trayActivated(QSystemTrayIcon::ActivationReason))); + + ackTrayIcon = false; + + tray_menu = new QMenu(this); + + disconnectAction = new QAction(tr("&Disconnect"), this); + reconnectAction = new QAction(tr("Re&connect"), this); + connect(disconnectAction, SIGNAL(triggered()), this, + SLOT(disconnect())); + connect(reconnectAction, SIGNAL(triggered()), this, + SLOT(connectB())); + tray_menu->addAction(disconnectAction); + tray_menu->addAction(reconnectAction); + tray_menu->addSeparator(); + + eventAction = new QAction(tr("&Event History"), this); + scanAction = new QAction(tr("Scan &Results"), this); + statAction = new QAction(tr("S&tatus"), this); + connect(eventAction, SIGNAL(triggered()), this, SLOT(eventHistory())); + connect(scanAction, SIGNAL(triggered()), this, SLOT(scan())); + connect(statAction, SIGNAL(triggered()), this, SLOT(showTrayStatus())); + tray_menu->addAction(eventAction); + tray_menu->addAction(scanAction); + tray_menu->addAction(statAction); + tray_menu->addSeparator(); + + showAction = new QAction(tr("&Show Window"), this); + hideAction = new QAction(tr("&Hide Window"), this); + quitAction = new QAction(tr("&Quit"), this); + connect(showAction, SIGNAL(triggered()), this, SLOT(show())); + connect(hideAction, SIGNAL(triggered()), this, SLOT(hide())); + connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit())); + tray_menu->addAction(showAction); + tray_menu->addAction(hideAction); + tray_menu->addSeparator(); + tray_menu->addAction(quitAction); + + tray_icon->setContextMenu(tray_menu); + + tray_icon->show(); + + if (!trayOnly) + show(); + inTray = trayOnly; +} + + +void WpaGui::showTrayMessage(QSystemTrayIcon::MessageIcon type, int sec, + const QString & msg) +{ + if (!QSystemTrayIcon::supportsMessages()) + return; + + if (isVisible() || !tray_icon || !tray_icon->isVisible() || quietMode) + return; + + tray_icon->showMessage(qAppName(), msg, type, sec * 1000); +} + + +void WpaGui::trayActivated(QSystemTrayIcon::ActivationReason how) + { + switch (how) { + /* use close() here instead of hide() and allow the + * custom closeEvent handler take care of children */ + case QSystemTrayIcon::Trigger: + ackTrayIcon = true; + if (isVisible()) { + close(); + inTray = true; + } else { + show(); + inTray = false; + } + break; + case QSystemTrayIcon::MiddleClick: + showTrayStatus(); + break; + default: + break; + } +} + + +void WpaGui::showTrayStatus() +{ + char buf[2048]; + size_t len; + + len = sizeof(buf) - 1; + if (ctrlRequest("STATUS", buf, &len) < 0) + return; + buf[len] = '\0'; + + QString msg, status(buf); + + QStringList lines = status.split(QRegExp("\\n")); + for (QStringList::Iterator it = lines.begin(); + it != lines.end(); it++) { + int pos = (*it).indexOf('=') + 1; + if (pos < 1) + continue; + + if ((*it).startsWith("bssid=")) + msg.append("BSSID:\t" + (*it).mid(pos) + "\n"); + else if ((*it).startsWith("ssid=")) + msg.append("SSID: \t" + (*it).mid(pos) + "\n"); + else if ((*it).startsWith("pairwise_cipher=")) + msg.append("PAIR: \t" + (*it).mid(pos) + "\n"); + else if ((*it).startsWith("group_cipher=")) + msg.append("GROUP:\t" + (*it).mid(pos) + "\n"); + else if ((*it).startsWith("key_mgmt=")) + msg.append("AUTH: \t" + (*it).mid(pos) + "\n"); + else if ((*it).startsWith("wpa_state=")) + msg.append("STATE:\t" + (*it).mid(pos) + "\n"); + else if ((*it).startsWith("ip_address=")) + msg.append("IP: \t" + (*it).mid(pos) + "\n"); + else if ((*it).startsWith("Supplicant PAE state=")) + msg.append("PAE: \t" + (*it).mid(pos) + "\n"); + else if ((*it).startsWith("EAP state=")) + msg.append("EAP: \t" + (*it).mid(pos) + "\n"); + } + + if (!msg.isEmpty()) + showTrayMessage(QSystemTrayIcon::Information, 10, msg); +} + + +void WpaGui::updateTrayToolTip(const QString &msg) +{ + if (tray_icon) + tray_icon->setToolTip(msg); +} + + +void WpaGui::updateTrayIcon(TrayIconType type) +{ + if (!tray_icon || currentIconType == type) + return; + + QIcon fallback_icon; + QStringList names; + + if (QImageReader::supportedImageFormats().contains(QByteArray("svg"))) + fallback_icon = QIcon(":/icons/wpa_gui.svg"); + else + fallback_icon = QIcon(":/icons/wpa_gui.png"); + + switch (type) { + case TrayIconOffline: + names << "network-wireless-offline-symbolic" + << "network-wireless-offline" + << "network-wireless-signal-none-symbolic" + << "network-wireless-signal-none"; + break; + case TrayIconAcquiring: + names << "network-wireless-acquiring-symbolic" + << "network-wireless-acquiring"; + break; + case TrayIconConnected: + names << "network-wireless-connected-symbolic" + << "network-wireless-connected"; + break; + case TrayIconSignalNone: + names << "network-wireless-signal-none-symbolic" + << "network-wireless-signal-none"; + break; + case TrayIconSignalWeak: + names << "network-wireless-signal-weak-symbolic" + << "network-wireless-signal-weak"; + break; + case TrayIconSignalOk: + names << "network-wireless-signal-ok-symbolic" + << "network-wireless-signal-ok"; + break; + case TrayIconSignalGood: + names << "network-wireless-signal-good-symbolic" + << "network-wireless-signal-good"; + break; + case TrayIconSignalExcellent: + names << "network-wireless-signal-excellent-symbolic" + << "network-wireless-signal-excellent"; + break; + } + + currentIconType = type; + tray_icon->setIcon(loadThemedIcon(names, fallback_icon)); +} + + +QIcon WpaGui::loadThemedIcon(const QStringList &names, + const QIcon &fallback) +{ + QIcon icon; + + for (QStringList::ConstIterator it = names.begin(); + it != names.end(); it++) { + icon = QIcon::fromTheme(*it); + if (!icon.isNull()) + return icon; + } + + return fallback; +} + + +void WpaGui::closeEvent(QCloseEvent *event) +{ + if (eh) { + eh->close(); + delete eh; + eh = NULL; + } + + if (scanres) { + scanres->close(); + delete scanres; + scanres = NULL; + } + + if (peers) { + peers->close(); + delete peers; + peers = NULL; + } + + if (udr) { + udr->close(); + delete udr; + udr = NULL; + } + + if (tray_icon && !ackTrayIcon) { + /* give user a visual hint that the tray icon exists */ + if (QSystemTrayIcon::supportsMessages()) { + hide(); + showTrayMessage(QSystemTrayIcon::Information, 3, + qAppName() + + tr(" will keep running in " + "the system tray.")); + } else { + QMessageBox::information(this, qAppName() + + tr(" systray"), + tr("The program will keep " + "running in the system " + "tray.")); + } + ackTrayIcon = true; + } + + event->accept(); +} + + +void WpaGui::wpsDialog() +{ + wpaguiTab->setCurrentWidget(wpsTab); +} + + +void WpaGui::peersDialog() +{ + if (peers) { + peers->close(); + delete peers; + } + + peers = new Peers(); + if (peers == NULL) + return; + peers->setWpaGui(this); + peers->show(); + peers->exec(); +} + + +void WpaGui::tabChanged(int index) +{ + if (index != 2) + return; + + if (wpsRunning) + return; + + wpsApPinEdit->setEnabled(!bssFromScan.isEmpty()); + if (bssFromScan.isEmpty()) + wpsApPinButton->setEnabled(false); +} + + +void WpaGui::wpsPbc() +{ + char reply[20]; + size_t reply_len = sizeof(reply); + + if (ctrlRequest("WPS_PBC", reply, &reply_len) < 0) + return; + + wpsPinEdit->setEnabled(false); + if (wpsStatusText->text().compare(tr("WPS AP in active PBC mode found"))) { + wpsInstructions->setText(tr("Press the push button on the AP to " + "start the PBC mode.")); + } else { + wpsInstructions->setText(tr("If you have not yet done so, press " + "the push button on the AP to start " + "the PBC mode.")); + } + wpsStatusText->setText(tr("Waiting for Registrar")); + wpsRunning = true; +} + + +void WpaGui::wpsGeneratePin() +{ + char reply[20]; + size_t reply_len = sizeof(reply) - 1; + + if (ctrlRequest("WPS_PIN any", reply, &reply_len) < 0) + return; + + reply[reply_len] = '\0'; + + wpsPinEdit->setText(reply); + wpsPinEdit->setEnabled(true); + wpsInstructions->setText(tr("Enter the generated PIN into the Registrar " + "(either the internal one in the AP or an " + "external one).")); + wpsStatusText->setText(tr("Waiting for Registrar")); + wpsRunning = true; +} + + +void WpaGui::setBssFromScan(const QString &bssid) +{ + bssFromScan = bssid; + wpsApPinEdit->setEnabled(!bssFromScan.isEmpty()); + wpsApPinButton->setEnabled(wpsApPinEdit->text().length() == 8); + wpsStatusText->setText(tr("WPS AP selected from scan results")); + wpsInstructions->setText(tr("If you want to use an AP device PIN, e.g., " + "from a label in the device, enter the eight " + "digit AP PIN and click Use AP PIN button.")); +} + + +void WpaGui::wpsApPinChanged(const QString &text) +{ + wpsApPinButton->setEnabled(text.length() == 8); +} + + +void WpaGui::wpsApPin() +{ + char reply[20]; + size_t reply_len = sizeof(reply); + + QString cmd("WPS_REG " + bssFromScan + " " + wpsApPinEdit->text()); + if (ctrlRequest(cmd.toLocal8Bit().constData(), reply, &reply_len) < 0) + return; + + wpsStatusText->setText(tr("Waiting for AP/Enrollee")); + wpsRunning = true; +} + + +void WpaGui::stopWpsRun(bool success) +{ + if (wpsRunning) + wpsStatusText->setText(success ? tr("Connected to the network") : + tr("Stopped")); + else + wpsStatusText->setText(""); + wpsPinEdit->setEnabled(false); + wpsInstructions->setText(""); + wpsRunning = false; + bssFromScan = ""; + wpsApPinEdit->setEnabled(false); + wpsApPinButton->setEnabled(false); +} + + +#ifdef CONFIG_NATIVE_WINDOWS + +#ifndef WPASVC_NAME +#define WPASVC_NAME TEXT("wpasvc") +#endif + +class ErrorMsg : public QMessageBox { +public: + ErrorMsg(QWidget *parent, DWORD last_err = GetLastError()); + void showMsg(QString msg); +private: + DWORD err; +}; + +ErrorMsg::ErrorMsg(QWidget *parent, DWORD last_err) : + QMessageBox(parent), err(last_err) +{ + setWindowTitle(tr("wpa_gui error")); + setIcon(QMessageBox::Warning); +} + +void ErrorMsg::showMsg(QString msg) +{ + LPTSTR buf; + + setText(msg); + if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, err, 0, (LPTSTR) (void *) &buf, + 0, NULL) > 0) { + QString msg = QString::fromWCharArray(buf); + setInformativeText(QString("[%1] %2").arg(err).arg(msg)); + LocalFree(buf); + } else { + setInformativeText(QString("[%1]").arg(err)); + } + + exec(); +} + + +void WpaGui::startService() +{ + SC_HANDLE svc, scm; + + scm = OpenSCManager(0, 0, SC_MANAGER_CONNECT); + if (!scm) { + ErrorMsg(this).showMsg(tr("OpenSCManager failed")); + return; + } + + svc = OpenService(scm, WPASVC_NAME, SERVICE_START); + if (!svc) { + ErrorMsg(this).showMsg(tr("OpenService failed")); + CloseServiceHandle(scm); + return; + } + + if (!StartService(svc, 0, NULL)) { + ErrorMsg(this).showMsg(tr("Failed to start wpa_supplicant " + "service")); + } + + CloseServiceHandle(svc); + CloseServiceHandle(scm); +} + + +void WpaGui::stopService() +{ + SC_HANDLE svc, scm; + SERVICE_STATUS status; + + scm = OpenSCManager(0, 0, SC_MANAGER_CONNECT); + if (!scm) { + ErrorMsg(this).showMsg(tr("OpenSCManager failed")); + return; + } + + svc = OpenService(scm, WPASVC_NAME, SERVICE_STOP); + if (!svc) { + ErrorMsg(this).showMsg(tr("OpenService failed")); + CloseServiceHandle(scm); + return; + } + + if (!ControlService(svc, SERVICE_CONTROL_STOP, &status)) { + ErrorMsg(this).showMsg(tr("Failed to stop wpa_supplicant " + "service")); + } + + CloseServiceHandle(svc); + CloseServiceHandle(scm); +} + + +bool WpaGui::serviceRunning() +{ + SC_HANDLE svc, scm; + SERVICE_STATUS status; + bool running = false; + + scm = OpenSCManager(0, 0, SC_MANAGER_CONNECT); + if (!scm) { + debug("OpenSCManager failed: %d", (int) GetLastError()); + return false; + } + + svc = OpenService(scm, WPASVC_NAME, SERVICE_QUERY_STATUS); + if (!svc) { + debug("OpenService failed: %d", (int) GetLastError()); + CloseServiceHandle(scm); + return false; + } + + if (QueryServiceStatus(svc, &status)) { + if (status.dwCurrentState != SERVICE_STOPPED) + running = true; + } + + CloseServiceHandle(svc); + CloseServiceHandle(scm); + + return running; +} + +#endif /* CONFIG_NATIVE_WINDOWS */ + + +void WpaGui::addInterface() +{ + if (add_iface) { + add_iface->close(); + delete add_iface; + } + add_iface = new AddInterface(this, this); + add_iface->show(); + add_iface->exec(); +} + + +#ifndef QT_NO_SESSIONMANAGER +void WpaGui::saveState() +{ + QSettings settings("wpa_supplicant", "wpa_gui"); + settings.beginGroup("state"); + settings.setValue("session_id", app->sessionId()); + settings.setValue("in_tray", inTray); + settings.endGroup(); +} +#endif diff --git a/wpa_supplicant/wpa_gui-qt4/wpagui.h b/wpa_supplicant/wpa_gui-qt4/wpagui.h new file mode 100644 index 0000000000000..f0a34c97ebe8e --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/wpagui.h @@ -0,0 +1,180 @@ +/* + * wpa_gui - WpaGui class + * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WPAGUI_H +#define WPAGUI_H + +#include <QSystemTrayIcon> +#include <QObject> +#include "ui_wpagui.h" +#include "addinterface.h" + +class UserDataRequest; + +class WpaGuiApp : public QApplication +{ + Q_OBJECT +public: + WpaGuiApp(int &argc, char **argv); + +#if !defined(QT_NO_SESSIONMANAGER) && QT_VERSION < 0x050000 + virtual void saveState(QSessionManager &manager); +#endif + + WpaGui *w; + int argc; + char **argv; +}; + +class WpaGui : public QMainWindow, public Ui::WpaGui +{ + Q_OBJECT + +public: + + enum TrayIconType { + TrayIconOffline = 0, + TrayIconAcquiring, + TrayIconConnected, + TrayIconSignalNone, + TrayIconSignalWeak, + TrayIconSignalOk, + TrayIconSignalGood, + TrayIconSignalExcellent, + }; + + WpaGui(QApplication *app, QWidget *parent = 0, const char *name = 0, + Qt::WindowFlags fl = 0); + ~WpaGui(); + + virtual int ctrlRequest(const char *cmd, char *buf, size_t *buflen); + virtual void triggerUpdate(); + virtual void editNetwork(const QString &sel); + virtual void removeNetwork(const QString &sel); + virtual void enableNetwork(const QString &sel); + virtual void disableNetwork(const QString &sel); + virtual int getNetworkDisabled(const QString &sel); + void setBssFromScan(const QString &bssid); +#ifndef QT_NO_SESSIONMANAGER + void saveState(); +#endif + +public slots: + virtual void parse_argv(); + virtual void updateStatus(); + virtual void updateNetworks(); + virtual void helpIndex(); + virtual void helpContents(); + virtual void helpAbout(); + virtual void disconnect(); + virtual void scan(); + virtual void eventHistory(); + virtual void ping(); + virtual void signalMeterUpdate(); + virtual void processMsg(char *msg); + virtual void processCtrlReq(const char *req); + virtual void receiveMsgs(); + virtual void connectB(); + virtual void selectNetwork(const QString &sel); + virtual void editSelectedNetwork(); + virtual void editListedNetwork(); + virtual void removeSelectedNetwork(); + virtual void removeListedNetwork(); + virtual void addNetwork(); + virtual void enableAllNetworks(); + virtual void disableAllNetworks(); + virtual void removeAllNetworks(); + virtual void saveConfig(); + virtual void selectAdapter(const QString &sel); + virtual void updateNetworkDisabledStatus(); + virtual void enableListedNetwork(bool); + virtual void disableListedNetwork(bool); + virtual void showTrayMessage(QSystemTrayIcon::MessageIcon type, + int sec, const QString &msg); + virtual void showTrayStatus(); + virtual void updateTrayIcon(TrayIconType type); + virtual void updateTrayToolTip(const QString &msg); + virtual QIcon loadThemedIcon(const QStringList &names, + const QIcon &fallback); + virtual void wpsDialog(); + virtual void peersDialog(); + virtual void tabChanged(int index); + virtual void wpsPbc(); + virtual void wpsGeneratePin(); + virtual void wpsApPinChanged(const QString &text); + virtual void wpsApPin(); +#ifdef CONFIG_NATIVE_WINDOWS + virtual void startService(); + virtual void stopService(); +#endif /* CONFIG_NATIVE_WINDOWS */ + virtual void addInterface(); + +protected slots: + virtual void languageChange(); + virtual void trayActivated(QSystemTrayIcon::ActivationReason how); + virtual void closeEvent(QCloseEvent *event); + +private: + ScanResults *scanres; + Peers *peers; + bool networkMayHaveChanged; + char *ctrl_iface; + EventHistory *eh; + struct wpa_ctrl *ctrl_conn; + QSocketNotifier *msgNotifier; + QTimer *timer; + int pingsToStatusUpdate; + WpaMsgList msgs; + char *ctrl_iface_dir; + struct wpa_ctrl *monitor_conn; + UserDataRequest *udr; + QAction *disconnectAction; + QAction *reconnectAction; + QAction *eventAction; + QAction *scanAction; + QAction *statAction; + QAction *showAction; + QAction *hideAction; + QAction *quitAction; + QMenu *tray_menu; + QSystemTrayIcon *tray_icon; + TrayIconType currentIconType; + QString wpaStateTranslate(char *state); + void createTrayIcon(bool); + bool ackTrayIcon; + bool startInTray; + bool quietMode; + + int openCtrlConnection(const char *ifname); + + bool wpsRunning; + + QString bssFromScan; + + void stopWpsRun(bool success); + + QTimer *signalMeterTimer; + int signalMeterInterval; + +#ifdef CONFIG_NATIVE_WINDOWS + QAction *fileStartServiceAction; + QAction *fileStopServiceAction; + + bool serviceRunning(); +#endif /* CONFIG_NATIVE_WINDOWS */ + + QAction *addInterfaceAction; + AddInterface *add_iface; + + bool connectedToService; + + QApplication *app; + bool inTray; +}; + +#endif /* WPAGUI_H */ diff --git a/wpa_supplicant/wpa_gui-qt4/wpagui.ui b/wpa_supplicant/wpa_gui-qt4/wpagui.ui new file mode 100644 index 0000000000000..9f9039f6c916b --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/wpagui.ui @@ -0,0 +1,524 @@ +<ui version="4.0" > + <class>WpaGui</class> + <widget class="QMainWindow" name="WpaGui" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>345</width> + <height>330</height> + </rect> + </property> + <property name="windowTitle" > + <string>wpa_gui</string> + </property> + <property name="windowIcon" > + <iconset resource="icons.qrc" > + <normaloff>:/icons/wpa_gui.svg</normaloff>:/icons/wpa_gui.svg</iconset> + </property> + <widget class="QWidget" name="widget" > + <layout class="QGridLayout" > + <item row="0" column="0" > + <widget class="QLabel" name="adapterLabel" > + <property name="text" > + <string>Adapter:</string> + </property> + </widget> + </item> + <item row="0" column="1" > + <widget class="QComboBox" name="adapterSelect" /> + </item> + <item row="1" column="0" > + <widget class="QLabel" name="networkLabel" > + <property name="text" > + <string>Network:</string> + </property> + </widget> + </item> + <item row="1" column="1" > + <widget class="QComboBox" name="networkSelect" /> + </item> + <item row="2" column="0" colspan="2" > + <widget class="QTabWidget" name="wpaguiTab" > + <property name="currentIndex" > + <number>0</number> + </property> + <widget class="QWidget" name="statusTab" > + <attribute name="title" > + <string>Current Status</string> + </attribute> + <layout class="QGridLayout" > + <item row="0" column="0" colspan="5" > + <widget class="QFrame" name="frame3" > + <property name="frameShape" > + <enum>QFrame::NoFrame</enum> + </property> + <property name="frameShadow" > + <enum>QFrame::Plain</enum> + </property> + <layout class="QGridLayout" > + <item row="0" column="0" > + <widget class="QLabel" name="statusLabel" > + <property name="text" > + <string>Status:</string> + </property> + </widget> + </item> + <item row="1" column="0" > + <widget class="QLabel" name="lastMessageLabel" > + <property name="text" > + <string>Last message:</string> + </property> + </widget> + </item> + <item row="2" column="0" > + <widget class="QLabel" name="authenticationLabel" > + <property name="text" > + <string>Authentication:</string> + </property> + </widget> + </item> + <item row="3" column="0" > + <widget class="QLabel" name="encryptionLabel" > + <property name="text" > + <string>Encryption:</string> + </property> + </widget> + </item> + <item row="4" column="0" > + <widget class="QLabel" name="ssidLabel" > + <property name="text" > + <string>SSID:</string> + </property> + </widget> + </item> + <item row="5" column="0" > + <widget class="QLabel" name="bssidLabel" > + <property name="text" > + <string>BSSID:</string> + </property> + </widget> + </item> + <item row="6" column="0" > + <widget class="QLabel" name="ipAddressLabel" > + <property name="text" > + <string>IP address:</string> + </property> + </widget> + </item> + <item row="0" column="1" > + <widget class="QLabel" name="textStatus" > + <property name="text" > + <string/> + </property> + </widget> + </item> + <item row="1" column="1" colspan="3" > + <widget class="QLabel" name="textLastMessage" > + <property name="text" > + <string/> + </property> + </widget> + </item> + <item row="2" column="1" > + <widget class="QLabel" name="textAuthentication" > + <property name="text" > + <string/> + </property> + </widget> + </item> + <item row="3" column="1" > + <widget class="QLabel" name="textEncryption" > + <property name="text" > + <string/> + </property> + </widget> + </item> + <item row="4" column="1" > + <widget class="QLabel" name="textSsid" > + <property name="text" > + <string/> + </property> + </widget> + </item> + <item row="5" column="1" > + <widget class="QLabel" name="textBssid" > + <property name="text" > + <string/> + </property> + </widget> + </item> + <item row="6" column="1" > + <widget class="QLabel" name="textIpAddress" > + <property name="text" > + <string/> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item row="1" column="0" > + <spacer> + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" > + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="1" > + <widget class="QPushButton" name="connectButton" > + <property name="text" > + <string>Connect</string> + </property> + </widget> + </item> + <item row="1" column="2" > + <widget class="QPushButton" name="disconnectButton" > + <property name="text" > + <string>Disconnect</string> + </property> + </widget> + </item> + <item row="1" column="3" > + <widget class="QPushButton" name="scanButton" > + <property name="text" > + <string>Scan</string> + </property> + </widget> + </item> + <item row="1" column="4" > + <spacer> + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" > + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <widget class="QWidget" name="networkconfigTab" > + <attribute name="title" > + <string>Manage Networks</string> + </attribute> + <layout class="QGridLayout" > + <item row="0" column="0" colspan="5" > + <widget class="QListWidget" name="networkList" > + <property name="selectionRectVisible" > + <bool>true</bool> + </property> + </widget> + </item> + <item rowspan="2" row="1" column="0" > + <spacer> + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" > + <size> + <width>20</width> + <height>61</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="1" > + <widget class="QRadioButton" name="enableRadioButton" > + <property name="text" > + <string>Enabled</string> + </property> + </widget> + </item> + <item row="1" column="2" > + <widget class="QPushButton" name="editNetworkButton" > + <property name="text" > + <string>Edit</string> + </property> + </widget> + </item> + <item row="1" column="3" > + <widget class="QPushButton" name="removeNetworkButton" > + <property name="text" > + <string>Remove</string> + </property> + </widget> + </item> + <item rowspan="2" row="1" column="4" > + <spacer> + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" > + <size> + <width>20</width> + <height>61</height> + </size> + </property> + </spacer> + </item> + <item row="2" column="1" > + <widget class="QRadioButton" name="disableRadioButton" > + <property name="text" > + <string>Disabled</string> + </property> + </widget> + </item> + <item row="2" column="2" > + <widget class="QPushButton" name="addNetworkButton" > + <property name="text" > + <string>Add</string> + </property> + </widget> + </item> + <item row="2" column="3" > + <widget class="QPushButton" name="scanNetworkButton" > + <property name="text" > + <string>Scan</string> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="wpsTab" > + <attribute name="title" > + <string>WPS</string> + </attribute> + <layout class="QGridLayout" name="wpsGridLayout" > + <item row="0" column="0" > + <widget class="QLabel" name="label_2" > + <property name="text" > + <string>Status:</string> + </property> + </widget> + </item> + <item row="0" column="1" colspan="3" > + <widget class="QLabel" name="wpsStatusText" > + <property name="text" > + <string/> + </property> + </widget> + </item> + <item row="1" column="0" colspan="2" > + <widget class="QPushButton" name="wpsPbcButton" > + <property name="text" > + <string>PBC - push button</string> + </property> + </widget> + </item> + <item row="2" column="0" colspan="2" > + <widget class="QPushButton" name="wpsPinButton" > + <property name="text" > + <string>Generate PIN</string> + </property> + </widget> + </item> + <item row="2" column="2" > + <widget class="QLabel" name="label" > + <property name="text" > + <string>PIN:</string> + </property> + </widget> + </item> + <item row="2" column="3" > + <widget class="QLineEdit" name="wpsPinEdit" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="readOnly" > + <bool>true</bool> + </property> + </widget> + </item> + <item row="3" column="0" colspan="2" > + <widget class="QPushButton" name="wpsApPinButton" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="text" > + <string>Use AP PIN</string> + </property> + </widget> + </item> + <item row="3" column="2" > + <widget class="QLabel" name="label_3" > + <property name="text" > + <string>AP PIN:</string> + </property> + </widget> + </item> + <item row="3" column="3" > + <widget class="QLineEdit" name="wpsApPinEdit" > + <property name="enabled" > + <bool>false</bool> + </property> + </widget> + </item> + <item row="4" column="0" colspan="4" > + <widget class="QTextEdit" name="wpsInstructions" > + <property name="readOnly" > + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + </widget> + </item> + </layout> + </widget> + <widget class="QMenuBar" name="MenuBar" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>345</width> + <height>24</height> + </rect> + </property> + <widget class="QMenu" name="fileMenu" > + <property name="title" > + <string>&File</string> + </property> + <addaction name="fileEventHistoryAction" /> + <addaction name="fileSaveConfigAction" /> + <addaction name="actionWPS" /> + <addaction name="actionPeers" /> + <addaction name="separator" /> + <addaction name="fileExitAction" /> + </widget> + <widget class="QMenu" name="networkMenu" > + <property name="title" > + <string>&Network</string> + </property> + <addaction name="networkAddAction" /> + <addaction name="networkEditAction" /> + <addaction name="networkRemoveAction" /> + <addaction name="separator" /> + <addaction name="networkEnableAllAction" /> + <addaction name="networkDisableAllAction" /> + <addaction name="networkRemoveAllAction" /> + </widget> + <widget class="QMenu" name="helpMenu" > + <property name="title" > + <string>&Help</string> + </property> + <addaction name="helpContentsAction" /> + <addaction name="helpIndexAction" /> + <addaction name="separator" /> + <addaction name="helpAboutAction" /> + </widget> + <addaction name="fileMenu" /> + <addaction name="networkMenu" /> + <addaction name="helpMenu" /> + </widget> + <action name="fileEventHistoryAction" > + <property name="text" > + <string>Event &History</string> + </property> + </action> + <action name="fileSaveConfigAction" > + <property name="text" > + <string>&Save Configuration</string> + </property> + <property name="shortcut" > + <string>Ctrl+S</string> + </property> + </action> + <action name="fileExitAction" > + <property name="text" > + <string>E&xit</string> + </property> + <property name="shortcut" > + <string>Ctrl+Q</string> + </property> + </action> + <action name="networkAddAction" > + <property name="text" > + <string>&Add</string> + </property> + </action> + <action name="networkEditAction" > + <property name="text" > + <string>&Edit</string> + </property> + </action> + <action name="networkRemoveAction" > + <property name="text" > + <string>&Remove</string> + </property> + </action> + <action name="networkEnableAllAction" > + <property name="text" > + <string>E&nable All</string> + </property> + </action> + <action name="networkDisableAllAction" > + <property name="text" > + <string>&Disable All</string> + </property> + </action> + <action name="networkRemoveAllAction" > + <property name="text" > + <string>Re&move All</string> + </property> + </action> + <action name="helpContentsAction" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="text" > + <string>&Contents...</string> + </property> + </action> + <action name="helpIndexAction" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="text" > + <string>&Index...</string> + </property> + </action> + <action name="helpAboutAction" > + <property name="text" > + <string>&About</string> + </property> + </action> + <action name="actionWPS" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="text" > + <string>&Wi-Fi Protected Setup</string> + </property> + </action> + <action name="actionPeers" > + <property name="text" > + <string>&Peers</string> + </property> + </action> + </widget> + <layoutdefault spacing="6" margin="11" /> + <pixmapfunction></pixmapfunction> + <includes> + <include location="global" >qtimer.h</include> + <include location="global" >qsocketnotifier.h</include> + <include location="local" >wpamsg.h</include> + <include location="local" >eventhistory.h</include> + <include location="local" >scanresults.h</include> + <include location="local" >peers.h</include> + </includes> + <resources> + <include location="icons.qrc" /> + </resources> + <connections/> +</ui> diff --git a/wpa_supplicant/wpa_gui-qt4/wpamsg.h b/wpa_supplicant/wpa_gui-qt4/wpamsg.h new file mode 100644 index 0000000000000..8f2fcdc419886 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/wpamsg.h @@ -0,0 +1,35 @@ +/* + * wpa_gui - WpaMsg class for storing event messages + * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WPAMSG_H +#define WPAMSG_H + +#include <QDateTime> +#include <QLinkedList> + +class WpaMsg { +public: + WpaMsg(const QString &_msg, int _priority = 2) + : msg(_msg), priority(_priority) + { + timestamp = QDateTime::currentDateTime(); + } + + QString getMsg() const { return msg; } + int getPriority() const { return priority; } + QDateTime getTimestamp() const { return timestamp; } + +private: + QString msg; + int priority; + QDateTime timestamp; +}; + +typedef QLinkedList<WpaMsg> WpaMsgList; + +#endif /* WPAMSG_H */ diff --git a/wpa_supplicant/wpa_priv.c b/wpa_supplicant/wpa_priv.c index 850ec405b42c2..511df4f18148e 100644 --- a/wpa_supplicant/wpa_priv.c +++ b/wpa_supplicant/wpa_priv.c @@ -29,6 +29,8 @@ struct wpa_priv_interface { char *sock_name; int fd; + void *ctx; + const struct wpa_driver_ops *driver; void *drv_priv; void *drv_global_priv; @@ -40,6 +42,10 @@ struct wpa_priv_interface { struct sockaddr_un l2_addr; }; +struct wpa_priv_global { + struct wpa_priv_interface *interfaces; +}; + static void wpa_priv_cmd_register(struct wpa_priv_interface *iface, struct sockaddr_un *from) @@ -65,7 +71,8 @@ static void wpa_priv_cmd_register(struct wpa_priv_interface *iface, if (iface->driver->init2) { if (iface->driver->global_init) { - iface->drv_global_priv = iface->driver->global_init(); + iface->drv_global_priv = + iface->driver->global_init(iface->ctx); if (!iface->drv_global_priv) { wpa_printf(MSG_INFO, "Failed to initialize driver global context"); @@ -638,7 +645,7 @@ static void wpa_priv_interface_deinit(struct wpa_priv_interface *iface) static struct wpa_priv_interface * -wpa_priv_interface_init(const char *dir, const char *params) +wpa_priv_interface_init(void *ctx, const char *dir, const char *params) { struct wpa_priv_interface *iface; char *pos; @@ -654,6 +661,7 @@ wpa_priv_interface_init(const char *dir, const char *params) if (iface == NULL) return NULL; iface->fd = -1; + iface->ctx = ctx; len = pos - params; iface->driver_name = dup_binstr(params, len); @@ -1002,6 +1010,37 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, } +void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event, + union wpa_event_data *data) +{ + struct wpa_priv_global *global = ctx; + struct wpa_priv_interface *iface; + + if (event != EVENT_INTERFACE_STATUS) + return; + + for (iface = global->interfaces; iface; iface = iface->next) { + if (os_strcmp(iface->ifname, data->interface_status.ifname) == + 0) + break; + } + if (iface && iface->driver->get_ifindex) { + unsigned int ifindex; + + ifindex = iface->driver->get_ifindex(iface->drv_priv); + if (ifindex != data->interface_status.ifindex) { + wpa_printf(MSG_DEBUG, + "%s: interface status ifindex %d mismatch (%d)", + iface->ifname, ifindex, + data->interface_status.ifindex); + return; + } + } + if (iface) + wpa_supplicant_event(iface, event, data); +} + + void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) { @@ -1060,7 +1099,7 @@ static void wpa_priv_fd_workaround(void) static void usage(void) { printf("wpa_priv v" VERSION_STR "\n" - "Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi> and " + "Copyright (c) 2007-2016, Jouni Malinen <j@w1.fi> and " "contributors\n" "\n" "usage:\n" @@ -1077,13 +1116,17 @@ int main(int argc, char *argv[]) char *pid_file = NULL; int daemonize = 0; char *ctrl_dir = "/var/run/wpa_priv"; - struct wpa_priv_interface *interfaces = NULL, *iface; + struct wpa_priv_global global; + struct wpa_priv_interface *iface; if (os_program_init()) return -1; wpa_priv_fd_workaround(); + os_memset(&global, 0, sizeof(global)); + global.interfaces = NULL; + for (;;) { c = getopt(argc, argv, "Bc:dP:"); if (c < 0) @@ -1121,14 +1164,14 @@ int main(int argc, char *argv[]) for (i = optind; i < argc; i++) { wpa_printf(MSG_DEBUG, "Adding driver:interface %s", argv[i]); - iface = wpa_priv_interface_init(ctrl_dir, argv[i]); + iface = wpa_priv_interface_init(&global, ctrl_dir, argv[i]); if (iface == NULL) goto out; - iface->next = interfaces; - interfaces = iface; + iface->next = global.interfaces; + global.interfaces = iface; } - if (daemonize && os_daemonize(pid_file)) + if (daemonize && os_daemonize(pid_file) && eloop_sock_requeue()) goto out; eloop_register_signal_terminate(wpa_priv_terminate, NULL); @@ -1137,7 +1180,7 @@ int main(int argc, char *argv[]) ret = 0; out: - iface = interfaces; + iface = global.interfaces; while (iface) { struct wpa_priv_interface *prev = iface; iface = iface->next; diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index ef55fdcf79c08..7361ee96d1df8 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2016, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -11,6 +11,10 @@ */ #include "includes.h" +#ifdef CONFIG_MATCH_IFACE +#include <net/if.h> +#include <fnmatch.h> +#endif /* CONFIG_MATCH_IFACE */ #include "common.h" #include "crypto/random.h" @@ -58,7 +62,7 @@ const char *const wpa_supplicant_version = "wpa_supplicant v" VERSION_STR "\n" -"Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> and contributors"; +"Copyright (c) 2003-2016, Jouni Malinen <j@w1.fi> and contributors"; const char *const wpa_supplicant_license = "This software may be distributed under the terms of the BSD license.\n" @@ -188,7 +192,9 @@ static void wpa_supplicant_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; const u8 *bssid = wpa_s->bssid; - if (is_zero_ether_addr(bssid)) + if (!is_zero_ether_addr(wpa_s->pending_bssid) && + (wpa_s->wpa_state == WPA_AUTHENTICATING || + wpa_s->wpa_state == WPA_ASSOCIATING)) bssid = wpa_s->pending_bssid; wpa_msg(wpa_s, MSG_INFO, "Authentication with " MACSTR " timed out.", MAC2STR(bssid)); @@ -397,6 +403,18 @@ void free_hw_features(struct wpa_supplicant *wpa_s) } +static void free_bss_tmp_disallowed(struct wpa_supplicant *wpa_s) +{ + struct wpa_bss_tmp_disallowed *bss, *prev; + + dl_list_for_each_safe(bss, prev, &wpa_s->bss_tmp_disallowed, + struct wpa_bss_tmp_disallowed, list) { + dl_list_del(&bss->list); + os_free(bss); + } +} + + static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) { int i; @@ -536,6 +554,8 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) wpa_s->last_scan_res = NULL; #ifdef CONFIG_HS20 + if (wpa_s->drv_priv) + wpa_drv_configure_frame_filters(wpa_s, 0); hs20_deinit(wpa_s); #endif /* CONFIG_HS20 */ @@ -545,6 +565,21 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) } wmm_ac_notify_disassoc(wpa_s); + + wpa_s->sched_scan_plans_num = 0; + os_free(wpa_s->sched_scan_plans); + wpa_s->sched_scan_plans = NULL; + +#ifdef CONFIG_MBO + wpa_s->non_pref_chan_num = 0; + os_free(wpa_s->non_pref_chan); + wpa_s->non_pref_chan = NULL; +#endif /* CONFIG_MBO */ + + free_bss_tmp_disallowed(wpa_s); + + wpabuf_free(wpa_s->lci); + wpa_s->lci = NULL; } @@ -963,6 +998,11 @@ static void wpa_supplicant_reconfig(int sig, void *signal_ctx) wpa_supplicant_terminate_proc(global); } } + + if (wpa_debug_reopen_file() < 0) { + /* Ignore errors since we cannot really do much to fix this */ + wpa_printf(MSG_DEBUG, "Could not reopen debug log file"); + } } @@ -1150,6 +1190,10 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, return -1; } +#ifdef CONFIG_NO_WPA + wpa_s->group_cipher = WPA_CIPHER_NONE; + wpa_s->pairwise_cipher = WPA_CIPHER_NONE; +#else /* CONFIG_NO_WPA */ sel = ie.group_cipher & ssid->group_cipher; wpa_s->group_cipher = wpa_pick_group_cipher(sel); if (wpa_s->group_cipher < 0) { @@ -1169,6 +1213,7 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, } wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK %s", wpa_cipher_txt(wpa_s->pairwise_cipher)); +#endif /* CONFIG_NO_WPA */ sel = ie.key_mgmt & ssid->key_mgmt; #ifdef CONFIG_SAE @@ -1279,7 +1324,8 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, int psk_set = 0; if (ssid->psk_set) { - wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN, NULL); + wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN, NULL, + NULL); psk_set = 1; } #ifndef CONFIG_NO_PBKDF2 @@ -1290,7 +1336,7 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, 4096, psk, PMK_LEN); wpa_hexdump_key(MSG_MSGDUMP, "PSK (from passphrase)", psk, PMK_LEN); - wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL); + wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL, NULL); psk_set = 1; os_memset(psk, 0, sizeof(psk)); } @@ -1328,7 +1374,8 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, wpa_hexdump_key(MSG_MSGDUMP, "PSK (from " "external passphrase)", psk, PMK_LEN); - wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL); + wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL, + NULL); psk_set = 1; os_memset(psk, 0, sizeof(psk)); } else @@ -1341,7 +1388,8 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, ext_password_free(pw); return -1; } - wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL); + wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL, + NULL); psk_set = 1; os_memset(psk, 0, sizeof(psk)); } else { @@ -1404,9 +1452,20 @@ static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx) if (wpa_s->conf->hs20) *pos |= 0x40; /* Bit 46 - WNM-Notification */ #endif /* CONFIG_HS20 */ +#ifdef CONFIG_MBO + *pos |= 0x40; /* Bit 46 - WNM-Notification */ +#endif /* CONFIG_MBO */ break; case 6: /* Bits 48-55 */ break; + case 7: /* Bits 56-63 */ + break; + case 8: /* Bits 64-71 */ + if (wpa_s->conf->ftm_responder) + *pos |= 0x40; /* Bit 70 - FTM responder */ + if (wpa_s->conf->ftm_initiator) + *pos |= 0x80; /* Bit 71 - FTM initiator */ + break; } } @@ -1416,6 +1475,9 @@ int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf, size_t buflen) u8 *pos = buf; u8 len = 6, i; + if (len < 9 && + (wpa_s->conf->ftm_initiator || wpa_s->conf->ftm_responder)) + len = 9; if (len < wpa_s->extended_capa_len) len = wpa_s->extended_capa_len; if (buflen < (size_t) len + 2) { @@ -1586,6 +1648,15 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, struct wpa_connect_work *cwork; int rand_style; + wpa_s->own_disconnect_req = 0; + + /* + * If we are starting a new connection, any previously pending EAPOL + * RX cannot be valid anymore. + */ + wpabuf_free(wpa_s->pending_eapol_rx); + wpa_s->pending_eapol_rx = NULL; + if (ssid->mac_addr == -1) rand_style = wpa_s->conf->mac_addr; else @@ -1593,9 +1664,11 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, wmm_ac_clear_saved_tspecs(wpa_s); wpa_s->reassoc_same_bss = 0; + wpa_s->reassoc_same_ess = 0; if (wpa_s->last_ssid == ssid) { wpa_dbg(wpa_s, MSG_DEBUG, "Re-association to the same ESS"); + wpa_s->reassoc_same_ess = 1; if (wpa_s->current_bss && wpa_s->current_bss == bss) { wmm_ac_save_tspecs(wpa_s); wpa_s->reassoc_same_bss = 1; @@ -1661,10 +1734,9 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, return; } wpa_s->current_bss = bss; - wpa_msg_ctrl(wpa_s, MSG_INFO, MESH_GROUP_STARTED - "ssid=\"%s\" id=%d", - wpa_ssid_txt(ssid->ssid, ssid->ssid_len), - ssid->id); + wpa_msg(wpa_s, MSG_INFO, MESH_GROUP_STARTED "ssid=\"%s\" id=%d", + wpa_ssid_txt(ssid->ssid, ssid->ssid_len), + ssid->id); #else /* CONFIG_MESH */ wpa_msg(wpa_s, MSG_ERROR, "mesh mode support not included in the build"); @@ -1694,6 +1766,8 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, return; } + wpas_abort_ongoing_scan(wpa_s); + cwork = os_zalloc(sizeof(*cwork)); if (cwork == NULL) return; @@ -1715,6 +1789,36 @@ static int bss_is_ibss(struct wpa_bss *bss) } +static int drv_supports_vht(struct wpa_supplicant *wpa_s, + const struct wpa_ssid *ssid) +{ + enum hostapd_hw_mode hw_mode; + struct hostapd_hw_modes *mode = NULL; + u8 channel; + int i; + +#ifdef CONFIG_HT_OVERRIDES + if (ssid->disable_ht) + return 0; +#endif /* CONFIG_HT_OVERRIDES */ + + hw_mode = ieee80211_freq_to_chan(ssid->frequency, &channel); + if (hw_mode == NUM_HOSTAPD_MODES) + return 0; + for (i = 0; wpa_s->hw.modes && i < wpa_s->hw.num_modes; i++) { + if (wpa_s->hw.modes[i].mode == hw_mode) { + mode = &wpa_s->hw.modes[i]; + break; + } + } + + if (!mode) + return 0; + + return mode->vht_capab != 0; +} + + void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s, const struct wpa_ssid *ssid, struct hostapd_freq_params *freq) @@ -1727,8 +1831,10 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s, struct hostapd_channel_data *pri_chan = NULL, *sec_chan = NULL; u8 channel; int i, chan_idx, ht40 = -1, res, obss_scan = 1; - unsigned int j; + unsigned int j, k; struct hostapd_freq_params vht_freq; + int chwidth, seg0, seg1; + u32 vht_caps = 0; freq->freq = ssid->frequency; @@ -1780,6 +1886,13 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s, if (!mode) return; +#ifdef CONFIG_HT_OVERRIDES + if (ssid->disable_ht) { + freq->ht_enabled = 0; + return; + } +#endif /* CONFIG_HT_OVERRIDES */ + freq->ht_enabled = ht_supported(mode); if (!freq->ht_enabled) return; @@ -1801,6 +1914,11 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s, if (pri_chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR)) return; +#ifdef CONFIG_HT_OVERRIDES + if (ssid->disable_ht40) + return; +#endif /* CONFIG_HT_OVERRIDES */ + /* Check/setup HT40+/HT40- */ for (j = 0; j < ARRAY_SIZE(ht40plus); j++) { if (ht40plus[j] == channel) { @@ -1825,22 +1943,16 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s, freq->channel = pri_chan->chan; - switch (ht40) { - case -1: + if (ht40 == -1) { if (!(pri_chan->flag & HOSTAPD_CHAN_HT40MINUS)) return; - freq->sec_channel_offset = -1; - break; - case 1: + } else { if (!(pri_chan->flag & HOSTAPD_CHAN_HT40PLUS)) return; - freq->sec_channel_offset = 1; - break; - default: - break; } + freq->sec_channel_offset = ht40; - if (freq->sec_channel_offset && obss_scan) { + if (obss_scan) { struct wpa_scan_results *scan_res; scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL, 0); @@ -1878,12 +1990,12 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s, "IBSS/mesh: setup freq channel %d, sec_channel_offset %d", freq->channel, freq->sec_channel_offset); - /* Not sure if mesh is ready for VHT */ - if (ssid->mode != WPAS_MODE_IBSS) + if (!drv_supports_vht(wpa_s, ssid)) return; /* For IBSS check VHT_IBSS flag */ - if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_VHT_IBSS)) + if (ssid->mode == WPAS_MODE_IBSS && + !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_VHT_IBSS)) return; vht_freq = *freq; @@ -1914,12 +2026,55 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s, return; } + chwidth = VHT_CHANWIDTH_80MHZ; + seg0 = vht80[j] + 6; + seg1 = 0; + + if (ssid->max_oper_chwidth == VHT_CHANWIDTH_80P80MHZ) { + /* setup center_freq2, bandwidth */ + for (k = 0; k < ARRAY_SIZE(vht80); k++) { + /* Only accept 80 MHz segments separated by a gap */ + if (j == k || abs(vht80[j] - vht80[k]) == 16) + continue; + for (i = vht80[k]; i < vht80[k] + 16; i += 4) { + struct hostapd_channel_data *chan; + + chan = hw_get_channel_chan(mode, i, NULL); + if (!chan) + continue; + + if (chan->flag & (HOSTAPD_CHAN_DISABLED | + HOSTAPD_CHAN_NO_IR | + HOSTAPD_CHAN_RADAR)) + continue; + + /* Found a suitable second segment for 80+80 */ + chwidth = VHT_CHANWIDTH_80P80MHZ; + vht_caps |= + VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ; + seg1 = vht80[k] + 6; + } + + if (chwidth == VHT_CHANWIDTH_80P80MHZ) + break; + } + } else if (ssid->max_oper_chwidth == VHT_CHANWIDTH_160MHZ) { + if (freq->freq == 5180) { + chwidth = VHT_CHANWIDTH_160MHZ; + vht_caps |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; + seg0 = 50; + } else if (freq->freq == 5520) { + chwidth = VHT_CHANWIDTH_160MHZ; + vht_caps |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; + seg0 = 114; + } + } + if (hostapd_set_freq_params(&vht_freq, mode->mode, freq->freq, freq->channel, freq->ht_enabled, vht_freq.vht_enabled, freq->sec_channel_offset, - VHT_CHANWIDTH_80MHZ, - vht80[j] + 6, 0, 0) != 0) + chwidth, seg0, seg1, vht_caps) != 0) return; *freq = vht_freq; @@ -1944,6 +2099,7 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) int wep_keys_set = 0; int assoc_failed = 0; struct wpa_ssid *old_ssid; + u8 prev_bssid[ETH_ALEN]; #ifdef CONFIG_HT_OVERRIDES struct ieee80211_ht_capabilities htcaps; struct ieee80211_ht_capabilities htcaps_mask; @@ -1952,6 +2108,9 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) struct ieee80211_vht_capabilities vhtcaps; struct ieee80211_vht_capabilities vhtcaps_mask; #endif /* CONFIG_VHT_OVERRIDES */ +#ifdef CONFIG_MBO + const u8 *mbo = NULL; +#endif /* CONFIG_MBO */ if (deinit) { if (work->started) { @@ -1974,6 +2133,7 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) return; } + os_memcpy(prev_bssid, wpa_s->bssid, ETH_ALEN); os_memset(¶ms, 0, sizeof(params)); wpa_s->reassociate = 0; wpa_s->eap_expected_failure = 0; @@ -2015,7 +2175,10 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) } else { wpa_msg(wpa_s, MSG_INFO, "Trying to associate with SSID '%s'", wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); - os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); + if (bss) + os_memcpy(wpa_s->pending_bssid, bss->bssid, ETH_ALEN); + else + os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); } if (!wpa_s->pno) wpa_supplicant_cancel_sched_scan(wpa_s); @@ -2136,25 +2299,21 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) os_memset(wpa_s->p2p_ip_addr_info, 0, sizeof(wpa_s->p2p_ip_addr_info)); #endif /* CONFIG_P2P */ -#ifdef CONFIG_HS20 - if (is_hs20_network(wpa_s, ssid, bss)) { - struct wpabuf *hs20; - hs20 = wpabuf_alloc(20); - if (hs20) { - int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid); - size_t len; - - wpas_hs20_add_indication(hs20, pps_mo_id); - len = sizeof(wpa_ie) - wpa_ie_len; - if (wpabuf_len(hs20) <= len) { - os_memcpy(wpa_ie + wpa_ie_len, - wpabuf_head(hs20), wpabuf_len(hs20)); - wpa_ie_len += wpabuf_len(hs20); - } - wpabuf_free(hs20); +#ifdef CONFIG_MBO + if (bss) { + mbo = wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE); + if (mbo) { + int len; + + len = wpas_mbo_supp_op_class_ie(wpa_s, bss->freq, + wpa_ie + wpa_ie_len, + sizeof(wpa_ie) - + wpa_ie_len); + if (len > 0) + wpa_ie_len += len; } } -#endif /* CONFIG_HS20 */ +#endif /* CONFIG_MBO */ /* * Workaround: Add Extended Capabilities element only if the AP @@ -2164,6 +2323,11 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) * element in all cases, it is justifiable to skip it to avoid * interoperability issues. */ + if (ssid->p2p_group) + wpa_drv_get_ext_capa(wpa_s, WPA_IF_P2P_CLIENT); + else + wpa_drv_get_ext_capa(wpa_s, WPA_IF_STATION); + if (!bss || wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB)) { u8 ext_capab[18]; int ext_capab_len; @@ -2180,6 +2344,29 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) } } +#ifdef CONFIG_HS20 + if (is_hs20_network(wpa_s, ssid, bss)) { + struct wpabuf *hs20; + + hs20 = wpabuf_alloc(20); + if (hs20) { + int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid); + size_t len; + + wpas_hs20_add_indication(hs20, pps_mo_id); + len = sizeof(wpa_ie) - wpa_ie_len; + if (wpabuf_len(hs20) <= len) { + os_memcpy(wpa_ie + wpa_ie_len, + wpabuf_head(hs20), wpabuf_len(hs20)); + wpa_ie_len += wpabuf_len(hs20); + } + wpabuf_free(hs20); + + hs20_configure_frame_filters(wpa_s); + } + } +#endif /* CONFIG_HS20 */ + if (wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]) { struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]; size_t len; @@ -2204,6 +2391,17 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) } #endif /* CONFIG_FST */ +#ifdef CONFIG_MBO + if (mbo) { + int len; + + len = wpas_mbo_ie(wpa_s, wpa_ie + wpa_ie_len, + sizeof(wpa_ie) - wpa_ie_len); + if (len >= 0) + wpa_ie_len += len; + } +#endif /* CONFIG_MBO */ + wpa_clear_keys(wpa_s, bss ? bss->bssid : NULL); use_crypt = 1; cipher_pairwise = wpa_s->pairwise_cipher; @@ -2256,9 +2454,11 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) } params.bssid_hint = bss->bssid; params.freq_hint = bss->freq; + params.pbss = bss_is_pbss(bss); } else { params.ssid = ssid->ssid; params.ssid_len = ssid->ssid_len; + params.pbss = (ssid->pbss != 2) ? ssid->pbss : 0; } if (ssid->mode == WPAS_MODE_IBSS && ssid->bssid_set && @@ -2342,8 +2542,8 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) params.p2p = ssid->p2p_group; - if (wpa_s->parent->set_sta_uapsd) - params.uapsd = wpa_s->parent->sta_uapsd; + if (wpa_s->p2pdev->set_sta_uapsd) + params.uapsd = wpa_s->p2pdev->sta_uapsd; else params.uapsd = -1; @@ -2384,6 +2584,10 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) } #endif /* CONFIG_P2P */ + if (wpa_s->reassoc_same_ess && !is_zero_ether_addr(prev_bssid) && + wpa_s->current_ssid) + params.prev_bssid = prev_bssid; + ret = wpa_drv_associate(wpa_s, ¶ms); if (ret < 0) { wpa_msg(wpa_s, MSG_INFO, "Association request to the driver " @@ -2451,8 +2655,14 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) } old_ssid = wpa_s->current_ssid; wpa_s->current_ssid = ssid; - if (!wpas_driver_bss_selection(wpa_s) || ssid->bssid_set) + + if (!wpas_driver_bss_selection(wpa_s) || ssid->bssid_set) { wpa_s->current_bss = bss; +#ifdef CONFIG_HS20 + hs20_configure_frame_filters(wpa_s); +#endif /* CONFIG_HS20 */ + } + wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid); wpa_supplicant_initiate_eapol(wpa_s); if (old_ssid != wpa_s->current_ssid) @@ -2497,12 +2707,12 @@ void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_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)) + 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 (!is_zero_ether_addr(wpa_s->bssid)) + addr = wpa_s->bssid; else if (wpa_s->wpa_state == WPA_ASSOCIATING) { /* * When using driver-based BSS selection, we may not know the @@ -2520,8 +2730,8 @@ void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s, #ifdef CONFIG_MESH if (wpa_s->ifmsh) { - wpa_msg_ctrl(wpa_s, MSG_INFO, MESH_GROUP_REMOVED "%s", - wpa_s->ifname); + wpa_msg(wpa_s, MSG_INFO, MESH_GROUP_REMOVED "%s", + wpa_s->ifname); wpa_supplicant_leave_mesh(wpa_s); } #endif /* CONFIG_MESH */ @@ -2559,6 +2769,95 @@ static void wpa_supplicant_enable_one_network(struct wpa_supplicant *wpa_s, /** + * wpa_supplicant_add_network - Add a new network + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: The new network configuration or %NULL if operation failed + * + * This function performs the following operations: + * 1. Adds a new network. + * 2. Send network addition notification. + * 3. Marks the network disabled. + * 4. Set network default parameters. + */ +struct wpa_ssid * wpa_supplicant_add_network(struct wpa_supplicant *wpa_s) +{ + struct wpa_ssid *ssid; + + ssid = wpa_config_add_network(wpa_s->conf); + if (!ssid) + return NULL; + wpas_notify_network_added(wpa_s, ssid); + ssid->disabled = 1; + wpa_config_set_network_defaults(ssid); + + return ssid; +} + + +/** + * wpa_supplicant_remove_network - Remove a configured network based on id + * @wpa_s: wpa_supplicant structure for a network interface + * @id: Unique network id to search for + * Returns: 0 on success, or -1 if the network was not found, -2 if the network + * could not be removed + * + * This function performs the following operations: + * 1. Removes the network. + * 2. Send network removal notification. + * 3. Update internal state machines. + * 4. Stop any running sched scans. + */ +int wpa_supplicant_remove_network(struct wpa_supplicant *wpa_s, int id) +{ + struct wpa_ssid *ssid; + int was_disabled; + + ssid = wpa_config_get_network(wpa_s->conf, id); + if (!ssid) + return -1; + wpas_notify_network_removed(wpa_s, ssid); + + if (wpa_s->last_ssid == ssid) + wpa_s->last_ssid = NULL; + + if (ssid == wpa_s->current_ssid || !wpa_s->current_ssid) { +#ifdef CONFIG_SME + wpa_s->sme.prev_bssid_set = 0; +#endif /* CONFIG_SME */ + /* + * Invalidate the EAP session cache if the current or + * previously used network is removed. + */ + eapol_sm_invalidate_cached_session(wpa_s->eapol); + } + + if (ssid == wpa_s->current_ssid) { + wpa_sm_set_config(wpa_s->wpa, NULL); + eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); + + if (wpa_s->wpa_state >= WPA_AUTHENTICATING) + wpa_s->own_disconnect_req = 1; + wpa_supplicant_deauthenticate(wpa_s, + WLAN_REASON_DEAUTH_LEAVING); + } + + was_disabled = ssid->disabled; + + if (wpa_config_remove_network(wpa_s->conf, id) < 0) + return -2; + + if (!was_disabled && wpa_s->sched_scanning) { + wpa_printf(MSG_DEBUG, + "Stop ongoing sched_scan to remove network from filters"); + wpa_supplicant_cancel_sched_scan(wpa_s); + wpa_supplicant_req_scan(wpa_s, 0, 0); + } + + return 0; +} + + +/** * wpa_supplicant_enable_network - Mark a configured network as enabled * @wpa_s: wpa_supplicant structure for a network interface * @ssid: wpa_ssid structure for a configured network or %NULL @@ -2688,7 +2987,8 @@ void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s, wpas_notify_network_enabled_changed(wpa_s, other_ssid); } - if (ssid && ssid == wpa_s->current_ssid && wpa_s->current_ssid) { + if (ssid && ssid == wpa_s->current_ssid && wpa_s->current_ssid && + wpa_s->wpa_state >= WPA_AUTHENTICATING) { /* We are already associated with the selected network */ wpa_printf(MSG_DEBUG, "Already associated with the " "selected network - do nothing"); @@ -2717,6 +3017,7 @@ void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s, if (wpa_s->connect_without_scan || wpa_supplicant_fast_associate(wpa_s) != 1) { wpa_s->scan_req = NORMAL_SCAN_REQ; + wpas_scan_reset_sched_scan(wpa_s); wpa_supplicant_req_scan(wpa_s, 0, disconnected ? 100000 : 0); } @@ -2994,7 +3295,7 @@ 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(); + global->drv_priv[i] = wpa_drivers[i]->global_init(global); if (global->drv_priv[i] == NULL) { wpa_printf(MSG_ERROR, "Failed to initialize driver " "'%s'", wpa_drivers[i]->name); @@ -3077,6 +3378,13 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, wpa_dbg(wpa_s, MSG_DEBUG, "RX EAPOL from " MACSTR, MAC2STR(src_addr)); wpa_hexdump(MSG_MSGDUMP, "RX EAPOL", buf, len); +#ifdef CONFIG_TESTING_OPTIONS + if (wpa_s->ignore_auth_resp) { + wpa_printf(MSG_INFO, "RX EAPOL - ignore_auth_resp active!"); + return; + } +#endif /* CONFIG_TESTING_OPTIONS */ + #ifdef CONFIG_PEERKEY if (wpa_s->wpa_state > WPA_ASSOCIATED && wpa_s->current_ssid && wpa_s->current_ssid->peerkey && @@ -3361,8 +3669,11 @@ wpa_supplicant_alloc(struct wpa_supplicant *parent) wpa_s->scan_interval = 5; wpa_s->new_connection = 1; wpa_s->parent = parent ? parent : wpa_s; + wpa_s->p2pdev = wpa_s->parent; wpa_s->sched_scanning = 0; + dl_list_init(&wpa_s->bss_tmp_disallowed); + return wpa_s; } @@ -3614,8 +3925,8 @@ void wpa_supplicant_apply_vht_overrides( if (!vhtcaps || !vhtcaps_mask) return; - vhtcaps->vht_capabilities_info = ssid->vht_capa; - vhtcaps_mask->vht_capabilities_info = ssid->vht_capa_mask; + vhtcaps->vht_capabilities_info = host_to_le32(ssid->vht_capa); + vhtcaps_mask->vht_capabilities_info = host_to_le32(ssid->vht_capa_mask); #ifdef CONFIG_HT_OVERRIDES /* if max ampdu is <= 3, we have to make the HT cap the same */ @@ -3637,15 +3948,17 @@ void wpa_supplicant_apply_vht_overrides( #define OVERRIDE_MCS(i) \ if (ssid->vht_tx_mcs_nss_ ##i >= 0) { \ vhtcaps_mask->vht_supported_mcs_set.tx_map |= \ - 3 << 2 * (i - 1); \ + host_to_le16(3 << 2 * (i - 1)); \ vhtcaps->vht_supported_mcs_set.tx_map |= \ - ssid->vht_tx_mcs_nss_ ##i << 2 * (i - 1); \ + host_to_le16(ssid->vht_tx_mcs_nss_ ##i << \ + 2 * (i - 1)); \ } \ if (ssid->vht_rx_mcs_nss_ ##i >= 0) { \ vhtcaps_mask->vht_supported_mcs_set.rx_map |= \ - 3 << 2 * (i - 1); \ + host_to_le16(3 << 2 * (i - 1)); \ vhtcaps->vht_supported_mcs_set.rx_map |= \ - ssid->vht_rx_mcs_nss_ ##i << 2 * (i - 1); \ + host_to_le16(ssid->vht_rx_mcs_nss_ ##i << \ + 2 * (i - 1)); \ } OVERRIDE_MCS(1); @@ -3817,8 +4130,9 @@ static void wpas_fst_update_mb_ie_cb(void *ctx, const u8 *addr, } -const u8 * wpas_fst_get_peer_first(void *ctx, struct fst_get_peer_ctx **get_ctx, - Boolean mb_only) +static const u8 * wpas_fst_get_peer_first(void *ctx, + struct fst_get_peer_ctx **get_ctx, + Boolean mb_only) { struct wpa_supplicant *wpa_s = ctx; @@ -3830,8 +4144,9 @@ const u8 * wpas_fst_get_peer_first(void *ctx, struct fst_get_peer_ctx **get_ctx, } -const u8 * wpas_fst_get_peer_next(void *ctx, struct fst_get_peer_ctx **get_ctx, - Boolean mb_only) +static const u8 * wpas_fst_get_peer_next(void *ctx, + struct fst_get_peer_ctx **get_ctx, + Boolean mb_only) { return NULL; } @@ -3870,6 +4185,55 @@ static int wpas_set_wowlan_triggers(struct wpa_supplicant *wpa_s, } +enum wpa_radio_work_band wpas_freq_to_band(int freq) +{ + if (freq < 3000) + return BAND_2_4_GHZ; + if (freq > 50000) + return BAND_60_GHZ; + return BAND_5_GHZ; +} + + +unsigned int wpas_get_bands(struct wpa_supplicant *wpa_s, const int *freqs) +{ + int i; + unsigned int band = 0; + + if (freqs) { + /* freqs are specified for the radio work */ + for (i = 0; freqs[i]; i++) + band |= wpas_freq_to_band(freqs[i]); + } else { + /* + * freqs are not specified, implies all + * the supported freqs by HW + */ + for (i = 0; i < wpa_s->hw.num_modes; i++) { + if (wpa_s->hw.modes[i].num_channels != 0) { + if (wpa_s->hw.modes[i].mode == + HOSTAPD_MODE_IEEE80211B || + wpa_s->hw.modes[i].mode == + HOSTAPD_MODE_IEEE80211G) + band |= BAND_2_4_GHZ; + else if (wpa_s->hw.modes[i].mode == + HOSTAPD_MODE_IEEE80211A) + band |= BAND_5_GHZ; + else if (wpa_s->hw.modes[i].mode == + HOSTAPD_MODE_IEEE80211AD) + band |= BAND_60_GHZ; + else if (wpa_s->hw.modes[i].mode == + HOSTAPD_MODE_IEEE80211ANY) + band = BAND_2_4_GHZ | BAND_5_GHZ | + BAND_60_GHZ; + } + } + } + + return band; +} + + static struct wpa_radio * radio_add_interface(struct wpa_supplicant *wpa_s, const char *rn) { @@ -3922,11 +4286,103 @@ static void radio_work_free(struct wpa_radio_work *work) } #endif /* CONFIG_P2P */ + if (work->started) { + work->wpa_s->radio->num_active_works--; + wpa_dbg(work->wpa_s, MSG_DEBUG, + "radio_work_free('%s'@%p: num_active_works --> %u", + work->type, work, + work->wpa_s->radio->num_active_works); + } + dl_list_del(&work->list); os_free(work); } +static struct wpa_radio_work * radio_work_get_next_work(struct wpa_radio *radio) +{ + struct wpa_radio_work *active_work = NULL; + struct wpa_radio_work *tmp; + + /* Get the active work to know the type and band. */ + dl_list_for_each(tmp, &radio->work, struct wpa_radio_work, list) { + if (tmp->started) { + active_work = tmp; + break; + } + } + + if (!active_work) { + /* No active work, start one */ + radio->num_active_works = 0; + dl_list_for_each(tmp, &radio->work, struct wpa_radio_work, + list) { + if (os_strcmp(tmp->type, "scan") == 0 && + radio->external_scan_running && + (((struct wpa_driver_scan_params *) + tmp->ctx)->only_new_results || + tmp->wpa_s->clear_driver_scan_cache)) + continue; + return tmp; + } + return NULL; + } + + if (os_strcmp(active_work->type, "sme-connect") == 0 || + os_strcmp(active_work->type, "connect") == 0) { + /* + * If the active work is either connect or sme-connect, + * do not parallelize them with other radio works. + */ + wpa_dbg(active_work->wpa_s, MSG_DEBUG, + "Do not parallelize radio work with %s", + active_work->type); + return NULL; + } + + dl_list_for_each(tmp, &radio->work, struct wpa_radio_work, list) { + if (tmp->started) + continue; + + /* + * If connect or sme-connect are enqueued, parallelize only + * those operations ahead of them in the queue. + */ + if (os_strcmp(tmp->type, "connect") == 0 || + os_strcmp(tmp->type, "sme-connect") == 0) + break; + + /* + * Check that the radio works are distinct and + * on different bands. + */ + if (os_strcmp(active_work->type, tmp->type) != 0 && + (active_work->bands != tmp->bands)) { + /* + * If a scan has to be scheduled through nl80211 scan + * interface and if an external scan is already running, + * do not schedule the scan since it is likely to get + * rejected by kernel. + */ + if (os_strcmp(tmp->type, "scan") == 0 && + radio->external_scan_running && + (((struct wpa_driver_scan_params *) + tmp->ctx)->only_new_results || + tmp->wpa_s->clear_driver_scan_cache)) + continue; + + wpa_dbg(active_work->wpa_s, MSG_DEBUG, + "active_work:%s new_work:%s", + active_work->type, tmp->type); + return tmp; + } + } + + /* Did not find a radio work to schedule in parallel. */ + return NULL; +} + + static void radio_start_next_work(void *eloop_ctx, void *timeout_ctx) { struct wpa_radio *radio = eloop_ctx; @@ -3935,26 +4391,48 @@ static void radio_start_next_work(void *eloop_ctx, void *timeout_ctx) struct wpa_supplicant *wpa_s; work = dl_list_first(&radio->work, struct wpa_radio_work, list); - if (work == NULL) + if (work == NULL) { + radio->num_active_works = 0; return; - - if (work->started) - return; /* already started and still in progress */ + } wpa_s = dl_list_first(&radio->ifaces, struct wpa_supplicant, radio_list); - if (wpa_s && wpa_s->radio->external_scan_running) { - wpa_printf(MSG_DEBUG, "Delay radio work start until externally triggered scan completes"); - return; + + if (!(wpa_s && + wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS)) { + if (work->started) + return; /* already started and still in progress */ + + if (wpa_s && wpa_s->radio->external_scan_running) { + wpa_printf(MSG_DEBUG, "Delay radio work start until externally triggered scan completes"); + return; + } + } else { + work = NULL; + if (radio->num_active_works < MAX_ACTIVE_WORKS) { + /* get the work to schedule next */ + work = radio_work_get_next_work(radio); + } + if (!work) + return; } + wpa_s = work->wpa_s; os_get_reltime(&now); os_reltime_sub(&now, &work->time, &diff); - wpa_dbg(work->wpa_s, MSG_DEBUG, "Starting radio work '%s'@%p after %ld.%06ld second wait", + wpa_dbg(wpa_s, MSG_DEBUG, + "Starting radio work '%s'@%p after %ld.%06ld second wait", work->type, work, diff.sec, diff.usec); work->started = 1; work->time = now; + radio->num_active_works++; + work->cb(work, 0); + + if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS) && + radio->num_active_works < MAX_ACTIVE_WORKS) + radio_work_check_next(wpa_s); } @@ -4062,6 +4540,7 @@ int radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq, void (*cb)(struct wpa_radio_work *work, int deinit), void *ctx) { + struct wpa_radio *radio = wpa_s->radio; struct wpa_radio_work *work; int was_empty; @@ -4076,6 +4555,16 @@ int radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq, work->cb = cb; work->ctx = ctx; + if (freq) + work->bands = wpas_freq_to_band(freq); + else if (os_strcmp(type, "scan") == 0 || + os_strcmp(type, "p2p-scan") == 0) + work->bands = wpas_get_bands(wpa_s, + ((struct wpa_driver_scan_params *) + ctx)->freqs); + else + work->bands = wpas_get_bands(wpa_s, NULL); + was_empty = dl_list_empty(&wpa_s->radio->work); if (next) dl_list_add(&wpa_s->radio->work, &work->list); @@ -4084,6 +4573,12 @@ int radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq, if (was_empty) { wpa_dbg(wpa_s, MSG_DEBUG, "First radio work item in the queue - schedule start immediately"); radio_work_check_next(wpa_s); + } else if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS) + && radio->num_active_works < MAX_ACTIVE_WORKS) { + wpa_dbg(wpa_s, MSG_DEBUG, + "Try to schedule a radio work (num_active_works=%u)", + radio->num_active_works); + radio_work_check_next(wpa_s); } return 0; @@ -4339,6 +4834,11 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, 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->max_sched_scan_plans = capa.max_sched_scan_plans; + wpa_s->max_sched_scan_plan_interval = + capa.max_sched_scan_plan_interval; + wpa_s->max_sched_scan_plan_iterations = + capa.max_sched_scan_plan_iterations; 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; @@ -4478,6 +4978,17 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, wpas_rrm_reset(wpa_s); + wpas_sched_scan_plans_set(wpa_s, wpa_s->conf->sched_scan_plans); + +#ifdef CONFIG_HS20 + hs20_init(wpa_s); +#endif /* CONFIG_HS20 */ +#ifdef CONFIG_MBO + wpas_mbo_update_non_pref_chan(wpa_s, wpa_s->conf->non_pref_chan); +#endif /* CONFIG_MBO */ + + wpa_supplicant_set_default_scan_ies(wpa_s); + return 0; } @@ -4493,6 +5004,8 @@ static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s, iface = global->ifaces; while (iface) { + if (iface->p2pdev == wpa_s) + iface->p2pdev = iface->parent; if (iface == wpa_s || iface->parent != wpa_s) { iface = iface->next; continue; @@ -4563,6 +5076,74 @@ static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s, } +#ifdef CONFIG_MATCH_IFACE + +/** + * wpa_supplicant_match_iface - Match an interface description to a name + * @global: Pointer to global data from wpa_supplicant_init() + * @ifname: Name of the interface to match + * Returns: Pointer to the created interface description or %NULL on failure + */ +struct wpa_interface * wpa_supplicant_match_iface(struct wpa_global *global, + const char *ifname) +{ + int i; + struct wpa_interface *iface, *miface; + + for (i = 0; i < global->params.match_iface_count; i++) { + miface = &global->params.match_ifaces[i]; + if (!miface->ifname || + fnmatch(miface->ifname, ifname, 0) == 0) { + iface = os_zalloc(sizeof(*iface)); + if (!iface) + return NULL; + *iface = *miface; + iface->ifname = ifname; + return iface; + } + } + + return NULL; +} + + +/** + * wpa_supplicant_match_existing - Match existing interfaces + * @global: Pointer to global data from wpa_supplicant_init() + * Returns: 0 on success, -1 on failure + */ +static int wpa_supplicant_match_existing(struct wpa_global *global) +{ + struct if_nameindex *ifi, *ifp; + struct wpa_supplicant *wpa_s; + struct wpa_interface *iface; + + ifp = if_nameindex(); + if (!ifp) { + wpa_printf(MSG_ERROR, "if_nameindex: %s", strerror(errno)); + return -1; + } + + for (ifi = ifp; ifi->if_name; ifi++) { + wpa_s = wpa_supplicant_get_iface(global, ifi->if_name); + if (wpa_s) + continue; + iface = wpa_supplicant_match_iface(global, ifi->if_name); + if (iface) { + wpa_s = wpa_supplicant_add_iface(global, iface, NULL); + os_free(iface); + if (wpa_s) + wpa_s->matched = 1; + } + } + + if_freenameindex(ifp); + return 0; +} + +#endif /* CONFIG_MATCH_IFACE */ + + /** * wpa_supplicant_add_iface - Add a new network interface * @global: Pointer to global data from wpa_supplicant_init() @@ -4864,6 +5445,18 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params) if (params->override_ctrl_interface) global->params.override_ctrl_interface = os_strdup(params->override_ctrl_interface); +#ifdef CONFIG_MATCH_IFACE + global->params.match_iface_count = params->match_iface_count; + if (params->match_iface_count) { + global->params.match_ifaces = + os_calloc(params->match_iface_count, + sizeof(struct wpa_interface)); + os_memcpy(global->params.match_ifaces, + params->match_ifaces, + params->match_iface_count * + sizeof(struct wpa_interface)); + } +#endif /* CONFIG_MATCH_IFACE */ #ifdef CONFIG_P2P if (params->conf_p2p_dev) global->params.conf_p2p_dev = @@ -4939,12 +5532,18 @@ int wpa_supplicant_run(struct wpa_global *global) struct wpa_supplicant *wpa_s; if (global->params.daemonize && - wpa_supplicant_daemon(global->params.pid_file)) + (wpa_supplicant_daemon(global->params.pid_file) || + eloop_sock_requeue())) + return -1; + +#ifdef CONFIG_MATCH_IFACE + if (wpa_supplicant_match_existing(global)) return -1; +#endif if (global->params.wait_for_monitor) { for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) - if (wpa_s->ctrl_iface) + if (wpa_s->ctrl_iface && !wpa_s->p2p_mgmt) wpa_supplicant_ctrl_iface_wait( wpa_s->ctrl_iface); } @@ -5010,6 +5609,9 @@ void wpa_supplicant_deinit(struct wpa_global *global) os_free(global->params.ctrl_interface_group); os_free(global->params.override_driver); os_free(global->params.override_ctrl_interface); +#ifdef CONFIG_MATCH_IFACE + os_free(global->params.match_ifaces); +#endif /* CONFIG_MATCH_IFACE */ #ifdef CONFIG_P2P os_free(global->params.conf_p2p_dev); #endif /* CONFIG_P2P */ @@ -5042,6 +5644,9 @@ void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s) if (wpa_s->conf->changed_parameters & CFG_CHANGED_EXT_PW_BACKEND) wpas_init_ext_pw(wpa_s); + if (wpa_s->conf->changed_parameters & CFG_CHANGED_SCHED_SCAN_PLANS) + wpas_sched_scan_plans_set(wpa_s, wpa_s->conf->sched_scan_plans); + #ifdef CONFIG_WPS wpas_wps_update_config(wpa_s); #endif /* CONFIG_WPS */ @@ -5281,6 +5886,16 @@ int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s, if (wpa_s->wpa_state == WPA_SCANNING && !wpa_s->scanning) wpa_supplicant_req_scan(wpa_s, 0, 0); break; + case WPA_CTRL_REQ_EXT_CERT_CHECK: + if (eap->pending_ext_cert_check != PENDING_CHECK) + return -1; + if (os_strcmp(value, "good") == 0) + eap->pending_ext_cert_check = EXT_CERT_CHECK_GOOD; + else if (os_strcmp(value, "bad") == 0) + eap->pending_ext_cert_check = EXT_CERT_CHECK_BAD; + else + return -1; + break; default: wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown field '%s'", field); return -1; @@ -5350,6 +5965,19 @@ int wpas_get_ssid_pmf(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) return NO_MGMT_FRAME_PROTECTION; } + if (ssid && + (ssid->key_mgmt & + ~(WPA_KEY_MGMT_NONE | WPA_KEY_MGMT_WPS | + WPA_KEY_MGMT_IEEE8021X_NO_WPA)) == 0) { + /* + * Do not use the default PMF value for non-RSN networks + * since PMF is available only with RSN and pmf=2 + * configuration would otherwise prevent connections to + * all open networks. + */ + return NO_MGMT_FRAME_PROTECTION; + } + return wpa_s->conf->pmf; } @@ -5508,6 +6136,27 @@ void wpas_request_connection(struct wpa_supplicant *wpa_s) } +/** + * wpas_request_disconnection - Request disconnection + * @wpa_s: Pointer to the network interface + * + * This function is used to request disconnection from the currently connected + * network. This will stop any ongoing scans and initiate deauthentication. + */ +void wpas_request_disconnection(struct wpa_supplicant *wpa_s) +{ +#ifdef CONFIG_SME + wpa_s->sme.prev_bssid_set = 0; +#endif /* CONFIG_SME */ + wpa_s->reassociate = 0; + wpa_s->disconnected = 1; + wpa_supplicant_cancel_sched_scan(wpa_s); + wpa_supplicant_cancel_scan(wpa_s); + wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); + eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL); +} + + void dump_freq_data(struct wpa_supplicant *wpa_s, const char *title, struct wpa_used_freq_data *freqs_data, unsigned int len) @@ -5690,11 +6339,19 @@ void wpas_rrm_process_neighbor_rep(struct wpa_supplicant *wpa_s, #define ECANCELED -1 #endif +/* Measurement Request element + Location Subject + Maximum Age subelement */ +#define MEASURE_REQUEST_LCI_LEN (3 + 1 + 4) +/* Measurement Request element + Location Civic Request */ +#define MEASURE_REQUEST_CIVIC_LEN (3 + 5) + + /** * wpas_rrm_send_neighbor_rep_request - Request a neighbor report from our AP * @wpa_s: Pointer to wpa_supplicant * @ssid: if not null, this is sent in the request. Otherwise, no SSID IE * is sent in the request. + * @lci: if set, neighbor request will include LCI request + * @civic: if set, neighbor request will include civic location request * @cb: Callback function to be called once the requested report arrives, or * timed out after RRM_NEIGHBOR_REPORT_TIMEOUT seconds. * In the former case, 'neighbor_rep' is a newly allocated wpabuf, and it's @@ -5708,7 +6365,8 @@ void wpas_rrm_process_neighbor_rep(struct wpa_supplicant *wpa_s, * Request must contain a callback function. */ int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s, - const struct wpa_ssid *ssid, + const struct wpa_ssid_value *ssid, + int lci, int civic, void (*cb)(void *ctx, struct wpabuf *neighbor_rep), void *cb_ctx) @@ -5749,7 +6407,9 @@ int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s, } /* 3 = action category + action code + dialog token */ - buf = wpabuf_alloc(3 + (ssid ? 2 + ssid->ssid_len : 0)); + buf = wpabuf_alloc(3 + (ssid ? 2 + ssid->ssid_len : 0) + + (lci ? 2 + MEASURE_REQUEST_LCI_LEN : 0) + + (civic ? 2 + MEASURE_REQUEST_CIVIC_LEN : 0)); if (buf == NULL) { wpa_printf(MSG_DEBUG, "RRM: Failed to allocate Neighbor Report Request"); @@ -5769,6 +6429,72 @@ int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s, wpabuf_put_data(buf, ssid->ssid, ssid->ssid_len); } + if (lci) { + /* IEEE P802.11-REVmc/D5.0 9.4.2.21 */ + wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST); + wpabuf_put_u8(buf, MEASURE_REQUEST_LCI_LEN); + + /* + * Measurement token; nonzero number that is unique among the + * Measurement Request elements in a particular frame. + */ + wpabuf_put_u8(buf, 1); /* Measurement Token */ + + /* + * Parallel, Enable, Request, and Report bits are 0, Duration is + * reserved. + */ + wpabuf_put_u8(buf, 0); /* Measurement Request Mode */ + wpabuf_put_u8(buf, MEASURE_TYPE_LCI); /* Measurement Type */ + + /* IEEE P802.11-REVmc/D5.0 9.4.2.21.10 - LCI request */ + /* Location Subject */ + wpabuf_put_u8(buf, LOCATION_SUBJECT_REMOTE); + + /* Optional Subelements */ + /* + * IEEE P802.11-REVmc/D5.0 Figure 9-170 + * The Maximum Age subelement is required, otherwise the AP can + * send only data that was determined after receiving the + * request. Setting it here to unlimited age. + */ + wpabuf_put_u8(buf, LCI_REQ_SUBELEM_MAX_AGE); + wpabuf_put_u8(buf, 2); + wpabuf_put_le16(buf, 0xffff); + } + + if (civic) { + /* IEEE P802.11-REVmc/D5.0 9.4.2.21 */ + wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST); + wpabuf_put_u8(buf, MEASURE_REQUEST_CIVIC_LEN); + + /* + * Measurement token; nonzero number that is unique among the + * Measurement Request elements in a particular frame. + */ + wpabuf_put_u8(buf, 2); /* Measurement Token */ + + /* + * Parallel, Enable, Request, and Report bits are 0, Duration is + * reserved. + */ + wpabuf_put_u8(buf, 0); /* Measurement Request Mode */ + /* Measurement Type */ + wpabuf_put_u8(buf, MEASURE_TYPE_LOCATION_CIVIC); + + /* IEEE P802.11-REVmc/D5.0 9.4.2.21.14: + * Location Civic request */ + /* Location Subject */ + wpabuf_put_u8(buf, LOCATION_SUBJECT_REMOTE); + wpabuf_put_u8(buf, 0); /* Civic Location Type: IETF RFC 4776 */ + /* Location Service Interval Units: Seconds */ + wpabuf_put_u8(buf, 0); + /* Location Service Interval: 0 - Only one report is requested + */ + wpabuf_put_le16(buf, 0); + /* No optional subelements */ + } + wpa_s->rrm.next_neighbor_rep_token++; if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, @@ -5791,6 +6517,147 @@ int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s, } +static struct wpabuf * wpas_rrm_build_lci_report(struct wpa_supplicant *wpa_s, + const u8 *request, size_t len, + struct wpabuf *report) +{ + u8 token, type, subject; + u16 max_age = 0; + struct os_reltime t, diff; + unsigned long diff_l; + u8 *ptoken; + const u8 *subelem; + + if (!wpa_s->lci || len < 3 + 4) + return report; + + token = *request++; + /* Measurement request mode isn't used */ + request++; + type = *request++; + subject = *request++; + + wpa_printf(MSG_DEBUG, + "Measurement request token %u type %u location subject %u", + token, type, subject); + + if (type != MEASURE_TYPE_LCI || subject != LOCATION_SUBJECT_REMOTE) { + wpa_printf(MSG_INFO, + "Not building LCI report - bad type or location subject"); + return report; + } + + /* Subelements are formatted exactly like elements */ + subelem = get_ie(request, len, LCI_REQ_SUBELEM_MAX_AGE); + if (subelem && subelem[1] == 2) + max_age = WPA_GET_LE16(subelem + 2); + + if (os_get_reltime(&t)) + return report; + + os_reltime_sub(&t, &wpa_s->lci_time, &diff); + /* LCI age is calculated in 10th of a second units. */ + diff_l = diff.sec * 10 + diff.usec / 100000; + + if (max_age != 0xffff && max_age < diff_l) + return report; + + if (wpabuf_resize(&report, 2 + wpabuf_len(wpa_s->lci))) + return report; + + wpabuf_put_u8(report, WLAN_EID_MEASURE_REPORT); + wpabuf_put_u8(report, wpabuf_len(wpa_s->lci)); + /* We'll override user's measurement token */ + ptoken = wpabuf_put(report, 0); + wpabuf_put_buf(report, wpa_s->lci); + *ptoken = token; + + return report; +} + + +void wpas_rrm_handle_radio_measurement_request(struct wpa_supplicant *wpa_s, + const u8 *src, + const u8 *frame, size_t len) +{ + struct wpabuf *buf, *report; + u8 token; + const u8 *ie, *end; + + if (wpa_s->wpa_state != WPA_COMPLETED) { + wpa_printf(MSG_INFO, + "RRM: Ignoring radio measurement request: Not associated"); + return; + } + + if (!wpa_s->rrm.rrm_used) { + wpa_printf(MSG_INFO, + "RRM: Ignoring radio measurement request: Not RRM network"); + return; + } + + if (len < 3) { + wpa_printf(MSG_INFO, + "RRM: Ignoring too short radio measurement request"); + return; + } + + end = frame + len; + + token = *frame++; + + /* Ignore number of repetitions because it's not used in LCI request */ + frame += 2; + + report = NULL; + while ((ie = get_ie(frame, end - frame, WLAN_EID_MEASURE_REQUEST)) && + ie[1] >= 3) { + u8 msmt_type; + + msmt_type = ie[4]; + wpa_printf(MSG_DEBUG, "RRM request %d", msmt_type); + + switch (msmt_type) { + case MEASURE_TYPE_LCI: + report = wpas_rrm_build_lci_report(wpa_s, ie + 2, ie[1], + report); + break; + default: + wpa_printf(MSG_INFO, + "RRM: Unsupported radio measurement request %d", + msmt_type); + break; + } + + frame = ie + ie[1] + 2; + } + + if (!report) + return; + + buf = wpabuf_alloc(3 + wpabuf_len(report)); + if (!buf) { + wpabuf_free(report); + return; + } + + wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT); + wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REPORT); + wpabuf_put_u8(buf, token); + + wpabuf_put_buf(buf, report); + wpabuf_free(report); + + if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, src, + wpa_s->own_addr, wpa_s->bssid, + wpabuf_head(buf), wpabuf_len(buf), 0)) { + wpa_printf(MSG_ERROR, + "RRM: Radio measurement report failed: Sending Action frame failed"); + } + wpabuf_free(buf); +} + + void wpas_rrm_handle_link_measurement_request(struct wpa_supplicant *wpa_s, const u8 *src, const u8 *frame, size_t len, @@ -5868,3 +6735,175 @@ void wpas_rrm_handle_link_measurement_request(struct wpa_supplicant *wpa_s, } wpabuf_free(buf); } + + +struct wpa_supplicant * +wpas_vendor_elem(struct wpa_supplicant *wpa_s, enum wpa_vendor_elem_frame frame) +{ + switch (frame) { +#ifdef CONFIG_P2P + case VENDOR_ELEM_PROBE_REQ_P2P: + case VENDOR_ELEM_PROBE_RESP_P2P: + case VENDOR_ELEM_PROBE_RESP_P2P_GO: + case VENDOR_ELEM_BEACON_P2P_GO: + case VENDOR_ELEM_P2P_PD_REQ: + case VENDOR_ELEM_P2P_PD_RESP: + case VENDOR_ELEM_P2P_GO_NEG_REQ: + case VENDOR_ELEM_P2P_GO_NEG_RESP: + case VENDOR_ELEM_P2P_GO_NEG_CONF: + case VENDOR_ELEM_P2P_INV_REQ: + case VENDOR_ELEM_P2P_INV_RESP: + case VENDOR_ELEM_P2P_ASSOC_REQ: + case VENDOR_ELEM_P2P_ASSOC_RESP: + return wpa_s->p2pdev; +#endif /* CONFIG_P2P */ + default: + return wpa_s; + } +} + + +void wpas_vendor_elem_update(struct wpa_supplicant *wpa_s) +{ + unsigned int i; + char buf[30]; + + wpa_printf(MSG_DEBUG, "Update vendor elements"); + + for (i = 0; i < NUM_VENDOR_ELEM_FRAMES; i++) { + if (wpa_s->vendor_elem[i]) { + int res; + + res = os_snprintf(buf, sizeof(buf), "frame[%u]", i); + if (!os_snprintf_error(sizeof(buf), res)) { + wpa_hexdump_buf(MSG_DEBUG, buf, + wpa_s->vendor_elem[i]); + } + } + } + +#ifdef CONFIG_P2P + if (wpa_s->parent == wpa_s && + wpa_s->global->p2p && + !wpa_s->global->p2p_disabled) + p2p_set_vendor_elems(wpa_s->global->p2p, wpa_s->vendor_elem); +#endif /* CONFIG_P2P */ +} + + +int wpas_vendor_elem_remove(struct wpa_supplicant *wpa_s, int frame, + const u8 *elem, size_t len) +{ + u8 *ie, *end; + + ie = wpabuf_mhead_u8(wpa_s->vendor_elem[frame]); + end = ie + wpabuf_len(wpa_s->vendor_elem[frame]); + + for (; ie + 1 < end; ie += 2 + ie[1]) { + if (ie + len > end) + break; + if (os_memcmp(ie, elem, len) != 0) + continue; + + if (wpabuf_len(wpa_s->vendor_elem[frame]) == len) { + wpabuf_free(wpa_s->vendor_elem[frame]); + wpa_s->vendor_elem[frame] = NULL; + } else { + os_memmove(ie, ie + len, end - (ie + len)); + wpa_s->vendor_elem[frame]->used -= len; + } + wpas_vendor_elem_update(wpa_s); + return 0; + } + + return -1; +} + + +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 struct +wpa_bss_tmp_disallowed * wpas_get_disallowed_bss(struct wpa_supplicant *wpa_s, + const u8 *bssid) +{ + struct wpa_bss_tmp_disallowed *bss; + + dl_list_for_each(bss, &wpa_s->bss_tmp_disallowed, + struct wpa_bss_tmp_disallowed, list) { + if (os_memcmp(bssid, bss->bssid, ETH_ALEN) == 0) + return bss; + } + + return NULL; +} + + +void wpa_bss_tmp_disallow(struct wpa_supplicant *wpa_s, const u8 *bssid, + unsigned int sec) +{ + struct wpa_bss_tmp_disallowed *bss; + struct os_reltime until; + + os_get_reltime(&until); + until.sec += sec; + + bss = wpas_get_disallowed_bss(wpa_s, bssid); + if (bss) { + bss->disallowed_until = until; + return; + } + + bss = os_malloc(sizeof(*bss)); + if (!bss) { + wpa_printf(MSG_DEBUG, + "Failed to allocate memory for temp disallow BSS"); + return; + } + + bss->disallowed_until = until; + os_memcpy(bss->bssid, bssid, ETH_ALEN); + dl_list_add(&wpa_s->bss_tmp_disallowed, &bss->list); +} + + +int wpa_is_bss_tmp_disallowed(struct wpa_supplicant *wpa_s, const u8 *bssid) +{ + struct wpa_bss_tmp_disallowed *bss = NULL, *tmp, *prev; + struct os_reltime now, age; + + os_get_reltime(&now); + + dl_list_for_each_safe(tmp, prev, &wpa_s->bss_tmp_disallowed, + struct wpa_bss_tmp_disallowed, list) { + if (!os_reltime_before(&now, &tmp->disallowed_until)) { + /* This BSS is not disallowed anymore */ + dl_list_del(&tmp->list); + os_free(tmp); + continue; + } + if (os_memcmp(bssid, tmp->bssid, ETH_ALEN) == 0) { + bss = tmp; + break; + } + } + if (!bss) + return 0; + + os_reltime_sub(&bss->disallowed_until, &now, &age); + wpa_printf(MSG_DEBUG, + "BSS " MACSTR " disabled for %ld.%0ld seconds", + MAC2STR(bss->bssid), age.sec, age.usec); + return 1; +} diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf index 6fe67e473c5ea..b3138e3017985 100644 --- a/wpa_supplicant/wpa_supplicant.conf +++ b/wpa_supplicant/wpa_supplicant.conf @@ -118,6 +118,25 @@ eapol_version=1 # networks are found, a new IBSS or AP mode network is created. ap_scan=1 +# Whether to force passive scan for network connection +# +# By default, scans will send out Probe Request frames on channels that allow +# active scanning. This advertise the local station to the world. Normally this +# is fine, but users may wish to do passive scanning where the radio should only +# listen quietly for Beacon frames and not send any Probe Request frames. Actual +# functionality may be driver dependent. +# +# This parameter can be used to force only passive scanning to be used +# for network connection cases. It should be noted that this will slow +# down scan operations and reduce likelihood of finding the AP. In +# addition, some use cases will override this due to functional +# requirements, e.g., for finding an AP that uses hidden SSID +# (scan_ssid=1) or P2P device discovery. +# +# 0: Do normal scans (allow active scans) (default) +# 1: Do passive scans. +#passive_scan=0 + # MPM residency # By default, wpa_supplicant implements the mesh peering manager (MPM) for an # open mesh. However, if the driver can implement the MPM, you may set this to @@ -149,10 +168,13 @@ ap_scan=1 fast_reauth=1 # OpenSSL Engine support -# These options can be used to load OpenSSL engines. +# These options can be used to load OpenSSL engines in special or legacy +# modes. # The two engines that are supported currently are shown below: # They are both from the opensc project (http://www.opensc.org/) -# By default no engines are loaded. +# By default the PKCS#11 engine is loaded if the client_cert or +# private_key option appear to be a PKCS#11 URI, and these options +# should not need to be used explicitly. # make the opensc engine available #opensc_engine_path=/usr/lib/opensc/engine_opensc.so # make the pkcs11 engine available @@ -178,7 +200,7 @@ fast_reauth=1 #load_dynamic_eap=/usr/lib/wpa_supplicant/eap_md5.so # Driver interface parameters -# This field can be used to configure arbitrary driver interace parameters. The +# This field can be used to configure arbitrary driver interface parameters. The # format is specific to the selected driver interface. This field is not used # in most cases. #driver_param="field=value" @@ -295,7 +317,9 @@ fast_reauth=1 # up to the limit of 300 seconds (3, 9, 27 ... 300) # For periodic module, parameters would be <fixed interval> #autoscan=periodic:30 -# So a delay of 30 seconds will be applied between each scan +# So a delay of 30 seconds will be applied between each scan. +# Note: If sched_scan_plans are configured and supported by the driver, +# autoscan is ignored. # filter_ssids - SSID-based scan result filtering # 0 = do not filter scan results (default) @@ -339,10 +363,12 @@ fast_reauth=1 # 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. +# parameter for RSN networks. 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. This global default value does not apply +# for non-RSN networks (key_mgmt=NONE) since PMF is available only when using +# RSN. #pmf=0 # Enabled SAE finite cyclic groups in preference order @@ -417,6 +443,28 @@ fast_reauth=1 # matching network block #auto_interworking=0 +# GAS Address3 field behavior +# 0 = P2P specification (Address3 = AP BSSID); default +# 1 = IEEE 802.11 standard compliant (Address3 = Wildcard BSSID when +# sent to not-associated AP; if associated, AP BSSID) +#gas_address3=0 + +# Publish fine timing measurement (FTM) responder functionality in +# the Extended Capabilities element bit 70. +# Controls whether FTM responder functionality will be published by AP/STA. +# Note that actual FTM responder operation is managed outside wpa_supplicant. +# 0 = Do not publish; default +# 1 = Publish +#ftm_responder=0 + +# Publish fine timing measurement (FTM) initiator functionality in +# the Extended Capabilities element bit 71. +# Controls whether FTM initiator functionality will be published by AP/STA. +# Note that actual FTM initiator operation is managed outside wpa_supplicant. +# 0 = Do not publish; default +# 1 = Publish +#ftm_initiator=0 + # credential block # # Each credential used for automatic network selection is configured as a set @@ -451,6 +499,10 @@ fast_reauth=1 # (EAP-TLS). Full path to the file should be used since working # directory may change when wpa_supplicant is run in the background. # +# Certificates from PKCS#11 tokens can be referenced by a PKCS#11 URI. +# +# For example: private_key="pkcs11:manufacturer=piv_II;id=%01" +# # Alternatively, a named configuration blob can be used by setting # this to blob://blob_name. # @@ -461,6 +513,9 @@ fast_reauth=1 # used since working directory may change when wpa_supplicant is run # in the background. # +# Keys in PKCS#11 tokens can be referenced by a PKCS#11 URI. +# For example: private_key="pkcs11:manufacturer=piv_II;id=%01" +# # Windows certificate store can be used by leaving client_cert out and # configuring private_key in one of the following formats: # @@ -565,6 +620,8 @@ fast_reauth=1 # 0 = do not use OCSP stapling (TLS certificate status extension) # 1 = try to use OCSP stapling, but not require response # 2 = require valid OCSP stapling response +# 3 = require valid OCSP stapling response for all not-trusted +# certificates in the server certificate chain # # sim_num: Identifier for which SIM to use in multi-SIM devices # @@ -597,6 +654,41 @@ fast_reauth=1 # Hotspot 2.0 # hs20=1 +# Scheduled scan plans +# +# A space delimited list of scan plans. Each scan plan specifies the scan +# interval and number of iterations, delimited by a colon. The last scan plan +# will run infinitely and thus must specify only the interval and not the number +# of iterations. +# +# The driver advertises the maximum number of scan plans supported. If more scan +# plans than supported are configured, only the first ones are set (up to the +# maximum supported). The last scan plan that specifies only the interval is +# always set as the last plan. +# +# If the scan interval or the number of iterations for a scan plan exceeds the +# maximum supported, it will be set to the maximum supported value. +# +# Format: +# sched_scan_plans=<interval:iterations> <interval:iterations> ... <interval> +# +# Example: +# sched_scan_plans=10:100 20:200 30 + +# Multi Band Operation (MBO) non-preferred channels +# A space delimited list of non-preferred channels where each channel is a colon +# delimited list of values. +# Format: +# non_pref_chan=<oper_class>:<chan>:<preference>:<reason> +# Example: +# non_pref_chan="81:5:10:2 81:1:0:2 81:9:0:2" + +# MBO Cellular Data Capabilities +# 1 = Cellular data connection available +# 2 = Cellular data connection not available +# 3 = Not cellular capable (default) +#mbo_cell_capa=3 + # network block # # Each network (usually AP's sharing the same SSID) is configured as a separate @@ -658,6 +750,17 @@ fast_reauth=1 # an IBSS network with the configured SSID is already present, the frequency of # the network will be used instead of this configured value. # +# pbss: Whether to use PBSS. Relevant to IEEE 802.11ad networks only. +# 0 = do not use PBSS +# 1 = use PBSS +# 2 = don't care (not allowed in AP mode) +# Used together with mode configuration. When mode is AP, it means to start a +# PCP instead of a regular AP. When mode is infrastructure it means connect +# to a PCP instead of AP. In this mode you can also specify 2 (don't care) +# which means connect to either PCP or AP. +# P2P_GO and P2P_GROUP_FORMATION modes must use PBSS in IEEE 802.11ad network. +# For more details, see IEEE Std 802.11ad-2012. +# # scan_freq: List of frequencies to scan # Space-separated list of frequencies in MHz to scan when searching for this # BSS. If the subset of channels used by the network is known, this option can @@ -706,8 +809,19 @@ fast_reauth=1 # IEEE8021X = IEEE 802.1X using EAP authentication and (optionally) dynamically # generated WEP keys # NONE = WPA is not used; plaintext or static WEP could be used +# WPA-NONE = WPA-None for IBSS (deprecated; use proto=RSN key_mgmt=WPA-PSK +# instead) +# FT-PSK = Fast BSS Transition (IEEE 802.11r) with pre-shared key +# FT-EAP = Fast BSS Transition (IEEE 802.11r) with EAP authentication # WPA-PSK-SHA256 = Like WPA-PSK but using stronger SHA256-based algorithms # WPA-EAP-SHA256 = Like WPA-EAP but using stronger SHA256-based algorithms +# SAE = Simultaneous authentication of equals; pre-shared key/password -based +# authentication with stronger security than WPA-PSK especially when using +# not that strong password +# FT-SAE = SAE with FT +# WPA-EAP-SUITE-B = Suite B 128-bit level +# WPA-EAP-SUITE-B-192 = Suite B 192-bit level +# OSEN = Hotspot 2.0 Rel 2 online signup connection # If not set, this defaults to: WPA-PSK WPA-EAP # # ieee80211w: whether management frame protection is enabled @@ -798,9 +912,13 @@ fast_reauth=1 # wpa_ptk_rekey: Maximum lifetime for PTK in seconds. This can be used to # enforce rekeying of PTK to mitigate some attacks against TKIP deficiencies. # +# group_rekey: Group rekeying time in seconds. This value, if non-zero, is used +# as the dot11RSNAConfigGroupRekeyTime parameter when operating in +# Authenticator role in IBSS. +# # Following fields are only used with internal EAP implementation. # eap: space-separated list of accepted EAP methods -# MD5 = EAP-MD5 (unsecure and does not generate keying material -> +# MD5 = EAP-MD5 (insecure and does not generate keying material -> # cannot be used with WPA; to be used as a Phase 2 method # with EAP-PEAP or EAP-TTLS) # MSCHAPV2 = EAP-MSCHAPv2 (cannot be used separately with WPA; to be used @@ -891,23 +1009,23 @@ fast_reauth=1 # automatically converted into DH params. # subject_match: Substring to be matched against the subject of the # authentication server certificate. If this string is set, the server -# sertificate is only accepted if it contains this string in the subject. +# certificate is only accepted if it contains this string in the subject. # The subject string is in following format: # /C=US/ST=CA/L=San Francisco/CN=Test AS/emailAddress=as@example.com -# Note: Since this is a substring match, this cannot be used securily to +# Note: Since this is a substring match, this cannot be used securely to # do a suffix match against a possible domain name in the CN entry. For # such a use case, domain_suffix_match or domain_match should be used # instead. # altsubject_match: Semicolon separated string of entries to be matched against # the alternative subject name of the authentication server certificate. -# If this string is set, the server sertificate is only accepted if it +# If this string is set, the server certificate is only accepted if it # contains one of the entries in an alternative subject name extension. # altSubjectName string is in following format: TYPE:VALUE # Example: EMAIL:server@example.com # Example: DNS:server.example.com;DNS:server2.example.com # Following types are supported: EMAIL, DNS, URI # domain_suffix_match: Constraint for server domain name. If set, this FQDN is -# used as a suffix match requirement for the AAAserver certificate in +# used as a suffix match requirement for the AAA server certificate in # SubjectAltName dNSName element(s). If a matching dNSName is found, this # constraint is met. If no dNSName values are present, this constraint is # matched against SubjectName CN using same suffix match comparison. @@ -995,6 +1113,12 @@ fast_reauth=1 # that have issues interoperating with updated TLS version) # tls_disable_tlsv1_2=1 - disable use of TLSv1.2 (a workaround for AAA servers # that have issues interoperating with updated TLS version) +# tls_ext_cert_check=0 - No external server certificate validation (default) +# tls_ext_cert_check=1 - External server certificate validation enabled; this +# requires an external program doing validation of server certificate +# chain when receiving CTRL-RSP-EXT_CERT_CHECK event from the control +# interface and report the result of the validation with +# CTRL-RSP_EXT_CERT_CHECK. # # Following certificate/private key fields are used in inner Phase2 # authentication when using EAP-TTLS or EAP-PEAP. @@ -1026,6 +1150,8 @@ fast_reauth=1 # 0 = do not use OCSP stapling (TLS certificate status extension) # 1 = try to use OCSP stapling, but not require response # 2 = require valid OCSP stapling response +# 3 = require valid OCSP stapling response for all not-trusted +# certificates in the server certificate chain # # openssl_ciphers: OpenSSL specific cipher configuration # This can be used to override the global openssl_ciphers configuration @@ -1059,6 +1185,9 @@ fast_reauth=1 # number of authentication servers. Strict EAP conformance mode can be # configured by disabling workarounds with eap_workaround=0. +# update_identifier: PPS MO ID +# (Hotspot 2.0 PerProviderSubscription/UpdateIdentifier) + # Station inactivity limit # # If a station does not send anything in ap_max_inactivity seconds, an @@ -1082,6 +1211,11 @@ fast_reauth=1 # Beacon interval (default: 100 TU) #beacon_int=100 +# WPS in AP mode +# 0 = WPS enabled and configured (default) +# 1 = WPS disabled +#wps_disabled=0 + # MAC address policy # 0 = use permanent MAC address # 1 = use random MAC address for each ESS connection @@ -1144,13 +1278,13 @@ fast_reauth=1 ##### Fast Session Transfer (FST) support ##################################### # # The options in this section are only available when the build configuration -# option CONFIG_FST is set while compiling hostapd. They allow this interface -# to be a part of FST setup. +# option CONFIG_FST is set while compiling wpa_supplicant. They allow this +# interface to be a part of FST setup. # # FST is the transfer of a session from a channel to another channel, in the # same or different frequency bands. # -# For detals, see IEEE Std 802.11ad-2012. +# For details, see IEEE Std 802.11ad-2012. # Identifier of an FST Group the interface belongs to. #fst_group_id=bond0 @@ -1483,22 +1617,10 @@ network={ group=CCMP TKIP identity="user@example.com" ca_cert="/etc/cert/ca.pem" - client_cert="/etc/cert/user.pem" - - engine=1 - - # The engine configured here must be available. Look at - # OpenSSL engine support in the global section. - # The key available through the engine must be the private key - # matching the client certificate configured above. - - # use the opensc engine - #engine_id="opensc" - #key_id="45" - # use the pkcs11 engine - engine_id="pkcs11" - key_id="id_45" + # Certificate and/or key identified by PKCS#11 URI (RFC7512) + client_cert="pkcs11:manufacturer=piv_II;id=%01" + private_key="pkcs11:manufacturer=piv_II;id=%01" # Optional PIN configuration; this can be left out and PIN will be # asked through the control interface diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 58df48c548ea5..ef9273d09a325 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -44,6 +44,7 @@ struct wpa_driver_associate_params; struct ctrl_iface_priv; struct ctrl_iface_global_priv; struct wpas_dbus_priv; +struct wpas_binder_priv; /** * struct wpa_interface - Parameters for wpa_supplicant_add_iface() @@ -228,6 +229,17 @@ struct wpa_params { char *conf_p2p_dev; #endif /* CONFIG_P2P */ +#ifdef CONFIG_MATCH_IFACE + /** + * match_ifaces - Interface descriptions to match + */ + struct wpa_interface *match_ifaces; + + /** + * match_iface_count - Number of defined matching interfaces + */ + int match_iface_count; +#endif /* CONFIG_MATCH_IFACE */ }; struct p2p_srv_bonjour { @@ -253,6 +265,7 @@ struct wpa_global { struct wpa_params params; struct ctrl_iface_global_priv *ctrl_iface; struct wpas_dbus_priv *dbus; + struct wpas_binder_priv *binder; void **drv_priv; size_t drv_count; struct os_time suspend_time; @@ -278,6 +291,7 @@ struct wpa_global { unsigned int p2p_24ghz_social_channels:1; unsigned int pending_p2ps_group:1; unsigned int pending_group_iface_for_p2ps:1; + unsigned int pending_p2ps_group_freq; #ifdef CONFIG_WIFI_DISPLAY int wifi_display; @@ -300,10 +314,14 @@ struct wpa_radio { char name[16]; /* from driver_ops get_radio_name() or empty if not * available */ unsigned int external_scan_running:1; + unsigned int num_active_works; struct dl_list ifaces; /* struct wpa_supplicant::radio_list entries */ struct dl_list work; /* struct wpa_radio_work::list entries */ }; +#define MAX_ACTIVE_WORKS 2 + + /** * struct wpa_radio_work - Radio work item */ @@ -316,6 +334,7 @@ struct wpa_radio_work { void *ctx; unsigned int started:1; struct os_reltime time; + unsigned int bands; }; int radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq, @@ -347,6 +366,9 @@ struct wpa_external_work { unsigned int timeout; }; +enum wpa_radio_work_band wpas_freq_to_band(int freq); +unsigned int wpas_get_bands(struct wpa_supplicant *wpa_s, const int *freqs); + /** * offchannel_send_action_result - Result of offchannel send Action frame */ @@ -371,11 +393,6 @@ struct wps_ap_info { u8 uuid[WPS_UUID_LEN]; }; -struct wpa_ssid_value { - u8 ssid[SSID_MAX_LEN]; - size_t ssid_len; -}; - #define WPA_FREQ_USED_BY_INFRA_STATION BIT(0) #define WPA_FREQ_USED_BY_P2P_CLIENT BIT(1) @@ -414,6 +431,21 @@ enum wpa_supplicant_test_failure { WPAS_TEST_FAILURE_SCAN_TRIGGER, }; +struct icon_entry { + struct dl_list list; + u8 bssid[ETH_ALEN]; + u8 dialog_token; + char *file_name; + u8 *image; + size_t image_len; +}; + +struct wpa_bss_tmp_disallowed { + struct dl_list list; + u8 bssid[ETH_ALEN]; + struct os_reltime disallowed_until; +}; + /** * struct wpa_supplicant - Internal data for wpa_supplicant interface * @@ -427,12 +459,16 @@ struct wpa_supplicant { struct wpa_radio *radio; /* shared radio context */ struct dl_list radio_list; /* list head: struct wpa_radio::ifaces */ struct wpa_supplicant *parent; + struct wpa_supplicant *p2pdev; struct wpa_supplicant *next; struct l2_packet_data *l2; struct l2_packet_data *l2_br; unsigned char own_addr[ETH_ALEN]; unsigned char perm_addr[ETH_ALEN]; char ifname[100]; +#ifdef CONFIG_MATCH_IFACE + int matched; +#endif /* CONFIG_MATCH_IFACE */ #ifdef CONFIG_CTRL_IFACE_DBUS char *dbus_path; #endif /* CONFIG_CTRL_IFACE_DBUS */ @@ -443,6 +479,9 @@ struct wpa_supplicant { char *preq_notify_peer; #endif /* CONFIG_AP */ #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */ +#ifdef CONFIG_CTRL_IFACE_BINDER + const void *binder_object_key; +#endif /* CONFIG_CTRL_IFACE_BINDER */ char bridge_ifname[16]; char *confname; @@ -455,7 +494,8 @@ struct wpa_supplicant { u8 pending_bssid[ETH_ALEN]; /* If wpa_state == WPA_ASSOCIATING, this * field contains the target BSSID. */ int reassociate; /* reassociation requested */ - int reassoc_same_bss; /* reassociating to the same bss */ + unsigned int reassoc_same_bss:1; /* reassociating to the same BSS */ + unsigned int reassoc_same_ess:1; /* reassociating to the same ESS */ int disconnected; /* all connections disabled; i.e., do no reassociate * before this has been cleared */ struct wpa_ssid *current_ssid; @@ -500,9 +540,10 @@ struct wpa_supplicant { 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; + struct sched_scan_plan *sched_scan_plans; + size_t sched_scan_plans_num; void (*scan_res_handler)(struct wpa_supplicant *wpa_s, struct wpa_scan_results *scan_res); @@ -533,6 +574,7 @@ struct wpa_supplicant { struct wpa_radio_work *scan_work; int scanning; int sched_scanning; + unsigned int sched_scan_stop_req:1; int new_connection; int eapol_received; /* number of EAPOL packets received after the @@ -613,6 +655,7 @@ struct wpa_supplicant { #define MAX_SCAN_ID 16 int scan_id[MAX_SCAN_ID]; unsigned int scan_id_count; + u8 next_scan_bssid[ETH_ALEN]; struct wpa_ssid_value *ssids_from_scan_req; unsigned int num_ssids_from_scan_req; @@ -634,6 +677,9 @@ struct wpa_supplicant { int max_scan_ssids; int max_sched_scan_ssids; + unsigned int max_sched_scan_plans; + unsigned int max_sched_scan_plan_interval; + unsigned int max_sched_scan_plan_iterations; int sched_scan_supported; unsigned int max_match_sets; unsigned int max_remain_on_chan; @@ -658,6 +704,7 @@ struct wpa_supplicant { unsigned int reattach:1; /* reassociation to the same BSS requested */ unsigned int mac_addr_changed:1; unsigned int added_vif:1; + unsigned int wnmsleep_used:1; struct os_reltime last_mac_addr_change; int last_mac_addr_style; @@ -722,7 +769,7 @@ struct wpa_supplicant { int mesh_if_idx; unsigned int mesh_if_created:1; unsigned int mesh_ht_enabled:1; - int mesh_auth_block_duration; /* sec */ + unsigned int mesh_vht_enabled:1; #endif /* CONFIG_MESH */ unsigned int off_channel_freq; @@ -844,6 +891,10 @@ struct wpa_supplicant { int *p2p_group_common_freqs; unsigned int p2p_group_common_freqs_num; u8 p2ps_join_addr[ETH_ALEN]; + + unsigned int p2p_go_max_oper_chwidth; + unsigned int p2p_go_vht_center_freq2; + int p2p_lo_started; #endif /* CONFIG_P2P */ struct wpa_ssid *bgscan_ssid; @@ -885,6 +936,7 @@ struct wpa_supplicant { unsigned int fetch_osu_icon_in_progress:1; struct wpa_bss *interworking_gas_bss; unsigned int osu_icon_id; + struct dl_list icon_head; /* struct icon_entry */ struct osu_provider *osu_prov; size_t osu_prov_count; struct os_reltime osu_icon_fetch_start; @@ -914,6 +966,9 @@ struct wpa_supplicant { /* WLAN_REASON_* reason codes. Negative if locally generated. */ int disconnect_reason; + /* WLAN_STATUS_* status codes from (Re)Association Response frame. */ + u16 assoc_status_code; + struct ext_password_data *ext_pw; struct wpabuf *last_gas_resp, *prev_gas_resp; @@ -969,6 +1024,10 @@ struct wpa_supplicant { struct l2_packet_data *l2_test; unsigned int extra_roc_dur; enum wpa_supplicant_test_failure test_failure; + unsigned int reject_btm_req_reason; + unsigned int p2p_go_csa_on_inv:1; + unsigned int ignore_auth_resp:1; + unsigned int ignore_assoc_disallow:1; #endif /* CONFIG_TESTING_OPTIONS */ struct wmm_ac_assoc_data *wmm_ac_assoc_info; @@ -985,6 +1044,31 @@ struct wpa_supplicant { const struct wpabuf *fst_ies; struct wpabuf *received_mb_ies; #endif /* CONFIG_FST */ + +#ifdef CONFIG_MBO + /* Multiband operation non-preferred channel */ + struct wpa_mbo_non_pref_channel { + enum mbo_non_pref_chan_reason reason; + u8 oper_class; + u8 chan; + u8 preference; + } *non_pref_chan; + size_t non_pref_chan_num; + u8 mbo_wnm_token; +#endif /* CONFIG_MBO */ + + /* + * This should be under CONFIG_MBO, but it is left out to allow using + * the bss_temp_disallowed list for other purposes as well. + */ + struct dl_list bss_tmp_disallowed; + + /* + * Content of a measurement report element with type 8 (LCI), + * own location. + */ + struct wpabuf *lci; + struct os_reltime lci_time; }; @@ -1026,6 +1110,8 @@ void wpa_supplicant_cancel_auth_timeout(struct wpa_supplicant *wpa_s); void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s, int reason_code); +struct wpa_ssid * wpa_supplicant_add_network(struct wpa_supplicant *wpa_s); +int wpa_supplicant_remove_network(struct wpa_supplicant *wpa_s, int id); void wpa_supplicant_enable_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); void wpa_supplicant_disable_network(struct wpa_supplicant *wpa_s, @@ -1050,6 +1136,8 @@ void free_hw_features(struct wpa_supplicant *wpa_s); void wpa_show_license(void); +struct wpa_interface * wpa_supplicant_match_iface(struct wpa_global *global, + const char *ifname); struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global, struct wpa_interface *iface, struct wpa_supplicant *parent); @@ -1079,6 +1167,7 @@ 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); +void wpas_request_disconnection(struct wpa_supplicant *wpa_s); int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf, size_t buflen); int wpas_update_random_addr(struct wpa_supplicant *wpa_s, int style); int wpas_update_random_addr_disassoc(struct wpa_supplicant *wpa_s); @@ -1088,15 +1177,37 @@ void wpas_rrm_reset(struct wpa_supplicant *wpa_s); void wpas_rrm_process_neighbor_rep(struct wpa_supplicant *wpa_s, const u8 *report, size_t report_len); int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s, - const struct wpa_ssid *ssid, + const struct wpa_ssid_value *ssid, + int lci, int civic, void (*cb)(void *ctx, struct wpabuf *neighbor_rep), void *cb_ctx); +void wpas_rrm_handle_radio_measurement_request(struct wpa_supplicant *wpa_s, + const u8 *src, + const u8 *frame, size_t len); void wpas_rrm_handle_link_measurement_request(struct wpa_supplicant *wpa_s, const u8 *src, const u8 *frame, size_t len, int rssi); + +/* MBO functions */ +int wpas_mbo_ie(struct wpa_supplicant *wpa_s, u8 *buf, size_t len); +const u8 * wpas_mbo_get_bss_attr(struct wpa_bss *bss, enum mbo_attr_id attr); +int wpas_mbo_update_non_pref_chan(struct wpa_supplicant *wpa_s, + const char *non_pref_chan); +void wpas_mbo_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ie); +int wpas_mbo_supp_op_class_ie(struct wpa_supplicant *wpa_s, int freq, u8 *pos, + size_t len); +void wpas_mbo_ie_trans_req(struct wpa_supplicant *wpa_s, const u8 *ie, + size_t len); +size_t wpas_mbo_ie_bss_trans_reject(struct wpa_supplicant *wpa_s, u8 *pos, + size_t len, + enum mbo_transition_reject_reason reason); +void wpas_mbo_update_cell_capa(struct wpa_supplicant *wpa_s, u8 mbo_cell_capa); +struct wpabuf * mbo_build_anqp_buf(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss); + /** * wpa_supplicant_ctrl_iface_ctrl_rsp_handle - Handle a control response * @wpa_s: Pointer to wpa_supplicant data @@ -1158,6 +1269,12 @@ int get_shared_radio_freqs(struct wpa_supplicant *wpa_s, void wpas_network_reenabled(void *eloop_ctx, void *timeout_ctx); +void wpas_vendor_elem_update(struct wpa_supplicant *wpa_s); +struct wpa_supplicant * wpas_vendor_elem(struct wpa_supplicant *wpa_s, + enum wpa_vendor_elem_frame frame); +int wpas_vendor_elem_remove(struct wpa_supplicant *wpa_s, int frame, + const u8 *elem, size_t len); + #ifdef CONFIG_FST struct fst_wpa_obj; @@ -1167,4 +1284,18 @@ void fst_wpa_supplicant_fill_iface_obj(struct wpa_supplicant *wpa_s, #endif /* CONFIG_FST */ +int wpas_sched_scan_plans_set(struct wpa_supplicant *wpa_s, const char *cmd); + +struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes, + u16 num_modes, enum hostapd_hw_mode mode); + +void wpa_bss_tmp_disallow(struct wpa_supplicant *wpa_s, const u8 *bssid, + unsigned int sec); +int wpa_is_bss_tmp_disallowed(struct wpa_supplicant *wpa_s, const u8 *bssid); + +struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, + int i, struct wpa_bss *bss, + struct wpa_ssid *group, + int only_first_ssid); + #endif /* WPA_SUPPLICANT_I_H */ diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c index 29c22ba2c967a..f84c8b90ac2fc 100644 --- a/wpa_supplicant/wpas_glue.c +++ b/wpa_supplicant/wpas_glue.c @@ -739,6 +739,8 @@ enum wpa_ctrl_req_type wpa_supplicant_ctrl_req_from_string(const char *field) return WPA_CTRL_REQ_SIM; else if (os_strcmp(field, "PSK_PASSPHRASE") == 0) return WPA_CTRL_REQ_PSK_PASSPHRASE; + else if (os_strcmp(field, "EXT_CERT_CHECK") == 0) + return WPA_CTRL_REQ_EXT_CERT_CHECK; return WPA_CTRL_REQ_UNKNOWN; } @@ -782,6 +784,10 @@ const char * wpa_supplicant_ctrl_req_to_string(enum wpa_ctrl_req_type field, *txt = "PSK or passphrase"; ret = "PSK_PASSPHRASE"; break; + case WPA_CTRL_REQ_EXT_CERT_CHECK: + *txt = "External server certificate validation"; + ret = "EXT_CERT_CHECK"; + break; default: break; } @@ -837,6 +843,8 @@ static void wpa_supplicant_eap_param_needed(void *ctx, if (ssid == NULL) return; + if (field == WPA_CTRL_REQ_EXT_CERT_CHECK) + ssid->eap.pending_ext_cert_check = PENDING_CHECK; wpas_notify_network_request(wpa_s, ssid, field, default_txt); field_name = wpa_supplicant_ctrl_req_to_string(field, default_txt, @@ -1013,7 +1021,6 @@ static void wpa_supplicant_set_rekey_offload(void *ctx, wpa_drv_set_rekey_info(wpa_s, kek, kek_len, kck, kck_len, replay_ctr); } -#endif /* CONFIG_NO_WPA */ static int wpa_supplicant_key_mgmt_set_pmk(void *ctx, const u8 *pmk, @@ -1028,6 +1035,7 @@ static int wpa_supplicant_key_mgmt_set_pmk(void *ctx, const u8 *pmk, else return 0; } +#endif /* CONFIG_NO_WPA */ int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s) @@ -1124,6 +1132,7 @@ void wpa_supplicant_rsn_supp_set_config(struct wpa_supplicant *wpa_s, } } #endif /* CONFIG_P2P */ + conf.wpa_rsc_relaxation = wpa_s->conf->wpa_rsc_relaxation; } wpa_sm_set_config(wpa_s->wpa, ssid ? &conf : NULL); } diff --git a/wpa_supplicant/wpas_kay.c b/wpa_supplicant/wpas_kay.c index 354decf98c8b7..d6ec8c5090e9a 100644 --- a/wpa_supplicant/wpas_kay.c +++ b/wpa_supplicant/wpas_kay.c @@ -50,10 +50,9 @@ static int wpas_set_replay_protect(void *wpa_s, Boolean enabled, u32 window) } -static int wpas_set_current_cipher_suite(void *wpa_s, const u8 *cs, - size_t cs_len) +static int wpas_set_current_cipher_suite(void *wpa_s, u64 cs) { - return wpa_drv_set_current_cipher_suite(wpa_s, cs, cs_len); + return wpa_drv_set_current_cipher_suite(wpa_s, cs); } @@ -109,7 +108,8 @@ static int wpas_create_receive_sc(void *wpa_s, u32 channel, enum validate_frames vf, enum confidentiality_offset co) { - return wpa_drv_create_receive_sc(wpa_s, channel, sci->addr, sci->port, + return wpa_drv_create_receive_sc(wpa_s, channel, sci->addr, + be_to_host16(sci->port), conf_offset_val(co), vf); } @@ -150,7 +150,8 @@ wpas_create_transmit_sc(void *wpa_s, u32 channel, const struct ieee802_1x_mka_sci *sci, enum confidentiality_offset co) { - return wpa_drv_create_transmit_sc(wpa_s, channel, sci->addr, sci->port, + return wpa_drv_create_transmit_sc(wpa_s, channel, sci->addr, + be_to_host16(sci->port), conf_offset_val(co)); } diff --git a/wpa_supplicant/wpas_module_tests.c b/wpa_supplicant/wpas_module_tests.c index 6af1678a4dfbb..4e37591be36c2 100644 --- a/wpa_supplicant/wpas_module_tests.c +++ b/wpa_supplicant/wpas_module_tests.c @@ -9,6 +9,7 @@ #include "utils/includes.h" #include "utils/common.h" +#include "utils/module_tests.h" #include "wpa_supplicant_i.h" #include "blacklist.h" @@ -79,30 +80,18 @@ int wpas_module_tests(void) ret = -1; #ifdef CONFIG_WPS - { - int wps_module_tests(void); - if (wps_module_tests() < 0) - ret = -1; - } + if (wps_module_tests() < 0) + ret = -1; #endif /* CONFIG_WPS */ - { - int utils_module_tests(void); - if (utils_module_tests() < 0) - ret = -1; - } - - { - int common_module_tests(void); - if (common_module_tests() < 0) - ret = -1; - } - - { - int crypto_module_tests(void); - if (crypto_module_tests() < 0) - ret = -1; - } + if (utils_module_tests() < 0) + ret = -1; + + if (common_module_tests() < 0) + ret = -1; + + if (crypto_module_tests() < 0) + ret = -1; return ret; } diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c index 60f761c81b809..74a420c671d0c 100644 --- a/wpa_supplicant/wps_supplicant.c +++ b/wpa_supplicant/wps_supplicant.c @@ -583,8 +583,8 @@ static void wpa_supplicant_wps_event_m2d(struct wpa_supplicant *wpa_s, 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 + if (wpa_s->p2pdev && wpa_s->p2pdev != wpa_s) { + wpa_msg(wpa_s->p2pdev, MSG_INFO, WPS_EVENT_M2D "dev_password_id=%d config_error=%d", m2d->dev_password_id, m2d->config_error); } @@ -617,8 +617,8 @@ static void wpa_supplicant_wps_event_fail(struct wpa_supplicant *wpa_s, WPS_EVENT_FAIL "msg=%d config_error=%d reason=%d (%s)", fail->msg, fail->config_error, fail->error_indication, wps_ei_str(fail->error_indication)); - if (wpa_s->parent && wpa_s->parent != wpa_s) - wpa_msg(wpa_s->parent, MSG_INFO, WPS_EVENT_FAIL + if (wpa_s->p2pdev && wpa_s->p2pdev != wpa_s) + wpa_msg(wpa_s->p2pdev, MSG_INFO, WPS_EVENT_FAIL "msg=%d config_error=%d reason=%d (%s)", fail->msg, fail->config_error, fail->error_indication, @@ -627,8 +627,8 @@ static void wpa_supplicant_wps_event_fail(struct wpa_supplicant *wpa_s, wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_FAIL "msg=%d config_error=%d", fail->msg, fail->config_error); - if (wpa_s->parent && wpa_s->parent != wpa_s) - wpa_msg(wpa_s->parent, MSG_INFO, WPS_EVENT_FAIL + if (wpa_s->p2pdev && wpa_s->p2pdev != wpa_s) + wpa_msg(wpa_s->p2pdev, MSG_INFO, WPS_EVENT_FAIL "msg=%d config_error=%d", fail->msg, fail->config_error); } @@ -683,6 +683,13 @@ static void wpas_wps_reenable_networks_cb(void *eloop_ctx, void *timeout_ctx) } +int wpas_wps_reenable_networks_pending(struct wpa_supplicant *wpa_s) +{ + return eloop_is_timeout_registered(wpas_wps_reenable_networks_cb, + wpa_s, NULL); +} + + static void wpa_supplicant_wps_event_success(struct wpa_supplicant *wpa_s) { wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_SUCCESS); @@ -1135,6 +1142,13 @@ int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid, return -1; ssid->temporary = 1; ssid->p2p_group = p2p_group; + /* + * When starting a regular WPS process (not P2P group formation) + * the registrar/final station can be either AP or PCP + * so use a "don't care" value for the pbss flag. + */ + if (!p2p_group) + ssid->pbss = 2; #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); @@ -1142,6 +1156,10 @@ int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid, ssid->ssid_len = wpa_s->go_params->ssid_len; os_memcpy(ssid->ssid, wpa_s->go_params->ssid, ssid->ssid_len); + if (wpa_s->go_params->freq > 56160) { + /* P2P in 60 GHz uses PBSS */ + ssid->pbss = 1; + } wpa_hexdump_ascii(MSG_DEBUG, "WPS: Use specific AP " "SSID", ssid->ssid, ssid->ssid_len); } @@ -1186,6 +1204,13 @@ static int wpas_wps_start_dev_pw(struct wpa_supplicant *wpa_s, } ssid->temporary = 1; ssid->p2p_group = p2p_group; + /* + * When starting a regular WPS process (not P2P group formation) + * the registrar/final station can be either AP or PCP + * so use a "don't care" value for the pbss flag. + */ + if (!p2p_group) + ssid->pbss = 2; if (ssid_val) { ssid->ssid = os_malloc(ssid_len); if (ssid->ssid) { @@ -1209,6 +1234,10 @@ static int wpas_wps_start_dev_pw(struct wpa_supplicant *wpa_s, ssid->ssid_len = wpa_s->go_params->ssid_len; os_memcpy(ssid->ssid, wpa_s->go_params->ssid, ssid->ssid_len); + if (wpa_s->go_params->freq > 56160) { + /* P2P in 60 GHz uses PBSS */ + ssid->pbss = 1; + } wpa_hexdump_ascii(MSG_DEBUG, "WPS: Use specific AP " "SSID", ssid->ssid, ssid->ssid_len); } @@ -1221,7 +1250,10 @@ static int wpas_wps_start_dev_pw(struct wpa_supplicant *wpa_s, os_snprintf(val, sizeof(val), "\"dev_pw_id=%u%s\"", dev_pw_id, hash); } else { - rpin = wps_generate_pin(); + if (wps_generate_pin(&rpin) < 0) { + wpa_printf(MSG_DEBUG, "WPS: Could not generate PIN"); + return -1; + } os_snprintf(val, sizeof(val), "\"pin=%08d dev_pw_id=%u%s\"", rpin, dev_pw_id, hash); } diff --git a/wpa_supplicant/wps_supplicant.h b/wpa_supplicant/wps_supplicant.h index 3c25ca86dc65e..c8fe47e372791 100644 --- a/wpa_supplicant/wps_supplicant.h +++ b/wpa_supplicant/wps_supplicant.h @@ -85,6 +85,7 @@ int wpas_er_wps_nfc_report_handover(struct wpa_supplicant *wpa_s, 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); +int wpas_wps_reenable_networks_pending(struct wpa_supplicant *wpa_s); #else /* CONFIG_WPS */ @@ -147,6 +148,12 @@ static inline void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s, { } +static inline int +wpas_wps_reenable_networks_pending(struct wpa_supplicant *wpa_s) +{ + return 0; +} + #endif /* CONFIG_WPS */ #endif /* WPS_SUPPLICANT_H */ |