diff options
author | rpaulo <rpaulo@FreeBSD.org> | 2010-06-13 20:32:04 +0000 |
---|---|---|
committer | rpaulo <rpaulo@FreeBSD.org> | 2010-06-13 20:32:04 +0000 |
commit | 60c44471bf25f9e84d8701afe1bbcbcc88e18c89 (patch) | |
tree | 538db23d436787038f980271529ae2be44235c1b | |
parent | ced3a3de988600636bda6479d27de8823307f171 (diff) | |
download | FreeBSD-src-60c44471bf25f9e84d8701afe1bbcbcc88e18c89.zip FreeBSD-src-60c44471bf25f9e84d8701afe1bbcbcc88e18c89.tar.gz |
Import wpa_supplicant & hostapd 0.6.9.
257 files changed, 52748 insertions, 916 deletions
diff --git a/hostapd/.gitignore b/hostapd/.gitignore deleted file mode 100644 index 6dd2c2f..0000000 --- a/hostapd/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -*.d -.config -driver_conf.c -hostapd -hostapd_cli -hlr_auc_gw -nt_password_hash diff --git a/hostapd/ChangeLog b/hostapd/ChangeLog index 46f0852..18af4b1 100644 --- a/hostapd/ChangeLog +++ b/hostapd/ChangeLog @@ -1,5 +1,37 @@ ChangeLog for hostapd +2010-01-12 - v0.6.10 + * fixed SHA-256 based key derivation function to match with the + standard when using CCMP (for IEEE 802.11r and IEEE 802.11w) + (note: this breaks interoperability with previous version) [Bug 307] + * fixed WPS selected registrar expiration for internal PIN registrar + * disable PMTU discovery for RADIUS packets + * fixed WPS UPnP SSDP on 32-bit targets + * fixed WPS AP reconfiguration with drivers that do not use hostapd + MLME + * fixed RSN parameter setting for multi-BSS case + * added WPS workarounds for known interoperability issues with broken, + deployed implementation + * update IEEE 802.11w implementation to match with the published + standard + * fixed OpCode when proxying WSC_ACK or WSC_NACK from WPS ER + * fixed proxying of WSC_NACK to WPS ER + * fixed compilation with newer GnuTLS versions + * added support for defining timeout for WPS PINs + * fixed WPS Probe Request processing to handle missing required + attribute + * fixed PKCS#12 use with OpenSSL 1.0.0 + +2009-03-23 - v0.6.9 + * driver_nl80211: fixed STA accounting data collection (TX/RX bytes + reported correctly; TX/RX packets not yet available from kernel) + * fixed EAPOL/EAP reauthentication when using an external RADIUS + authentication server + * driver_prism54: fixed segmentation fault on initialization + * fixed TNC with EAP-TTLS + * fixed IEEE 802.11r key derivation function to match with the standard + (note: this breaks interoperability with previous version) [Bug 303] + 2009-02-15 - v0.6.8 * increased hostapd_cli ping interval to 5 seconds and made this configurable with a new command line options (-G<seconds>) diff --git a/hostapd/Makefile b/hostapd/Makefile new file mode 100644 index 0000000..3b3d7fe --- /dev/null +++ b/hostapd/Makefile @@ -0,0 +1,635 @@ +ifndef CC +CC=gcc +endif + +ifndef CFLAGS +CFLAGS = -MMD -O2 -Wall -g +endif + +# define HOSTAPD_DUMP_STATE to include SIGUSR1 handler for dumping state to +# a file (undefine it, if you want to save in binary size) +CFLAGS += -DHOSTAPD_DUMP_STATE + +CFLAGS += -I../src +CFLAGS += -I../src/crypto +CFLAGS += -I../src/utils +CFLAGS += -I../src/common + +# Uncomment following line and set the path to your kernel tree include +# directory if your C library does not include all header files. +# CFLAGS += -DUSE_KERNEL_HEADERS -I/usr/src/linux/include + +-include .config + +ifndef CONFIG_OS +ifdef CONFIG_NATIVE_WINDOWS +CONFIG_OS=win32 +else +CONFIG_OS=unix +endif +endif + +ifeq ($(CONFIG_OS), internal) +CFLAGS += -DOS_NO_C_LIB_DEFINES +endif + +ifdef CONFIG_NATIVE_WINDOWS +CFLAGS += -DCONFIG_NATIVE_WINDOWS +LIBS += -lws2_32 +endif + +OBJS = hostapd.o ieee802_1x.o eapol_sm.o \ + ieee802_11.o config.o ieee802_11_auth.o accounting.o \ + sta_info.o wpa.o ctrl_iface.o \ + drivers.o preauth.o pmksa_cache.o beacon.o \ + hw_features.o wme.o ap_list.o \ + mlme.o vlan_init.o wpa_auth_ie.o + +OBJS += ../src/utils/eloop.o +OBJS += ../src/utils/common.o +OBJS += ../src/utils/wpa_debug.o +OBJS += ../src/utils/wpabuf.o +OBJS += ../src/utils/os_$(CONFIG_OS).o +OBJS += ../src/utils/ip_addr.o + +OBJS += ../src/common/ieee802_11_common.o +OBJS += ../src/common/wpa_common.o + +OBJS += ../src/radius/radius.o +OBJS += ../src/radius/radius_client.o + +OBJS += ../src/crypto/md5.o +OBJS += ../src/crypto/rc4.o +OBJS += ../src/crypto/md4.o +OBJS += ../src/crypto/sha1.o +OBJS += ../src/crypto/des.o +OBJS += ../src/crypto/aes_wrap.o +OBJS += ../src/crypto/aes.o + +HOBJS=../src/hlr_auc_gw/hlr_auc_gw.o ../src/utils/common.o ../src/utils/wpa_debug.o ../src/utils/os_$(CONFIG_OS).o ../src/hlr_auc_gw/milenage.o ../src/crypto/aes_wrap.o ../src/crypto/aes.o + +CFLAGS += -DCONFIG_CTRL_IFACE -DCONFIG_CTRL_IFACE_UNIX + +ifdef CONFIG_IAPP +CFLAGS += -DCONFIG_IAPP +OBJS += iapp.o +endif + +ifdef CONFIG_RSN_PREAUTH +CFLAGS += -DCONFIG_RSN_PREAUTH +CONFIG_L2_PACKET=y +endif + +ifdef CONFIG_PEERKEY +CFLAGS += -DCONFIG_PEERKEY +OBJS += peerkey.o +endif + +ifdef CONFIG_IEEE80211W +CFLAGS += -DCONFIG_IEEE80211W +NEED_SHA256=y +endif + +ifdef CONFIG_IEEE80211R +CFLAGS += -DCONFIG_IEEE80211R +OBJS += wpa_ft.o +NEED_SHA256=y +endif + +ifdef CONFIG_IEEE80211N +CFLAGS += -DCONFIG_IEEE80211N +endif + +ifdef CONFIG_DRIVER_HOSTAP +CFLAGS += -DCONFIG_DRIVER_HOSTAP +OBJS += driver_hostap.o +endif + +ifdef CONFIG_DRIVER_WIRED +CFLAGS += -DCONFIG_DRIVER_WIRED +OBJS += driver_wired.o +endif + +ifdef CONFIG_DRIVER_MADWIFI +CFLAGS += -DCONFIG_DRIVER_MADWIFI +OBJS += driver_madwifi.o +CONFIG_L2_PACKET=y +endif + +ifdef CONFIG_DRIVER_ATHEROS +CFLAGS += -DCONFIG_DRIVER_ATHEROS +OBJS += driver_atheros.o +CONFIG_L2_PACKET=y +endif + +ifdef CONFIG_DRIVER_PRISM54 +CFLAGS += -DCONFIG_DRIVER_PRISM54 +OBJS += driver_prism54.o +endif + +ifdef CONFIG_DRIVER_NL80211 +CFLAGS += -DCONFIG_DRIVER_NL80211 +OBJS += driver_nl80211.o radiotap.o +LIBS += -lnl +ifdef CONFIG_LIBNL20 +LIBS += -lnl-genl +CFLAGS += -DCONFIG_LIBNL20 +endif +endif + +ifdef CONFIG_DRIVER_BSD +CFLAGS += -DCONFIG_DRIVER_BSD +OBJS += driver_bsd.o +CONFIG_L2_PACKET=y +CONFIG_DNET_PCAP=y +CONFIG_L2_FREEBSD=y +endif + +ifdef CONFIG_DRIVER_TEST +CFLAGS += -DCONFIG_DRIVER_TEST +OBJS += driver_test.o +endif + +ifdef CONFIG_DRIVER_NONE +CFLAGS += -DCONFIG_DRIVER_NONE +OBJS += driver_none.o +endif + +ifdef CONFIG_L2_PACKET +ifdef CONFIG_DNET_PCAP +ifdef CONFIG_L2_FREEBSD +LIBS += -lpcap +OBJS += ../src/l2_packet/l2_packet_freebsd.o +else +LIBS += -ldnet -lpcap +OBJS += ../src/l2_packet/l2_packet_pcap.o +endif +else +OBJS += ../src/l2_packet/l2_packet_linux.o +endif +else +OBJS += ../src/l2_packet/l2_packet_none.o +endif + + +ifdef CONFIG_EAP_MD5 +CFLAGS += -DEAP_MD5 +OBJS += ../src/eap_server/eap_md5.o +CHAP=y +endif + +ifdef CONFIG_EAP_TLS +CFLAGS += -DEAP_TLS +OBJS += ../src/eap_server/eap_tls.o +TLS_FUNCS=y +endif + +ifdef CONFIG_EAP_PEAP +CFLAGS += -DEAP_PEAP +OBJS += ../src/eap_server/eap_peap.o +OBJS += ../src/eap_common/eap_peap_common.o +TLS_FUNCS=y +CONFIG_EAP_MSCHAPV2=y +endif + +ifdef CONFIG_EAP_TTLS +CFLAGS += -DEAP_TTLS +OBJS += ../src/eap_server/eap_ttls.o +TLS_FUNCS=y +CHAP=y +endif + +ifdef CONFIG_EAP_MSCHAPV2 +CFLAGS += -DEAP_MSCHAPv2 +OBJS += ../src/eap_server/eap_mschapv2.o +MS_FUNCS=y +endif + +ifdef CONFIG_EAP_GTC +CFLAGS += -DEAP_GTC +OBJS += ../src/eap_server/eap_gtc.o +endif + +ifdef CONFIG_EAP_SIM +CFLAGS += -DEAP_SIM +OBJS += ../src/eap_server/eap_sim.o +CONFIG_EAP_SIM_COMMON=y +endif + +ifdef CONFIG_EAP_AKA +CFLAGS += -DEAP_AKA +OBJS += ../src/eap_server/eap_aka.o +CONFIG_EAP_SIM_COMMON=y +NEED_SHA256=y +endif + +ifdef CONFIG_EAP_AKA_PRIME +CFLAGS += -DEAP_AKA_PRIME +endif + +ifdef CONFIG_EAP_SIM_COMMON +OBJS += ../src/eap_common/eap_sim_common.o +# Example EAP-SIM/AKA interface for GSM/UMTS authentication. This can be +# replaced with another file implementating the interface specified in +# eap_sim_db.h. +OBJS += ../src/eap_server/eap_sim_db.o +NEED_FIPS186_2_PRF=y +endif + +ifdef CONFIG_EAP_PAX +CFLAGS += -DEAP_PAX +OBJS += ../src/eap_server/eap_pax.o ../src/eap_common/eap_pax_common.o +endif + +ifdef CONFIG_EAP_PSK +CFLAGS += -DEAP_PSK +OBJS += ../src/eap_server/eap_psk.o ../src/eap_common/eap_psk_common.o +endif + +ifdef CONFIG_EAP_SAKE +CFLAGS += -DEAP_SAKE +OBJS += ../src/eap_server/eap_sake.o ../src/eap_common/eap_sake_common.o +endif + +ifdef CONFIG_EAP_GPSK +CFLAGS += -DEAP_GPSK +OBJS += ../src/eap_server/eap_gpsk.o ../src/eap_common/eap_gpsk_common.o +ifdef CONFIG_EAP_GPSK_SHA256 +CFLAGS += -DEAP_GPSK_SHA256 +endif +NEED_SHA256=y +endif + +ifdef CONFIG_EAP_VENDOR_TEST +CFLAGS += -DEAP_VENDOR_TEST +OBJS += ../src/eap_server/eap_vendor_test.o +endif + +ifdef CONFIG_EAP_FAST +CFLAGS += -DEAP_FAST +OBJS += ../src/eap_server/eap_fast.o +OBJS += ../src/eap_common/eap_fast_common.o +TLS_FUNCS=y +NEED_T_PRF=y +endif + +ifdef CONFIG_WPS +CFLAGS += -DCONFIG_WPS -DEAP_WSC +OBJS += ../src/utils/uuid.o +OBJS += wps_hostapd.o +OBJS += ../src/eap_server/eap_wsc.o ../src/eap_common/eap_wsc_common.o +OBJS += ../src/wps/wps.o +OBJS += ../src/wps/wps_common.o +OBJS += ../src/wps/wps_attr_parse.o +OBJS += ../src/wps/wps_attr_build.o +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 +NEED_DH_GROUPS=y +NEED_SHA256=y +NEED_CRYPTO=y +NEED_BASE64=y + +ifdef CONFIG_WPS_UPNP +CFLAGS += -DCONFIG_WPS_UPNP +OBJS += ../src/wps/wps_upnp.o +OBJS += ../src/wps/wps_upnp_ssdp.o +OBJS += ../src/wps/wps_upnp_web.o +OBJS += ../src/wps/wps_upnp_event.o +OBJS += ../src/wps/httpread.o +endif + +endif + +ifdef CONFIG_EAP_IKEV2 +CFLAGS += -DEAP_IKEV2 +OBJS += ../src/eap_server/eap_ikev2.o ../src/eap_server/ikev2.o +OBJS += ../src/eap_common/eap_ikev2_common.o ../src/eap_common/ikev2_common.o +NEED_DH_GROUPS=y +NEED_DH_GROUPS_ALL=y +endif + +ifdef CONFIG_EAP_TNC +CFLAGS += -DEAP_TNC +OBJS += ../src/eap_server/eap_tnc.o +OBJS += ../src/eap_server/tncs.o +NEED_BASE64=y +ifndef CONFIG_DRIVER_BSD +LIBS += -ldl +endif +endif + +# Basic EAP functionality is needed for EAPOL +OBJS += ../src/eap_server/eap.o +OBJS += ../src/eap_common/eap_common.o +OBJS += ../src/eap_server/eap_methods.o +OBJS += ../src/eap_server/eap_identity.o + +ifdef CONFIG_EAP +CFLAGS += -DEAP_SERVER +endif + +ifndef CONFIG_TLS +CONFIG_TLS=openssl +endif + +ifeq ($(CONFIG_TLS), internal) +ifndef CONFIG_CRYPTO +CONFIG_CRYPTO=internal +endif +endif +ifeq ($(CONFIG_CRYPTO), libtomcrypt) +CFLAGS += -DCONFIG_INTERNAL_X509 +endif +ifeq ($(CONFIG_CRYPTO), internal) +CFLAGS += -DCONFIG_INTERNAL_X509 +endif + + +ifdef TLS_FUNCS +# Shared TLS functions (needed for EAP_TLS, EAP_PEAP, and EAP_TTLS) +CFLAGS += -DEAP_TLS_FUNCS +OBJS += ../src/eap_server/eap_tls_common.o +NEED_TLS_PRF=y +ifeq ($(CONFIG_TLS), openssl) +OBJS += ../src/crypto/tls_openssl.o +LIBS += -lssl -lcrypto +LIBS_p += -lcrypto +LIBS_h += -lcrypto +endif +ifeq ($(CONFIG_TLS), gnutls) +OBJS += ../src/crypto/tls_gnutls.o +LIBS += -lgnutls -lgcrypt -lgpg-error +LIBS_p += -lgcrypt +LIBS_h += -lgcrypt +endif +ifdef CONFIG_GNUTLS_EXTRA +CFLAGS += -DCONFIG_GNUTLS_EXTRA +LIBS += -lgnutls-extra +endif +ifeq ($(CONFIG_TLS), internal) +OBJS += ../src/crypto/tls_internal.o +OBJS += ../src/tls/tlsv1_common.o ../src/tls/tlsv1_record.o +OBJS += ../src/tls/tlsv1_cred.o ../src/tls/tlsv1_server.o +OBJS += ../src/tls/tlsv1_server_write.o ../src/tls/tlsv1_server_read.o +OBJS += ../src/tls/asn1.o ../src/tls/x509v3.o +OBJS_p += ../src/tls/asn1.o +OBJS_p += ../src/crypto/rc4.o ../src/crypto/aes_wrap.o ../src/crypto/aes.o +NEED_BASE64=y +CFLAGS += -DCONFIG_TLS_INTERNAL +CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER +ifeq ($(CONFIG_CRYPTO), internal) +ifdef CONFIG_INTERNAL_LIBTOMMATH +CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH +else +LIBS += -ltommath +LIBS_p += -ltommath +endif +endif +ifeq ($(CONFIG_CRYPTO), libtomcrypt) +LIBS += -ltomcrypt -ltfm +LIBS_p += -ltomcrypt -ltfm +endif +endif +NEED_CRYPTO=y +else +OBJS += ../src/crypto/tls_none.o +endif + +ifdef CONFIG_PKCS12 +CFLAGS += -DPKCS12_FUNCS +endif + +ifdef MS_FUNCS +OBJS += ../src/crypto/ms_funcs.o +NEED_CRYPTO=y +endif + +ifdef CHAP +OBJS += ../src/eap_common/chap.o +endif + +ifdef NEED_CRYPTO +ifndef TLS_FUNCS +ifeq ($(CONFIG_TLS), openssl) +LIBS += -lcrypto +LIBS_p += -lcrypto +LIBS_h += -lcrypto +endif +ifeq ($(CONFIG_TLS), gnutls) +LIBS += -lgcrypt +LIBS_p += -lgcrypt +LIBS_h += -lgcrypt +endif +ifeq ($(CONFIG_TLS), internal) +ifeq ($(CONFIG_CRYPTO), libtomcrypt) +LIBS += -ltomcrypt -ltfm +LIBS_p += -ltomcrypt -ltfm +endif +endif +endif +ifeq ($(CONFIG_TLS), openssl) +OBJS += ../src/crypto/crypto_openssl.o +OBJS_p += ../src/crypto/crypto_openssl.o +HOBJS += ../src/crypto/crypto_openssl.o +CONFIG_INTERNAL_SHA256=y +endif +ifeq ($(CONFIG_TLS), gnutls) +OBJS += ../src/crypto/crypto_gnutls.o +OBJS_p += ../src/crypto/crypto_gnutls.o +HOBJS += ../src/crypto/crypto_gnutls.o +CONFIG_INTERNAL_SHA256=y +endif +ifeq ($(CONFIG_TLS), internal) +ifeq ($(CONFIG_CRYPTO), libtomcrypt) +OBJS += ../src/crypto/crypto_libtomcrypt.o +OBJS_p += ../src/crypto/crypto_libtomcrypt.o +CONFIG_INTERNAL_SHA256=y +endif +ifeq ($(CONFIG_CRYPTO), internal) +OBJS += ../src/crypto/crypto_internal.o ../src/tls/rsa.o ../src/tls/bignum.o +OBJS_p += ../src/crypto/crypto_internal.o ../src/tls/rsa.o ../src/tls/bignum.o +CFLAGS += -DCONFIG_CRYPTO_INTERNAL +ifdef CONFIG_INTERNAL_LIBTOMMATH +CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH +ifdef CONFIG_INTERNAL_LIBTOMMATH_FAST +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 +endif +endif +else +CONFIG_INTERNAL_AES=y +CONFIG_INTERNAL_SHA1=y +CONFIG_INTERNAL_MD5=y +CONFIG_INTERNAL_SHA256=y +endif + +ifdef CONFIG_INTERNAL_AES +CFLAGS += -DINTERNAL_AES +endif +ifdef CONFIG_INTERNAL_SHA1 +CFLAGS += -DINTERNAL_SHA1 +endif +ifdef CONFIG_INTERNAL_SHA256 +CFLAGS += -DINTERNAL_SHA256 +endif +ifdef CONFIG_INTERNAL_MD5 +CFLAGS += -DINTERNAL_MD5 +endif +ifdef CONFIG_INTERNAL_MD4 +CFLAGS += -DINTERNAL_MD4 +endif +ifdef CONFIG_INTERNAL_DES +CFLAGS += -DINTERNAL_DES +endif + +ifdef NEED_SHA256 +OBJS += ../src/crypto/sha256.o +endif + +ifdef NEED_DH_GROUPS +OBJS += ../src/crypto/dh_groups.o +ifdef NEED_DH_GROUPS_ALL +CFLAGS += -DALL_DH_GROUPS +endif +endif + +ifndef NEED_FIPS186_2_PRF +CFLAGS += -DCONFIG_NO_FIPS186_2_PRF +endif + +ifndef NEED_T_PRF +CFLAGS += -DCONFIG_NO_T_PRF +endif + +ifndef NEED_TLS_PRF +CFLAGS += -DCONFIG_NO_TLS_PRF +endif + +ifdef CONFIG_RADIUS_SERVER +CFLAGS += -DRADIUS_SERVER +OBJS += ../src/radius/radius_server.o +endif + +ifdef CONFIG_IPV6 +CFLAGS += -DCONFIG_IPV6 +endif + +ifdef CONFIG_DRIVER_RADIUS_ACL +CFLAGS += -DCONFIG_DRIVER_RADIUS_ACL +endif + +ifdef CONFIG_FULL_DYNAMIC_VLAN +# define CONFIG_FULL_DYNAMIC_VLAN to have hostapd manipulate bridges +# and vlan interfaces for the vlan feature. +CFLAGS += -DCONFIG_FULL_DYNAMIC_VLAN +endif + +ifdef NEED_BASE64 +OBJS += ../src/utils/base64.o +endif + +ifdef CONFIG_NO_STDOUT_DEBUG +CFLAGS += -DCONFIG_NO_STDOUT_DEBUG +endif + +ifdef CONFIG_NO_AES_EXTRAS +CFLAGS += -DCONFIG_NO_AES_UNWRAP +CFLAGS += -DCONFIG_NO_AES_CTR -DCONFIG_NO_AES_OMAC1 +CFLAGS += -DCONFIG_NO_AES_EAX -DCONFIG_NO_AES_CBC +CFLAGS += -DCONFIG_NO_AES_DECRYPT +CFLAGS += -DCONFIG_NO_AES_ENCRYPT_BLOCK +endif + +ALL=hostapd hostapd_cli + +all: verify_config $(ALL) + +Q=@ +E=echo +ifeq ($(V), 1) +Q= +E=true +endif + +%.o: %.c + $(Q)$(CC) -c -o $@ $(CFLAGS) $< + @$(E) " CC " $< + +verify_config: + @if [ ! -r .config ]; then \ + echo 'Building hostapd requires a configuration file'; \ + echo '(.config). See README for more instructions. You can'; \ + echo 'run "cp defconfig .config" to create an example'; \ + echo 'configuration.'; \ + exit 1; \ + fi + +install: all + for i in $(ALL); do cp $$i /usr/local/bin/$$i; done + +hostapd: $(OBJS) + $(CC) -o hostapd $(OBJS) $(LIBS) + +OBJS_c = hostapd_cli.o ../src/common/wpa_ctrl.o ../src/utils/os_$(CONFIG_OS).o +hostapd_cli: $(OBJS_c) + $(CC) -o hostapd_cli $(OBJS_c) + +NOBJS = nt_password_hash.o ../src/crypto/ms_funcs.o ../src/crypto/sha1.o ../src/crypto/rc4.o ../src/crypto/md5.o +NOBJS += ../src/crypto/crypto_openssl.o ../src/utils/os_$(CONFIG_OS).o +ifdef TLS_FUNCS +LIBS_n += -lcrypto +endif + +nt_password_hash: $(NOBJS) + $(CC) -o nt_password_hash $(NOBJS) $(LIBS_n) + +hlr_auc_gw: $(HOBJS) + $(CC) -o hlr_auc_gw $(HOBJS) $(LIBS_h) + +clean: + $(MAKE) -C ../src clean + rm -f core *~ *.o hostapd hostapd_cli nt_password_hash hlr_auc_gw + rm -f *.d + +%.eps: %.fig + fig2dev -L eps $*.fig $*.eps + +%.png: %.fig + fig2dev -L png -m 3 $*.fig | pngtopnm | pnmscale 0.4 | pnmtopng \ + > $*.png + +docs-pics: doc/hostapd.png doc/hostapd.eps + +docs: docs-pics + (cd ..; doxygen hostapd/doc/doxygen.full; cd hostapd) + $(MAKE) -C doc/latex + cp doc/latex/refman.pdf hostapd-devel.pdf + +docs-fast: docs-pics + (cd ..; doxygen hostapd/doc/doxygen.fast; cd hostapd) + +clean-docs: + rm -rf doc/latex doc/html + rm -f doc/hostapd.{eps,png} hostapd-devel.pdf + +TEST_SRC_MILENAGE = ../src/hlr_auc_gw/milenage.c ../src/crypto/aes_wrap.c ../src/crypto/aes.c ../src/utils/common.c ../src/utils/wpa_debug.o ../src/utils/os_$(CONFIG_OS).c +test-milenage: $(TEST_SRC_MILENAGE) + $(CC) -o test-milenage -Wall -Werror $(TEST_SRC_MILENAGE) \ + -DTEST_MAIN_MILENAGE -I. -DINTERNAL_AES \ + -I../src/crypto -I../src/utils + ./test-milenage + rm test-milenage + +-include $(OBJS:%.o=%.d) diff --git a/hostapd/README-WPS b/hostapd/README-WPS index b46d767..e0e370b 100644 --- a/hostapd/README-WPS +++ b/hostapd/README-WPS @@ -165,10 +165,17 @@ Example command to add a PIN (12345670) for an Enrollee: hostapd_cli wps_pin 53b63a98-d29e-4457-a2ed-094d7e6a669c 12345670 If the UUID-E is not available (e.g., Enrollee waits for the Registrar -to be selected before connecting), wildcard UUID may be used to allow the PIN to be used once with any UUID: +to be selected before connecting), wildcard UUID may be used to allow +the PIN to be used once with any UUID: hostapd_cli wps_pin any 12345670 +To reduce likelihood of PIN being used with other devices or of +forgetting an active PIN available for potential attackers, expiration +time can be set for the new PIN: + +hostapd_cli wps_pin any 12345670 300 + After this, the Enrollee can connect to the AP again and complete WPS negotiation. At that point, a new, random WPA PSK is generated for the diff --git a/hostapd/ap.h b/hostapd/ap.h index 98f8ee7..2c6d7e9 100644 --- a/hostapd/ap.h +++ b/hostapd/ap.h @@ -30,7 +30,7 @@ #define WLAN_STA_PENDING_POLL BIT(6) /* pending activity poll not ACKed */ #define WLAN_STA_SHORT_PREAMBLE BIT(7) #define WLAN_STA_PREAUTH BIT(8) -#define WLAN_STA_WME BIT(9) +#define WLAN_STA_WMM BIT(9) #define WLAN_STA_MFP BIT(10) #define WLAN_STA_HT BIT(11) #define WLAN_STA_WPS BIT(12) diff --git a/hostapd/beacon.c b/hostapd/beacon.c index 31323e8..1f82d9c 100644 --- a/hostapd/beacon.c +++ b/hostapd/beacon.c @@ -298,8 +298,8 @@ void handle_probe_req(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt, pos = hostapd_eid_wpa(hapd, pos, epos - pos, sta); - /* Wi-Fi Wireless Multimedia Extensions */ - pos = hostapd_eid_wme(hapd, pos); + /* Wi-Fi Alliance WMM */ + pos = hostapd_eid_wmm(hapd, pos); pos = hostapd_eid_ht_capabilities_info(hapd, pos); pos = hostapd_eid_ht_operation(hapd, pos); @@ -395,8 +395,8 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd) tailpos = hostapd_eid_wpa(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE - tailpos, NULL); - /* Wi-Fi Wireless Multimedia Extensions */ - tailpos = hostapd_eid_wme(hapd, tailpos); + /* Wi-Fi Alliance WMM */ + tailpos = hostapd_eid_wmm(hapd, tailpos); #ifdef CONFIG_IEEE80211N if (hapd->iconf->ieee80211n) { diff --git a/hostapd/config.c b/hostapd/config.c index 6ad14d2..692b1a4 100644 --- a/hostapd/config.c +++ b/hostapd/config.c @@ -201,15 +201,15 @@ static struct hostapd_config * hostapd_config_defaults(void) struct hostapd_config *conf; struct hostapd_bss_config *bss; int i; - const int aCWmin = 15, aCWmax = 1024; - const struct hostapd_wme_ac_params ac_bk = + const int aCWmin = 4, aCWmax = 10; + const struct hostapd_wmm_ac_params ac_bk = { aCWmin, aCWmax, 7, 0, 0 }; /* background traffic */ - const struct hostapd_wme_ac_params ac_be = + const struct hostapd_wmm_ac_params ac_be = { aCWmin, aCWmax, 3, 0, 0 }; /* best effort traffic */ - const struct hostapd_wme_ac_params ac_vi = /* video traffic */ - { aCWmin >> 1, aCWmin, 2, 3000 / 32, 1 }; - const struct hostapd_wme_ac_params ac_vo = /* voice traffic */ - { aCWmin >> 2, aCWmin >> 1, 2, 1500 / 32, 1 }; + const struct hostapd_wmm_ac_params ac_vi = /* video traffic */ + { aCWmin - 1, aCWmin, 2, 3000 / 32, 1 }; + const struct hostapd_wmm_ac_params ac_vo = /* voice traffic */ + { aCWmin - 2, aCWmin - 1, 2, 1500 / 32, 1 }; conf = os_zalloc(sizeof(*conf)); bss = os_zalloc(sizeof(*bss)); @@ -251,10 +251,10 @@ static struct hostapd_config * hostapd_config_defaults(void) for (i = 0; i < NUM_TX_QUEUES; i++) conf->tx_queue[i].aifs = -1; /* use hw default */ - conf->wme_ac_params[0] = ac_be; - conf->wme_ac_params[1] = ac_bk; - conf->wme_ac_params[2] = ac_vi; - conf->wme_ac_params[3] = ac_vo; + conf->wmm_ac_params[0] = ac_be; + conf->wmm_ac_params[1] = ac_bk; + conf->wmm_ac_params[2] = ac_vi; + conf->wmm_ac_params[3] = ac_vo; #ifdef CONFIG_IEEE80211N conf->ht_capab = HT_CAP_INFO_SMPS_DISABLED; @@ -1166,14 +1166,14 @@ static int hostapd_config_tx_queue(struct hostapd_config *conf, char *name, } -static int hostapd_config_wme_ac(struct hostapd_config *conf, char *name, - char *val) +static int hostapd_config_wmm_ac(struct hostapd_config *conf, char *name, + char *val) { int num, v; char *pos; - struct hostapd_wme_ac_params *ac; + struct hostapd_wmm_ac_params *ac; - /* skip 'wme_ac_' prefix */ + /* skip 'wme_ac_' or 'wmm_ac_' prefix */ pos = name + 7; if (os_strncmp(pos, "be_", 3) == 0) { num = 0; @@ -1188,11 +1188,11 @@ static int hostapd_config_wme_ac(struct hostapd_config *conf, char *name, num = 3; pos += 3; } else { - wpa_printf(MSG_ERROR, "Unknown wme name '%s'", pos); + wpa_printf(MSG_ERROR, "Unknown WMM name '%s'", pos); return -1; } - ac = &conf->wme_ac_params[num]; + ac = &conf->wmm_ac_params[num]; if (os_strcmp(pos, "aifs") == 0) { v = atoi(val); @@ -1221,7 +1221,7 @@ static int hostapd_config_wme_ac(struct hostapd_config *conf, char *name, wpa_printf(MSG_ERROR, "Invalid txop value %d", v); return -1; } - ac->txopLimit = v; + ac->txop_limit = v; } else if (os_strcmp(pos, "acm") == 0) { v = atoi(val); if (v < 0 || v > 1) { @@ -1230,7 +1230,7 @@ static int hostapd_config_wme_ac(struct hostapd_config *conf, char *name, } ac->admission_control_mandatory = v; } else { - wpa_printf(MSG_ERROR, "Unknown wme_ac_ field '%s'", pos); + wpa_printf(MSG_ERROR, "Unknown wmm_ac_ field '%s'", pos); return -1; } @@ -1452,13 +1452,13 @@ struct hostapd_config * hostapd_config_read(const char *fname) } else if (os_strcmp(buf, "bridge") == 0) { os_strlcpy(bss->bridge, pos, sizeof(bss->bridge)); } else if (os_strcmp(buf, "driver") == 0) { - int i; + int j; /* clear to get error below if setting is invalid */ conf->driver = NULL; - for (i = 0; hostapd_drivers[i]; i++) { - if (os_strcmp(pos, hostapd_drivers[i]->name) == + for (j = 0; hostapd_drivers[j]; j++) { + if (os_strcmp(pos, hostapd_drivers[j]->name) == 0) { - conf->driver = hostapd_drivers[i]; + conf->driver = hostapd_drivers[j]; break; } } @@ -2070,11 +2070,13 @@ struct hostapd_config * hostapd_config_read(const char *fname) "queue item", line); errors++; } - } else if (os_strcmp(buf, "wme_enabled") == 0) { - bss->wme_enabled = atoi(pos); - } else if (os_strncmp(buf, "wme_ac_", 7) == 0) { - if (hostapd_config_wme_ac(conf, buf, pos)) { - wpa_printf(MSG_ERROR, "Line %d: invalid wme " + } else if (os_strcmp(buf, "wme_enabled") == 0 || + os_strcmp(buf, "wmm_enabled") == 0) { + bss->wmm_enabled = atoi(pos); + } else if (os_strncmp(buf, "wme_ac_", 7) == 0 || + os_strncmp(buf, "wmm_ac_", 7) == 0) { + if (hostapd_config_wmm_ac(conf, buf, pos)) { + wpa_printf(MSG_ERROR, "Line %d: invalid WMM " "ac item", line); errors++; } @@ -2255,29 +2257,30 @@ struct hostapd_config * hostapd_config_read(const char *fname) fclose(f); - if (bss->individual_wep_key_len == 0) { - /* individual keys are not use; can use key idx0 for broadcast - * keys */ - bss->broadcast_key_idx_min = 0; - } - - /* Select group cipher based on the enabled pairwise cipher suites */ - pairwise = 0; - if (bss->wpa & 1) - pairwise |= bss->wpa_pairwise; - if (bss->wpa & 2) { - if (bss->rsn_pairwise == 0) - bss->rsn_pairwise = bss->wpa_pairwise; - pairwise |= bss->rsn_pairwise; - } - if (pairwise & WPA_CIPHER_TKIP) - bss->wpa_group = WPA_CIPHER_TKIP; - else - bss->wpa_group = WPA_CIPHER_CCMP; - for (i = 0; i < conf->num_bss; i++) { bss = &conf->bss[i]; + if (bss->individual_wep_key_len == 0) { + /* individual keys are not use; can use key idx0 for + * broadcast keys */ + bss->broadcast_key_idx_min = 0; + } + + /* Select group cipher based on the enabled pairwise cipher + * suites */ + pairwise = 0; + if (bss->wpa & 1) + pairwise |= bss->wpa_pairwise; + if (bss->wpa & 2) { + if (bss->rsn_pairwise == 0) + bss->rsn_pairwise = bss->wpa_pairwise; + pairwise |= bss->rsn_pairwise; + } + if (pairwise & WPA_CIPHER_TKIP) + bss->wpa_group = WPA_CIPHER_TKIP; + else + bss->wpa_group = WPA_CIPHER_CCMP; + bss->radius->auth_server = bss->radius->auth_servers; bss->radius->acct_server = bss->radius->acct_servers; @@ -2476,6 +2479,8 @@ void hostapd_config_free(struct hostapd_config *conf) for (i = 0; i < conf->num_bss; i++) hostapd_config_free_bss(&conf->bss[i]); os_free(conf->bss); + os_free(conf->supported_rates); + os_free(conf->basic_rates); os_free(conf); } diff --git a/hostapd/config.h b/hostapd/config.h index a3247f2..ea530d4 100644 --- a/hostapd/config.h +++ b/hostapd/config.h @@ -135,11 +135,11 @@ struct hostapd_tx_queue_params { int configured; }; -struct hostapd_wme_ac_params { +struct hostapd_wmm_ac_params { int cwmin; int cwmax; int aifs; - int txopLimit; /* in units of 32us */ + int txop_limit; /* in units of 32us */ int admission_control_mandatory; }; @@ -271,7 +271,7 @@ struct hostapd_bss_config { int ap_max_inactivity; int ignore_broadcast_ssid; - int wme_enabled; + int wmm_enabled; struct hostapd_vlan *vlan, *vlan_tail; @@ -371,13 +371,13 @@ struct hostapd_config { struct hostapd_tx_queue_params tx_queue[NUM_TX_QUEUES]; /* - * WME AC parameters, in same order as 802.1D, i.e. + * WMM AC parameters, in same order as 802.1D, i.e. * 0 = BE (best effort) * 1 = BK (background) * 2 = VI (video) * 3 = VO (voice) */ - struct hostapd_wme_ac_params wme_ac_params[4]; + struct hostapd_wmm_ac_params wmm_ac_params[4]; enum { INTERNAL_BRIDGE_DO_NOT_CONTROL = -1, diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c index fe63e7c..9dec724 100644 --- a/hostapd/ctrl_iface.c +++ b/hostapd/ctrl_iface.c @@ -18,6 +18,7 @@ #include <sys/un.h> #include <sys/stat.h> +#include <stddef.h> #include "hostapd.h" #include "eloop.h" @@ -60,7 +61,8 @@ static int hostapd_ctrl_iface_attach(struct hostapd_data *hapd, dst->next = hapd->ctrl_dst; hapd->ctrl_dst = dst; wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor attached", - (u8 *) from->sun_path, fromlen); + (u8 *) from->sun_path, + fromlen - offsetof(struct sockaddr_un, sun_path)); return 0; } @@ -74,15 +76,18 @@ static int hostapd_ctrl_iface_detach(struct hostapd_data *hapd, dst = hapd->ctrl_dst; while (dst) { if (fromlen == dst->addrlen && - os_memcmp(from->sun_path, dst->addr.sun_path, fromlen) == - 0) { + os_memcmp(from->sun_path, dst->addr.sun_path, + fromlen - offsetof(struct sockaddr_un, sun_path)) + == 0) { if (prev == NULL) hapd->ctrl_dst = dst->next; else prev->next = dst->next; os_free(dst); wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor detached", - (u8 *) from->sun_path, fromlen); + (u8 *) from->sun_path, + fromlen - + offsetof(struct sockaddr_un, sun_path)); return 0; } prev = dst; @@ -104,10 +109,12 @@ static int hostapd_ctrl_iface_level(struct hostapd_data *hapd, dst = hapd->ctrl_dst; while (dst) { if (fromlen == dst->addrlen && - os_memcmp(from->sun_path, dst->addr.sun_path, fromlen) == - 0) { + os_memcmp(from->sun_path, dst->addr.sun_path, + fromlen - offsetof(struct sockaddr_un, sun_path)) + == 0) { wpa_hexdump(MSG_DEBUG, "CTRL_IFACE changed monitor " - "level", (u8 *) from->sun_path, fromlen); + "level", (u8 *) from->sun_path, fromlen - + offsetof(struct sockaddr_un, sun_path)); dst->debug_level = atoi(level); return 0; } @@ -246,10 +253,21 @@ static int hostapd_ctrl_iface_sa_query(struct hostapd_data *hapd, static int hostapd_ctrl_iface_wps_pin(struct hostapd_data *hapd, char *txt) { char *pin = os_strchr(txt, ' '); + char *timeout_txt; + int timeout; + if (pin == NULL) return -1; *pin++ = '\0'; - return hostapd_wps_add_pin(hapd, txt, pin); + + timeout_txt = os_strchr(pin, ' '); + if (timeout_txt) { + *timeout_txt++ = '\0'; + timeout = atoi(timeout_txt); + } else + timeout = 0; + + return hostapd_wps_add_pin(hapd, txt, pin, timeout); } #endif /* CONFIG_WPS */ @@ -434,14 +452,44 @@ int hostapd_ctrl_iface_init(struct hostapd_data *hapd) } os_memset(&addr, 0, sizeof(addr)); +#ifdef __FreeBSD__ + addr.sun_len = sizeof(addr); +#endif /* __FreeBSD__ */ addr.sun_family = AF_UNIX; fname = hostapd_ctrl_iface_path(hapd); if (fname == NULL) goto fail; os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path)); if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("bind(PF_UNIX)"); - goto fail; + wpa_printf(MSG_DEBUG, "ctrl_iface bind(PF_UNIX) failed: %s", + strerror(errno)); + if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not" + " allow connections - assuming it was left" + "over from forced program termination"); + if (unlink(fname) < 0) { + perror("unlink[ctrl_iface]"); + wpa_printf(MSG_ERROR, "Could not unlink " + "existing ctrl_iface socket '%s'", + fname); + goto fail; + } + if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < + 0) { + perror("bind(PF_UNIX)"); + goto fail; + } + wpa_printf(MSG_DEBUG, "Successfully replaced leftover " + "ctrl_iface socket '%s'", fname); + } else { + wpa_printf(MSG_INFO, "ctrl_iface exists and seems to " + "be in use - cannot override it"); + wpa_printf(MSG_INFO, "Delete '%s' manually if it is " + "not used anymore", fname); + os_free(fname); + fname = NULL; + goto fail; + } } if (hapd->conf->ctrl_interface_gid_set && @@ -536,15 +584,17 @@ static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, next = dst->next; if (level >= dst->debug_level) { wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor send", - (u8 *) dst->addr.sun_path, dst->addrlen); + (u8 *) dst->addr.sun_path, dst->addrlen - + offsetof(struct sockaddr_un, sun_path)); msg.msg_name = &dst->addr; msg.msg_namelen = dst->addrlen; if (sendmsg(hapd->ctrl_sock, &msg, 0) < 0) { - fprintf(stderr, "CTRL_IFACE monitor[%d]: ", - idx); - perror("sendmsg"); + int _errno = errno; + wpa_printf(MSG_INFO, "CTRL_IFACE monitor[%d]: " + "%d - %s", + idx, errno, strerror(errno)); dst->errors++; - if (dst->errors > 10) { + if (dst->errors > 10 || _errno == ENOENT) { hostapd_ctrl_iface_detach( hapd, &dst->addr, dst->addrlen); diff --git a/hostapd/doc/.gitignore b/hostapd/doc/.gitignore deleted file mode 100644 index 987a5e9..0000000 --- a/hostapd/doc/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -html -latex -hostapd.eps -hostapd.png diff --git a/hostapd/driver_atheros.c b/hostapd/driver_atheros.c new file mode 100644 index 0000000..558a8bb --- /dev/null +++ b/hostapd/driver_atheros.c @@ -0,0 +1,1457 @@ +/* + * hostapd / Driver interaction with Atheros driver + * Copyright (c) 2004, Sam Leffler <sam@errno.com> + * Copyright (c) 2004, Video54 Technologies + * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2009, Atheros Communications + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <net/if.h> +#include <sys/ioctl.h> + +#include "common.h" +#ifndef _BYTE_ORDER +#ifdef WORDS_BIGENDIAN +#define _BYTE_ORDER _BIG_ENDIAN +#else +#define _BYTE_ORDER _LITTLE_ENDIAN +#endif +#endif /* _BYTE_ORDER */ + +#include <net80211/ieee80211.h> +#include <net80211/_ieee80211.h> +#include <net80211/ieee80211_crypto.h> + +/* + * Note, the ATH_WPS_IE setting must match with the driver build.. If the + * driver does not include this, the IEEE80211_IOCTL_GETWPAIE ioctl will fail. + */ +#define ATH_WPS_IE +#include <net80211/ieee80211_ioctl.h> + +#ifdef CONFIG_WPS +#ifdef IEEE80211_IOCTL_FILTERFRAME +#include <netpacket/packet.h> + +#ifndef ETH_P_80211_RAW +#define ETH_P_80211_RAW 0x0019 +#endif +#endif /* IEEE80211_IOCTL_FILTERFRAME */ +#endif /* CONFIG_WPS */ + +/* + * Avoid conflicts with hostapd definitions by undefining couple of defines + * from madwifi header files. + */ +#undef WPA_OUI_TYPE +#undef WME_OUI_TYPE + +#include "wireless_copy.h" + +#include "hostapd.h" +#include "driver.h" +#include "eloop.h" +#include "priv_netlink.h" +#include "l2_packet/l2_packet.h" + +#include "wps_hostapd.h" +#include "ieee802_11_defs.h" + + +struct madwifi_driver_data { + struct hostapd_data *hapd; /* back pointer */ + + char iface[IFNAMSIZ + 1]; + int ifindex; + struct l2_packet_data *sock_xmit; /* raw packet xmit socket */ + struct l2_packet_data *sock_recv; /* raw packet recv socket */ + int ioctl_sock; /* socket for ioctl() use */ + int wext_sock; /* socket for wireless events */ + int we_version; + u8 acct_mac[ETH_ALEN]; + struct hostap_sta_driver_data acct_data; + + struct l2_packet_data *sock_raw; /* raw 802.11 management frames */ +}; + +static int madwifi_sta_deauth(void *priv, const u8 *addr, int reason_code); + +/* hostapd 0.7.x compatibility - START */ +#include "ieee802_1x.h" +#include "sta_info.h" +#include "wpa.h" +#include "radius/radius.h" +#include "ieee802_11.h" +#include "accounting.h" + +static int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, + const u8 *ie, size_t ielen) +{ + struct sta_info *sta; + int new_assoc, res; + + hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "associated"); + + sta = ap_get_sta(hapd, addr); + if (sta) { + accounting_sta_stop(hapd, sta); + } else { + sta = ap_sta_add(hapd, addr); + if (sta == NULL) + return -1; + } + sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS); + + if (hapd->conf->wpa) { + if (ie == NULL || ielen == 0) { + if (hapd->conf->wps_state) { + wpa_printf(MSG_DEBUG, "STA did not include " + "WPA/RSN IE in (Re)Association " + "Request - possible WPS use"); + sta->flags |= WLAN_STA_MAYBE_WPS; + goto skip_wpa_check; + } + + wpa_printf(MSG_DEBUG, "No WPA/RSN IE from STA"); + return -1; + } + if (hapd->conf->wps_state && ie[0] == 0xdd && ie[1] >= 4 && + os_memcmp(ie + 2, "\x00\x50\xf2\x04", 4) == 0) { + sta->flags |= WLAN_STA_WPS; + goto skip_wpa_check; + } + + if (sta->wpa_sm == NULL) + sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, + sta->addr); + if (sta->wpa_sm == NULL) { + wpa_printf(MSG_ERROR, "Failed to initialize WPA state " + "machine"); + return -1; + } + res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, + ie, ielen, NULL, 0); + if (res != WPA_IE_OK) { + wpa_printf(MSG_DEBUG, "WPA/RSN information element " + "rejected? (res %u)", res); + wpa_hexdump(MSG_DEBUG, "IE", ie, ielen); + return -1; + } + } else if (hapd->conf->wps_state) { + if (ie && ielen > 4 && ie[0] == 0xdd && ie[1] >= 4 && + os_memcmp(ie + 2, "\x00\x50\xf2\x04", 4) == 0) { + sta->flags |= WLAN_STA_WPS; + } else + sta->flags |= WLAN_STA_MAYBE_WPS; + } +skip_wpa_check: + + new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0; + sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC; + wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC); + + hostapd_new_assoc_sta(hapd, sta, !new_assoc); + + ieee802_1x_notify_port_enabled(sta->eapol_sm, 1); + + return 0; +} + + +static void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr) +{ + struct sta_info *sta; + + hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "disassociated"); + + sta = ap_get_sta(hapd, addr); + if (sta == NULL) { + wpa_printf(MSG_DEBUG, "Disassociation notification for " + "unknown STA " MACSTR, MAC2STR(addr)); + return; + } + + sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); + wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC); + sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST; + ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); + ap_free_sta(hapd, sta); +} + + +static void hostapd_eapol_receive(struct hostapd_data *hapd, const u8 *sa, + const u8 *buf, size_t len) +{ + ieee802_1x_receive(hapd, sa, buf, len); +} + + +static void hostapd_michael_mic_failure(struct hostapd_data *hapd, + const u8 *addr) +{ + ieee80211_michael_mic_failure(hapd, addr, 1); +} +/* hostapd 0.7.x compatibility - END */ + +static int +set80211priv(struct madwifi_driver_data *drv, int op, void *data, int len) +{ + struct iwreq iwr; + int do_inline = len < IFNAMSIZ; + + /* Certain ioctls must use the non-inlined method */ + if (op == IEEE80211_IOCTL_SET_APPIEBUF || + op == IEEE80211_IOCTL_FILTERFRAME) + do_inline = 0; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + if (do_inline) { + /* + * Argument data fits inline; put it there. + */ + memcpy(iwr.u.name, data, len); + } else { + /* + * Argument data too big for inline transfer; setup a + * parameter block instead; the kernel will transfer + * the data for the driver. + */ + iwr.u.data.pointer = data; + iwr.u.data.length = len; + } + + if (ioctl(drv->ioctl_sock, op, &iwr) < 0) { + int first = IEEE80211_IOCTL_SETPARAM; + static const char *opnames[] = { + "ioctl[IEEE80211_IOCTL_SETPARAM]", + "ioctl[IEEE80211_IOCTL_GETPARAM]", + "ioctl[IEEE80211_IOCTL_SETKEY]", + "ioctl[IEEE80211_IOCTL_SETWMMPARAMS]", + "ioctl[IEEE80211_IOCTL_DELKEY]", + "ioctl[IEEE80211_IOCTL_GETWMMPARAMS]", + "ioctl[IEEE80211_IOCTL_SETMLME]", + "ioctl[IEEE80211_IOCTL_GETCHANINFO]", + "ioctl[IEEE80211_IOCTL_SETOPTIE]", + "ioctl[IEEE80211_IOCTL_GETOPTIE]", + "ioctl[IEEE80211_IOCTL_ADDMAC]", + "ioctl[IEEE80211_IOCTL_DELMAC]", + "ioctl[IEEE80211_IOCTL_GETCHANLIST]", + "ioctl[IEEE80211_IOCTL_SETCHANLIST]", + "ioctl[IEEE80211_IOCTL_KICKMAC]", + "ioctl[IEEE80211_IOCTL_CHANSWITCH]", + "ioctl[IEEE80211_IOCTL_GETMODE]", + "ioctl[IEEE80211_IOCTL_SETMODE]", + "ioctl[IEEE80211_IOCTL_GET_APPIEBUF]", + "ioctl[IEEE80211_IOCTL_SET_APPIEBUF]", + NULL, + "ioctl[IEEE80211_IOCTL_FILTERFRAME]", + }; + int idx = op - first; + if (first <= op && + idx < (int) (sizeof(opnames) / sizeof(opnames[0])) && + opnames[idx]) + perror(opnames[idx]); + else { + perror("ioctl[unknown???]"); + wpa_printf(MSG_DEBUG, "Failed ioctl: 0x%x", op); + } + return -1; + } + return 0; +} + +static int +set80211param(struct madwifi_driver_data *drv, int op, int arg) +{ + struct iwreq iwr; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.mode = op; + memcpy(iwr.u.name+sizeof(__u32), &arg, sizeof(arg)); + + if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_SETPARAM, &iwr) < 0) { + perror("ioctl[IEEE80211_IOCTL_SETPARAM]"); + wpa_printf(MSG_DEBUG, "%s: Failed to set parameter (op %d " + "arg %d)", __func__, op, arg); + return -1; + } + return 0; +} + +#ifndef CONFIG_NO_STDOUT_DEBUG +static const char * +ether_sprintf(const u8 *addr) +{ + static char buf[sizeof(MACSTR)]; + + if (addr != NULL) + snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr)); + else + snprintf(buf, sizeof(buf), MACSTR, 0,0,0,0,0,0); + return buf; +} +#endif /* CONFIG_NO_STDOUT_DEBUG */ + +/* + * Configure WPA parameters. + */ +static int +madwifi_configure_wpa(struct madwifi_driver_data *drv) +{ + struct hostapd_data *hapd = drv->hapd; + struct hostapd_bss_config *conf = hapd->conf; + int v; + + switch (conf->wpa_group) { + case WPA_CIPHER_CCMP: + v = IEEE80211_CIPHER_AES_CCM; + break; + case WPA_CIPHER_TKIP: + v = IEEE80211_CIPHER_TKIP; + break; + case WPA_CIPHER_WEP104: + v = IEEE80211_CIPHER_WEP; + break; + case WPA_CIPHER_WEP40: + v = IEEE80211_CIPHER_WEP; + break; + case WPA_CIPHER_NONE: + v = IEEE80211_CIPHER_NONE; + break; + default: + wpa_printf(MSG_ERROR, "Unknown group key cipher %u", + conf->wpa_group); + return -1; + } + wpa_printf(MSG_DEBUG, "%s: group key cipher=%d", __func__, v); + if (set80211param(drv, IEEE80211_PARAM_MCASTCIPHER, v)) { + printf("Unable to set group key cipher to %u\n", v); + return -1; + } + if (v == IEEE80211_CIPHER_WEP) { + /* key length is done only for specific ciphers */ + v = (conf->wpa_group == WPA_CIPHER_WEP104 ? 13 : 5); + if (set80211param(drv, IEEE80211_PARAM_MCASTKEYLEN, v)) { + printf("Unable to set group key length to %u\n", v); + return -1; + } + } + + v = 0; + if (conf->wpa_pairwise & WPA_CIPHER_CCMP) + v |= 1<<IEEE80211_CIPHER_AES_CCM; + if (conf->wpa_pairwise & WPA_CIPHER_TKIP) + v |= 1<<IEEE80211_CIPHER_TKIP; + if (conf->wpa_pairwise & WPA_CIPHER_NONE) + v |= 1<<IEEE80211_CIPHER_NONE; + wpa_printf(MSG_DEBUG, "%s: pairwise key ciphers=0x%x", __func__, v); + if (set80211param(drv, IEEE80211_PARAM_UCASTCIPHERS, v)) { + printf("Unable to set pairwise key ciphers to 0x%x\n", v); + return -1; + } + + wpa_printf(MSG_DEBUG, "%s: key management algorithms=0x%x", + __func__, conf->wpa_key_mgmt); + if (set80211param(drv, IEEE80211_PARAM_KEYMGTALGS, conf->wpa_key_mgmt)) { + printf("Unable to set key management algorithms to 0x%x\n", + conf->wpa_key_mgmt); + return -1; + } + + v = 0; + if (conf->rsn_preauth) + v |= BIT(0); + wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x", + __func__, conf->rsn_preauth); + if (set80211param(drv, IEEE80211_PARAM_RSNCAPS, v)) { + printf("Unable to set RSN capabilities to 0x%x\n", v); + return -1; + } + + wpa_printf(MSG_DEBUG, "%s: enable WPA=0x%x", __func__, conf->wpa); + if (set80211param(drv, IEEE80211_PARAM_WPA, conf->wpa)) { + printf("Unable to set WPA to %u\n", conf->wpa); + return -1; + } + return 0; +} + + +static int +madwifi_set_iface_flags(void *priv, int dev_up) +{ + struct madwifi_driver_data *drv = priv; + struct ifreq ifr; + + wpa_printf(MSG_DEBUG, "%s: dev_up=%d", __func__, dev_up); + + if (drv->ioctl_sock < 0) + return -1; + + memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, drv->iface, IFNAMSIZ); + + if (ioctl(drv->ioctl_sock, SIOCGIFFLAGS, &ifr) != 0) { + perror("ioctl[SIOCGIFFLAGS]"); + return -1; + } + + if (dev_up) + ifr.ifr_flags |= IFF_UP; + else + ifr.ifr_flags &= ~IFF_UP; + + if (ioctl(drv->ioctl_sock, SIOCSIFFLAGS, &ifr) != 0) { + perror("ioctl[SIOCSIFFLAGS]"); + return -1; + } + + if (dev_up) { + memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, drv->iface, IFNAMSIZ); + ifr.ifr_mtu = HOSTAPD_MTU; + if (ioctl(drv->ioctl_sock, SIOCSIFMTU, &ifr) != 0) { + perror("ioctl[SIOCSIFMTU]"); + printf("Setting MTU failed - trying to survive with " + "current value\n"); + } + } + + return 0; +} + +static int +madwifi_set_ieee8021x(const char *ifname, void *priv, int enabled) +{ + struct madwifi_driver_data *drv = priv; + struct hostapd_data *hapd = drv->hapd; + struct hostapd_bss_config *conf = hapd->conf; + + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); + + if (!enabled) { + /* XXX restore state */ + return set80211param(priv, IEEE80211_PARAM_AUTHMODE, + IEEE80211_AUTH_AUTO); + } + if (!conf->wpa && !conf->ieee802_1x) { + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_DRIVER, + HOSTAPD_LEVEL_WARNING, "No 802.1X or WPA enabled!"); + return -1; + } + if (conf->wpa && madwifi_configure_wpa(drv) != 0) { + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_DRIVER, + HOSTAPD_LEVEL_WARNING, "Error configuring WPA state!"); + return -1; + } + if (set80211param(priv, IEEE80211_PARAM_AUTHMODE, + (conf->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) { + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_DRIVER, + HOSTAPD_LEVEL_WARNING, "Error enabling WPA/802.1X!"); + return -1; + } + + return 0; +} + +static int +madwifi_set_privacy(const char *ifname, void *priv, int enabled) +{ + struct madwifi_driver_data *drv = priv; + + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); + + return set80211param(drv, IEEE80211_PARAM_PRIVACY, enabled); +} + +static int +madwifi_set_sta_authorized(void *priv, const u8 *addr, int authorized) +{ + struct madwifi_driver_data *drv = priv; + struct ieee80211req_mlme mlme; + int ret; + + wpa_printf(MSG_DEBUG, "%s: addr=%s authorized=%d", + __func__, ether_sprintf(addr), authorized); + + if (authorized) + mlme.im_op = IEEE80211_MLME_AUTHORIZE; + else + mlme.im_op = IEEE80211_MLME_UNAUTHORIZE; + mlme.im_reason = 0; + memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to %sauthorize STA " MACSTR, + __func__, authorized ? "" : "un", MAC2STR(addr)); + } + + return ret; +} + +static int +madwifi_sta_set_flags(void *priv, const u8 *addr, int total_flags, + int flags_or, int flags_and) +{ + /* For now, only support setting Authorized flag */ + if (flags_or & WLAN_STA_AUTHORIZED) + return madwifi_set_sta_authorized(priv, addr, 1); + if (!(flags_and & WLAN_STA_AUTHORIZED)) + return madwifi_set_sta_authorized(priv, addr, 0); + return 0; +} + +static int +madwifi_del_key(void *priv, const u8 *addr, int key_idx) +{ + struct madwifi_driver_data *drv = priv; + struct ieee80211req_del_key wk; + int ret; + + wpa_printf(MSG_DEBUG, "%s: addr=%s key_idx=%d", + __func__, ether_sprintf(addr), key_idx); + + memset(&wk, 0, sizeof(wk)); + if (addr != NULL) { + memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN); + wk.idk_keyix = (u8) IEEE80211_KEYIX_NONE; + } else { + wk.idk_keyix = key_idx; + } + + ret = set80211priv(drv, IEEE80211_IOCTL_DELKEY, &wk, sizeof(wk)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to delete key (addr %s" + " key_idx %d)", __func__, ether_sprintf(addr), + key_idx); + } + + return ret; +} + +static int +madwifi_set_key(const char *ifname, void *priv, const char *alg, + const u8 *addr, int key_idx, + const u8 *key, size_t key_len, int txkey) +{ + struct madwifi_driver_data *drv = priv; + struct ieee80211req_key wk; + u_int8_t cipher; + int ret; + + if (strcmp(alg, "none") == 0) + return madwifi_del_key(drv, addr, key_idx); + + wpa_printf(MSG_DEBUG, "%s: alg=%s addr=%s key_idx=%d", + __func__, alg, ether_sprintf(addr), key_idx); + + if (strcmp(alg, "WEP") == 0) + cipher = IEEE80211_CIPHER_WEP; + else if (strcmp(alg, "TKIP") == 0) + cipher = IEEE80211_CIPHER_TKIP; + else if (strcmp(alg, "CCMP") == 0) + cipher = IEEE80211_CIPHER_AES_CCM; + else { + printf("%s: unknown/unsupported algorithm %s\n", + __func__, alg); + return -1; + } + + if (key_len > sizeof(wk.ik_keydata)) { + printf("%s: key length %lu too big\n", __func__, + (unsigned long) key_len); + return -3; + } + + memset(&wk, 0, sizeof(wk)); + wk.ik_type = cipher; + wk.ik_flags = IEEE80211_KEY_RECV | IEEE80211_KEY_XMIT; + if (addr == NULL) { + memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN); + wk.ik_keyix = key_idx; + wk.ik_flags |= IEEE80211_KEY_DEFAULT; + } else { + memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); + wk.ik_keyix = IEEE80211_KEYIX_NONE; + } + wk.ik_keylen = key_len; + memcpy(wk.ik_keydata, key, key_len); + + ret = set80211priv(drv, IEEE80211_IOCTL_SETKEY, &wk, sizeof(wk)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to set key (addr %s" + " key_idx %d alg '%s' key_len %lu txkey %d)", + __func__, ether_sprintf(wk.ik_macaddr), key_idx, + alg, (unsigned long) key_len, txkey); + } + + return ret; +} + + +static int +madwifi_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx, + u8 *seq) +{ + struct madwifi_driver_data *drv = priv; + struct ieee80211req_key wk; + + wpa_printf(MSG_DEBUG, "%s: addr=%s idx=%d", + __func__, ether_sprintf(addr), idx); + + memset(&wk, 0, sizeof(wk)); + if (addr == NULL) + memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN); + else + memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); + wk.ik_keyix = idx; + + if (set80211priv(drv, IEEE80211_IOCTL_GETKEY, &wk, sizeof(wk))) { + wpa_printf(MSG_DEBUG, "%s: Failed to get encryption data " + "(addr " MACSTR " key_idx %d)", + __func__, MAC2STR(wk.ik_macaddr), idx); + return -1; + } + +#ifdef WORDS_BIGENDIAN + { + /* + * wk.ik_keytsc is in host byte order (big endian), need to + * swap it to match with the byte order used in WPA. + */ + int i; + u8 tmp[WPA_KEY_RSC_LEN]; + memcpy(tmp, &wk.ik_keytsc, sizeof(wk.ik_keytsc)); + for (i = 0; i < WPA_KEY_RSC_LEN; i++) { + seq[i] = tmp[WPA_KEY_RSC_LEN - i - 1]; + } + } +#else /* WORDS_BIGENDIAN */ + memcpy(seq, &wk.ik_keytsc, sizeof(wk.ik_keytsc)); +#endif /* WORDS_BIGENDIAN */ + return 0; +} + + +static int +madwifi_flush(void *priv) +{ + u8 allsta[IEEE80211_ADDR_LEN]; + memset(allsta, 0xff, IEEE80211_ADDR_LEN); + return madwifi_sta_deauth(priv, allsta, IEEE80211_REASON_AUTH_LEAVE); +} + + +static int +madwifi_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data, + const u8 *addr) +{ + struct madwifi_driver_data *drv = priv; + struct ieee80211req_sta_stats stats; + + memset(data, 0, sizeof(*data)); + + /* + * Fetch statistics for station from the system. + */ + memset(&stats, 0, sizeof(stats)); + memcpy(stats.is_u.macaddr, addr, IEEE80211_ADDR_LEN); + if (set80211priv(drv, IEEE80211_IOCTL_STA_STATS, + &stats, sizeof(stats))) { + wpa_printf(MSG_DEBUG, "%s: Failed to fetch STA stats (addr " + MACSTR ")", __func__, MAC2STR(addr)); + if (memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) { + memcpy(data, &drv->acct_data, sizeof(*data)); + return 0; + } + + printf("Failed to get station stats information element.\n"); + return -1; + } + + data->rx_packets = stats.is_stats.ns_rx_data; + data->rx_bytes = stats.is_stats.ns_rx_bytes; + data->tx_packets = stats.is_stats.ns_tx_data; + data->tx_bytes = stats.is_stats.ns_tx_bytes; + return 0; +} + + +static int +madwifi_sta_clear_stats(void *priv, const u8 *addr) +{ + struct madwifi_driver_data *drv = priv; + struct ieee80211req_mlme mlme; + int ret; + + wpa_printf(MSG_DEBUG, "%s: addr=%s", __func__, ether_sprintf(addr)); + + mlme.im_op = IEEE80211_MLME_CLEAR_STATS; + memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, + sizeof(mlme)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to clear STA stats (addr " + MACSTR ")", __func__, MAC2STR(addr)); + } + + return ret; +} + + +static int +madwifi_set_opt_ie(const char *ifname, void *priv, const u8 *ie, size_t ie_len) +{ + /* + * Do nothing; we setup parameters at startup that define the + * contents of the beacon information element. + */ + return 0; +} + +static int +madwifi_sta_deauth(void *priv, const u8 *addr, int reason_code) +{ + struct madwifi_driver_data *drv = priv; + struct ieee80211req_mlme mlme; + int ret; + + wpa_printf(MSG_DEBUG, "%s: addr=%s reason_code=%d", + __func__, ether_sprintf(addr), reason_code); + + mlme.im_op = IEEE80211_MLME_DEAUTH; + mlme.im_reason = reason_code; + memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to deauth STA (addr " MACSTR + " reason %d)", + __func__, MAC2STR(addr), reason_code); + } + + return ret; +} + +static int +madwifi_sta_disassoc(void *priv, const u8 *addr, int reason_code) +{ + struct madwifi_driver_data *drv = priv; + struct ieee80211req_mlme mlme; + int ret; + + wpa_printf(MSG_DEBUG, "%s: addr=%s reason_code=%d", + __func__, ether_sprintf(addr), reason_code); + + mlme.im_op = IEEE80211_MLME_DISASSOC; + mlme.im_reason = reason_code; + memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to disassoc STA (addr " + MACSTR " reason %d)", + __func__, MAC2STR(addr), reason_code); + } + + return ret; +} + +#ifdef CONFIG_WPS +static void madwifi_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len) +{ + struct madwifi_driver_data *drv = ctx; + const struct ieee80211_mgmt *mgmt; + const u8 *end, *ie; + u16 fc; + size_t ie_len; + + /* Send Probe Request information to WPS processing */ + + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)) + return; + mgmt = (const struct ieee80211_mgmt *) buf; + + fc = le_to_host16(mgmt->frame_control); + if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT || + WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_PROBE_REQ) + return; + + end = buf + len; + ie = mgmt->u.probe_req.variable; + ie_len = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)); + + hostapd_wps_probe_req_rx(drv->hapd, mgmt->sa, ie, ie_len); +} +#endif /* CONFIG_WPS */ + +static int madwifi_receive_probe_req(struct madwifi_driver_data *drv) +{ + int ret = 0; +#ifdef CONFIG_WPS + struct ieee80211req_set_filter filt; + + wpa_printf(MSG_DEBUG, "%s Enter", __func__); + filt.app_filterype = IEEE80211_FILTER_TYPE_PROBE_REQ; + + ret = set80211priv(drv, IEEE80211_IOCTL_FILTERFRAME, &filt, + sizeof(struct ieee80211req_set_filter)); + if (ret) + return ret; + + drv->sock_raw = l2_packet_init(drv->iface, NULL, ETH_P_80211_RAW, + madwifi_raw_receive, drv, 1); + if (drv->sock_raw == NULL) + return -1; +#endif /* CONFIG_WPS */ + return ret; +} + +#ifdef CONFIG_WPS +static int +madwifi_set_wps_ie(void *priv, const u8 *ie, size_t len, u32 frametype) +{ + struct madwifi_driver_data *drv = priv; + u8 buf[256]; + struct ieee80211req_getset_appiebuf *beac_ie; + + wpa_printf(MSG_DEBUG, "%s buflen = %lu", __func__, + (unsigned long) len); + + beac_ie = (struct ieee80211req_getset_appiebuf *) buf; + beac_ie->app_frmtype = frametype; + beac_ie->app_buflen = len; + memcpy(&(beac_ie->app_buf[0]), ie, len); + + return set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, beac_ie, + sizeof(struct ieee80211req_getset_appiebuf) + len); +} + +static int +madwifi_set_wps_beacon_ie(const char *ifname, void *priv, const u8 *ie, + size_t len) +{ + return madwifi_set_wps_ie(priv, ie, len, IEEE80211_APPIE_FRAME_BEACON); +} + +static int +madwifi_set_wps_probe_resp_ie(const char *ifname, void *priv, const u8 *ie, + size_t len) +{ + return madwifi_set_wps_ie(priv, ie, len, + IEEE80211_APPIE_FRAME_PROBE_RESP); +} +#else /* CONFIG_WPS */ +#define madwifi_set_wps_beacon_ie NULL +#define madwifi_set_wps_probe_resp_ie NULL +#endif /* CONFIG_WPS */ + +static int +madwifi_new_sta(struct madwifi_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) +{ + struct hostapd_data *hapd = drv->hapd; + struct ieee80211req_wpaie ie; + int ielen = 0, res; + u8 *iebuf = NULL; + + /* + * Fetch negotiated WPA/RSN parameters from the system. + */ + memset(&ie, 0, sizeof(ie)); + memcpy(ie.wpa_macaddr, addr, IEEE80211_ADDR_LEN); + if (set80211priv(drv, IEEE80211_IOCTL_GETWPAIE, &ie, sizeof(ie))) { + /* + * See ATH_WPS_IE comment in the beginning of the file for a + * possible cause for the failure.. + */ + wpa_printf(MSG_DEBUG, "%s: Failed to get WPA/RSN IE: %s", + __func__, strerror(errno)); + goto no_ie; + } + wpa_hexdump(MSG_MSGDUMP, "madwifi req WPA IE", + ie.wpa_ie, IEEE80211_MAX_OPT_IE); + wpa_hexdump(MSG_MSGDUMP, "madwifi req RSN IE", + ie.rsn_ie, IEEE80211_MAX_OPT_IE); + iebuf = ie.wpa_ie; + /* madwifi seems to return some random data if WPA/RSN IE is not set. + * Assume the IE was not included if the IE type is unknown. */ + if (iebuf[0] != WLAN_EID_VENDOR_SPECIFIC) + iebuf[1] = 0; + if (iebuf[1] == 0 && ie.rsn_ie[1] > 0) { + /* madwifi-ng svn #1453 added rsn_ie. Use it, if wpa_ie was not + * set. This is needed for WPA2. */ + iebuf = ie.rsn_ie; + if (iebuf[0] != WLAN_EID_RSN) + iebuf[1] = 0; + } + + ielen = iebuf[1]; + if (ielen == 0) + iebuf = NULL; + else + ielen += 2; + +no_ie: + res = hostapd_notif_assoc(hapd, addr, iebuf, ielen); + + if (memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) { + /* Cached accounting data is not valid anymore. */ + memset(drv->acct_mac, 0, ETH_ALEN); + memset(&drv->acct_data, 0, sizeof(drv->acct_data)); + } + + return res; +} + +static void +madwifi_wireless_event_wireless_custom(struct madwifi_driver_data *drv, + char *custom, char *end) +{ + wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom); + + if (strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) { + char *pos; + u8 addr[ETH_ALEN]; + pos = strstr(custom, "addr="); + if (pos == NULL) { + wpa_printf(MSG_DEBUG, + "MLME-MICHAELMICFAILURE.indication " + "without sender address ignored"); + return; + } + pos += 5; + if (hwaddr_aton(pos, addr) == 0) { + hostapd_michael_mic_failure(drv->hapd, addr); + } else { + wpa_printf(MSG_DEBUG, + "MLME-MICHAELMICFAILURE.indication " + "with invalid MAC address"); + } + } else if (strncmp(custom, "STA-TRAFFIC-STAT", 16) == 0) { + char *key, *value; + u32 val; + key = custom; + while ((key = strchr(key, '\n')) != NULL) { + key++; + value = strchr(key, '='); + if (value == NULL) + continue; + *value++ = '\0'; + val = strtoul(value, NULL, 10); + if (strcmp(key, "mac") == 0) + hwaddr_aton(value, drv->acct_mac); + else if (strcmp(key, "rx_packets") == 0) + drv->acct_data.rx_packets = val; + else if (strcmp(key, "tx_packets") == 0) + drv->acct_data.tx_packets = val; + else if (strcmp(key, "rx_bytes") == 0) + drv->acct_data.rx_bytes = val; + else if (strcmp(key, "tx_bytes") == 0) + drv->acct_data.tx_bytes = val; + key = value; + } +#ifdef CONFIG_WPS + } else if (strncmp(custom, "PUSH-BUTTON.indication", 22) == 0) { + /* Some atheros kernels send push button as a wireless event */ + /* PROBLEM! this event is received for ALL BSSs ... + * so all are enabled for WPS... ugh. + */ + hostapd_wps_button_pushed(drv->hapd); + } else if (strncmp(custom, "Manage.prob_req ", 16) == 0) { + /* + * Atheros driver uses a hack to pass Probe Request frames as a + * binary data in the custom wireless event. The old way (using + * packet sniffing) didn't work when bridging. + * Format: "Manage.prob_req <frame len>" | zero padding | frame + */ +#define WPS_FRAM_TAG_SIZE 30 /* hardcoded in driver */ + int len = atoi(custom + 16); + if (len < 0 || custom + WPS_FRAM_TAG_SIZE + len > end) { + wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req event " + "length %d", len); + return; + } + madwifi_raw_receive(drv, NULL, + (u8 *) custom + WPS_FRAM_TAG_SIZE, len); +#endif /* CONFIG_WPS */ + } +} + +static void +madwifi_wireless_event_wireless(struct madwifi_driver_data *drv, + char *data, int len) +{ + struct iw_event iwe_buf, *iwe = &iwe_buf; + char *pos, *end, *custom, *buf; + + pos = data; + end = data + len; + + while (pos + IW_EV_LCP_LEN <= end) { + /* Event data may be unaligned, so make a local, aligned copy + * before processing. */ + memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); + wpa_printf(MSG_MSGDUMP, "Wireless event: cmd=0x%x len=%d", + iwe->cmd, iwe->len); + if (iwe->len <= IW_EV_LCP_LEN) + return; + + custom = pos + IW_EV_POINT_LEN; + if (drv->we_version > 18 && + (iwe->cmd == IWEVMICHAELMICFAILURE || + iwe->cmd == IWEVASSOCREQIE || + iwe->cmd == IWEVCUSTOM)) { + /* WE-19 removed the pointer from struct iw_point */ + char *dpos = (char *) &iwe_buf.u.data.length; + int dlen = dpos - (char *) &iwe_buf; + memcpy(dpos, pos + IW_EV_LCP_LEN, + sizeof(struct iw_event) - dlen); + } else { + memcpy(&iwe_buf, pos, sizeof(struct iw_event)); + custom += IW_EV_POINT_OFF; + } + + switch (iwe->cmd) { + case IWEVEXPIRED: + hostapd_notif_disassoc(drv->hapd, + (u8 *) iwe->u.addr.sa_data); + break; + case IWEVREGISTERED: + madwifi_new_sta(drv, (u8 *) iwe->u.addr.sa_data); + break; + case IWEVASSOCREQIE: + /* Driver hack.. Use IWEVASSOCREQIE to bypass + * IWEVCUSTOM size limitations. Need to handle this + * just like IWEVCUSTOM. + */ + case IWEVCUSTOM: + if (custom + iwe->u.data.length > end) + return; + buf = malloc(iwe->u.data.length + 1); + if (buf == NULL) + return; /* XXX */ + memcpy(buf, custom, iwe->u.data.length); + buf[iwe->u.data.length] = '\0'; + madwifi_wireless_event_wireless_custom( + drv, buf, buf + iwe->u.data.length); + free(buf); + break; + } + + pos += iwe->len; + } +} + + +static void +madwifi_wireless_event_rtm_newlink(struct madwifi_driver_data *drv, + struct nlmsghdr *h, int len) +{ + struct ifinfomsg *ifi; + int attrlen, nlmsg_len, rta_len; + struct rtattr * attr; + + if (len < (int) sizeof(*ifi)) + return; + + ifi = NLMSG_DATA(h); + + if (ifi->ifi_index != drv->ifindex) + return; + + nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); + + attrlen = h->nlmsg_len - nlmsg_len; + if (attrlen < 0) + return; + + attr = (struct rtattr *) (((char *) ifi) + nlmsg_len); + + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + if (attr->rta_type == IFLA_WIRELESS) { + madwifi_wireless_event_wireless( + drv, ((char *) attr) + rta_len, + attr->rta_len - rta_len); + } + attr = RTA_NEXT(attr, attrlen); + } +} + + +static void +madwifi_wireless_event_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + char buf[256]; + int left; + struct sockaddr_nl from; + socklen_t fromlen; + struct nlmsghdr *h; + struct madwifi_driver_data *drv = eloop_ctx; + + fromlen = sizeof(from); + left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, + (struct sockaddr *) &from, &fromlen); + if (left < 0) { + if (errno != EINTR && errno != EAGAIN) + perror("recvfrom(netlink)"); + return; + } + + h = (struct nlmsghdr *) buf; + while (left >= (int) sizeof(*h)) { + int len, plen; + + len = h->nlmsg_len; + plen = len - sizeof(*h); + if (len > left || plen < 0) { + printf("Malformed netlink message: " + "len=%d left=%d plen=%d\n", + len, left, plen); + break; + } + + switch (h->nlmsg_type) { + case RTM_NEWLINK: + madwifi_wireless_event_rtm_newlink(drv, h, plen); + break; + } + + len = NLMSG_ALIGN(len); + left -= len; + h = (struct nlmsghdr *) ((char *) h + len); + } + + if (left > 0) { + printf("%d extra bytes in the end of netlink message\n", left); + } +} + + +static int +madwifi_get_we_version(struct madwifi_driver_data *drv) +{ + struct iw_range *range; + struct iwreq iwr; + int minlen; + size_t buflen; + + drv->we_version = 0; + + /* + * Use larger buffer than struct iw_range in order to allow the + * structure to grow in the future. + */ + buflen = sizeof(struct iw_range) + 500; + range = os_zalloc(buflen); + if (range == NULL) + return -1; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) range; + iwr.u.data.length = buflen; + + minlen = ((char *) &range->enc_capa) - (char *) range + + sizeof(range->enc_capa); + + if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) { + perror("ioctl[SIOCGIWRANGE]"); + free(range); + return -1; + } else if (iwr.u.data.length >= minlen && + range->we_version_compiled >= 18) { + wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d " + "WE(source)=%d enc_capa=0x%x", + range->we_version_compiled, + range->we_version_source, + range->enc_capa); + drv->we_version = range->we_version_compiled; + } + + free(range); + return 0; +} + + +static int +madwifi_wireless_event_init(void *priv) +{ + struct madwifi_driver_data *drv = priv; + int s; + struct sockaddr_nl local; + + madwifi_get_we_version(drv); + + drv->wext_sock = -1; + + s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (s < 0) { + perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)"); + return -1; + } + + memset(&local, 0, sizeof(local)); + local.nl_family = AF_NETLINK; + local.nl_groups = RTMGRP_LINK; + if (bind(s, (struct sockaddr *) &local, sizeof(local)) < 0) { + perror("bind(netlink)"); + close(s); + return -1; + } + + eloop_register_read_sock(s, madwifi_wireless_event_receive, drv, NULL); + drv->wext_sock = s; + + return 0; +} + + +static void +madwifi_wireless_event_deinit(void *priv) +{ + struct madwifi_driver_data *drv = priv; + + if (drv != NULL) { + if (drv->wext_sock < 0) + return; + eloop_unregister_read_sock(drv->wext_sock); + close(drv->wext_sock); + } +} + + +static int +madwifi_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len, + int encrypt, const u8 *own_addr) +{ + struct madwifi_driver_data *drv = priv; + unsigned char buf[3000]; + unsigned char *bp = buf; + struct l2_ethhdr *eth; + size_t len; + int status; + + /* + * Prepend the Ethernet header. If the caller left us + * space at the front we could just insert it but since + * we don't know we copy to a local buffer. Given the frequency + * and size of frames this probably doesn't matter. + */ + len = data_len + sizeof(struct l2_ethhdr); + if (len > sizeof(buf)) { + bp = malloc(len); + if (bp == NULL) { + printf("EAPOL frame discarded, cannot malloc temp " + "buffer of size %lu!\n", (unsigned long) len); + return -1; + } + } + eth = (struct l2_ethhdr *) bp; + memcpy(eth->h_dest, addr, ETH_ALEN); + memcpy(eth->h_source, own_addr, ETH_ALEN); + eth->h_proto = host_to_be16(ETH_P_EAPOL); + memcpy(eth+1, data, data_len); + + wpa_hexdump(MSG_MSGDUMP, "TX EAPOL", bp, len); + + status = l2_packet_send(drv->sock_xmit, addr, ETH_P_EAPOL, bp, len); + + if (bp != buf) + free(bp); + return status; +} + +static void +handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) +{ + struct madwifi_driver_data *drv = ctx; + hostapd_eapol_receive(drv->hapd, src_addr, + buf + sizeof(struct l2_ethhdr), + len - sizeof(struct l2_ethhdr)); +} + +static void * +madwifi_init(struct hostapd_data *hapd) +{ + struct madwifi_driver_data *drv; + struct ifreq ifr; + struct iwreq iwr; + + drv = os_zalloc(sizeof(struct madwifi_driver_data)); + if (drv == NULL) { + printf("Could not allocate memory for madwifi driver data\n"); + return NULL; + } + + drv->hapd = hapd; + drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->ioctl_sock < 0) { + perror("socket[PF_INET,SOCK_DGRAM]"); + goto bad; + } + memcpy(drv->iface, hapd->conf->iface, sizeof(drv->iface)); + + memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, drv->iface, sizeof(ifr.ifr_name)); + if (ioctl(drv->ioctl_sock, SIOCGIFINDEX, &ifr) != 0) { + perror("ioctl(SIOCGIFINDEX)"); + goto bad; + } + drv->ifindex = ifr.ifr_ifindex; + + drv->sock_xmit = l2_packet_init(drv->iface, NULL, ETH_P_EAPOL, + handle_read, drv, 1); + if (drv->sock_xmit == NULL) + goto bad; + if (l2_packet_get_own_addr(drv->sock_xmit, hapd->own_addr)) + goto bad; + if (hapd->conf->bridge[0] != '\0') { + wpa_printf(MSG_DEBUG, "Configure bridge %s for EAPOL traffic.", + hapd->conf->bridge); + drv->sock_recv = l2_packet_init(hapd->conf->bridge, NULL, + ETH_P_EAPOL, handle_read, drv, + 1); + if (drv->sock_recv == NULL) + goto bad; + } else + drv->sock_recv = drv->sock_xmit; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + + iwr.u.mode = IW_MODE_MASTER; + + if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) < 0) { + perror("ioctl[SIOCSIWMODE]"); + printf("Could not set interface to master mode!\n"); + goto bad; + } + + madwifi_set_iface_flags(drv, 0); /* mark down during setup */ + madwifi_set_privacy(drv->iface, drv, 0); /* default to no privacy */ + + madwifi_receive_probe_req(drv); + + return drv; +bad: + if (drv->sock_xmit != NULL) + l2_packet_deinit(drv->sock_xmit); + if (drv->ioctl_sock >= 0) + close(drv->ioctl_sock); + if (drv != NULL) + free(drv); + return NULL; +} + + +static void +madwifi_deinit(void *priv) +{ + struct madwifi_driver_data *drv = priv; + + (void) madwifi_set_iface_flags(drv, 0); + if (drv->ioctl_sock >= 0) + close(drv->ioctl_sock); + if (drv->sock_recv != NULL && drv->sock_recv != drv->sock_xmit) + l2_packet_deinit(drv->sock_recv); + if (drv->sock_xmit != NULL) + l2_packet_deinit(drv->sock_xmit); + if (drv->sock_raw) + l2_packet_deinit(drv->sock_raw); + free(drv); +} + +static int +madwifi_set_ssid(const char *ifname, void *priv, const u8 *buf, int len) +{ + struct madwifi_driver_data *drv = priv; + struct iwreq iwr; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.essid.flags = 1; /* SSID active */ + iwr.u.essid.pointer = (caddr_t) buf; + iwr.u.essid.length = len + 1; + + if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) { + perror("ioctl[SIOCSIWESSID]"); + printf("len=%d\n", len); + return -1; + } + return 0; +} + +static int +madwifi_get_ssid(const char *ifname, void *priv, u8 *buf, int len) +{ + struct madwifi_driver_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.essid.pointer = (caddr_t) buf; + iwr.u.essid.length = len; + + if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) { + perror("ioctl[SIOCGIWESSID]"); + ret = -1; + } else + ret = iwr.u.essid.length; + + return ret; +} + +static int +madwifi_set_countermeasures(void *priv, int enabled) +{ + struct madwifi_driver_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + return set80211param(drv, IEEE80211_PARAM_COUNTERMEASURES, enabled); +} + +static int +madwifi_commit(void *priv) +{ + return madwifi_set_iface_flags(priv, 1); +} + +const struct wpa_driver_ops wpa_driver_atheros_ops = { + .name = "atheros", + .init = madwifi_init, + .deinit = madwifi_deinit, + .set_ieee8021x = madwifi_set_ieee8021x, + .set_privacy = madwifi_set_privacy, + .set_encryption = madwifi_set_key, + .get_seqnum = madwifi_get_seqnum, + .flush = madwifi_flush, + .set_generic_elem = madwifi_set_opt_ie, + .wireless_event_init = madwifi_wireless_event_init, + .wireless_event_deinit = madwifi_wireless_event_deinit, + .sta_set_flags = madwifi_sta_set_flags, + .read_sta_data = madwifi_read_sta_driver_data, + .send_eapol = madwifi_send_eapol, + .sta_disassoc = madwifi_sta_disassoc, + .sta_deauth = madwifi_sta_deauth, + .set_ssid = madwifi_set_ssid, + .get_ssid = madwifi_get_ssid, + .set_countermeasures = madwifi_set_countermeasures, + .sta_clear_stats = madwifi_sta_clear_stats, + .commit = madwifi_commit, + .set_wps_beacon_ie = madwifi_set_wps_beacon_ie, + .set_wps_probe_resp_ie = madwifi_set_wps_probe_resp_ie, +}; diff --git a/hostapd/driver_bsd.c b/hostapd/driver_bsd.c new file mode 100644 index 0000000..43d57d9 --- /dev/null +++ b/hostapd/driver_bsd.c @@ -0,0 +1,839 @@ +/* + * hostapd / Driver interaction with BSD net80211 layer + * Copyright (c) 2004, Sam Leffler <sam@errno.com> + * Copyright (c) 2004, 2Wire, Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <sys/ioctl.h> + +#include <net/if.h> + +#include <net80211/ieee80211.h> +#include <net80211/ieee80211_crypto.h> +#include <net80211/ieee80211_ioctl.h> + +/* + * Avoid conflicts with hostapd definitions by undefining couple of defines + * from net80211 header files. + */ +#undef RSN_VERSION +#undef WPA_VERSION +#undef WPA_OUI_TYPE +#undef WME_OUI_TYPE + +#include "hostapd.h" +#include "driver.h" +#include "ieee802_1x.h" +#include "eloop.h" +#include "sta_info.h" +#include "l2_packet/l2_packet.h" + +#include "eapol_sm.h" +#include "wpa.h" +#include "radius/radius.h" +#include "ieee802_11.h" +#include "common.h" + +struct bsd_driver_data { + struct hostapd_data *hapd; /* back pointer */ + + char iface[IFNAMSIZ + 1]; + struct l2_packet_data *sock_xmit; /* raw packet xmit socket */ + int ioctl_sock; /* socket for ioctl() use */ + int wext_sock; /* socket for wireless events */ +}; + +static int bsd_sta_deauth(void *priv, const u8 *addr, int reason_code); + +static int +set80211var(struct bsd_driver_data *drv, int op, const void *arg, int arg_len) +{ + struct ieee80211req ireq; + + memset(&ireq, 0, sizeof(ireq)); + os_strlcpy(ireq.i_name, drv->iface, IFNAMSIZ); + ireq.i_type = op; + ireq.i_len = arg_len; + ireq.i_data = (void *) arg; + + if (ioctl(drv->ioctl_sock, SIOCS80211, &ireq) < 0) { + perror("ioctl[SIOCS80211]"); + return -1; + } + return 0; +} + +static int +get80211var(struct bsd_driver_data *drv, int op, void *arg, int arg_len) +{ + struct ieee80211req ireq; + + memset(&ireq, 0, sizeof(ireq)); + os_strlcpy(ireq.i_name, drv->iface, IFNAMSIZ); + ireq.i_type = op; + ireq.i_len = arg_len; + ireq.i_data = arg; + + if (ioctl(drv->ioctl_sock, SIOCG80211, &ireq) < 0) { + perror("ioctl[SIOCG80211]"); + return -1; + } + return ireq.i_len; +} + +static int +set80211param(struct bsd_driver_data *drv, int op, int arg) +{ + struct ieee80211req ireq; + + memset(&ireq, 0, sizeof(ireq)); + os_strlcpy(ireq.i_name, drv->iface, IFNAMSIZ); + ireq.i_type = op; + ireq.i_val = arg; + + if (ioctl(drv->ioctl_sock, SIOCS80211, &ireq) < 0) { + perror("ioctl[SIOCS80211]"); + return -1; + } + return 0; +} + +static const char * +ether_sprintf(const u8 *addr) +{ + static char buf[sizeof(MACSTR)]; + + if (addr != NULL) + snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr)); + else + snprintf(buf, sizeof(buf), MACSTR, 0,0,0,0,0,0); + return buf; +} + +/* + * Configure WPA parameters. + */ +static int +bsd_configure_wpa(struct bsd_driver_data *drv) +{ + static const char *ciphernames[] = + { "WEP", "TKIP", "AES-OCB", "AES-CCM", "CKIP", "NONE" }; + struct hostapd_data *hapd = drv->hapd; + struct hostapd_bss_config *conf = hapd->conf; + int v; + + switch (conf->wpa_group) { + case WPA_CIPHER_CCMP: + v = IEEE80211_CIPHER_AES_CCM; + break; + case WPA_CIPHER_TKIP: + v = IEEE80211_CIPHER_TKIP; + break; + case WPA_CIPHER_WEP104: + v = IEEE80211_CIPHER_WEP; + break; + case WPA_CIPHER_WEP40: + v = IEEE80211_CIPHER_WEP; + break; + case WPA_CIPHER_NONE: + v = IEEE80211_CIPHER_NONE; + break; + default: + printf("Unknown group key cipher %u\n", + conf->wpa_group); + return -1; + } + wpa_printf(MSG_DEBUG, "%s: group key cipher=%s (%u)", + __func__, ciphernames[v], v); + if (set80211param(drv, IEEE80211_IOC_MCASTCIPHER, v)) { + printf("Unable to set group key cipher to %u (%s)\n", + v, ciphernames[v]); + return -1; + } + if (v == IEEE80211_CIPHER_WEP) { + /* key length is done only for specific ciphers */ + v = (conf->wpa_group == WPA_CIPHER_WEP104 ? 13 : 5); + if (set80211param(drv, IEEE80211_IOC_MCASTKEYLEN, v)) { + printf("Unable to set group key length to %u\n", v); + return -1; + } + } + + v = 0; + if (conf->wpa_pairwise & WPA_CIPHER_CCMP) + v |= 1<<IEEE80211_CIPHER_AES_CCM; + if (conf->wpa_pairwise & WPA_CIPHER_TKIP) + v |= 1<<IEEE80211_CIPHER_TKIP; + if (conf->wpa_pairwise & WPA_CIPHER_NONE) + v |= 1<<IEEE80211_CIPHER_NONE; + wpa_printf(MSG_DEBUG, "%s: pairwise key ciphers=0x%x", __func__, v); + if (set80211param(drv, IEEE80211_IOC_UCASTCIPHERS, v)) { + printf("Unable to set pairwise key ciphers to 0x%x\n", v); + return -1; + } + + wpa_printf(MSG_DEBUG, "%s: key management algorithms=0x%x", + __func__, conf->wpa_key_mgmt); + if (set80211param(drv, IEEE80211_IOC_KEYMGTALGS, conf->wpa_key_mgmt)) { + printf("Unable to set key management algorithms to 0x%x\n", + conf->wpa_key_mgmt); + return -1; + } + + v = 0; + if (conf->rsn_preauth) + v |= BIT(0); + wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x", + __func__, conf->rsn_preauth); + if (set80211param(drv, IEEE80211_IOC_RSNCAPS, v)) { + printf("Unable to set RSN capabilities to 0x%x\n", v); + return -1; + } + + wpa_printf(MSG_DEBUG, "%s: enable WPA= 0x%x", __func__, conf->wpa); + if (set80211param(drv, IEEE80211_IOC_WPA, conf->wpa)) { + printf("Unable to set WPA to %u\n", conf->wpa); + return -1; + } + return 0; +} + + +static int +bsd_set_iface_flags(void *priv, int dev_up) +{ + struct bsd_driver_data *drv = priv; + struct ifreq ifr; + + wpa_printf(MSG_DEBUG, "%s: dev_up=%d", __func__, dev_up); + + if (drv->ioctl_sock < 0) + return -1; + + memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, drv->iface, IFNAMSIZ); + + if (ioctl(drv->ioctl_sock, SIOCGIFFLAGS, &ifr) != 0) { + perror("ioctl[SIOCGIFFLAGS]"); + return -1; + } + + if (dev_up) + ifr.ifr_flags |= IFF_UP; + else + ifr.ifr_flags &= ~IFF_UP; + + if (ioctl(drv->ioctl_sock, SIOCSIFFLAGS, &ifr) != 0) { + perror("ioctl[SIOCSIFFLAGS]"); + return -1; + } + + if (dev_up) { + memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, drv->iface, IFNAMSIZ); + ifr.ifr_mtu = HOSTAPD_MTU; + if (ioctl(drv->ioctl_sock, SIOCSIFMTU, &ifr) != 0) { + perror("ioctl[SIOCSIFMTU]"); + printf("Setting MTU failed - trying to survive with " + "current value\n"); + } + } + + return 0; +} + +static int +bsd_set_ieee8021x(const char *ifname, void *priv, int enabled) +{ + struct bsd_driver_data *drv = priv; + struct hostapd_data *hapd = drv->hapd; + struct hostapd_bss_config *conf = hapd->conf; + + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); + + if (!enabled) { + /* XXX restore state */ + return set80211param(priv, IEEE80211_IOC_AUTHMODE, + IEEE80211_AUTH_AUTO); + } + if (!conf->wpa && !conf->ieee802_1x) { + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_DRIVER, + HOSTAPD_LEVEL_WARNING, "No 802.1X or WPA enabled!"); + return -1; + } + if (conf->wpa && bsd_configure_wpa(drv) != 0) { + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_DRIVER, + HOSTAPD_LEVEL_WARNING, "Error configuring WPA state!"); + return -1; + } + if (set80211param(priv, IEEE80211_IOC_AUTHMODE, + (conf->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) { + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_DRIVER, + HOSTAPD_LEVEL_WARNING, "Error enabling WPA/802.1X!"); + return -1; + } + return bsd_set_iface_flags(priv, 1); +} + +static int +bsd_set_privacy(const char *ifname, void *priv, int enabled) +{ + struct bsd_driver_data *drv = priv; + + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); + + return set80211param(drv, IEEE80211_IOC_PRIVACY, enabled); +} + +static int +bsd_set_sta_authorized(void *priv, const u8 *addr, int authorized) +{ + struct bsd_driver_data *drv = priv; + struct ieee80211req_mlme mlme; + + wpa_printf(MSG_DEBUG, "%s: addr=%s authorized=%d", + __func__, ether_sprintf(addr), authorized); + + if (authorized) + mlme.im_op = IEEE80211_MLME_AUTHORIZE; + else + mlme.im_op = IEEE80211_MLME_UNAUTHORIZE; + mlme.im_reason = 0; + memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + return set80211var(drv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme)); +} + +static int +bsd_sta_set_flags(void *priv, const u8 *addr, int total_flags, int flags_or, + int flags_and) +{ + /* For now, only support setting Authorized flag */ + if (flags_or & WLAN_STA_AUTHORIZED) + return bsd_set_sta_authorized(priv, addr, 1); + if (!(flags_and & WLAN_STA_AUTHORIZED)) + return bsd_set_sta_authorized(priv, addr, 0); + return 0; +} + +static int +bsd_del_key(void *priv, const u8 *addr, int key_idx) +{ + struct bsd_driver_data *drv = priv; + struct ieee80211req_del_key wk; + + wpa_printf(MSG_DEBUG, "%s: addr=%s key_idx=%d", + __func__, ether_sprintf(addr), key_idx); + + memset(&wk, 0, sizeof(wk)); + if (addr != NULL) { + memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN); + wk.idk_keyix = (u_int8_t) IEEE80211_KEYIX_NONE; /* XXX */ + } else { + wk.idk_keyix = key_idx; + } + + return set80211var(drv, IEEE80211_IOC_DELKEY, &wk, sizeof(wk)); +} + +static int +bsd_set_key(const char *ifname, void *priv, const char *alg, + const u8 *addr, int key_idx, + const u8 *key, size_t key_len, int txkey) +{ + struct bsd_driver_data *drv = priv; + struct ieee80211req_key wk; + u_int8_t cipher; + + if (strcmp(alg, "none") == 0) + return bsd_del_key(drv, addr, key_idx); + + wpa_printf(MSG_DEBUG, "%s: alg=%s addr=%s key_idx=%d", + __func__, alg, ether_sprintf(addr), key_idx); + + if (strcmp(alg, "WEP") == 0) + cipher = IEEE80211_CIPHER_WEP; + else if (strcmp(alg, "TKIP") == 0) + cipher = IEEE80211_CIPHER_TKIP; + else if (strcmp(alg, "CCMP") == 0) + cipher = IEEE80211_CIPHER_AES_CCM; + else { + printf("%s: unknown/unsupported algorithm %s\n", + __func__, alg); + return -1; + } + + if (key_len > sizeof(wk.ik_keydata)) { + printf("%s: key length %d too big\n", __func__, key_len); + return -3; + } + + memset(&wk, 0, sizeof(wk)); + wk.ik_type = cipher; + wk.ik_flags = IEEE80211_KEY_RECV | IEEE80211_KEY_XMIT; + if (addr == NULL) { + memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN); + wk.ik_keyix = key_idx; + wk.ik_flags |= IEEE80211_KEY_DEFAULT; + } else { + memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); + wk.ik_keyix = IEEE80211_KEYIX_NONE; + } + wk.ik_keylen = key_len; + memcpy(wk.ik_keydata, key, key_len); + + return set80211var(drv, IEEE80211_IOC_WPAKEY, &wk, sizeof(wk)); +} + + +static int +bsd_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx, + u8 *seq) +{ + struct bsd_driver_data *drv = priv; + struct ieee80211req_key wk; + + wpa_printf(MSG_DEBUG, "%s: addr=%s idx=%d", + __func__, ether_sprintf(addr), idx); + + memset(&wk, 0, sizeof(wk)); + if (addr == NULL) + memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN); + else + memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); + wk.ik_keyix = idx; + + if (get80211var(drv, IEEE80211_IOC_WPAKEY, &wk, sizeof(wk)) < 0) { + printf("Failed to get encryption.\n"); + return -1; + } + +#ifdef WORDS_BIGENDIAN + { + /* + * wk.ik_keytsc is in host byte order (big endian), need to + * swap it to match with the byte order used in WPA. + */ + int i; + u8 tmp[WPA_KEY_RSC_LEN]; + memcpy(tmp, &wk.ik_keytsc, sizeof(wk.ik_keytsc)); + for (i = 0; i < WPA_KEY_RSC_LEN; i++) { + seq[i] = tmp[WPA_KEY_RSC_LEN - i - 1]; + } + } +#else /* WORDS_BIGENDIAN */ + memcpy(seq, &wk.ik_keytsc, sizeof(wk.ik_keytsc)); +#endif /* WORDS_BIGENDIAN */ + return 0; +} + + +static int +bsd_flush(void *priv) +{ + u8 allsta[IEEE80211_ADDR_LEN]; + + memset(allsta, 0xff, IEEE80211_ADDR_LEN); + return bsd_sta_deauth(priv, allsta, IEEE80211_REASON_AUTH_LEAVE); +} + + +static int +bsd_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data, + const u8 *addr) +{ + struct bsd_driver_data *drv = priv; + struct ieee80211req_sta_stats stats; + + memcpy(stats.is_u.macaddr, addr, IEEE80211_ADDR_LEN); + if (get80211var(drv, IEEE80211_IOC_STA_STATS, &stats, sizeof(stats)) > 0) { + /* XXX? do packets counts include non-data frames? */ + data->rx_packets = stats.is_stats.ns_rx_data; + data->rx_bytes = stats.is_stats.ns_rx_bytes; + data->tx_packets = stats.is_stats.ns_tx_data; + data->tx_bytes = stats.is_stats.ns_tx_bytes; + } + return 0; +} + +static int +bsd_set_opt_ie(const char *ifname, void *priv, const u8 *ie, size_t ie_len) +{ + /* + * Do nothing; we setup parameters at startup that define the + * contents of the beacon information element. + */ + return 0; +} + +static int +bsd_sta_deauth(void *priv, const u8 *addr, int reason_code) +{ + struct bsd_driver_data *drv = priv; + struct ieee80211req_mlme mlme; + + wpa_printf(MSG_DEBUG, "%s: addr=%s reason_code=%d", + __func__, ether_sprintf(addr), reason_code); + + mlme.im_op = IEEE80211_MLME_DEAUTH; + mlme.im_reason = reason_code; + memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + return set80211var(drv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme)); +} + +static int +bsd_sta_disassoc(void *priv, const u8 *addr, int reason_code) +{ + struct bsd_driver_data *drv = priv; + struct ieee80211req_mlme mlme; + + wpa_printf(MSG_DEBUG, "%s: addr=%s reason_code=%d", + __func__, ether_sprintf(addr), reason_code); + + mlme.im_op = IEEE80211_MLME_DISASSOC; + mlme.im_reason = reason_code; + memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + return set80211var(drv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme)); +} + +static int +bsd_del_sta(struct bsd_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) +{ + struct hostapd_data *hapd = drv->hapd; + struct hostapd_bss_config *conf = hapd->conf; + struct sta_info *sta; + + hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "deassociated"); + + sta = ap_get_sta(hapd, addr); + if (sta != NULL) { + sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); + if (conf->wpa) + wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC); + sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST; + ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); + ap_free_sta(hapd, sta); + } + return 0; +} + +static int +bsd_new_sta(struct bsd_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) +{ + struct hostapd_data *hapd = drv->hapd; + struct hostapd_bss_config *conf = hapd->conf; + struct sta_info *sta; + struct ieee80211req_wpaie ie; + int new_assoc, ielen, res; + + hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "associated"); + + sta = ap_sta_add(hapd, addr); + if (sta == NULL) + return -1; + /* + * Fetch and validate any negotiated WPA/RSN parameters. + */ + if (conf->wpa) { + memset(&ie, 0, sizeof(ie)); + memcpy(ie.wpa_macaddr, addr, IEEE80211_ADDR_LEN); + if (get80211var(drv, IEEE80211_IOC_WPAIE, &ie, sizeof(ie)) < 0) { + printf("Failed to get WPA/RSN information element.\n"); + return -1; /* XXX not right */ + } + ielen = ie.wpa_ie[1]; + if (ielen == 0) { + printf("No WPA/RSN information element for station!\n"); + return -1; /* XXX not right */ + } + ielen += 2; + if (sta->wpa_sm == NULL) + sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, + sta->addr); + if (sta->wpa_sm == NULL) { + printf("Failed to initialize WPA state machine\n"); + return -1; + } + res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, + ie.wpa_ie, ielen, NULL, 0); + if (res != WPA_IE_OK) { + printf("WPA/RSN information element rejected? " + "(res %u)\n", res); + return -1; + } + } + + /* + * Now that the internal station state is setup + * kick the authenticator into action. + */ + new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0; + sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC; + wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC); + hostapd_new_assoc_sta(hapd, sta, !new_assoc); + ieee802_1x_notify_port_enabled(sta->eapol_sm, 1); + return 0; +} + +#include <net/route.h> +#include <net80211/ieee80211_freebsd.h> + +static void +bsd_wireless_event_receive(int sock, void *ctx, void *sock_ctx) +{ + struct bsd_driver_data *drv = ctx; + struct hostapd_data *hapd = drv->hapd; + char buf[2048]; + struct if_announcemsghdr *ifan; + struct rt_msghdr *rtm; + struct ieee80211_michael_event *mic; + struct ieee80211_join_event *join; + struct ieee80211_leave_event *leave; + int n; + + n = read(sock, buf, sizeof(buf)); + if (n < 0) { + if (errno != EINTR && errno != EAGAIN) + perror("read(PF_ROUTE)"); + return; + } + + rtm = (struct rt_msghdr *) buf; + if (rtm->rtm_version != RTM_VERSION) { + wpa_printf(MSG_DEBUG, "Routing message version %d not " + "understood\n", rtm->rtm_version); + return; + } + ifan = (struct if_announcemsghdr *) rtm; + switch (rtm->rtm_type) { + case RTM_IEEE80211: + switch (ifan->ifan_what) { + case RTM_IEEE80211_ASSOC: + case RTM_IEEE80211_REASSOC: + case RTM_IEEE80211_DISASSOC: + case RTM_IEEE80211_SCAN: + break; + case RTM_IEEE80211_LEAVE: + leave = (struct ieee80211_leave_event *) &ifan[1]; + bsd_del_sta(drv, leave->iev_addr); + break; + case RTM_IEEE80211_JOIN: +#ifdef RTM_IEEE80211_REJOIN + case RTM_IEEE80211_REJOIN: +#endif + join = (struct ieee80211_join_event *) &ifan[1]; + bsd_new_sta(drv, join->iev_addr); + break; + case RTM_IEEE80211_REPLAY: + /* ignore */ + break; + case RTM_IEEE80211_MICHAEL: + mic = (struct ieee80211_michael_event *) &ifan[1]; + wpa_printf(MSG_DEBUG, + "Michael MIC failure wireless event: " + "keyix=%u src_addr=" MACSTR, mic->iev_keyix, + MAC2STR(mic->iev_src)); + ieee80211_michael_mic_failure(hapd, mic->iev_src, 1); + break; + } + break; + } +} + +static int +bsd_wireless_event_init(void *priv) +{ + struct bsd_driver_data *drv = priv; + int s; + + drv->wext_sock = -1; + + s = socket(PF_ROUTE, SOCK_RAW, 0); + if (s < 0) { + perror("socket(PF_ROUTE,SOCK_RAW)"); + return -1; + } + eloop_register_read_sock(s, bsd_wireless_event_receive, drv, NULL); + drv->wext_sock = s; + + return 0; +} + +static void +bsd_wireless_event_deinit(void *priv) +{ + struct bsd_driver_data *drv = priv; + + if (drv != NULL) { + if (drv->wext_sock < 0) + return; + eloop_unregister_read_sock(drv->wext_sock); + close(drv->wext_sock); + } +} + + +static int +bsd_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len, + int encrypt, const u8 *own_addr) +{ + struct bsd_driver_data *drv = priv; + unsigned char buf[3000]; + unsigned char *bp = buf; + struct l2_ethhdr *eth; + size_t len; + int status; + + /* + * Prepend the Etherent header. If the caller left us + * space at the front we could just insert it but since + * we don't know we copy to a local buffer. Given the frequency + * and size of frames this probably doesn't matter. + */ + len = data_len + sizeof(struct l2_ethhdr); + if (len > sizeof(buf)) { + bp = malloc(len); + if (bp == NULL) { + printf("EAPOL frame discarded, cannot malloc temp " + "buffer of size %u!\n", len); + return -1; + } + } + eth = (struct l2_ethhdr *) bp; + memcpy(eth->h_dest, addr, ETH_ALEN); + memcpy(eth->h_source, own_addr, ETH_ALEN); + eth->h_proto = htons(ETH_P_EAPOL); + memcpy(eth+1, data, data_len); + + wpa_hexdump(MSG_MSGDUMP, "TX EAPOL", bp, len); + + status = l2_packet_send(drv->sock_xmit, addr, ETH_P_EAPOL, bp, len); + + if (bp != buf) + free(bp); + return status; +} + +static void +handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) +{ + struct bsd_driver_data *drv = ctx; + struct hostapd_data *hapd = drv->hapd; + struct sta_info *sta; + + sta = ap_get_sta(hapd, src_addr); + if (!sta || !(sta->flags & WLAN_STA_ASSOC)) { + printf("Data frame from not associated STA %s\n", + ether_sprintf(src_addr)); + /* XXX cannot happen */ + return; + } + ieee802_1x_receive(hapd, src_addr, buf + sizeof(struct l2_ethhdr), + len - sizeof(struct l2_ethhdr)); +} + +static int +bsd_get_ssid(const char *ifname, void *priv, u8 *buf, int len) +{ + struct bsd_driver_data *drv = priv; + int ssid_len = get80211var(drv, IEEE80211_IOC_SSID, buf, len); + + wpa_printf(MSG_DEBUG, "%s: ssid=\"%.*s\"", __func__, ssid_len, buf); + + return ssid_len; +} + +static int +bsd_set_ssid(const char *ifname, void *priv, const u8 *buf, int len) +{ + struct bsd_driver_data *drv = priv; + + wpa_printf(MSG_DEBUG, "%s: ssid=\"%.*s\"", __func__, len, buf); + + return set80211var(drv, IEEE80211_IOC_SSID, buf, len); +} + +static void * +bsd_init(struct hostapd_data *hapd) +{ + struct bsd_driver_data *drv; + + drv = os_zalloc(sizeof(struct bsd_driver_data)); + if (drv == NULL) { + printf("Could not allocate memory for bsd driver data\n"); + goto bad; + } + + drv->hapd = hapd; + drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->ioctl_sock < 0) { + perror("socket[PF_INET,SOCK_DGRAM]"); + goto bad; + } + memcpy(drv->iface, hapd->conf->iface, sizeof(drv->iface)); + + drv->sock_xmit = l2_packet_init(drv->iface, NULL, ETH_P_EAPOL, + handle_read, drv, 1); + if (drv->sock_xmit == NULL) + goto bad; + if (l2_packet_get_own_addr(drv->sock_xmit, hapd->own_addr)) + goto bad; + + bsd_set_iface_flags(drv, 0); /* mark down during setup */ + + return drv; +bad: + if (drv->sock_xmit != NULL) + l2_packet_deinit(drv->sock_xmit); + if (drv->ioctl_sock >= 0) + close(drv->ioctl_sock); + if (drv != NULL) + free(drv); + return NULL; +} + + +static void +bsd_deinit(void *priv) +{ + struct bsd_driver_data *drv = priv; + + (void) bsd_set_iface_flags(drv, 0); + if (drv->ioctl_sock >= 0) + close(drv->ioctl_sock); + if (drv->sock_xmit != NULL) + l2_packet_deinit(drv->sock_xmit); + free(drv); +} + +const struct wpa_driver_ops wpa_driver_bsd_ops = { + .name = "bsd", + .init = bsd_init, + .deinit = bsd_deinit, + .set_ieee8021x = bsd_set_ieee8021x, + .set_privacy = bsd_set_privacy, + .set_encryption = bsd_set_key, + .get_seqnum = bsd_get_seqnum, + .flush = bsd_flush, + .set_generic_elem = bsd_set_opt_ie, + .wireless_event_init = bsd_wireless_event_init, + .wireless_event_deinit = bsd_wireless_event_deinit, + .sta_set_flags = bsd_sta_set_flags, + .read_sta_data = bsd_read_sta_driver_data, + .send_eapol = bsd_send_eapol, + .sta_disassoc = bsd_sta_disassoc, + .sta_deauth = bsd_sta_deauth, + .set_ssid = bsd_set_ssid, + .get_ssid = bsd_get_ssid, +}; diff --git a/hostapd/driver_hostap.c b/hostapd/driver_hostap.c new file mode 100644 index 0000000..ceff099 --- /dev/null +++ b/hostapd/driver_hostap.c @@ -0,0 +1,1279 @@ +/* + * hostapd / Kernel driver communication with Linux Host AP driver + * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <sys/ioctl.h> + +#ifdef USE_KERNEL_HEADERS +/* compat-wireless does not include linux/compiler.h to define __user, so + * define it here */ +#ifndef __user +#define __user +#endif /* __user */ +#include <asm/types.h> +#include <linux/if_packet.h> +#include <linux/if_ether.h> /* The L2 protocols */ +#include <linux/if_arp.h> +#include <linux/wireless.h> +#else /* USE_KERNEL_HEADERS */ +#include <net/if_arp.h> +#include <netpacket/packet.h> +#include "wireless_copy.h" +#endif /* USE_KERNEL_HEADERS */ + +#include "hostapd.h" +#include "driver.h" +#include "ieee802_1x.h" +#include "eloop.h" +#include "priv_netlink.h" +#include "ieee802_11.h" +#include "sta_info.h" +#include "hostap_common.h" +#include "hw_features.h" + + +struct hostap_driver_data { + struct hostapd_data *hapd; + + char iface[IFNAMSIZ + 1]; + int sock; /* raw packet socket for driver access */ + int ioctl_sock; /* socket for ioctl() use */ + int wext_sock; /* socket for wireless events */ + + int we_version; + + u8 *generic_ie; + size_t generic_ie_len; + u8 *wps_ie; + size_t wps_ie_len; +}; + + +static int hostapd_ioctl(void *priv, struct prism2_hostapd_param *param, + int len); +static int hostap_set_iface_flags(void *priv, int dev_up); + +static void handle_data(struct hostapd_data *hapd, u8 *buf, size_t len, + u16 stype) +{ + struct ieee80211_hdr *hdr; + u16 fc, ethertype; + u8 *pos, *sa; + size_t left; + struct sta_info *sta; + + if (len < sizeof(struct ieee80211_hdr)) + return; + + hdr = (struct ieee80211_hdr *) buf; + fc = le_to_host16(hdr->frame_control); + + if ((fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) != WLAN_FC_TODS) { + printf("Not ToDS data frame (fc=0x%04x)\n", fc); + return; + } + + sa = hdr->addr2; + sta = ap_get_sta(hapd, sa); + if (!sta || !(sta->flags & WLAN_STA_ASSOC)) { + printf("Data frame from not associated STA " MACSTR "\n", + MAC2STR(sa)); + if (sta && (sta->flags & WLAN_STA_AUTH)) + hostapd_sta_disassoc( + hapd, sa, + WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA); + else + hostapd_sta_deauth( + hapd, sa, + WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA); + return; + } + + pos = (u8 *) (hdr + 1); + left = len - sizeof(*hdr); + + if (left < sizeof(rfc1042_header)) { + printf("Too short data frame\n"); + return; + } + + if (memcmp(pos, rfc1042_header, sizeof(rfc1042_header)) != 0) { + printf("Data frame with no RFC1042 header\n"); + return; + } + pos += sizeof(rfc1042_header); + left -= sizeof(rfc1042_header); + + if (left < 2) { + printf("No ethertype in data frame\n"); + return; + } + + ethertype = WPA_GET_BE16(pos); + pos += 2; + left -= 2; + switch (ethertype) { + case ETH_P_PAE: + ieee802_1x_receive(hapd, sa, pos, left); + break; + + default: + printf("Unknown ethertype 0x%04x in data frame\n", ethertype); + break; + } +} + + +static void handle_tx_callback(struct hostapd_data *hapd, u8 *buf, size_t len, + int ok) +{ + struct ieee80211_hdr *hdr; + u16 fc, type, stype; + struct sta_info *sta; + + hdr = (struct ieee80211_hdr *) buf; + fc = le_to_host16(hdr->frame_control); + + type = WLAN_FC_GET_TYPE(fc); + stype = WLAN_FC_GET_STYPE(fc); + + switch (type) { + case WLAN_FC_TYPE_MGMT: + wpa_printf(MSG_DEBUG, "MGMT (TX callback) %s", + ok ? "ACK" : "fail"); + ieee802_11_mgmt_cb(hapd, buf, len, stype, ok); + break; + case WLAN_FC_TYPE_CTRL: + wpa_printf(MSG_DEBUG, "CTRL (TX callback) %s", + ok ? "ACK" : "fail"); + break; + case WLAN_FC_TYPE_DATA: + wpa_printf(MSG_DEBUG, "DATA (TX callback) %s", + ok ? "ACK" : "fail"); + sta = ap_get_sta(hapd, hdr->addr1); + if (sta && sta->flags & WLAN_STA_PENDING_POLL) { + wpa_printf(MSG_DEBUG, "STA " MACSTR + " %s pending activity poll", + MAC2STR(sta->addr), + ok ? "ACKed" : "did not ACK"); + if (ok) + sta->flags &= ~WLAN_STA_PENDING_POLL; + } + if (sta) + ieee802_1x_tx_status(hapd, sta, buf, len, ok); + break; + default: + printf("unknown TX callback frame type %d\n", type); + break; + } +} + + +static void handle_frame(struct hostapd_data *hapd, u8 *buf, size_t len) +{ + struct ieee80211_hdr *hdr; + u16 fc, extra_len, type, stype; + unsigned char *extra = NULL; + size_t data_len = len; + int ver; + + /* PSPOLL is only 16 bytes, but driver does not (at least yet) pass + * these to user space */ + if (len < 24) { + wpa_printf(MSG_MSGDUMP, "handle_frame: too short (%lu)", + (unsigned long) len); + return; + } + + hdr = (struct ieee80211_hdr *) buf; + fc = le_to_host16(hdr->frame_control); + type = WLAN_FC_GET_TYPE(fc); + stype = WLAN_FC_GET_STYPE(fc); + + if (type != WLAN_FC_TYPE_MGMT || stype != WLAN_FC_STYPE_BEACON) { + wpa_hexdump(MSG_MSGDUMP, "Received management frame", + buf, len); + } + + ver = fc & WLAN_FC_PVER; + + /* protocol version 3 is reserved for indicating extra data after the + * payload, version 2 for indicating ACKed frame (TX callbacks), and + * version 1 for indicating failed frame (no ACK, TX callbacks) */ + if (ver == 3) { + u8 *pos = buf + len - 2; + extra_len = WPA_GET_LE16(pos); + printf("extra data in frame (elen=%d)\n", extra_len); + if ((size_t) extra_len + 2 > len) { + printf(" extra data overflow\n"); + return; + } + len -= extra_len + 2; + extra = buf + len; + } else if (ver == 1 || ver == 2) { + handle_tx_callback(hapd, buf, data_len, ver == 2 ? 1 : 0); + return; + } else if (ver != 0) { + printf("unknown protocol version %d\n", ver); + return; + } + + switch (type) { + case WLAN_FC_TYPE_MGMT: + if (stype != WLAN_FC_STYPE_BEACON) + wpa_printf(MSG_MSGDUMP, "MGMT"); + ieee802_11_mgmt(hapd, buf, data_len, stype, NULL); + break; + case WLAN_FC_TYPE_CTRL: + wpa_printf(MSG_DEBUG, "CTRL"); + break; + case WLAN_FC_TYPE_DATA: + wpa_printf(MSG_DEBUG, "DATA"); + handle_data(hapd, buf, data_len, stype); + break; + default: + wpa_printf(MSG_DEBUG, "unknown frame type %d", type); + break; + } +} + + +static void handle_read(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct hostapd_data *hapd = (struct hostapd_data *) eloop_ctx; + int len; + unsigned char buf[3000]; + + len = recv(sock, buf, sizeof(buf), 0); + if (len < 0) { + perror("recv"); + return; + } + + handle_frame(hapd, buf, len); +} + + +static int hostap_init_sockets(struct hostap_driver_data *drv) +{ + struct ifreq ifr; + struct sockaddr_ll addr; + + drv->sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + if (drv->sock < 0) { + perror("socket[PF_PACKET,SOCK_RAW]"); + return -1; + } + + if (eloop_register_read_sock(drv->sock, handle_read, drv->hapd, NULL)) + { + printf("Could not register read socket\n"); + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%sap", drv->iface); + if (ioctl(drv->sock, SIOCGIFINDEX, &ifr) != 0) { + perror("ioctl(SIOCGIFINDEX)"); + return -1; + } + + if (hostap_set_iface_flags(drv, 1)) { + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sll_family = AF_PACKET; + addr.sll_ifindex = ifr.ifr_ifindex; + wpa_printf(MSG_DEBUG, "Opening raw packet socket for ifindex %d", + addr.sll_ifindex); + + if (bind(drv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("bind"); + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, drv->iface, sizeof(ifr.ifr_name)); + if (ioctl(drv->sock, SIOCGIFHWADDR, &ifr) != 0) { + perror("ioctl(SIOCGIFHWADDR)"); + return -1; + } + + if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { + printf("Invalid HW-addr family 0x%04x\n", + ifr.ifr_hwaddr.sa_family); + return -1; + } + memcpy(drv->hapd->own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); + + return 0; +} + + +static int hostap_send_mgmt_frame(void *priv, const void *msg, size_t len, + int flags) +{ + struct hostap_driver_data *drv = priv; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) msg; + int res; + + /* Request TX callback */ + hdr->frame_control |= host_to_le16(BIT(1)); + res = send(drv->sock, msg, len, flags); + hdr->frame_control &= ~host_to_le16(BIT(1)); + + return res; +} + + +static int hostap_send_eapol(void *priv, const u8 *addr, const u8 *data, + size_t data_len, int encrypt, const u8 *own_addr) +{ + struct hostap_driver_data *drv = priv; + struct ieee80211_hdr *hdr; + size_t len; + u8 *pos; + int res; + + len = sizeof(*hdr) + sizeof(rfc1042_header) + 2 + data_len; + hdr = os_zalloc(len); + if (hdr == NULL) { + printf("malloc() failed for hostapd_send_data(len=%lu)\n", + (unsigned long) len); + return -1; + } + + hdr->frame_control = + IEEE80211_FC(WLAN_FC_TYPE_DATA, WLAN_FC_STYPE_DATA); + hdr->frame_control |= host_to_le16(WLAN_FC_FROMDS); + if (encrypt) + hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP); + memcpy(hdr->IEEE80211_DA_FROMDS, addr, ETH_ALEN); + memcpy(hdr->IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN); + memcpy(hdr->IEEE80211_SA_FROMDS, own_addr, ETH_ALEN); + + pos = (u8 *) (hdr + 1); + memcpy(pos, rfc1042_header, sizeof(rfc1042_header)); + pos += sizeof(rfc1042_header); + *((u16 *) pos) = htons(ETH_P_PAE); + pos += 2; + memcpy(pos, data, data_len); + + res = hostap_send_mgmt_frame(drv, (u8 *) hdr, len, 0); + free(hdr); + + if (res < 0) { + perror("hostapd_send_eapol: send"); + printf("hostapd_send_eapol - packet len: %lu - failed\n", + (unsigned long) len); + } + + return res; +} + + +static int hostap_sta_set_flags(void *priv, const u8 *addr, + int total_flags, int flags_or, int flags_and) +{ + struct hostap_driver_data *drv = priv; + struct prism2_hostapd_param param; + + memset(¶m, 0, sizeof(param)); + param.cmd = PRISM2_HOSTAPD_SET_FLAGS_STA; + memcpy(param.sta_addr, addr, ETH_ALEN); + param.u.set_flags_sta.flags_or = flags_or; + param.u.set_flags_sta.flags_and = flags_and; + return hostapd_ioctl(drv, ¶m, sizeof(param)); +} + + +static int hostap_set_iface_flags(void *priv, int dev_up) +{ + struct hostap_driver_data *drv = priv; + struct ifreq ifr; + + if (drv->ioctl_sock < 0) + return -1; + + memset(&ifr, 0, sizeof(ifr)); + snprintf(ifr.ifr_name, IFNAMSIZ, "%sap", drv->iface); + + if (ioctl(drv->ioctl_sock, SIOCGIFFLAGS, &ifr) != 0) { + perror("ioctl[SIOCGIFFLAGS]"); + return -1; + } + + if (dev_up) + ifr.ifr_flags |= IFF_UP; + else + ifr.ifr_flags &= ~IFF_UP; + + if (ioctl(drv->ioctl_sock, SIOCSIFFLAGS, &ifr) != 0) { + perror("ioctl[SIOCSIFFLAGS]"); + return -1; + } + + if (dev_up) { + memset(&ifr, 0, sizeof(ifr)); + snprintf(ifr.ifr_name, IFNAMSIZ, "%sap", drv->iface); + ifr.ifr_mtu = HOSTAPD_MTU; + if (ioctl(drv->ioctl_sock, SIOCSIFMTU, &ifr) != 0) { + perror("ioctl[SIOCSIFMTU]"); + printf("Setting MTU failed - trying to survive with " + "current value\n"); + } + } + + return 0; +} + + +static int hostapd_ioctl(void *priv, struct prism2_hostapd_param *param, + int len) +{ + struct hostap_driver_data *drv = priv; + struct iwreq iwr; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) param; + iwr.u.data.length = len; + + if (ioctl(drv->ioctl_sock, PRISM2_IOCTL_HOSTAPD, &iwr) < 0) { + perror("ioctl[PRISM2_IOCTL_HOSTAPD]"); + return -1; + } + + return 0; +} + + +static int hostap_set_encryption(const char *ifname, void *priv, + const char *alg, const u8 *addr, + int idx, const u8 *key, size_t key_len, + int txkey) +{ + struct hostap_driver_data *drv = priv; + struct prism2_hostapd_param *param; + u8 *buf; + size_t blen; + int ret = 0; + + blen = sizeof(*param) + key_len; + buf = os_zalloc(blen); + if (buf == NULL) + return -1; + + param = (struct prism2_hostapd_param *) buf; + param->cmd = PRISM2_SET_ENCRYPTION; + if (addr == NULL) + memset(param->sta_addr, 0xff, ETH_ALEN); + else + memcpy(param->sta_addr, addr, ETH_ALEN); + os_strlcpy((char *) param->u.crypt.alg, alg, + HOSTAP_CRYPT_ALG_NAME_LEN); + param->u.crypt.flags = txkey ? HOSTAP_CRYPT_FLAG_SET_TX_KEY : 0; + param->u.crypt.idx = idx; + param->u.crypt.key_len = key_len; + memcpy((u8 *) (param + 1), key, key_len); + + if (hostapd_ioctl(drv, param, blen)) { + printf("Failed to set encryption.\n"); + ret = -1; + } + free(buf); + + return ret; +} + + +static int hostap_get_seqnum(const char *ifname, void *priv, const u8 *addr, + int idx, u8 *seq) +{ + struct hostap_driver_data *drv = priv; + struct prism2_hostapd_param *param; + u8 *buf; + size_t blen; + int ret = 0; + + blen = sizeof(*param) + 32; + buf = os_zalloc(blen); + if (buf == NULL) + return -1; + + param = (struct prism2_hostapd_param *) buf; + param->cmd = PRISM2_GET_ENCRYPTION; + if (addr == NULL) + memset(param->sta_addr, 0xff, ETH_ALEN); + else + memcpy(param->sta_addr, addr, ETH_ALEN); + param->u.crypt.idx = idx; + + if (hostapd_ioctl(drv, param, blen)) { + printf("Failed to get encryption.\n"); + ret = -1; + } else { + memcpy(seq, param->u.crypt.seq, 8); + } + free(buf); + + return ret; +} + + +static int hostap_ioctl_prism2param(void *priv, int param, int value) +{ + struct hostap_driver_data *drv = priv; + struct iwreq iwr; + int *i; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + i = (int *) iwr.u.name; + *i++ = param; + *i++ = value; + + if (ioctl(drv->ioctl_sock, PRISM2_IOCTL_PRISM2_PARAM, &iwr) < 0) { + perror("ioctl[PRISM2_IOCTL_PRISM2_PARAM]"); + return -1; + } + + return 0; +} + + +static int hostap_set_ieee8021x(const char *ifname, void *priv, int enabled) +{ + struct hostap_driver_data *drv = priv; + + /* enable kernel driver support for IEEE 802.1X */ + if (hostap_ioctl_prism2param(drv, PRISM2_PARAM_IEEE_802_1X, enabled)) { + printf("Could not setup IEEE 802.1X support in kernel driver." + "\n"); + return -1; + } + + if (!enabled) + return 0; + + /* use host driver implementation of encryption to allow + * individual keys and passing plaintext EAPOL frames */ + if (hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOST_DECRYPT, 1) || + hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOST_ENCRYPT, 1)) { + printf("Could not setup host-based encryption in kernel " + "driver.\n"); + return -1; + } + + return 0; +} + + +static int hostap_set_privacy(const char *ifname, void *priv, int enabled) +{ + struct hostap_drvier_data *drv = priv; + + return hostap_ioctl_prism2param(drv, PRISM2_PARAM_PRIVACY_INVOKED, + enabled); +} + + +static int hostap_set_ssid(const char *ifname, void *priv, const u8 *buf, + int len) +{ + struct hostap_driver_data *drv = priv; + struct iwreq iwr; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.essid.flags = 1; /* SSID active */ + iwr.u.essid.pointer = (caddr_t) buf; + iwr.u.essid.length = len + 1; + + if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) { + perror("ioctl[SIOCSIWESSID]"); + printf("len=%d\n", len); + return -1; + } + + return 0; +} + + +static int hostap_flush(void *priv) +{ + struct hostap_driver_data *drv = priv; + struct prism2_hostapd_param param; + + memset(¶m, 0, sizeof(param)); + param.cmd = PRISM2_HOSTAPD_FLUSH; + return hostapd_ioctl(drv, ¶m, sizeof(param)); +} + + +static int hostap_read_sta_data(void *priv, + struct hostap_sta_driver_data *data, + const u8 *addr) +{ + struct hostap_driver_data *drv = priv; + char buf[1024], line[128], *pos; + FILE *f; + unsigned long val; + + memset(data, 0, sizeof(*data)); + snprintf(buf, sizeof(buf), "/proc/net/hostap/%s/" MACSTR, + drv->iface, MAC2STR(addr)); + + f = fopen(buf, "r"); + if (!f) + return -1; + /* Need to read proc file with in one piece, so use large enough + * buffer. */ + setbuffer(f, buf, sizeof(buf)); + + while (fgets(line, sizeof(line), f)) { + pos = strchr(line, '='); + if (!pos) + continue; + *pos++ = '\0'; + val = strtoul(pos, NULL, 10); + if (strcmp(line, "rx_packets") == 0) + data->rx_packets = val; + else if (strcmp(line, "tx_packets") == 0) + data->tx_packets = val; + else if (strcmp(line, "rx_bytes") == 0) + data->rx_bytes = val; + else if (strcmp(line, "tx_bytes") == 0) + data->tx_bytes = val; + } + + fclose(f); + + return 0; +} + + +static int hostap_sta_add(const char *ifname, void *priv, const u8 *addr, + u16 aid, u16 capability, u8 *supp_rates, + size_t supp_rates_len, int flags, + u16 listen_interval) +{ + struct hostap_driver_data *drv = priv; + struct prism2_hostapd_param param; + int tx_supp_rates = 0; + size_t i; + +#define WLAN_RATE_1M BIT(0) +#define WLAN_RATE_2M BIT(1) +#define WLAN_RATE_5M5 BIT(2) +#define WLAN_RATE_11M BIT(3) + + for (i = 0; i < supp_rates_len; i++) { + if ((supp_rates[i] & 0x7f) == 2) + tx_supp_rates |= WLAN_RATE_1M; + if ((supp_rates[i] & 0x7f) == 4) + tx_supp_rates |= WLAN_RATE_2M; + if ((supp_rates[i] & 0x7f) == 11) + tx_supp_rates |= WLAN_RATE_5M5; + if ((supp_rates[i] & 0x7f) == 22) + tx_supp_rates |= WLAN_RATE_11M; + } + + memset(¶m, 0, sizeof(param)); + param.cmd = PRISM2_HOSTAPD_ADD_STA; + memcpy(param.sta_addr, addr, ETH_ALEN); + param.u.add_sta.aid = aid; + param.u.add_sta.capability = capability; + param.u.add_sta.tx_supp_rates = tx_supp_rates; + return hostapd_ioctl(drv, ¶m, sizeof(param)); +} + + +static int hostap_sta_remove(void *priv, const u8 *addr) +{ + struct hostap_driver_data *drv = priv; + struct prism2_hostapd_param param; + + hostap_sta_set_flags(drv, addr, 0, 0, ~WLAN_STA_AUTHORIZED); + + memset(¶m, 0, sizeof(param)); + param.cmd = PRISM2_HOSTAPD_REMOVE_STA; + memcpy(param.sta_addr, addr, ETH_ALEN); + if (hostapd_ioctl(drv, ¶m, sizeof(param))) { + printf("Could not remove station from kernel driver.\n"); + return -1; + } + return 0; +} + + +static int hostap_get_inact_sec(void *priv, const u8 *addr) +{ + struct hostap_driver_data *drv = priv; + struct prism2_hostapd_param param; + + memset(¶m, 0, sizeof(param)); + param.cmd = PRISM2_HOSTAPD_GET_INFO_STA; + memcpy(param.sta_addr, addr, ETH_ALEN); + if (hostapd_ioctl(drv, ¶m, sizeof(param))) { + return -1; + } + + return param.u.get_info_sta.inactive_sec; +} + + +static int hostap_sta_clear_stats(void *priv, const u8 *addr) +{ + struct hostap_driver_data *drv = priv; + struct prism2_hostapd_param param; + + memset(¶m, 0, sizeof(param)); + param.cmd = PRISM2_HOSTAPD_STA_CLEAR_STATS; + memcpy(param.sta_addr, addr, ETH_ALEN); + if (hostapd_ioctl(drv, ¶m, sizeof(param))) { + return -1; + } + + return 0; +} + + +static int hostap_set_assoc_ap(void *priv, const u8 *addr) +{ + struct hostap_driver_data *drv = priv; + struct prism2_hostapd_param param; + + memset(¶m, 0, sizeof(param)); + param.cmd = PRISM2_HOSTAPD_SET_ASSOC_AP_ADDR; + memcpy(param.sta_addr, addr, ETH_ALEN); + if (hostapd_ioctl(drv, ¶m, sizeof(param))) + return -1; + + return 0; +} + + +static int hostapd_ioctl_set_generic_elem(struct hostap_driver_data *drv) +{ + struct prism2_hostapd_param *param; + int res; + size_t blen, elem_len; + + elem_len = drv->generic_ie_len + drv->wps_ie_len; + blen = PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN + elem_len; + if (blen < sizeof(*param)) + blen = sizeof(*param); + + param = os_zalloc(blen); + if (param == NULL) + return -1; + + param->cmd = PRISM2_HOSTAPD_SET_GENERIC_ELEMENT; + param->u.generic_elem.len = elem_len; + if (drv->generic_ie) { + os_memcpy(param->u.generic_elem.data, drv->generic_ie, + drv->generic_ie_len); + } + if (drv->wps_ie) { + os_memcpy(¶m->u.generic_elem.data[drv->generic_ie_len], + drv->wps_ie, drv->wps_ie_len); + } + wpa_hexdump(MSG_DEBUG, "hostap: Set generic IE", + param->u.generic_elem.data, elem_len); + res = hostapd_ioctl(drv, param, blen); + + os_free(param); + + return res; +} + + +static int hostap_set_generic_elem(const char *ifname, void *priv, + const u8 *elem, size_t elem_len) +{ + struct hostap_driver_data *drv = priv; + + os_free(drv->generic_ie); + drv->generic_ie = NULL; + drv->generic_ie_len = 0; + if (elem) { + drv->generic_ie = os_malloc(elem_len); + if (drv->generic_ie == NULL) + return -1; + os_memcpy(drv->generic_ie, elem, elem_len); + drv->generic_ie_len = elem_len; + } + + return hostapd_ioctl_set_generic_elem(drv); +} + + +static int hostap_set_wps_beacon_ie(const char *ifname, void *priv, + const u8 *ie, size_t len) +{ + /* Host AP driver supports only one set of extra IEs, so we need to + * use the ProbeResp IEs also for Beacon frames since they include more + * information. */ + return 0; +} + + +static int hostap_set_wps_probe_resp_ie(const char *ifname, void *priv, + const u8 *ie, size_t len) +{ + struct hostap_driver_data *drv = priv; + + os_free(drv->wps_ie); + drv->wps_ie = NULL; + drv->wps_ie_len = 0; + if (ie) { + drv->wps_ie = os_malloc(len); + if (drv->wps_ie == NULL) + return -1; + os_memcpy(drv->wps_ie, ie, len); + drv->wps_ie_len = len; + } + + return hostapd_ioctl_set_generic_elem(drv); +} + + +static void +hostapd_wireless_event_wireless_custom(struct hostap_driver_data *drv, + char *custom) +{ + wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom); + + if (strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) { + char *pos; + u8 addr[ETH_ALEN]; + pos = strstr(custom, "addr="); + if (pos == NULL) { + wpa_printf(MSG_DEBUG, + "MLME-MICHAELMICFAILURE.indication " + "without sender address ignored"); + return; + } + pos += 5; + if (hwaddr_aton(pos, addr) == 0) { + ieee80211_michael_mic_failure(drv->hapd, addr, 1); + } else { + wpa_printf(MSG_DEBUG, + "MLME-MICHAELMICFAILURE.indication " + "with invalid MAC address"); + } + } +} + + +static void hostapd_wireless_event_wireless(struct hostap_driver_data *drv, + char *data, int len) +{ + struct iw_event iwe_buf, *iwe = &iwe_buf; + char *pos, *end, *custom, *buf; + + pos = data; + end = data + len; + + while (pos + IW_EV_LCP_LEN <= end) { + /* Event data may be unaligned, so make a local, aligned copy + * before processing. */ + memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); + wpa_printf(MSG_DEBUG, "Wireless event: cmd=0x%x len=%d", + iwe->cmd, iwe->len); + if (iwe->len <= IW_EV_LCP_LEN) + return; + + custom = pos + IW_EV_POINT_LEN; + if (drv->we_version > 18 && + (iwe->cmd == IWEVMICHAELMICFAILURE || + iwe->cmd == IWEVCUSTOM)) { + /* WE-19 removed the pointer from struct iw_point */ + char *dpos = (char *) &iwe_buf.u.data.length; + int dlen = dpos - (char *) &iwe_buf; + memcpy(dpos, pos + IW_EV_LCP_LEN, + sizeof(struct iw_event) - dlen); + } else { + memcpy(&iwe_buf, pos, sizeof(struct iw_event)); + custom += IW_EV_POINT_OFF; + } + + switch (iwe->cmd) { + case IWEVCUSTOM: + if (custom + iwe->u.data.length > end) + return; + buf = malloc(iwe->u.data.length + 1); + if (buf == NULL) + return; + memcpy(buf, custom, iwe->u.data.length); + buf[iwe->u.data.length] = '\0'; + hostapd_wireless_event_wireless_custom(drv, buf); + free(buf); + break; + } + + pos += iwe->len; + } +} + + +static void hostapd_wireless_event_rtm_newlink(struct hostap_driver_data *drv, + struct nlmsghdr *h, int len) +{ + struct ifinfomsg *ifi; + int attrlen, nlmsg_len, rta_len; + struct rtattr * attr; + + if (len < (int) sizeof(*ifi)) + return; + + ifi = NLMSG_DATA(h); + + /* TODO: use ifi->ifi_index to filter out wireless events from other + * interfaces */ + + nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); + + attrlen = h->nlmsg_len - nlmsg_len; + if (attrlen < 0) + return; + + attr = (struct rtattr *) (((char *) ifi) + nlmsg_len); + + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + if (attr->rta_type == IFLA_WIRELESS) { + hostapd_wireless_event_wireless( + drv, ((char *) attr) + rta_len, + attr->rta_len - rta_len); + } + attr = RTA_NEXT(attr, attrlen); + } +} + + +static void hostapd_wireless_event_receive(int sock, void *eloop_ctx, + void *sock_ctx) +{ + char buf[256]; + int left; + struct sockaddr_nl from; + socklen_t fromlen; + struct nlmsghdr *h; + struct hostap_driver_data *drv = eloop_ctx; + + fromlen = sizeof(from); + left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, + (struct sockaddr *) &from, &fromlen); + if (left < 0) { + if (errno != EINTR && errno != EAGAIN) + perror("recvfrom(netlink)"); + return; + } + + h = (struct nlmsghdr *) buf; + while (left >= (int) sizeof(*h)) { + int len, plen; + + len = h->nlmsg_len; + plen = len - sizeof(*h); + if (len > left || plen < 0) { + printf("Malformed netlink message: " + "len=%d left=%d plen=%d\n", + len, left, plen); + break; + } + + switch (h->nlmsg_type) { + case RTM_NEWLINK: + hostapd_wireless_event_rtm_newlink(drv, h, plen); + break; + } + + len = NLMSG_ALIGN(len); + left -= len; + h = (struct nlmsghdr *) ((char *) h + len); + } + + if (left > 0) { + printf("%d extra bytes in the end of netlink message\n", left); + } +} + + +static int hostap_get_we_version(struct hostap_driver_data *drv) +{ + struct iw_range *range; + struct iwreq iwr; + int minlen; + size_t buflen; + + drv->we_version = 0; + + /* + * Use larger buffer than struct iw_range in order to allow the + * structure to grow in the future. + */ + buflen = sizeof(struct iw_range) + 500; + range = os_zalloc(buflen); + if (range == NULL) + return -1; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) range; + iwr.u.data.length = buflen; + + minlen = ((char *) &range->enc_capa) - (char *) range + + sizeof(range->enc_capa); + + if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) { + perror("ioctl[SIOCGIWRANGE]"); + free(range); + return -1; + } else if (iwr.u.data.length >= minlen && + range->we_version_compiled >= 18) { + wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d " + "WE(source)=%d enc_capa=0x%x", + range->we_version_compiled, + range->we_version_source, + range->enc_capa); + drv->we_version = range->we_version_compiled; + } + + free(range); + return 0; +} + + +static int hostap_wireless_event_init(void *priv) +{ + struct hostap_driver_data *drv = priv; + int s; + struct sockaddr_nl local; + + hostap_get_we_version(drv); + + drv->wext_sock = -1; + + s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (s < 0) { + perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)"); + return -1; + } + + memset(&local, 0, sizeof(local)); + local.nl_family = AF_NETLINK; + local.nl_groups = RTMGRP_LINK; + if (bind(s, (struct sockaddr *) &local, sizeof(local)) < 0) { + perror("bind(netlink)"); + close(s); + return -1; + } + + eloop_register_read_sock(s, hostapd_wireless_event_receive, drv, + NULL); + drv->wext_sock = s; + + return 0; +} + + +static void hostap_wireless_event_deinit(void *priv) +{ + struct hostap_driver_data *drv = priv; + if (drv->wext_sock < 0) + return; + eloop_unregister_read_sock(drv->wext_sock); + close(drv->wext_sock); +} + + +static void * hostap_init(struct hostapd_data *hapd) +{ + struct hostap_driver_data *drv; + + drv = os_zalloc(sizeof(struct hostap_driver_data)); + if (drv == NULL) { + printf("Could not allocate memory for hostapd driver data\n"); + return NULL; + } + + drv->hapd = hapd; + drv->ioctl_sock = drv->sock = -1; + memcpy(drv->iface, hapd->conf->iface, sizeof(drv->iface)); + + drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->ioctl_sock < 0) { + perror("socket[PF_INET,SOCK_DGRAM]"); + free(drv); + return NULL; + } + + if (hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOSTAPD, 1)) { + printf("Could not enable hostapd mode for interface %s\n", + drv->iface); + close(drv->ioctl_sock); + free(drv); + return NULL; + } + + if (hostap_init_sockets(drv)) { + close(drv->ioctl_sock); + free(drv); + return NULL; + } + + return drv; +} + + +static void hostap_driver_deinit(void *priv) +{ + struct hostap_driver_data *drv = priv; + + (void) hostap_set_iface_flags(drv, 0); + (void) hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOSTAPD, 0); + (void) hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOSTAPD_STA, 0); + + if (drv->ioctl_sock >= 0) + close(drv->ioctl_sock); + + if (drv->sock >= 0) + close(drv->sock); + + os_free(drv->generic_ie); + os_free(drv->wps_ie); + + free(drv); +} + + +static int hostap_sta_deauth(void *priv, const u8 *addr, int reason) +{ + struct hostap_driver_data *drv = priv; + struct ieee80211_mgmt mgmt; + + memset(&mgmt, 0, sizeof(mgmt)); + mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_DEAUTH); + memcpy(mgmt.da, addr, ETH_ALEN); + memcpy(mgmt.sa, drv->hapd->own_addr, ETH_ALEN); + memcpy(mgmt.bssid, drv->hapd->own_addr, ETH_ALEN); + mgmt.u.deauth.reason_code = host_to_le16(reason); + return hostap_send_mgmt_frame(drv, &mgmt, IEEE80211_HDRLEN + + sizeof(mgmt.u.deauth), 0); +} + + +static int hostap_sta_disassoc(void *priv, const u8 *addr, int reason) +{ + struct hostap_driver_data *drv = priv; + struct ieee80211_mgmt mgmt; + + memset(&mgmt, 0, sizeof(mgmt)); + mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_DISASSOC); + memcpy(mgmt.da, addr, ETH_ALEN); + memcpy(mgmt.sa, drv->hapd->own_addr, ETH_ALEN); + memcpy(mgmt.bssid, drv->hapd->own_addr, ETH_ALEN); + mgmt.u.disassoc.reason_code = host_to_le16(reason); + return hostap_send_mgmt_frame(drv, &mgmt, IEEE80211_HDRLEN + + sizeof(mgmt.u.disassoc), 0); +} + + +static struct hostapd_hw_modes * hostap_get_hw_feature_data(void *priv, + u16 *num_modes, + u16 *flags) +{ + struct hostapd_hw_modes *mode; + int i, clen, rlen; + const short chan2freq[14] = { + 2412, 2417, 2422, 2427, 2432, 2437, 2442, + 2447, 2452, 2457, 2462, 2467, 2472, 2484 + }; + + mode = os_zalloc(sizeof(struct hostapd_hw_modes)); + if (mode == NULL) + return NULL; + + *num_modes = 1; + *flags = 0; + + mode->mode = HOSTAPD_MODE_IEEE80211B; + mode->num_channels = 14; + mode->num_rates = 4; + + clen = mode->num_channels * sizeof(struct hostapd_channel_data); + rlen = mode->num_rates * sizeof(struct hostapd_rate_data); + + mode->channels = os_zalloc(clen); + mode->rates = os_zalloc(rlen); + if (mode->channels == NULL || mode->rates == NULL) { + hostapd_free_hw_features(mode, *num_modes); + return NULL; + } + + for (i = 0; i < 14; i++) { + mode->channels[i].chan = i + 1; + mode->channels[i].freq = chan2freq[i]; + /* TODO: Get allowed channel list from the driver */ + if (i >= 11) + mode->channels[i].flag = HOSTAPD_CHAN_DISABLED; + } + + mode->rates[0].rate = 10; + mode->rates[0].flags = HOSTAPD_RATE_CCK; + mode->rates[1].rate = 20; + mode->rates[1].flags = HOSTAPD_RATE_CCK; + mode->rates[2].rate = 55; + mode->rates[2].flags = HOSTAPD_RATE_CCK; + mode->rates[3].rate = 110; + mode->rates[3].flags = HOSTAPD_RATE_CCK; + + return mode; +} + + +const struct wpa_driver_ops wpa_driver_hostap_ops = { + .name = "hostap", + .init = hostap_init, + .deinit = hostap_driver_deinit, + .wireless_event_init = hostap_wireless_event_init, + .wireless_event_deinit = hostap_wireless_event_deinit, + .set_ieee8021x = hostap_set_ieee8021x, + .set_privacy = hostap_set_privacy, + .set_encryption = hostap_set_encryption, + .get_seqnum = hostap_get_seqnum, + .flush = hostap_flush, + .set_generic_elem = hostap_set_generic_elem, + .read_sta_data = hostap_read_sta_data, + .send_eapol = hostap_send_eapol, + .sta_set_flags = hostap_sta_set_flags, + .sta_deauth = hostap_sta_deauth, + .sta_disassoc = hostap_sta_disassoc, + .sta_remove = hostap_sta_remove, + .set_ssid = hostap_set_ssid, + .send_mgmt_frame = hostap_send_mgmt_frame, + .set_assoc_ap = hostap_set_assoc_ap, + .sta_add = hostap_sta_add, + .get_inact_sec = hostap_get_inact_sec, + .sta_clear_stats = hostap_sta_clear_stats, + .get_hw_feature_data = hostap_get_hw_feature_data, + .set_wps_beacon_ie = hostap_set_wps_beacon_ie, + .set_wps_probe_resp_ie = hostap_set_wps_probe_resp_ie, +}; diff --git a/hostapd/driver_madwifi.c b/hostapd/driver_madwifi.c new file mode 100644 index 0000000..4083fda --- /dev/null +++ b/hostapd/driver_madwifi.c @@ -0,0 +1,1483 @@ +/* + * hostapd / Driver interaction with MADWIFI 802.11 driver + * Copyright (c) 2004, Sam Leffler <sam@errno.com> + * Copyright (c) 2004, Video54 Technologies + * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <net/if.h> +#include <sys/ioctl.h> + +#include <include/compat.h> +#include <net80211/ieee80211.h> +#ifdef WME_NUM_AC +/* Assume this is built against BSD branch of madwifi driver. */ +#define MADWIFI_BSD +#include <net80211/_ieee80211.h> +#endif /* WME_NUM_AC */ +#include <net80211/ieee80211_crypto.h> +#include <net80211/ieee80211_ioctl.h> + +#ifdef CONFIG_WPS +#ifdef IEEE80211_IOCTL_FILTERFRAME +#include <netpacket/packet.h> + +#ifndef ETH_P_80211_RAW +#define ETH_P_80211_RAW 0x0019 +#endif +#endif /* IEEE80211_IOCTL_FILTERFRAME */ +#endif /* CONFIG_WPS */ + +/* + * Avoid conflicts with hostapd definitions by undefining couple of defines + * from madwifi header files. + */ +#undef RSN_VERSION +#undef WPA_VERSION +#undef WPA_OUI_TYPE +#undef WME_OUI_TYPE + + +#ifdef IEEE80211_IOCTL_SETWMMPARAMS +/* Assume this is built against madwifi-ng */ +#define MADWIFI_NG +#endif /* IEEE80211_IOCTL_SETWMMPARAMS */ + +#include "wireless_copy.h" + +#include "hostapd.h" +#include "driver.h" +#include "ieee802_1x.h" +#include "eloop.h" +#include "priv_netlink.h" +#include "sta_info.h" +#include "l2_packet/l2_packet.h" + +#include "wpa.h" +#include "radius/radius.h" +#include "ieee802_11.h" +#include "accounting.h" +#include "common.h" +#include "wps_hostapd.h" + + +struct madwifi_driver_data { + struct hostapd_data *hapd; /* back pointer */ + + char iface[IFNAMSIZ + 1]; + int ifindex; + struct l2_packet_data *sock_xmit; /* raw packet xmit socket */ + struct l2_packet_data *sock_recv; /* raw packet recv socket */ + int ioctl_sock; /* socket for ioctl() use */ + int wext_sock; /* socket for wireless events */ + int we_version; + u8 acct_mac[ETH_ALEN]; + struct hostap_sta_driver_data acct_data; + + struct l2_packet_data *sock_raw; /* raw 802.11 management frames */ +}; + +static int madwifi_sta_deauth(void *priv, const u8 *addr, int reason_code); + +static int +set80211priv(struct madwifi_driver_data *drv, int op, void *data, int len) +{ + struct iwreq iwr; + int do_inline = len < IFNAMSIZ; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); +#ifdef IEEE80211_IOCTL_FILTERFRAME + /* FILTERFRAME must be NOT inline, regardless of size. */ + if (op == IEEE80211_IOCTL_FILTERFRAME) + do_inline = 0; +#endif /* IEEE80211_IOCTL_FILTERFRAME */ + if (op == IEEE80211_IOCTL_SET_APPIEBUF) + do_inline = 0; + if (do_inline) { + /* + * Argument data fits inline; put it there. + */ + memcpy(iwr.u.name, data, len); + } else { + /* + * Argument data too big for inline transfer; setup a + * parameter block instead; the kernel will transfer + * the data for the driver. + */ + iwr.u.data.pointer = data; + iwr.u.data.length = len; + } + + if (ioctl(drv->ioctl_sock, op, &iwr) < 0) { +#ifdef MADWIFI_NG + int first = IEEE80211_IOCTL_SETPARAM; + static const char *opnames[] = { + "ioctl[IEEE80211_IOCTL_SETPARAM]", + "ioctl[IEEE80211_IOCTL_GETPARAM]", + "ioctl[IEEE80211_IOCTL_SETMODE]", + "ioctl[IEEE80211_IOCTL_GETMODE]", + "ioctl[IEEE80211_IOCTL_SETWMMPARAMS]", + "ioctl[IEEE80211_IOCTL_GETWMMPARAMS]", + "ioctl[IEEE80211_IOCTL_SETCHANLIST]", + "ioctl[IEEE80211_IOCTL_GETCHANLIST]", + "ioctl[IEEE80211_IOCTL_CHANSWITCH]", + "ioctl[IEEE80211_IOCTL_GET_APPIEBUF]", + "ioctl[IEEE80211_IOCTL_SET_APPIEBUF]", + "ioctl[IEEE80211_IOCTL_GETSCANRESULTS]", + "ioctl[IEEE80211_IOCTL_FILTERFRAME]", + "ioctl[IEEE80211_IOCTL_GETCHANINFO]", + "ioctl[IEEE80211_IOCTL_SETOPTIE]", + "ioctl[IEEE80211_IOCTL_GETOPTIE]", + "ioctl[IEEE80211_IOCTL_SETMLME]", + NULL, + "ioctl[IEEE80211_IOCTL_SETKEY]", + NULL, + "ioctl[IEEE80211_IOCTL_DELKEY]", + NULL, + "ioctl[IEEE80211_IOCTL_ADDMAC]", + NULL, + "ioctl[IEEE80211_IOCTL_DELMAC]", + NULL, + "ioctl[IEEE80211_IOCTL_WDSMAC]", + NULL, + "ioctl[IEEE80211_IOCTL_WDSDELMAC]", + NULL, + "ioctl[IEEE80211_IOCTL_KICKMAC]", + }; +#else /* MADWIFI_NG */ + int first = IEEE80211_IOCTL_SETPARAM; + static const char *opnames[] = { + "ioctl[IEEE80211_IOCTL_SETPARAM]", + "ioctl[IEEE80211_IOCTL_GETPARAM]", + "ioctl[IEEE80211_IOCTL_SETKEY]", + "ioctl[SIOCIWFIRSTPRIV+3]", + "ioctl[IEEE80211_IOCTL_DELKEY]", + "ioctl[SIOCIWFIRSTPRIV+5]", + "ioctl[IEEE80211_IOCTL_SETMLME]", + "ioctl[SIOCIWFIRSTPRIV+7]", + "ioctl[IEEE80211_IOCTL_SETOPTIE]", + "ioctl[IEEE80211_IOCTL_GETOPTIE]", + "ioctl[IEEE80211_IOCTL_ADDMAC]", + "ioctl[SIOCIWFIRSTPRIV+11]", + "ioctl[IEEE80211_IOCTL_DELMAC]", + "ioctl[SIOCIWFIRSTPRIV+13]", + "ioctl[IEEE80211_IOCTL_CHANLIST]", + "ioctl[SIOCIWFIRSTPRIV+15]", + "ioctl[IEEE80211_IOCTL_GETRSN]", + "ioctl[SIOCIWFIRSTPRIV+17]", + "ioctl[IEEE80211_IOCTL_GETKEY]", + }; +#endif /* MADWIFI_NG */ + int idx = op - first; + if (first <= op && + idx < (int) (sizeof(opnames) / sizeof(opnames[0])) && + opnames[idx]) + perror(opnames[idx]); + else + perror("ioctl[unknown???]"); + return -1; + } + return 0; +} + +static int +set80211param(struct madwifi_driver_data *drv, int op, int arg) +{ + struct iwreq iwr; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.mode = op; + memcpy(iwr.u.name+sizeof(__u32), &arg, sizeof(arg)); + + if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_SETPARAM, &iwr) < 0) { + perror("ioctl[IEEE80211_IOCTL_SETPARAM]"); + wpa_printf(MSG_DEBUG, "%s: Failed to set parameter (op %d " + "arg %d)", __func__, op, arg); + return -1; + } + return 0; +} + +static const char * +ether_sprintf(const u8 *addr) +{ + static char buf[sizeof(MACSTR)]; + + if (addr != NULL) + snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr)); + else + snprintf(buf, sizeof(buf), MACSTR, 0,0,0,0,0,0); + return buf; +} + +/* + * Configure WPA parameters. + */ +static int +madwifi_configure_wpa(struct madwifi_driver_data *drv) +{ + struct hostapd_data *hapd = drv->hapd; + struct hostapd_bss_config *conf = hapd->conf; + int v; + + switch (conf->wpa_group) { + case WPA_CIPHER_CCMP: + v = IEEE80211_CIPHER_AES_CCM; + break; + case WPA_CIPHER_TKIP: + v = IEEE80211_CIPHER_TKIP; + break; + case WPA_CIPHER_WEP104: + v = IEEE80211_CIPHER_WEP; + break; + case WPA_CIPHER_WEP40: + v = IEEE80211_CIPHER_WEP; + break; + case WPA_CIPHER_NONE: + v = IEEE80211_CIPHER_NONE; + break; + default: + wpa_printf(MSG_ERROR, "Unknown group key cipher %u", + conf->wpa_group); + return -1; + } + wpa_printf(MSG_DEBUG, "%s: group key cipher=%d", __func__, v); + if (set80211param(drv, IEEE80211_PARAM_MCASTCIPHER, v)) { + printf("Unable to set group key cipher to %u\n", v); + return -1; + } + if (v == IEEE80211_CIPHER_WEP) { + /* key length is done only for specific ciphers */ + v = (conf->wpa_group == WPA_CIPHER_WEP104 ? 13 : 5); + if (set80211param(drv, IEEE80211_PARAM_MCASTKEYLEN, v)) { + printf("Unable to set group key length to %u\n", v); + return -1; + } + } + + v = 0; + if (conf->wpa_pairwise & WPA_CIPHER_CCMP) + v |= 1<<IEEE80211_CIPHER_AES_CCM; + if (conf->wpa_pairwise & WPA_CIPHER_TKIP) + v |= 1<<IEEE80211_CIPHER_TKIP; + if (conf->wpa_pairwise & WPA_CIPHER_NONE) + v |= 1<<IEEE80211_CIPHER_NONE; + wpa_printf(MSG_DEBUG, "%s: pairwise key ciphers=0x%x", __func__, v); + if (set80211param(drv, IEEE80211_PARAM_UCASTCIPHERS, v)) { + printf("Unable to set pairwise key ciphers to 0x%x\n", v); + return -1; + } + + wpa_printf(MSG_DEBUG, "%s: key management algorithms=0x%x", + __func__, conf->wpa_key_mgmt); + if (set80211param(drv, IEEE80211_PARAM_KEYMGTALGS, conf->wpa_key_mgmt)) { + printf("Unable to set key management algorithms to 0x%x\n", + conf->wpa_key_mgmt); + return -1; + } + + v = 0; + if (conf->rsn_preauth) + v |= BIT(0); + wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x", + __func__, conf->rsn_preauth); + if (set80211param(drv, IEEE80211_PARAM_RSNCAPS, v)) { + printf("Unable to set RSN capabilities to 0x%x\n", v); + return -1; + } + + wpa_printf(MSG_DEBUG, "%s: enable WPA=0x%x", __func__, conf->wpa); + if (set80211param(drv, IEEE80211_PARAM_WPA, conf->wpa)) { + printf("Unable to set WPA to %u\n", conf->wpa); + return -1; + } + return 0; +} + + +static int +madwifi_set_iface_flags(void *priv, int dev_up) +{ + struct madwifi_driver_data *drv = priv; + struct ifreq ifr; + + wpa_printf(MSG_DEBUG, "%s: dev_up=%d", __func__, dev_up); + + if (drv->ioctl_sock < 0) + return -1; + + memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, drv->iface, IFNAMSIZ); + + if (ioctl(drv->ioctl_sock, SIOCGIFFLAGS, &ifr) != 0) { + perror("ioctl[SIOCGIFFLAGS]"); + return -1; + } + + if (dev_up) + ifr.ifr_flags |= IFF_UP; + else + ifr.ifr_flags &= ~IFF_UP; + + if (ioctl(drv->ioctl_sock, SIOCSIFFLAGS, &ifr) != 0) { + perror("ioctl[SIOCSIFFLAGS]"); + return -1; + } + + if (dev_up) { + memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, drv->iface, IFNAMSIZ); + ifr.ifr_mtu = HOSTAPD_MTU; + if (ioctl(drv->ioctl_sock, SIOCSIFMTU, &ifr) != 0) { + perror("ioctl[SIOCSIFMTU]"); + printf("Setting MTU failed - trying to survive with " + "current value\n"); + } + } + + return 0; +} + +static int +madwifi_set_ieee8021x(const char *ifname, void *priv, int enabled) +{ + struct madwifi_driver_data *drv = priv; + struct hostapd_data *hapd = drv->hapd; + struct hostapd_bss_config *conf = hapd->conf; + + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); + + if (!enabled) { + /* XXX restore state */ + return set80211param(priv, IEEE80211_PARAM_AUTHMODE, + IEEE80211_AUTH_AUTO); + } + if (!conf->wpa && !conf->ieee802_1x) { + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_DRIVER, + HOSTAPD_LEVEL_WARNING, "No 802.1X or WPA enabled!"); + return -1; + } + if (conf->wpa && madwifi_configure_wpa(drv) != 0) { + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_DRIVER, + HOSTAPD_LEVEL_WARNING, "Error configuring WPA state!"); + return -1; + } + if (set80211param(priv, IEEE80211_PARAM_AUTHMODE, + (conf->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) { + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_DRIVER, + HOSTAPD_LEVEL_WARNING, "Error enabling WPA/802.1X!"); + return -1; + } + + return 0; +} + +static int +madwifi_set_privacy(const char *ifname, void *priv, int enabled) +{ + struct madwifi_driver_data *drv = priv; + + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); + + return set80211param(drv, IEEE80211_PARAM_PRIVACY, enabled); +} + +static int +madwifi_set_sta_authorized(void *priv, const u8 *addr, int authorized) +{ + struct madwifi_driver_data *drv = priv; + struct ieee80211req_mlme mlme; + int ret; + + wpa_printf(MSG_DEBUG, "%s: addr=%s authorized=%d", + __func__, ether_sprintf(addr), authorized); + + if (authorized) + mlme.im_op = IEEE80211_MLME_AUTHORIZE; + else + mlme.im_op = IEEE80211_MLME_UNAUTHORIZE; + mlme.im_reason = 0; + memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to %sauthorize STA " MACSTR, + __func__, authorized ? "" : "un", MAC2STR(addr)); + } + + return ret; +} + +static int +madwifi_sta_set_flags(void *priv, const u8 *addr, int total_flags, + int flags_or, int flags_and) +{ + /* For now, only support setting Authorized flag */ + if (flags_or & WLAN_STA_AUTHORIZED) + return madwifi_set_sta_authorized(priv, addr, 1); + if (!(flags_and & WLAN_STA_AUTHORIZED)) + return madwifi_set_sta_authorized(priv, addr, 0); + return 0; +} + +static int +madwifi_del_key(void *priv, const u8 *addr, int key_idx) +{ + struct madwifi_driver_data *drv = priv; + struct ieee80211req_del_key wk; + int ret; + + wpa_printf(MSG_DEBUG, "%s: addr=%s key_idx=%d", + __func__, ether_sprintf(addr), key_idx); + + memset(&wk, 0, sizeof(wk)); + if (addr != NULL) { + memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN); + wk.idk_keyix = (u8) IEEE80211_KEYIX_NONE; + } else { + wk.idk_keyix = key_idx; + } + + ret = set80211priv(drv, IEEE80211_IOCTL_DELKEY, &wk, sizeof(wk)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to delete key (addr %s" + " key_idx %d)", __func__, ether_sprintf(addr), + key_idx); + } + + return ret; +} + +static int +madwifi_set_key(const char *ifname, void *priv, const char *alg, + const u8 *addr, int key_idx, + const u8 *key, size_t key_len, int txkey) +{ + struct madwifi_driver_data *drv = priv; + struct ieee80211req_key wk; + u_int8_t cipher; + int ret; + + if (strcmp(alg, "none") == 0) + return madwifi_del_key(drv, addr, key_idx); + + wpa_printf(MSG_DEBUG, "%s: alg=%s addr=%s key_idx=%d", + __func__, alg, ether_sprintf(addr), key_idx); + + if (strcmp(alg, "WEP") == 0) + cipher = IEEE80211_CIPHER_WEP; + else if (strcmp(alg, "TKIP") == 0) + cipher = IEEE80211_CIPHER_TKIP; + else if (strcmp(alg, "CCMP") == 0) + cipher = IEEE80211_CIPHER_AES_CCM; + else { + printf("%s: unknown/unsupported algorithm %s\n", + __func__, alg); + return -1; + } + + if (key_len > sizeof(wk.ik_keydata)) { + printf("%s: key length %lu too big\n", __func__, + (unsigned long) key_len); + return -3; + } + + memset(&wk, 0, sizeof(wk)); + wk.ik_type = cipher; + wk.ik_flags = IEEE80211_KEY_RECV | IEEE80211_KEY_XMIT; + if (addr == NULL) { + memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN); + wk.ik_keyix = key_idx; + wk.ik_flags |= IEEE80211_KEY_DEFAULT; + } else { + memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); + wk.ik_keyix = IEEE80211_KEYIX_NONE; + } + wk.ik_keylen = key_len; + memcpy(wk.ik_keydata, key, key_len); + + ret = set80211priv(drv, IEEE80211_IOCTL_SETKEY, &wk, sizeof(wk)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to set key (addr %s" + " key_idx %d alg '%s' key_len %lu txkey %d)", + __func__, ether_sprintf(wk.ik_macaddr), key_idx, + alg, (unsigned long) key_len, txkey); + } + + return ret; +} + + +static int +madwifi_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx, + u8 *seq) +{ + struct madwifi_driver_data *drv = priv; + struct ieee80211req_key wk; + + wpa_printf(MSG_DEBUG, "%s: addr=%s idx=%d", + __func__, ether_sprintf(addr), idx); + + memset(&wk, 0, sizeof(wk)); + if (addr == NULL) + memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN); + else + memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); + wk.ik_keyix = idx; + + if (set80211priv(drv, IEEE80211_IOCTL_GETKEY, &wk, sizeof(wk))) { + wpa_printf(MSG_DEBUG, "%s: Failed to get encryption data " + "(addr " MACSTR " key_idx %d)", + __func__, MAC2STR(wk.ik_macaddr), idx); + return -1; + } + +#ifdef WORDS_BIGENDIAN + { + /* + * wk.ik_keytsc is in host byte order (big endian), need to + * swap it to match with the byte order used in WPA. + */ + int i; + u8 tmp[WPA_KEY_RSC_LEN]; + memcpy(tmp, &wk.ik_keytsc, sizeof(wk.ik_keytsc)); + for (i = 0; i < WPA_KEY_RSC_LEN; i++) { + seq[i] = tmp[WPA_KEY_RSC_LEN - i - 1]; + } + } +#else /* WORDS_BIGENDIAN */ + memcpy(seq, &wk.ik_keytsc, sizeof(wk.ik_keytsc)); +#endif /* WORDS_BIGENDIAN */ + return 0; +} + + +static int +madwifi_flush(void *priv) +{ +#ifdef MADWIFI_BSD + u8 allsta[IEEE80211_ADDR_LEN]; + memset(allsta, 0xff, IEEE80211_ADDR_LEN); + return madwifi_sta_deauth(priv, allsta, IEEE80211_REASON_AUTH_LEAVE); +#else /* MADWIFI_BSD */ + return 0; /* XXX */ +#endif /* MADWIFI_BSD */ +} + + +static int +madwifi_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data, + const u8 *addr) +{ + struct madwifi_driver_data *drv = priv; + +#ifdef MADWIFI_BSD + struct ieee80211req_sta_stats stats; + + memset(data, 0, sizeof(*data)); + + /* + * Fetch statistics for station from the system. + */ + memset(&stats, 0, sizeof(stats)); + memcpy(stats.is_u.macaddr, addr, IEEE80211_ADDR_LEN); + if (set80211priv(drv, +#ifdef MADWIFI_NG + IEEE80211_IOCTL_STA_STATS, +#else /* MADWIFI_NG */ + IEEE80211_IOCTL_GETSTASTATS, +#endif /* MADWIFI_NG */ + &stats, sizeof(stats))) { + wpa_printf(MSG_DEBUG, "%s: Failed to fetch STA stats (addr " + MACSTR ")", __func__, MAC2STR(addr)); + if (memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) { + memcpy(data, &drv->acct_data, sizeof(*data)); + return 0; + } + + printf("Failed to get station stats information element.\n"); + return -1; + } + + data->rx_packets = stats.is_stats.ns_rx_data; + data->rx_bytes = stats.is_stats.ns_rx_bytes; + data->tx_packets = stats.is_stats.ns_tx_data; + data->tx_bytes = stats.is_stats.ns_tx_bytes; + return 0; + +#else /* MADWIFI_BSD */ + + char buf[1024], line[128], *pos; + FILE *f; + unsigned long val; + + memset(data, 0, sizeof(*data)); + snprintf(buf, sizeof(buf), "/proc/net/madwifi/%s/" MACSTR, + drv->iface, MAC2STR(addr)); + + f = fopen(buf, "r"); + if (!f) { + if (memcmp(addr, drv->acct_mac, ETH_ALEN) != 0) + return -1; + memcpy(data, &drv->acct_data, sizeof(*data)); + return 0; + } + /* Need to read proc file with in one piece, so use large enough + * buffer. */ + setbuffer(f, buf, sizeof(buf)); + + while (fgets(line, sizeof(line), f)) { + pos = strchr(line, '='); + if (!pos) + continue; + *pos++ = '\0'; + val = strtoul(pos, NULL, 10); + if (strcmp(line, "rx_packets") == 0) + data->rx_packets = val; + else if (strcmp(line, "tx_packets") == 0) + data->tx_packets = val; + else if (strcmp(line, "rx_bytes") == 0) + data->rx_bytes = val; + else if (strcmp(line, "tx_bytes") == 0) + data->tx_bytes = val; + } + + fclose(f); + + return 0; +#endif /* MADWIFI_BSD */ +} + + +static int +madwifi_sta_clear_stats(void *priv, const u8 *addr) +{ +#if defined(MADWIFI_BSD) && defined(IEEE80211_MLME_CLEAR_STATS) + struct madwifi_driver_data *drv = priv; + struct ieee80211req_mlme mlme; + int ret; + + wpa_printf(MSG_DEBUG, "%s: addr=%s", __func__, ether_sprintf(addr)); + + mlme.im_op = IEEE80211_MLME_CLEAR_STATS; + memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, + sizeof(mlme)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to clear STA stats (addr " + MACSTR ")", __func__, MAC2STR(addr)); + } + + return ret; +#else /* MADWIFI_BSD && IEEE80211_MLME_CLEAR_STATS */ + return 0; /* FIX */ +#endif /* MADWIFI_BSD && IEEE80211_MLME_CLEAR_STATS */ +} + + +static int +madwifi_set_opt_ie(const char *ifname, void *priv, const u8 *ie, size_t ie_len) +{ + /* + * Do nothing; we setup parameters at startup that define the + * contents of the beacon information element. + */ + return 0; +} + +static int +madwifi_sta_deauth(void *priv, const u8 *addr, int reason_code) +{ + struct madwifi_driver_data *drv = priv; + struct ieee80211req_mlme mlme; + int ret; + + wpa_printf(MSG_DEBUG, "%s: addr=%s reason_code=%d", + __func__, ether_sprintf(addr), reason_code); + + mlme.im_op = IEEE80211_MLME_DEAUTH; + mlme.im_reason = reason_code; + memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to deauth STA (addr " MACSTR + " reason %d)", + __func__, MAC2STR(addr), reason_code); + } + + return ret; +} + +static int +madwifi_sta_disassoc(void *priv, const u8 *addr, int reason_code) +{ + struct madwifi_driver_data *drv = priv; + struct ieee80211req_mlme mlme; + int ret; + + wpa_printf(MSG_DEBUG, "%s: addr=%s reason_code=%d", + __func__, ether_sprintf(addr), reason_code); + + mlme.im_op = IEEE80211_MLME_DISASSOC; + mlme.im_reason = reason_code; + memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to disassoc STA (addr " + MACSTR " reason %d)", + __func__, MAC2STR(addr), reason_code); + } + + return ret; +} + +#ifdef CONFIG_WPS +#ifdef IEEE80211_IOCTL_FILTERFRAME +static void madwifi_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len) +{ + struct madwifi_driver_data *drv = ctx; + const struct ieee80211_mgmt *mgmt; + const u8 *end, *ie; + u16 fc; + size_t ie_len; + + /* Send Probe Request information to WPS processing */ + + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)) + return; + mgmt = (const struct ieee80211_mgmt *) buf; + + fc = le_to_host16(mgmt->frame_control); + if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT || + WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_PROBE_REQ) + return; + + end = buf + len; + ie = mgmt->u.probe_req.variable; + ie_len = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)); + + hostapd_wps_probe_req_rx(drv->hapd, mgmt->sa, ie, ie_len); +} +#endif /* IEEE80211_IOCTL_FILTERFRAME */ +#endif /* CONFIG_WPS */ + +static int madwifi_receive_probe_req(struct madwifi_driver_data *drv) +{ + int ret = 0; +#ifdef CONFIG_WPS +#ifdef IEEE80211_IOCTL_FILTERFRAME + struct ieee80211req_set_filter filt; + + wpa_printf(MSG_DEBUG, "%s Enter", __func__); + filt.app_filterype = IEEE80211_FILTER_TYPE_PROBE_REQ; + + ret = set80211priv(drv, IEEE80211_IOCTL_FILTERFRAME, &filt, + sizeof(struct ieee80211req_set_filter)); + if (ret) + return ret; + + drv->sock_raw = l2_packet_init(drv->iface, NULL, ETH_P_80211_RAW, + madwifi_raw_receive, drv, 1); + if (drv->sock_raw == NULL) + return -1; +#endif /* IEEE80211_IOCTL_FILTERFRAME */ +#endif /* CONFIG_WPS */ + return ret; +} + +static int +madwifi_del_sta(struct madwifi_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) +{ + struct hostapd_data *hapd = drv->hapd; + struct sta_info *sta; + + hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "disassociated"); + + sta = ap_get_sta(hapd, addr); + if (sta != NULL) { + sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); + wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC); + sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST; + ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); + ap_free_sta(hapd, sta); + } + return 0; +} + +#ifdef CONFIG_WPS +static int +madwifi_set_wps_ie(void *priv, const u8 *ie, size_t len, u32 frametype) +{ + struct madwifi_driver_data *drv = priv; + u8 buf[256]; + struct ieee80211req_getset_appiebuf *beac_ie; + + wpa_printf(MSG_DEBUG, "%s buflen = %lu", __func__, + (unsigned long) len); + + beac_ie = (struct ieee80211req_getset_appiebuf *) buf; + beac_ie->app_frmtype = frametype; + beac_ie->app_buflen = len; + memcpy(&(beac_ie->app_buf[0]), ie, len); + + return set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, beac_ie, + sizeof(struct ieee80211req_getset_appiebuf) + len); +} + +static int +madwifi_set_wps_beacon_ie(const char *ifname, void *priv, const u8 *ie, + size_t len) +{ + return madwifi_set_wps_ie(priv, ie, len, IEEE80211_APPIE_FRAME_BEACON); +} + +static int +madwifi_set_wps_probe_resp_ie(const char *ifname, void *priv, const u8 *ie, + size_t len) +{ + return madwifi_set_wps_ie(priv, ie, len, + IEEE80211_APPIE_FRAME_PROBE_RESP); +} +#else /* CONFIG_WPS */ +#define madwifi_set_wps_beacon_ie NULL +#define madwifi_set_wps_probe_resp_ie NULL +#endif /* CONFIG_WPS */ + +static int +madwifi_process_wpa_ie(struct madwifi_driver_data *drv, struct sta_info *sta) +{ + struct hostapd_data *hapd = drv->hapd; + struct ieee80211req_wpaie ie; + int ielen, res; + u8 *iebuf; + + /* + * Fetch negotiated WPA/RSN parameters from the system. + */ + memset(&ie, 0, sizeof(ie)); + memcpy(ie.wpa_macaddr, sta->addr, IEEE80211_ADDR_LEN); + if (set80211priv(drv, IEEE80211_IOCTL_GETWPAIE, &ie, sizeof(ie))) { + wpa_printf(MSG_ERROR, "%s: Failed to get WPA/RSN IE", + __func__); + printf("Failed to get WPA/RSN information element.\n"); + return -1; /* XXX not right */ + } + wpa_hexdump(MSG_MSGDUMP, "madwifi req WPA IE", + ie.wpa_ie, IEEE80211_MAX_OPT_IE); + iebuf = ie.wpa_ie; + /* madwifi seems to return some random data if WPA/RSN IE is not set. + * Assume the IE was not included if the IE type is unknown. */ + if (iebuf[0] != WLAN_EID_VENDOR_SPECIFIC) + iebuf[1] = 0; +#ifdef MADWIFI_NG + wpa_hexdump(MSG_MSGDUMP, "madwifi req RSN IE", + ie.rsn_ie, IEEE80211_MAX_OPT_IE); + if (iebuf[1] == 0 && ie.rsn_ie[1] > 0) { + /* madwifi-ng svn #1453 added rsn_ie. Use it, if wpa_ie was not + * set. This is needed for WPA2. */ + iebuf = ie.rsn_ie; + if (iebuf[0] != WLAN_EID_RSN) + iebuf[1] = 0; + } +#endif /* MADWIFI_NG */ + ielen = iebuf[1]; + if (ielen == 0) { +#ifdef CONFIG_WPS + if (hapd->conf->wps_state) { + wpa_printf(MSG_DEBUG, "STA did not include WPA/RSN IE " + "in (Re)Association Request - possible WPS " + "use"); + sta->flags |= WLAN_STA_MAYBE_WPS; + return 0; + } +#endif /* CONFIG_WPS */ + printf("No WPA/RSN information element for station!?\n"); + return -1; /* XXX not right */ + } + ielen += 2; + if (sta->wpa_sm == NULL) + sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr); + if (sta->wpa_sm == NULL) { + printf("Failed to initialize WPA state machine\n"); + return -1; + } + res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, + iebuf, ielen, NULL, 0); + if (res != WPA_IE_OK) { + printf("WPA/RSN information element rejected? (res %u)\n", res); + return -1; + } + return 0; +} + +static int +madwifi_new_sta(struct madwifi_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) +{ + struct hostapd_data *hapd = drv->hapd; + struct sta_info *sta; + int new_assoc; + + hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "associated"); + + sta = ap_get_sta(hapd, addr); + if (sta) { + accounting_sta_stop(hapd, sta); + } else { + sta = ap_sta_add(hapd, addr); + if (sta == NULL) + return -1; + } + + if (memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) { + /* Cached accounting data is not valid anymore. */ + memset(drv->acct_mac, 0, ETH_ALEN); + memset(&drv->acct_data, 0, sizeof(drv->acct_data)); + } + + if (hapd->conf->wpa) { + if (madwifi_process_wpa_ie(drv, sta)) + return -1; + } + + /* + * Now that the internal station state is setup + * kick the authenticator into action. + */ + new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0; + sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC; + wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC); + hostapd_new_assoc_sta(hapd, sta, !new_assoc); + ieee802_1x_notify_port_enabled(sta->eapol_sm, 1); + return 0; +} + +static void +madwifi_wireless_event_wireless_custom(struct madwifi_driver_data *drv, + char *custom) +{ + wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom); + + if (strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) { + char *pos; + u8 addr[ETH_ALEN]; + pos = strstr(custom, "addr="); + if (pos == NULL) { + wpa_printf(MSG_DEBUG, + "MLME-MICHAELMICFAILURE.indication " + "without sender address ignored"); + return; + } + pos += 5; + if (hwaddr_aton(pos, addr) == 0) { + ieee80211_michael_mic_failure(drv->hapd, addr, 1); + } else { + wpa_printf(MSG_DEBUG, + "MLME-MICHAELMICFAILURE.indication " + "with invalid MAC address"); + } + } else if (strncmp(custom, "STA-TRAFFIC-STAT", 16) == 0) { + char *key, *value; + u32 val; + key = custom; + while ((key = strchr(key, '\n')) != NULL) { + key++; + value = strchr(key, '='); + if (value == NULL) + continue; + *value++ = '\0'; + val = strtoul(value, NULL, 10); + if (strcmp(key, "mac") == 0) + hwaddr_aton(value, drv->acct_mac); + else if (strcmp(key, "rx_packets") == 0) + drv->acct_data.rx_packets = val; + else if (strcmp(key, "tx_packets") == 0) + drv->acct_data.tx_packets = val; + else if (strcmp(key, "rx_bytes") == 0) + drv->acct_data.rx_bytes = val; + else if (strcmp(key, "tx_bytes") == 0) + drv->acct_data.tx_bytes = val; + key = value; + } + } +} + +static void +madwifi_wireless_event_wireless(struct madwifi_driver_data *drv, + char *data, int len) +{ + struct iw_event iwe_buf, *iwe = &iwe_buf; + char *pos, *end, *custom, *buf; + + pos = data; + end = data + len; + + while (pos + IW_EV_LCP_LEN <= end) { + /* Event data may be unaligned, so make a local, aligned copy + * before processing. */ + memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); + wpa_printf(MSG_MSGDUMP, "Wireless event: cmd=0x%x len=%d", + iwe->cmd, iwe->len); + if (iwe->len <= IW_EV_LCP_LEN) + return; + + custom = pos + IW_EV_POINT_LEN; + if (drv->we_version > 18 && + (iwe->cmd == IWEVMICHAELMICFAILURE || + iwe->cmd == IWEVCUSTOM)) { + /* WE-19 removed the pointer from struct iw_point */ + char *dpos = (char *) &iwe_buf.u.data.length; + int dlen = dpos - (char *) &iwe_buf; + memcpy(dpos, pos + IW_EV_LCP_LEN, + sizeof(struct iw_event) - dlen); + } else { + memcpy(&iwe_buf, pos, sizeof(struct iw_event)); + custom += IW_EV_POINT_OFF; + } + + switch (iwe->cmd) { + case IWEVEXPIRED: + madwifi_del_sta(drv, (u8 *) iwe->u.addr.sa_data); + break; + case IWEVREGISTERED: + madwifi_new_sta(drv, (u8 *) iwe->u.addr.sa_data); + break; + case IWEVCUSTOM: + if (custom + iwe->u.data.length > end) + return; + buf = malloc(iwe->u.data.length + 1); + if (buf == NULL) + return; /* XXX */ + memcpy(buf, custom, iwe->u.data.length); + buf[iwe->u.data.length] = '\0'; + madwifi_wireless_event_wireless_custom(drv, buf); + free(buf); + break; + } + + pos += iwe->len; + } +} + + +static void +madwifi_wireless_event_rtm_newlink(struct madwifi_driver_data *drv, + struct nlmsghdr *h, int len) +{ + struct ifinfomsg *ifi; + int attrlen, nlmsg_len, rta_len; + struct rtattr * attr; + + if (len < (int) sizeof(*ifi)) + return; + + ifi = NLMSG_DATA(h); + + if (ifi->ifi_index != drv->ifindex) + return; + + nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); + + attrlen = h->nlmsg_len - nlmsg_len; + if (attrlen < 0) + return; + + attr = (struct rtattr *) (((char *) ifi) + nlmsg_len); + + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + if (attr->rta_type == IFLA_WIRELESS) { + madwifi_wireless_event_wireless( + drv, ((char *) attr) + rta_len, + attr->rta_len - rta_len); + } + attr = RTA_NEXT(attr, attrlen); + } +} + + +static void +madwifi_wireless_event_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + char buf[256]; + int left; + struct sockaddr_nl from; + socklen_t fromlen; + struct nlmsghdr *h; + struct madwifi_driver_data *drv = eloop_ctx; + + fromlen = sizeof(from); + left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, + (struct sockaddr *) &from, &fromlen); + if (left < 0) { + if (errno != EINTR && errno != EAGAIN) + perror("recvfrom(netlink)"); + return; + } + + h = (struct nlmsghdr *) buf; + while (left >= (int) sizeof(*h)) { + int len, plen; + + len = h->nlmsg_len; + plen = len - sizeof(*h); + if (len > left || plen < 0) { + printf("Malformed netlink message: " + "len=%d left=%d plen=%d\n", + len, left, plen); + break; + } + + switch (h->nlmsg_type) { + case RTM_NEWLINK: + madwifi_wireless_event_rtm_newlink(drv, h, plen); + break; + } + + len = NLMSG_ALIGN(len); + left -= len; + h = (struct nlmsghdr *) ((char *) h + len); + } + + if (left > 0) { + printf("%d extra bytes in the end of netlink message\n", left); + } +} + + +static int +madwifi_get_we_version(struct madwifi_driver_data *drv) +{ + struct iw_range *range; + struct iwreq iwr; + int minlen; + size_t buflen; + + drv->we_version = 0; + + /* + * Use larger buffer than struct iw_range in order to allow the + * structure to grow in the future. + */ + buflen = sizeof(struct iw_range) + 500; + range = os_zalloc(buflen); + if (range == NULL) + return -1; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) range; + iwr.u.data.length = buflen; + + minlen = ((char *) &range->enc_capa) - (char *) range + + sizeof(range->enc_capa); + + if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) { + perror("ioctl[SIOCGIWRANGE]"); + free(range); + return -1; + } else if (iwr.u.data.length >= minlen && + range->we_version_compiled >= 18) { + wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d " + "WE(source)=%d enc_capa=0x%x", + range->we_version_compiled, + range->we_version_source, + range->enc_capa); + drv->we_version = range->we_version_compiled; + } + + free(range); + return 0; +} + + +static int +madwifi_wireless_event_init(void *priv) +{ + struct madwifi_driver_data *drv = priv; + int s; + struct sockaddr_nl local; + + madwifi_get_we_version(drv); + + drv->wext_sock = -1; + + s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (s < 0) { + perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)"); + return -1; + } + + memset(&local, 0, sizeof(local)); + local.nl_family = AF_NETLINK; + local.nl_groups = RTMGRP_LINK; + if (bind(s, (struct sockaddr *) &local, sizeof(local)) < 0) { + perror("bind(netlink)"); + close(s); + return -1; + } + + eloop_register_read_sock(s, madwifi_wireless_event_receive, drv, NULL); + drv->wext_sock = s; + + return 0; +} + + +static void +madwifi_wireless_event_deinit(void *priv) +{ + struct madwifi_driver_data *drv = priv; + + if (drv != NULL) { + if (drv->wext_sock < 0) + return; + eloop_unregister_read_sock(drv->wext_sock); + close(drv->wext_sock); + } +} + + +static int +madwifi_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len, + int encrypt, const u8 *own_addr) +{ + struct madwifi_driver_data *drv = priv; + unsigned char buf[3000]; + unsigned char *bp = buf; + struct l2_ethhdr *eth; + size_t len; + int status; + + /* + * Prepend the Ethernet header. If the caller left us + * space at the front we could just insert it but since + * we don't know we copy to a local buffer. Given the frequency + * and size of frames this probably doesn't matter. + */ + len = data_len + sizeof(struct l2_ethhdr); + if (len > sizeof(buf)) { + bp = malloc(len); + if (bp == NULL) { + printf("EAPOL frame discarded, cannot malloc temp " + "buffer of size %lu!\n", (unsigned long) len); + return -1; + } + } + eth = (struct l2_ethhdr *) bp; + memcpy(eth->h_dest, addr, ETH_ALEN); + memcpy(eth->h_source, own_addr, ETH_ALEN); + eth->h_proto = htons(ETH_P_EAPOL); + memcpy(eth+1, data, data_len); + + wpa_hexdump(MSG_MSGDUMP, "TX EAPOL", bp, len); + + status = l2_packet_send(drv->sock_xmit, addr, ETH_P_EAPOL, bp, len); + + if (bp != buf) + free(bp); + return status; +} + +static void +handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) +{ + struct madwifi_driver_data *drv = ctx; + struct hostapd_data *hapd = drv->hapd; + struct sta_info *sta; + + sta = ap_get_sta(hapd, src_addr); + if (!sta || !(sta->flags & WLAN_STA_ASSOC)) { + printf("Data frame from not associated STA %s\n", + ether_sprintf(src_addr)); + /* XXX cannot happen */ + return; + } + ieee802_1x_receive(hapd, src_addr, buf + sizeof(struct l2_ethhdr), + len - sizeof(struct l2_ethhdr)); +} + +static void * +madwifi_init(struct hostapd_data *hapd) +{ + struct madwifi_driver_data *drv; + struct ifreq ifr; + struct iwreq iwr; + + drv = os_zalloc(sizeof(struct madwifi_driver_data)); + if (drv == NULL) { + printf("Could not allocate memory for madwifi driver data\n"); + return NULL; + } + + drv->hapd = hapd; + drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->ioctl_sock < 0) { + perror("socket[PF_INET,SOCK_DGRAM]"); + goto bad; + } + memcpy(drv->iface, hapd->conf->iface, sizeof(drv->iface)); + + memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, drv->iface, sizeof(ifr.ifr_name)); + if (ioctl(drv->ioctl_sock, SIOCGIFINDEX, &ifr) != 0) { + perror("ioctl(SIOCGIFINDEX)"); + goto bad; + } + drv->ifindex = ifr.ifr_ifindex; + + drv->sock_xmit = l2_packet_init(drv->iface, NULL, ETH_P_EAPOL, + handle_read, drv, 1); + if (drv->sock_xmit == NULL) + goto bad; + if (l2_packet_get_own_addr(drv->sock_xmit, hapd->own_addr)) + goto bad; + if (hapd->conf->bridge[0] != '\0') { + wpa_printf(MSG_DEBUG, "Configure bridge %s for EAPOL traffic.", + hapd->conf->bridge); + drv->sock_recv = l2_packet_init(hapd->conf->bridge, NULL, + ETH_P_EAPOL, handle_read, drv, + 1); + if (drv->sock_recv == NULL) + goto bad; + } else + drv->sock_recv = drv->sock_xmit; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + + iwr.u.mode = IW_MODE_MASTER; + + if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) < 0) { + perror("ioctl[SIOCSIWMODE]"); + printf("Could not set interface to master mode!\n"); + goto bad; + } + + madwifi_set_iface_flags(drv, 0); /* mark down during setup */ + madwifi_set_privacy(drv->iface, drv, 0); /* default to no privacy */ + + madwifi_receive_probe_req(drv); + + return drv; +bad: + if (drv->sock_xmit != NULL) + l2_packet_deinit(drv->sock_xmit); + if (drv->ioctl_sock >= 0) + close(drv->ioctl_sock); + if (drv != NULL) + free(drv); + return NULL; +} + + +static void +madwifi_deinit(void *priv) +{ + struct madwifi_driver_data *drv = priv; + + (void) madwifi_set_iface_flags(drv, 0); + if (drv->ioctl_sock >= 0) + close(drv->ioctl_sock); + if (drv->sock_recv != NULL && drv->sock_recv != drv->sock_xmit) + l2_packet_deinit(drv->sock_recv); + if (drv->sock_xmit != NULL) + l2_packet_deinit(drv->sock_xmit); + if (drv->sock_raw) + l2_packet_deinit(drv->sock_raw); + free(drv); +} + +static int +madwifi_set_ssid(const char *ifname, void *priv, const u8 *buf, int len) +{ + struct madwifi_driver_data *drv = priv; + struct iwreq iwr; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.essid.flags = 1; /* SSID active */ + iwr.u.essid.pointer = (caddr_t) buf; + iwr.u.essid.length = len + 1; + + if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) { + perror("ioctl[SIOCSIWESSID]"); + printf("len=%d\n", len); + return -1; + } + return 0; +} + +static int +madwifi_get_ssid(const char *ifname, void *priv, u8 *buf, int len) +{ + struct madwifi_driver_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.essid.pointer = (caddr_t) buf; + iwr.u.essid.length = len; + + if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) { + perror("ioctl[SIOCGIWESSID]"); + ret = -1; + } else + ret = iwr.u.essid.length; + + return ret; +} + +static int +madwifi_set_countermeasures(void *priv, int enabled) +{ + struct madwifi_driver_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + return set80211param(drv, IEEE80211_PARAM_COUNTERMEASURES, enabled); +} + +static int +madwifi_commit(void *priv) +{ + return madwifi_set_iface_flags(priv, 1); +} + +const struct wpa_driver_ops wpa_driver_madwifi_ops = { + .name = "madwifi", + .init = madwifi_init, + .deinit = madwifi_deinit, + .set_ieee8021x = madwifi_set_ieee8021x, + .set_privacy = madwifi_set_privacy, + .set_encryption = madwifi_set_key, + .get_seqnum = madwifi_get_seqnum, + .flush = madwifi_flush, + .set_generic_elem = madwifi_set_opt_ie, + .wireless_event_init = madwifi_wireless_event_init, + .wireless_event_deinit = madwifi_wireless_event_deinit, + .sta_set_flags = madwifi_sta_set_flags, + .read_sta_data = madwifi_read_sta_driver_data, + .send_eapol = madwifi_send_eapol, + .sta_disassoc = madwifi_sta_disassoc, + .sta_deauth = madwifi_sta_deauth, + .set_ssid = madwifi_set_ssid, + .get_ssid = madwifi_get_ssid, + .set_countermeasures = madwifi_set_countermeasures, + .sta_clear_stats = madwifi_sta_clear_stats, + .commit = madwifi_commit, + .set_wps_beacon_ie = madwifi_set_wps_beacon_ie, + .set_wps_probe_resp_ie = madwifi_set_wps_probe_resp_ie, +}; diff --git a/hostapd/driver_nl80211.c b/hostapd/driver_nl80211.c new file mode 100644 index 0000000..4599e99 --- /dev/null +++ b/hostapd/driver_nl80211.c @@ -0,0 +1,2708 @@ +/* + * hostapd / Kernel driver communication via nl80211 + * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2004, Instant802 Networks, Inc. + * Copyright (c) 2005-2006, Devicescape Software, Inc. + * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include <sys/ioctl.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/family.h> +#include <netlink/genl/ctrl.h> +#include <netlink/msg.h> +#include <netlink/attr.h> +#include "nl80211_copy.h" +#include <net/if.h> +#include <netpacket/packet.h> +#include "wireless_copy.h" +#include <linux/filter.h> +#include <net/if_arp.h> + +#include "hostapd.h" +#include "driver.h" +#include "ieee802_1x.h" +#include "eloop.h" +#include "ieee802_11.h" +#include "sta_info.h" +#include "hw_features.h" +#include "mlme.h" +#include "radiotap.h" +#include "radiotap_iter.h" + +#ifdef CONFIG_LIBNL20 +/* libnl 2.0 compatibility code */ +#define nl_handle_alloc_cb nl_socket_alloc_cb +#define nl_handle_destroy nl_socket_free +#endif /* CONFIG_LIBNL20 */ + +enum ieee80211_msg_type { + ieee80211_msg_normal = 0, + ieee80211_msg_tx_callback_ack = 1, + ieee80211_msg_tx_callback_fail = 2, +}; + +struct i802_driver_data { + struct hostapd_data *hapd; + + char iface[IFNAMSIZ + 1]; + int bridge; + int ioctl_sock; /* socket for ioctl() use */ + int wext_sock; /* socket for wireless events */ + int eapol_sock; /* socket for EAPOL frames */ + int monitor_sock; /* socket for monitor */ + int monitor_ifidx; + + int default_if_indices[16]; + int *if_indices; + int num_if_indices; + + int we_version; + struct nl_handle *nl_handle; + struct nl_cache *nl_cache; + struct nl_cb *nl_cb; + struct genl_family *nl80211; + int dtim_period, beacon_int; + unsigned int beacon_set:1; + unsigned int ieee802_1x_active:1; + + int last_freq; + int last_freq_ht; +}; + + +static void add_ifidx(struct i802_driver_data *drv, int ifidx) +{ + int i; + int *old; + + for (i = 0; i < drv->num_if_indices; i++) { + if (drv->if_indices[i] == 0) { + drv->if_indices[i] = ifidx; + return; + } + } + + if (drv->if_indices != drv->default_if_indices) + old = drv->if_indices; + else + old = NULL; + + drv->if_indices = realloc(old, + sizeof(int) * (drv->num_if_indices + 1)); + if (!drv->if_indices) { + if (!old) + drv->if_indices = drv->default_if_indices; + else + drv->if_indices = old; + wpa_printf(MSG_ERROR, "Failed to reallocate memory for " + "interfaces"); + wpa_printf(MSG_ERROR, "Ignoring EAPOL on interface %d", ifidx); + return; + } + drv->if_indices[drv->num_if_indices] = ifidx; + drv->num_if_indices++; +} + + +static void del_ifidx(struct i802_driver_data *drv, int ifidx) +{ + int i; + + for (i = 0; i < drv->num_if_indices; i++) { + if (drv->if_indices[i] == ifidx) { + drv->if_indices[i] = 0; + break; + } + } +} + + +static int have_ifidx(struct i802_driver_data *drv, int ifidx) +{ + int i; + + if (ifidx == drv->bridge) + return 1; + + for (i = 0; i < drv->num_if_indices; i++) + if (drv->if_indices[i] == ifidx) + return 1; + + return 0; +} + + +/* nl80211 code */ +static int ack_handler(struct nl_msg *msg, void *arg) +{ + int *err = arg; + *err = 0; + return NL_STOP; +} + +static int finish_handler(struct nl_msg *msg, void *arg) +{ + int *ret = arg; + *ret = 0; + return NL_SKIP; +} + +static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, + void *arg) +{ + int *ret = arg; + *ret = err->error; + return NL_SKIP; +} + +static int send_and_recv_msgs(struct i802_driver_data *drv, + struct nl_msg *msg, + int (*valid_handler)(struct nl_msg *, void *), + void *valid_data) +{ + struct nl_cb *cb; + int err = -ENOMEM; + + cb = nl_cb_clone(drv->nl_cb); + if (!cb) + goto out; + + err = nl_send_auto_complete(drv->nl_handle, msg); + if (err < 0) + goto out; + + err = 1; + + nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err); + nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err); + nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err); + + if (valid_handler) + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, + valid_handler, valid_data); + + while (err > 0) + nl_recvmsgs(drv->nl_handle, cb); + out: + nl_cb_put(cb); + nlmsg_free(msg); + return err; +} + +static int hostapd_set_iface_flags(struct i802_driver_data *drv, + const char *ifname, int dev_up) +{ + struct ifreq ifr; + + if (drv->ioctl_sock < 0) + return -1; + + memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + + if (ioctl(drv->ioctl_sock, SIOCGIFFLAGS, &ifr) != 0) { + perror("ioctl[SIOCGIFFLAGS]"); + wpa_printf(MSG_DEBUG, "Could not read interface flags (%s)", + drv->iface); + return -1; + } + + if (dev_up) + ifr.ifr_flags |= IFF_UP; + else + ifr.ifr_flags &= ~IFF_UP; + + if (ioctl(drv->ioctl_sock, SIOCSIFFLAGS, &ifr) != 0) { + perror("ioctl[SIOCSIFFLAGS]"); + return -1; + } + + return 0; +} + + +static int nl_set_encr(int ifindex, struct i802_driver_data *drv, + const char *alg, const u8 *addr, int idx, const u8 *key, + size_t key_len, int txkey) +{ + struct nl_msg *msg; + int ret; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + if (strcmp(alg, "none") == 0) { + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_DEL_KEY, 0); + } else { + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_NEW_KEY, 0); + NLA_PUT(msg, NL80211_ATTR_KEY_DATA, key_len, key); + if (strcmp(alg, "WEP") == 0) { + if (key_len == 5) + NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, + 0x000FAC01); + else + NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, + 0x000FAC05); + } else if (strcmp(alg, "TKIP") == 0) + NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, 0x000FAC02); + else if (strcmp(alg, "CCMP") == 0) + NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, 0x000FAC04); + else if (strcmp(alg, "IGTK") == 0) + NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, 0x000FAC06); + else { + wpa_printf(MSG_ERROR, "%s: Unsupported encryption " + "algorithm '%s'", __func__, alg); + nlmsg_free(msg); + return -1; + } + } + + if (addr) + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret == -ENOENT) + ret = 0; + + /* + * If we failed or don't need to set the default TX key (below), + * we're done here. + */ + if (ret || !txkey || addr) + return ret; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_SET_KEY, 0); + NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); + if (strcmp(alg, "IGTK") == 0) + NLA_PUT_FLAG(msg, NL80211_ATTR_KEY_DEFAULT_MGMT); + else + NLA_PUT_FLAG(msg, NL80211_ATTR_KEY_DEFAULT); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret == -ENOENT) + ret = 0; + return ret; + nla_put_failure: + return -ENOBUFS; +} + + +static int i802_set_encryption(const char *iface, void *priv, const char *alg, + const u8 *addr, int idx, const u8 *key, + size_t key_len, int txkey) +{ + struct i802_driver_data *drv = priv; + int ret; + + ret = nl_set_encr(if_nametoindex(iface), drv, alg, addr, idx, key, + key_len, txkey); + if (ret < 0) + return ret; + + return ret; +} + + +static inline int min_int(int a, int b) +{ + if (a < b) + return a; + return b; +} + + +static int get_key_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + /* + * TODO: validate the key index and mac address! + * Otherwise, there's a race condition as soon as + * the kernel starts sending key notifications. + */ + + if (tb[NL80211_ATTR_KEY_SEQ]) + memcpy(arg, nla_data(tb[NL80211_ATTR_KEY_SEQ]), + min_int(nla_len(tb[NL80211_ATTR_KEY_SEQ]), 6)); + return NL_SKIP; +} + + +static int i802_get_seqnum(const char *iface, void *priv, const u8 *addr, + int idx, u8 *seq) +{ + struct i802_driver_data *drv = priv; + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_GET_KEY, 0); + + if (addr) + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(iface)); + + memset(seq, 0, 6); + + return send_and_recv_msgs(drv, msg, get_key_handler, seq); + nla_put_failure: + return -ENOBUFS; +} + + +static int i802_set_rate_sets(void *priv, int *supp_rates, int *basic_rates, + int mode) +{ + struct i802_driver_data *drv = priv; + struct nl_msg *msg; + u8 rates[NL80211_MAX_SUPP_RATES]; + u8 rates_len = 0; + int i; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, + NL80211_CMD_SET_BSS, 0); + + for (i = 0; i < NL80211_MAX_SUPP_RATES && basic_rates[i] >= 0; i++) + rates[rates_len++] = basic_rates[i] / 5; + + NLA_PUT(msg, NL80211_ATTR_BSS_BASIC_RATES, rates_len, rates); + + /* TODO: multi-BSS support */ + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(drv->iface)); + + return send_and_recv_msgs(drv, msg, NULL, NULL); + nla_put_failure: + return -ENOBUFS; +} + + +static int i802_send_frame(void *priv, const void *data, size_t len, + int encrypt, int flags) +{ + __u8 rtap_hdr[] = { + 0x00, 0x00, /* radiotap version */ + 0x0e, 0x00, /* radiotap length */ + 0x02, 0xc0, 0x00, 0x00, /* bmap: flags, tx and rx flags */ + IEEE80211_RADIOTAP_F_FRAG, /* F_FRAG (fragment if required) */ + 0x00, /* padding */ + 0x00, 0x00, /* RX and TX flags to indicate that */ + 0x00, 0x00, /* this is the injected frame directly */ + }; + struct i802_driver_data *drv = priv; + struct iovec iov[2] = { + { + .iov_base = &rtap_hdr, + .iov_len = sizeof(rtap_hdr), + }, + { + .iov_base = (void*)data, + .iov_len = len, + } + }; + struct msghdr msg = { + .msg_name = NULL, + .msg_namelen = 0, + .msg_iov = iov, + .msg_iovlen = 2, + .msg_control = NULL, + .msg_controllen = 0, + .msg_flags = 0, + }; + + if (encrypt) + rtap_hdr[8] |= IEEE80211_RADIOTAP_F_WEP; + + return sendmsg(drv->monitor_sock, &msg, flags); +} + +static int i802_send_mgmt_frame(void *priv, const void *data, size_t len, + int flags) +{ + struct ieee80211_mgmt *mgmt; + int do_not_encrypt = 0; + u16 fc; + + mgmt = (struct ieee80211_mgmt *) data; + fc = le_to_host16(mgmt->frame_control); + + if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && + WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_AUTH) { + /* + * Only one of the authentication frame types is encrypted. + * In order for static WEP encryption to work properly (i.e., + * to not encrypt the frame), we need to tell mac80211 about + * the frames that must not be encrypted. + */ + u16 auth_alg = le_to_host16(mgmt->u.auth.auth_alg); + u16 auth_trans = le_to_host16(mgmt->u.auth.auth_transaction); + if (auth_alg == WLAN_AUTH_OPEN || + (auth_alg == WLAN_AUTH_SHARED_KEY && auth_trans != 3)) + do_not_encrypt = 1; + } + + return i802_send_frame(priv, data, len, !do_not_encrypt, flags); +} + +/* Set kernel driver on given frequency (MHz) */ +static int i802_set_freq2(void *priv, struct hostapd_freq_params *freq) +{ + struct i802_driver_data *drv = priv; + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + drv->last_freq = freq->freq; + drv->last_freq_ht = freq->ht_enabled; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, + NL80211_CMD_SET_WIPHY, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(drv->iface)); + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq->freq); + if (freq->ht_enabled) { + switch (freq->sec_channel_offset) { + case -1: + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, + NL80211_CHAN_HT40MINUS); + break; + case 1: + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, + NL80211_CHAN_HT40PLUS); + break; + default: + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, + NL80211_CHAN_HT20); + break; + } + } + + if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0) + return 0; + nla_put_failure: + return -1; +} + + +static int i802_set_rts(void *priv, int rts) +{ + struct i802_driver_data *drv = priv; + struct iwreq iwr; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->hapd->conf->iface, IFNAMSIZ); + iwr.u.rts.value = rts; + iwr.u.rts.fixed = 1; + + if (ioctl(drv->ioctl_sock, SIOCSIWRTS, &iwr) < 0) { + perror("ioctl[SIOCSIWRTS]"); + return -1; + } + + return 0; +} + + +static int i802_get_rts(void *priv, int *rts) +{ + struct i802_driver_data *drv = priv; + struct iwreq iwr; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->hapd->conf->iface, IFNAMSIZ); + + if (ioctl(drv->ioctl_sock, SIOCGIWRTS, &iwr) < 0) { + perror("ioctl[SIOCGIWRTS]"); + return -1; + } + + *rts = iwr.u.rts.value; + + return 0; +} + + +static int i802_set_frag(void *priv, int frag) +{ + struct i802_driver_data *drv = priv; + struct iwreq iwr; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->hapd->conf->iface, IFNAMSIZ); + iwr.u.frag.value = frag; + iwr.u.frag.fixed = 1; + + if (ioctl(drv->ioctl_sock, SIOCSIWFRAG, &iwr) < 0) { + perror("ioctl[SIOCSIWFRAG]"); + return -1; + } + + return 0; +} + + +static int i802_get_frag(void *priv, int *frag) +{ + struct i802_driver_data *drv = priv; + struct iwreq iwr; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->hapd->conf->iface, IFNAMSIZ); + + if (ioctl(drv->ioctl_sock, SIOCGIWFRAG, &iwr) < 0) { + perror("ioctl[SIOCGIWFRAG]"); + return -1; + } + + *frag = iwr.u.frag.value; + + return 0; +} + + +static int i802_set_retry(void *priv, int short_retry, int long_retry) +{ + struct i802_driver_data *drv = priv; + struct iwreq iwr; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->hapd->conf->iface, IFNAMSIZ); + + iwr.u.retry.value = short_retry; + iwr.u.retry.flags = IW_RETRY_LIMIT | IW_RETRY_MIN; + if (ioctl(drv->ioctl_sock, SIOCSIWRETRY, &iwr) < 0) { + perror("ioctl[SIOCSIWRETRY(short)]"); + return -1; + } + + iwr.u.retry.value = long_retry; + iwr.u.retry.flags = IW_RETRY_LIMIT | IW_RETRY_MAX; + if (ioctl(drv->ioctl_sock, SIOCSIWRETRY, &iwr) < 0) { + perror("ioctl[SIOCSIWRETRY(long)]"); + return -1; + } + + return 0; +} + + +static int i802_get_retry(void *priv, int *short_retry, int *long_retry) +{ + struct i802_driver_data *drv = priv; + struct iwreq iwr; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->hapd->conf->iface, IFNAMSIZ); + + iwr.u.retry.flags = IW_RETRY_LIMIT | IW_RETRY_MIN; + if (ioctl(drv->ioctl_sock, SIOCGIWRETRY, &iwr) < 0) { + perror("ioctl[SIOCGIWFRAG(short)]"); + return -1; + } + *short_retry = iwr.u.retry.value; + + iwr.u.retry.flags = IW_RETRY_LIMIT | IW_RETRY_MAX; + if (ioctl(drv->ioctl_sock, SIOCGIWRETRY, &iwr) < 0) { + perror("ioctl[SIOCGIWFRAG(long)]"); + return -1; + } + *long_retry = iwr.u.retry.value; + + return 0; +} + + +static int i802_flush(void *priv) +{ + struct i802_driver_data *drv = priv; + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_DEL_STATION, 0); + + /* + * XXX: FIX! this needs to flush all VLANs too + */ + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, + if_nametoindex(drv->iface)); + + return send_and_recv_msgs(drv, msg, NULL, NULL); + nla_put_failure: + return -ENOBUFS; +} + + +static int get_sta_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct hostap_sta_driver_data *data = arg; + struct nlattr *stats[NL80211_STA_INFO_MAX + 1]; + static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = { + [NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 }, + [NL80211_STA_INFO_RX_BYTES] = { .type = NLA_U32 }, + [NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 }, + [NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 }, + [NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 }, + }; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + /* + * TODO: validate the interface and mac address! + * Otherwise, there's a race condition as soon as + * the kernel starts sending station notifications. + */ + + if (!tb[NL80211_ATTR_STA_INFO]) { + wpa_printf(MSG_DEBUG, "sta stats missing!"); + return NL_SKIP; + } + if (nla_parse_nested(stats, NL80211_STA_INFO_MAX, + tb[NL80211_ATTR_STA_INFO], + stats_policy)) { + wpa_printf(MSG_DEBUG, "failed to parse nested attributes!"); + return NL_SKIP; + } + + if (stats[NL80211_STA_INFO_INACTIVE_TIME]) + data->inactive_msec = + nla_get_u32(stats[NL80211_STA_INFO_INACTIVE_TIME]); + if (stats[NL80211_STA_INFO_RX_BYTES]) + data->rx_bytes = nla_get_u32(stats[NL80211_STA_INFO_RX_BYTES]); + if (stats[NL80211_STA_INFO_TX_BYTES]) + data->tx_bytes = nla_get_u32(stats[NL80211_STA_INFO_TX_BYTES]); + if (stats[NL80211_STA_INFO_RX_PACKETS]) + data->rx_packets = + nla_get_u32(stats[NL80211_STA_INFO_RX_PACKETS]); + if (stats[NL80211_STA_INFO_TX_PACKETS]) + data->tx_packets = + nla_get_u32(stats[NL80211_STA_INFO_TX_PACKETS]); + + return NL_SKIP; +} + +static int i802_read_sta_data(void *priv, struct hostap_sta_driver_data *data, + const u8 *addr) +{ + struct i802_driver_data *drv = priv; + struct nl_msg *msg; + + os_memset(data, 0, sizeof(*data)); + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_GET_STATION, 0); + + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(drv->iface)); + + return send_and_recv_msgs(drv, msg, get_sta_handler, data); + nla_put_failure: + return -ENOBUFS; +} + + +static int i802_send_eapol(void *priv, const u8 *addr, const u8 *data, + size_t data_len, int encrypt, const u8 *own_addr) +{ + struct i802_driver_data *drv = priv; + struct ieee80211_hdr *hdr; + size_t len; + u8 *pos; + int res; +#if 0 /* FIX */ + int qos = sta->flags & WLAN_STA_WME; +#else + int qos = 0; +#endif + + len = sizeof(*hdr) + (qos ? 2 : 0) + sizeof(rfc1042_header) + 2 + + data_len; + hdr = os_zalloc(len); + if (hdr == NULL) { + printf("malloc() failed for i802_send_data(len=%lu)\n", + (unsigned long) len); + return -1; + } + + hdr->frame_control = + IEEE80211_FC(WLAN_FC_TYPE_DATA, WLAN_FC_STYPE_DATA); + hdr->frame_control |= host_to_le16(WLAN_FC_FROMDS); + if (encrypt) + hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP); +#if 0 /* To be enabled if qos determination is added above */ + if (qos) { + hdr->frame_control |= + host_to_le16(WLAN_FC_STYPE_QOS_DATA << 4); + } +#endif + + memcpy(hdr->IEEE80211_DA_FROMDS, addr, ETH_ALEN); + memcpy(hdr->IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN); + memcpy(hdr->IEEE80211_SA_FROMDS, own_addr, ETH_ALEN); + pos = (u8 *) (hdr + 1); + +#if 0 /* To be enabled if qos determination is added above */ + if (qos) { + /* add an empty QoS header if needed */ + pos[0] = 0; + pos[1] = 0; + pos += 2; + } +#endif + + memcpy(pos, rfc1042_header, sizeof(rfc1042_header)); + pos += sizeof(rfc1042_header); + WPA_PUT_BE16(pos, ETH_P_PAE); + pos += 2; + memcpy(pos, data, data_len); + + res = i802_send_frame(drv, (u8 *) hdr, len, encrypt, 0); + free(hdr); + + if (res < 0) { + perror("i802_send_eapol: send"); + printf("i802_send_eapol - packet len: %lu - failed\n", + (unsigned long) len); + } + + return res; +} + + +static int i802_sta_add2(const char *ifname, void *priv, + struct hostapd_sta_add_params *params) +{ + struct i802_driver_data *drv = priv; + struct nl_msg *msg; + int ret = -ENOBUFS; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_NEW_STATION, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, + if_nametoindex(drv->iface)); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->addr); + NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, params->aid); + NLA_PUT(msg, NL80211_ATTR_STA_SUPPORTED_RATES, params->supp_rates_len, + params->supp_rates); + NLA_PUT_U16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL, + params->listen_interval); + +#ifdef CONFIG_IEEE80211N + if (params->ht_capabilities) { + NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY, + params->ht_capabilities->length, + ¶ms->ht_capabilities->data); + } +#endif /* CONFIG_IEEE80211N */ + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret) + wpa_printf(MSG_DEBUG, "nl80211: NL80211_CMD_NEW_STATION " + "result: %d (%s)", ret, strerror(-ret)); + if (ret == -EEXIST) + ret = 0; + nla_put_failure: + return ret; +} + + +static int i802_sta_remove(void *priv, const u8 *addr) +{ + struct i802_driver_data *drv = priv; + struct nl_msg *msg; + int ret; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_DEL_STATION, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, + if_nametoindex(drv->iface)); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret == -ENOENT) + return 0; + return ret; + nla_put_failure: + return -ENOBUFS; +} + + +static int i802_sta_set_flags(void *priv, const u8 *addr, + int total_flags, int flags_or, int flags_and) +{ + struct i802_driver_data *drv = priv; + struct nl_msg *msg, *flags = NULL; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + flags = nlmsg_alloc(); + if (!flags) { + nlmsg_free(msg); + return -ENOMEM; + } + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_SET_STATION, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, + if_nametoindex(drv->iface)); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + + if (total_flags & WLAN_STA_AUTHORIZED || !drv->ieee802_1x_active) + NLA_PUT_FLAG(flags, NL80211_STA_FLAG_AUTHORIZED); + + if (total_flags & WLAN_STA_WMM) + NLA_PUT_FLAG(flags, NL80211_STA_FLAG_WME); + + if (total_flags & WLAN_STA_SHORT_PREAMBLE) + NLA_PUT_FLAG(flags, NL80211_STA_FLAG_SHORT_PREAMBLE); + + if (total_flags & WLAN_STA_MFP) + NLA_PUT_FLAG(flags, NL80211_STA_FLAG_MFP); + + if (nla_put_nested(msg, NL80211_ATTR_STA_FLAGS, flags)) + goto nla_put_failure; + + nlmsg_free(flags); + + return send_and_recv_msgs(drv, msg, NULL, NULL); + nla_put_failure: + nlmsg_free(flags); + return -ENOBUFS; +} + + +static int i802_set_regulatory_domain(void *priv, unsigned int rd) +{ + return -1; +} + + +static int i802_set_tx_queue_params(void *priv, int queue, int aifs, + int cw_min, int cw_max, int burst_time) +{ + struct i802_driver_data *drv = priv; + struct nl_msg *msg; + struct nlattr *txq, *params; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_SET_WIPHY, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(drv->iface)); + + txq = nla_nest_start(msg, NL80211_ATTR_WIPHY_TXQ_PARAMS); + if (!txq) + goto nla_put_failure; + + /* We are only sending parameters for a single TXQ at a time */ + params = nla_nest_start(msg, 1); + if (!params) + goto nla_put_failure; + + NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, queue); + /* Burst time is configured in units of 0.1 msec and TXOP parameter in + * 32 usec, so need to convert the value here. */ + NLA_PUT_U16(msg, NL80211_TXQ_ATTR_TXOP, (burst_time * 100 + 16) / 32); + NLA_PUT_U16(msg, NL80211_TXQ_ATTR_CWMIN, cw_min); + NLA_PUT_U16(msg, NL80211_TXQ_ATTR_CWMAX, cw_max); + NLA_PUT_U8(msg, NL80211_TXQ_ATTR_AIFS, aifs); + + nla_nest_end(msg, params); + + nla_nest_end(msg, txq); + + if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0) + return 0; + nla_put_failure: + return -1; +} + + +static void nl80211_remove_iface(struct i802_driver_data *drv, int ifidx) +{ + struct nl_msg *msg; + + /* stop listening for EAPOL on this interface */ + del_ifidx(drv, ifidx); + + msg = nlmsg_alloc(); + if (!msg) + goto nla_put_failure; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_DEL_INTERFACE, 0); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifidx); + + if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0) + return; + nla_put_failure: + printf("Failed to remove interface.\n"); +} + + +static int nl80211_create_iface(struct i802_driver_data *drv, + const char *ifname, + enum nl80211_iftype iftype, + const u8 *addr) +{ + struct nl_msg *msg, *flags = NULL; + int ifidx; + struct ifreq ifreq; + struct iwreq iwr; + int ret = -ENOBUFS; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_NEW_INTERFACE, 0); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, + if_nametoindex(drv->hapd->conf->iface)); + NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, ifname); + NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, iftype); + + if (iftype == NL80211_IFTYPE_MONITOR) { + int err; + + flags = nlmsg_alloc(); + if (!flags) + goto nla_put_failure; + + NLA_PUT_FLAG(flags, NL80211_MNTR_FLAG_COOK_FRAMES); + + err = nla_put_nested(msg, NL80211_ATTR_MNTR_FLAGS, flags); + + nlmsg_free(flags); + + if (err) + goto nla_put_failure; + } + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret) { + nla_put_failure: + printf("Failed to create interface %s.\n", ifname); + return ret; + } + + ifidx = if_nametoindex(ifname); + + if (ifidx <= 0) + return -1; + + /* start listening for EAPOL on this interface */ + add_ifidx(drv, ifidx); + + if (addr) { + switch (iftype) { + case NL80211_IFTYPE_AP: + os_strlcpy(ifreq.ifr_name, ifname, IFNAMSIZ); + memcpy(ifreq.ifr_hwaddr.sa_data, addr, ETH_ALEN); + ifreq.ifr_hwaddr.sa_family = ARPHRD_ETHER; + + if (ioctl(drv->ioctl_sock, SIOCSIFHWADDR, &ifreq)) { + nl80211_remove_iface(drv, ifidx); + return -1; + } + break; + case NL80211_IFTYPE_WDS: + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, ifname, IFNAMSIZ); + iwr.u.addr.sa_family = ARPHRD_ETHER; + memcpy(iwr.u.addr.sa_data, addr, ETH_ALEN); + if (ioctl(drv->ioctl_sock, SIOCSIWAP, &iwr)) + return -1; + break; + default: + /* nothing */ + break; + } + } + + return ifidx; +} + + +static int i802_bss_add(void *priv, const char *ifname, const u8 *bssid) +{ + int ifidx; + + /* + * The kernel supports that when the low-level driver does, + * but we currently don't because we need per-BSS data that + * currently we can't handle easily. + */ + return -1; + + ifidx = nl80211_create_iface(priv, ifname, NL80211_IFTYPE_AP, bssid); + if (ifidx < 0) + return -1; + if (hostapd_set_iface_flags(priv, ifname, 1)) { + nl80211_remove_iface(priv, ifidx); + return -1; + } + return 0; +} + + +static int i802_bss_remove(void *priv, const char *ifname) +{ + nl80211_remove_iface(priv, if_nametoindex(ifname)); + return 0; +} + + +static int i802_set_beacon(const char *iface, void *priv, + u8 *head, size_t head_len, + u8 *tail, size_t tail_len) +{ + struct i802_driver_data *drv = priv; + struct nl_msg *msg; + u8 cmd = NL80211_CMD_NEW_BEACON; + int ret; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + if (drv->beacon_set) + cmd = NL80211_CMD_SET_BEACON; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, cmd, 0); + NLA_PUT(msg, NL80211_ATTR_BEACON_HEAD, head_len, head); + NLA_PUT(msg, NL80211_ATTR_BEACON_TAIL, tail_len, tail); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(iface)); + NLA_PUT_U32(msg, NL80211_ATTR_BEACON_INTERVAL, drv->beacon_int); + + if (!drv->dtim_period) + drv->dtim_period = 2; + NLA_PUT_U32(msg, NL80211_ATTR_DTIM_PERIOD, drv->dtim_period); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (!ret) + drv->beacon_set = 1; + return ret; + nla_put_failure: + return -ENOBUFS; +} + + +static int i802_del_beacon(struct i802_driver_data *drv) +{ + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_DEL_BEACON, 0); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(drv->iface)); + + return send_and_recv_msgs(drv, msg, NULL, NULL); + nla_put_failure: + return -ENOBUFS; +} + + +static int i802_set_ieee8021x(const char *ifname, void *priv, int enabled) +{ + struct i802_driver_data *drv = priv; + + /* + * FIXME: This needs to be per interface (BSS) + */ + drv->ieee802_1x_active = enabled; + return 0; +} + + +static int i802_set_privacy(const char *ifname, void *priv, int enabled) +{ + struct i802_driver_data *drv = priv; + struct iwreq iwr; + + memset(&iwr, 0, sizeof(iwr)); + + os_strlcpy(iwr.ifr_name, ifname, IFNAMSIZ); + iwr.u.param.flags = IW_AUTH_PRIVACY_INVOKED; + iwr.u.param.value = enabled; + + ioctl(drv->ioctl_sock, SIOCSIWAUTH, &iwr); + + /* ignore errors, the kernel/driver might not care */ + return 0; +} + + +static int i802_set_internal_bridge(void *priv, int value) +{ + return -1; +} + + +static int i802_set_beacon_int(void *priv, int value) +{ + struct i802_driver_data *drv = priv; + struct nl_msg *msg; + + drv->beacon_int = value; + + if (!drv->beacon_set) + return 0; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_SET_BEACON, 0); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(drv->iface)); + + NLA_PUT_U32(msg, NL80211_ATTR_BEACON_INTERVAL, value); + + return send_and_recv_msgs(drv, msg, NULL, NULL); + nla_put_failure: + return -ENOBUFS; +} + + +static int i802_set_dtim_period(const char *iface, void *priv, int value) +{ + struct i802_driver_data *drv = priv; + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_SET_BEACON, 0); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(iface)); + + drv->dtim_period = value; + NLA_PUT_U32(msg, NL80211_ATTR_DTIM_PERIOD, drv->dtim_period); + + return send_and_recv_msgs(drv, msg, NULL, NULL); + nla_put_failure: + return -ENOBUFS; +} + + +static int i802_set_bss(void *priv, int cts, int preamble, int slot) +{ + struct i802_driver_data *drv = priv; + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, + NL80211_CMD_SET_BSS, 0); + + if (cts >= 0) + NLA_PUT_U8(msg, NL80211_ATTR_BSS_CTS_PROT, cts); + if (preamble >= 0) + NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_PREAMBLE, preamble); + if (slot >= 0) + NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_SLOT_TIME, slot); + + /* TODO: multi-BSS support */ + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(drv->iface)); + + return send_and_recv_msgs(drv, msg, NULL, NULL); + nla_put_failure: + return -ENOBUFS; +} + + +static int i802_set_cts_protect(void *priv, int value) +{ + return i802_set_bss(priv, value, -1, -1); +} + + +static int i802_set_preamble(void *priv, int value) +{ + return i802_set_bss(priv, -1, value, -1); +} + + +static int i802_set_short_slot_time(void *priv, int value) +{ + return i802_set_bss(priv, -1, -1, value); +} + + +static enum nl80211_iftype i802_if_type(enum hostapd_driver_if_type type) +{ + switch (type) { + case HOSTAPD_IF_VLAN: + return NL80211_IFTYPE_AP_VLAN; + case HOSTAPD_IF_WDS: + return NL80211_IFTYPE_WDS; + } + return -1; +} + + +static int i802_if_add(const char *iface, void *priv, + enum hostapd_driver_if_type type, char *ifname, + const u8 *addr) +{ + if (nl80211_create_iface(priv, ifname, i802_if_type(type), addr) < 0) + return -1; + return 0; +} + + +static int i802_if_update(void *priv, enum hostapd_driver_if_type type, + char *ifname, const u8 *addr) +{ + /* unused at the moment */ + return -1; +} + + +static int i802_if_remove(void *priv, enum hostapd_driver_if_type type, + const char *ifname, const u8 *addr) +{ + nl80211_remove_iface(priv, if_nametoindex(ifname)); + return 0; +} + + +struct phy_info_arg { + u16 *num_modes; + struct hostapd_hw_modes *modes; +}; + +static int phy_info_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct phy_info_arg *phy_info = arg; + + struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1]; + + struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1]; + static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = { + [NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 }, + [NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_PASSIVE_SCAN] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_NO_IBSS] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 }, + }; + + struct nlattr *tb_rate[NL80211_BITRATE_ATTR_MAX + 1]; + static struct nla_policy rate_policy[NL80211_BITRATE_ATTR_MAX + 1] = { + [NL80211_BITRATE_ATTR_RATE] = { .type = NLA_U32 }, + [NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE] = { .type = NLA_FLAG }, + }; + + struct nlattr *nl_band; + struct nlattr *nl_freq; + struct nlattr *nl_rate; + int rem_band, rem_freq, rem_rate; + struct hostapd_hw_modes *mode; + int idx, mode_is_set; + + nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (!tb_msg[NL80211_ATTR_WIPHY_BANDS]) + return NL_SKIP; + + nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band) { + mode = realloc(phy_info->modes, (*phy_info->num_modes + 1) * sizeof(*mode)); + if (!mode) + return NL_SKIP; + phy_info->modes = mode; + + mode_is_set = 0; + + mode = &phy_info->modes[*(phy_info->num_modes)]; + memset(mode, 0, sizeof(*mode)); + *(phy_info->num_modes) += 1; + + nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band), + nla_len(nl_band), NULL); + + if (tb_band[NL80211_BAND_ATTR_HT_CAPA]) { + mode->ht_capab = nla_get_u16( + tb_band[NL80211_BAND_ATTR_HT_CAPA]); + } + + nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) { + nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_freq), + nla_len(nl_freq), freq_policy); + if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ]) + continue; + mode->num_channels++; + } + + mode->channels = calloc(mode->num_channels, sizeof(struct hostapd_channel_data)); + if (!mode->channels) + return NL_SKIP; + + idx = 0; + + nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) { + nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_freq), + nla_len(nl_freq), freq_policy); + if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ]) + continue; + + mode->channels[idx].freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]); + mode->channels[idx].flag = 0; + + if (!mode_is_set) { + /* crude heuristic */ + if (mode->channels[idx].freq < 4000) + mode->mode = HOSTAPD_MODE_IEEE80211B; + else + mode->mode = HOSTAPD_MODE_IEEE80211A; + mode_is_set = 1; + } + + /* crude heuristic */ + if (mode->channels[idx].freq < 4000) + if (mode->channels[idx].freq == 2484) + mode->channels[idx].chan = 14; + else + mode->channels[idx].chan = (mode->channels[idx].freq - 2407) / 5; + else + mode->channels[idx].chan = mode->channels[idx].freq/5 - 1000; + + if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED]) + mode->channels[idx].flag |= + HOSTAPD_CHAN_DISABLED; + if (tb_freq[NL80211_FREQUENCY_ATTR_PASSIVE_SCAN]) + mode->channels[idx].flag |= + HOSTAPD_CHAN_PASSIVE_SCAN; + if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IBSS]) + mode->channels[idx].flag |= + HOSTAPD_CHAN_NO_IBSS; + if (tb_freq[NL80211_FREQUENCY_ATTR_RADAR]) + mode->channels[idx].flag |= + HOSTAPD_CHAN_RADAR; + + if (tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER] && + !tb_freq[NL80211_FREQUENCY_ATTR_DISABLED]) + mode->channels[idx].max_tx_power = + nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER]) / 100; + + idx++; + } + + nla_for_each_nested(nl_rate, tb_band[NL80211_BAND_ATTR_RATES], rem_rate) { + nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, nla_data(nl_rate), + nla_len(nl_rate), rate_policy); + if (!tb_rate[NL80211_BITRATE_ATTR_RATE]) + continue; + mode->num_rates++; + } + + mode->rates = calloc(mode->num_rates, sizeof(struct hostapd_rate_data)); + if (!mode->rates) + return NL_SKIP; + + idx = 0; + + nla_for_each_nested(nl_rate, tb_band[NL80211_BAND_ATTR_RATES], rem_rate) { + nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, nla_data(nl_rate), + nla_len(nl_rate), rate_policy); + if (!tb_rate[NL80211_BITRATE_ATTR_RATE]) + continue; + mode->rates[idx].rate = nla_get_u32(tb_rate[NL80211_BITRATE_ATTR_RATE]); + + /* crude heuristic */ + if (mode->mode == HOSTAPD_MODE_IEEE80211B && + mode->rates[idx].rate > 200) + mode->mode = HOSTAPD_MODE_IEEE80211G; + + if (tb_rate[NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE]) + mode->rates[idx].flags |= HOSTAPD_RATE_PREAMBLE2; + + idx++; + } + } + + return NL_SKIP; +} + +static struct hostapd_hw_modes *i802_add_11b(struct hostapd_hw_modes *modes, + u16 *num_modes) +{ + u16 m; + struct hostapd_hw_modes *mode11g = NULL, *nmodes, *mode; + int i, mode11g_idx = -1; + + /* If only 802.11g mode is included, use it to construct matching + * 802.11b mode data. */ + + for (m = 0; m < *num_modes; m++) { + if (modes[m].mode == HOSTAPD_MODE_IEEE80211B) + return modes; /* 802.11b already included */ + if (modes[m].mode == HOSTAPD_MODE_IEEE80211G) + mode11g_idx = m; + } + + if (mode11g_idx < 0) + return modes; /* 2.4 GHz band not supported at all */ + + nmodes = os_realloc(modes, (*num_modes + 1) * sizeof(*nmodes)); + if (nmodes == NULL) + return modes; /* Could not add 802.11b mode */ + + mode = &nmodes[*num_modes]; + os_memset(mode, 0, sizeof(*mode)); + (*num_modes)++; + modes = nmodes; + + mode->mode = HOSTAPD_MODE_IEEE80211B; + + mode11g = &modes[mode11g_idx]; + mode->num_channels = mode11g->num_channels; + mode->channels = os_malloc(mode11g->num_channels * + sizeof(struct hostapd_channel_data)); + if (mode->channels == NULL) { + (*num_modes)--; + return modes; /* Could not add 802.11b mode */ + } + os_memcpy(mode->channels, mode11g->channels, + mode11g->num_channels * sizeof(struct hostapd_channel_data)); + + mode->num_rates = 0; + mode->rates = os_malloc(4 * sizeof(struct hostapd_rate_data)); + if (mode->rates == NULL) { + os_free(mode->channels); + (*num_modes)--; + return modes; /* Could not add 802.11b mode */ + } + + for (i = 0; i < mode11g->num_rates; i++) { + if (mode11g->rates[i].rate > 110 || + mode11g->rates[i].flags & + (HOSTAPD_RATE_ERP | HOSTAPD_RATE_OFDM)) + continue; + mode->rates[mode->num_rates] = mode11g->rates[i]; + mode->num_rates++; + if (mode->num_rates == 4) + break; + } + + if (mode->num_rates == 0) { + os_free(mode->channels); + os_free(mode->rates); + (*num_modes)--; + return modes; /* No 802.11b rates */ + } + + wpa_printf(MSG_DEBUG, "nl80211: Added 802.11b mode based on 802.11g " + "information"); + + return modes; +} + +static struct hostapd_hw_modes *i802_get_hw_feature_data(void *priv, + u16 *num_modes, + u16 *flags) +{ + struct i802_driver_data *drv = priv; + struct nl_msg *msg; + struct phy_info_arg result = { + .num_modes = num_modes, + .modes = NULL, + }; + + *num_modes = 0; + *flags = 0; + + msg = nlmsg_alloc(); + if (!msg) + return NULL; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_GET_WIPHY, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(drv->iface)); + + if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) + return i802_add_11b(result.modes, num_modes); + nla_put_failure: + return NULL; +} + + +static int i802_set_sta_vlan(void *priv, const u8 *addr, + const char *ifname, int vlan_id) +{ + struct i802_driver_data *drv = priv; + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_SET_STATION, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, + if_nametoindex(drv->iface)); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, + if_nametoindex(ifname)); + + return send_and_recv_msgs(drv, msg, NULL, NULL); + nla_put_failure: + return -ENOBUFS; +} + + +static int i802_set_country(void *priv, const char *country) +{ + struct i802_driver_data *drv = priv; + struct nl_msg *msg; + char alpha2[3]; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_REQ_SET_REG, 0); + + alpha2[0] = country[0]; + alpha2[1] = country[1]; + alpha2[2] = '\0'; + NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, alpha2); + + return send_and_recv_msgs(drv, msg, NULL, NULL); + nla_put_failure: + return -ENOBUFS; +} + + +static void handle_unknown_sta(struct hostapd_data *hapd, u8 *ta) +{ + struct sta_info *sta; + + sta = ap_get_sta(hapd, ta); + if (!sta || !(sta->flags & WLAN_STA_ASSOC)) { + printf("Data/PS-poll frame from not associated STA " + MACSTR "\n", MAC2STR(ta)); + if (sta && (sta->flags & WLAN_STA_AUTH)) + hostapd_sta_disassoc( + hapd, ta, + WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA); + else + hostapd_sta_deauth( + hapd, ta, + WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA); + } +} + + +static void handle_tx_callback(struct hostapd_data *hapd, u8 *buf, size_t len, + int ok) +{ + struct ieee80211_hdr *hdr; + u16 fc, type, stype; + struct sta_info *sta; + + hdr = (struct ieee80211_hdr *) buf; + fc = le_to_host16(hdr->frame_control); + + type = WLAN_FC_GET_TYPE(fc); + stype = WLAN_FC_GET_STYPE(fc); + + switch (type) { + case WLAN_FC_TYPE_MGMT: + wpa_printf(MSG_DEBUG, "MGMT (TX callback) %s", + ok ? "ACK" : "fail"); + ieee802_11_mgmt_cb(hapd, buf, len, stype, ok); + break; + case WLAN_FC_TYPE_CTRL: + wpa_printf(MSG_DEBUG, "CTRL (TX callback) %s", + ok ? "ACK" : "fail"); + break; + case WLAN_FC_TYPE_DATA: + sta = ap_get_sta(hapd, hdr->addr1); + if (sta && sta->flags & WLAN_STA_PENDING_POLL) { + wpa_printf(MSG_DEBUG, "STA " MACSTR " %s pending " + "activity poll", MAC2STR(sta->addr), + ok ? "ACKed" : "did not ACK"); + if (ok) + sta->flags &= ~WLAN_STA_PENDING_POLL; + } + if (sta) + ieee802_1x_tx_status(hapd, sta, buf, len, ok); + break; + default: + printf("unknown TX callback frame type %d\n", type); + break; + } +} + + +static void handle_frame(struct hostapd_iface *iface, u8 *buf, size_t len, + struct hostapd_frame_info *hfi, + enum ieee80211_msg_type msg_type) +{ + struct ieee80211_hdr *hdr; + u16 fc, type, stype; + size_t data_len = len; + struct hostapd_data *hapd = NULL; + int broadcast_bssid = 0; + size_t i; + u8 *bssid; + + /* + * PS-Poll frames are 16 bytes. All other frames are + * 24 bytes or longer. + */ + if (len < 16) + return; + + hdr = (struct ieee80211_hdr *) buf; + fc = le_to_host16(hdr->frame_control); + + type = WLAN_FC_GET_TYPE(fc); + stype = WLAN_FC_GET_STYPE(fc); + + switch (type) { + case WLAN_FC_TYPE_DATA: + if (len < 24) + return; + switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) { + case WLAN_FC_TODS: + bssid = hdr->addr1; + break; + case WLAN_FC_FROMDS: + bssid = hdr->addr2; + break; + default: + /* discard */ + return; + } + break; + case WLAN_FC_TYPE_CTRL: + /* discard non-ps-poll frames */ + if (stype != WLAN_FC_STYPE_PSPOLL) + return; + bssid = hdr->addr1; + break; + case WLAN_FC_TYPE_MGMT: + bssid = hdr->addr3; + break; + default: + /* discard */ + return; + } + + /* find interface frame belongs to */ + for (i = 0; i < iface->num_bss; i++) { + if (memcmp(bssid, iface->bss[i]->own_addr, ETH_ALEN) == 0) { + hapd = iface->bss[i]; + break; + } + } + + if (hapd == NULL) { + hapd = iface->bss[0]; + + if (bssid[0] != 0xff || bssid[1] != 0xff || + bssid[2] != 0xff || bssid[3] != 0xff || + bssid[4] != 0xff || bssid[5] != 0xff) { + /* + * Unknown BSSID - drop frame if this is not from + * passive scanning or a beacon (at least ProbeReq + * frames to other APs may be allowed through RX + * filtering in the wlan hw/driver) + */ + if ((type != WLAN_FC_TYPE_MGMT || + stype != WLAN_FC_STYPE_BEACON)) + return; + } else + broadcast_bssid = 1; + } + + switch (msg_type) { + case ieee80211_msg_normal: + /* continue processing */ + break; + case ieee80211_msg_tx_callback_ack: + handle_tx_callback(hapd, buf, data_len, 1); + return; + case ieee80211_msg_tx_callback_fail: + handle_tx_callback(hapd, buf, data_len, 0); + return; + } + + switch (type) { + case WLAN_FC_TYPE_MGMT: + if (stype != WLAN_FC_STYPE_BEACON && + stype != WLAN_FC_STYPE_PROBE_REQ) + wpa_printf(MSG_MSGDUMP, "MGMT"); + if (broadcast_bssid) { + for (i = 0; i < iface->num_bss; i++) + ieee802_11_mgmt(iface->bss[i], buf, data_len, + stype, hfi); + } else + ieee802_11_mgmt(hapd, buf, data_len, stype, hfi); + break; + case WLAN_FC_TYPE_CTRL: + /* can only get here with PS-Poll frames */ + wpa_printf(MSG_DEBUG, "CTRL"); + handle_unknown_sta(hapd, hdr->addr2); + break; + case WLAN_FC_TYPE_DATA: + handle_unknown_sta(hapd, hdr->addr2); + break; + } +} + + +static void handle_eapol(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct i802_driver_data *drv = eloop_ctx; + struct hostapd_data *hapd = drv->hapd; + struct sockaddr_ll lladdr; + unsigned char buf[3000]; + int len; + socklen_t fromlen = sizeof(lladdr); + + len = recvfrom(sock, buf, sizeof(buf), 0, + (struct sockaddr *)&lladdr, &fromlen); + if (len < 0) { + perror("recv"); + return; + } + + if (have_ifidx(drv, lladdr.sll_ifindex)) + ieee802_1x_receive(hapd, lladdr.sll_addr, buf, len); +} + + +static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct i802_driver_data *drv = eloop_ctx; + int len; + unsigned char buf[3000]; + struct hostapd_data *hapd = drv->hapd; + struct ieee80211_radiotap_iterator iter; + int ret; + struct hostapd_frame_info hfi; + int injected = 0, failed = 0, msg_type, rxflags = 0; + + len = recv(sock, buf, sizeof(buf), 0); + if (len < 0) { + perror("recv"); + return; + } + + if (ieee80211_radiotap_iterator_init(&iter, (void*)buf, len)) { + printf("received invalid radiotap frame\n"); + return; + } + + memset(&hfi, 0, sizeof(hfi)); + + while (1) { + ret = ieee80211_radiotap_iterator_next(&iter); + if (ret == -ENOENT) + break; + if (ret) { + printf("received invalid radiotap frame (%d)\n", ret); + return; + } + switch (iter.this_arg_index) { + case IEEE80211_RADIOTAP_FLAGS: + if (*iter.this_arg & IEEE80211_RADIOTAP_F_FCS) + len -= 4; + break; + case IEEE80211_RADIOTAP_RX_FLAGS: + rxflags = 1; + break; + case IEEE80211_RADIOTAP_TX_FLAGS: + injected = 1; + failed = le_to_host16((*(uint16_t *) iter.this_arg)) & + IEEE80211_RADIOTAP_F_TX_FAIL; + break; + case IEEE80211_RADIOTAP_DATA_RETRIES: + break; + case IEEE80211_RADIOTAP_CHANNEL: + /* TODO convert from freq/flags to channel number + hfi.channel = XXX; + hfi.phytype = XXX; + */ + break; + case IEEE80211_RADIOTAP_RATE: + hfi.datarate = *iter.this_arg * 5; + break; + case IEEE80211_RADIOTAP_DB_ANTSIGNAL: + hfi.ssi_signal = *iter.this_arg; + break; + } + } + + if (rxflags && injected) + return; + + if (!injected) + msg_type = ieee80211_msg_normal; + else if (failed) + msg_type = ieee80211_msg_tx_callback_fail; + else + msg_type = ieee80211_msg_tx_callback_ack; + + handle_frame(hapd->iface, buf + iter.max_length, + len - iter.max_length, &hfi, msg_type); +} + + +/* + * we post-process the filter code later and rewrite + * this to the offset to the last instruction + */ +#define PASS 0xFF +#define FAIL 0xFE + +static struct sock_filter msock_filter_insns[] = { + /* + * do a little-endian load of the radiotap length field + */ + /* load lower byte into A */ + BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 2), + /* put it into X (== index register) */ + BPF_STMT(BPF_MISC| BPF_TAX, 0), + /* load upper byte into A */ + BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 3), + /* left-shift it by 8 */ + BPF_STMT(BPF_ALU | BPF_LSH | BPF_K, 8), + /* or with X */ + BPF_STMT(BPF_ALU | BPF_OR | BPF_X, 0), + /* put result into X */ + BPF_STMT(BPF_MISC| BPF_TAX, 0), + + /* + * Allow management frames through, this also gives us those + * management frames that we sent ourselves with status + */ + /* load the lower byte of the IEEE 802.11 frame control field */ + BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0), + /* mask off frame type and version */ + BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0xF), + /* accept frame if it's both 0, fall through otherwise */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, PASS, 0), + + /* + * TODO: add a bit to radiotap RX flags that indicates + * that the sending station is not associated, then + * add a filter here that filters on our DA and that flag + * to allow us to deauth frames to that bad station. + * + * Not a regression -- we didn't do it before either. + */ + +#if 0 + /* + * drop non-data frames, WDS frames + */ + /* load the lower byte of the frame control field */ + BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0), + /* mask off QoS bit */ + BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x0c), + /* drop non-data frames */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 8, 0, FAIL), + /* load the upper byte of the frame control field */ + BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0), + /* mask off toDS/fromDS */ + BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x03), + /* drop WDS frames */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 3, FAIL, 0), +#endif + + /* + * add header length to index + */ + /* load the lower byte of the frame control field */ + BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0), + /* mask off QoS bit */ + BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x80), + /* right shift it by 6 to give 0 or 2 */ + BPF_STMT(BPF_ALU | BPF_RSH | BPF_K, 6), + /* add data frame header length */ + BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 24), + /* add index, was start of 802.11 header */ + BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0), + /* move to index, now start of LL header */ + BPF_STMT(BPF_MISC | BPF_TAX, 0), + + /* + * Accept empty data frames, we use those for + * polling activity. + */ + BPF_STMT(BPF_LD | BPF_W | BPF_LEN, 0), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_X, 0, PASS, 0), + + /* + * Accept EAPOL frames + */ + BPF_STMT(BPF_LD | BPF_W | BPF_IND, 0), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0xAAAA0300, 0, FAIL), + BPF_STMT(BPF_LD | BPF_W | BPF_IND, 4), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0000888E, PASS, FAIL), + + /* keep these last two statements or change the code below */ + /* return 0 == "DROP" */ + BPF_STMT(BPF_RET | BPF_K, 0), + /* return ~0 == "keep all" */ + BPF_STMT(BPF_RET | BPF_K, ~0), +}; + +static struct sock_fprog msock_filter = { + .len = sizeof(msock_filter_insns)/sizeof(msock_filter_insns[0]), + .filter = msock_filter_insns, +}; + + +static int add_monitor_filter(int s) +{ + int idx; + + /* rewrite all PASS/FAIL jump offsets */ + for (idx = 0; idx < msock_filter.len; idx++) { + struct sock_filter *insn = &msock_filter_insns[idx]; + + if (BPF_CLASS(insn->code) == BPF_JMP) { + if (insn->code == (BPF_JMP|BPF_JA)) { + if (insn->k == PASS) + insn->k = msock_filter.len - idx - 2; + else if (insn->k == FAIL) + insn->k = msock_filter.len - idx - 3; + } + + if (insn->jt == PASS) + insn->jt = msock_filter.len - idx - 2; + else if (insn->jt == FAIL) + insn->jt = msock_filter.len - idx - 3; + + if (insn->jf == PASS) + insn->jf = msock_filter.len - idx - 2; + else if (insn->jf == FAIL) + insn->jf = msock_filter.len - idx - 3; + } + } + + if (setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, + &msock_filter, sizeof(msock_filter))) { + perror("SO_ATTACH_FILTER"); + return -1; + } + + return 0; +} + + +static int nl80211_create_monitor_interface(struct i802_driver_data *drv) +{ + char buf[IFNAMSIZ]; + struct sockaddr_ll ll; + int optval; + socklen_t optlen; + + snprintf(buf, IFNAMSIZ, "mon.%s", drv->iface); + buf[IFNAMSIZ - 1] = '\0'; + + drv->monitor_ifidx = + nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR, NULL); + + if (drv->monitor_ifidx < 0) + return -1; + + if (hostapd_set_iface_flags(drv, buf, 1)) + goto error; + + memset(&ll, 0, sizeof(ll)); + ll.sll_family = AF_PACKET; + ll.sll_ifindex = drv->monitor_ifidx; + drv->monitor_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + if (drv->monitor_sock < 0) { + perror("socket[PF_PACKET,SOCK_RAW]"); + goto error; + } + + if (add_monitor_filter(drv->monitor_sock)) { + wpa_printf(MSG_INFO, "Failed to set socket filter for monitor " + "interface; do filtering in user space"); + /* This works, but will cost in performance. */ + } + + if (bind(drv->monitor_sock, (struct sockaddr *) &ll, + sizeof(ll)) < 0) { + perror("monitor socket bind"); + goto error; + } + + optlen = sizeof(optval); + optval = 20; + if (setsockopt + (drv->monitor_sock, SOL_SOCKET, SO_PRIORITY, &optval, optlen)) { + perror("Failed to set socket priority"); + goto error; + } + + if (eloop_register_read_sock(drv->monitor_sock, handle_monitor_read, + drv, NULL)) { + printf("Could not register monitor read socket\n"); + goto error; + } + + return 0; + error: + nl80211_remove_iface(drv, drv->monitor_ifidx); + return -1; +} + + +static int nl80211_set_master_mode(struct i802_driver_data *drv, + const char *ifname) +{ + struct nl_msg *msg; + int ret = -ENOBUFS; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_SET_INTERFACE, 0); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, + if_nametoindex(ifname)); + NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_AP); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (!ret) + return 0; + nla_put_failure: + wpa_printf(MSG_ERROR, "Failed to set interface %s to master " + "mode.", ifname); + return ret; +} + + +static int i802_init_sockets(struct i802_driver_data *drv, const u8 *bssid) +{ + struct ifreq ifr; + struct sockaddr_ll addr; + + drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->ioctl_sock < 0) { + perror("socket[PF_INET,SOCK_DGRAM]"); + return -1; + } + + /* start listening for EAPOL on the default AP interface */ + add_ifidx(drv, if_nametoindex(drv->iface)); + + if (hostapd_set_iface_flags(drv, drv->iface, 0)) + return -1; + + if (bssid) { + os_strlcpy(ifr.ifr_name, drv->iface, IFNAMSIZ); + memcpy(ifr.ifr_hwaddr.sa_data, bssid, ETH_ALEN); + ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER; + + if (ioctl(drv->ioctl_sock, SIOCSIFHWADDR, &ifr)) { + perror("ioctl(SIOCSIFHWADDR)"); + return -1; + } + } + + /* + * initialise generic netlink and nl80211 + */ + drv->nl_cb = nl_cb_alloc(NL_CB_DEFAULT); + if (!drv->nl_cb) { + printf("Failed to allocate netlink callbacks.\n"); + return -1; + } + + drv->nl_handle = nl_handle_alloc_cb(drv->nl_cb); + if (!drv->nl_handle) { + printf("Failed to allocate netlink handle.\n"); + return -1; + } + + if (genl_connect(drv->nl_handle)) { + printf("Failed to connect to generic netlink.\n"); + return -1; + } + +#ifdef CONFIG_LIBNL20 + if (genl_ctrl_alloc_cache(drv->nl_handle, &drv->nl_cache) < 0) { + printf("Failed to allocate generic netlink cache.\n"); + return -1; + } +#else /* CONFIG_LIBNL20 */ + drv->nl_cache = genl_ctrl_alloc_cache(drv->nl_handle); + if (!drv->nl_cache) { + printf("Failed to allocate generic netlink cache.\n"); + return -1; + } +#endif /* CONFIG_LIBNL20 */ + + drv->nl80211 = genl_ctrl_search_by_name(drv->nl_cache, "nl80211"); + if (!drv->nl80211) { + printf("nl80211 not found.\n"); + return -1; + } + + /* Initialise a monitor interface */ + if (nl80211_create_monitor_interface(drv)) + return -1; + + if (nl80211_set_master_mode(drv, drv->iface)) + goto fail1; + + if (hostapd_set_iface_flags(drv, drv->iface, 1)) + goto fail1; + + memset(&addr, 0, sizeof(addr)); + addr.sll_family = AF_PACKET; + addr.sll_ifindex = ifr.ifr_ifindex; + wpa_printf(MSG_DEBUG, "Opening raw packet socket for ifindex %d", + addr.sll_ifindex); + + drv->eapol_sock = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_PAE)); + if (drv->eapol_sock < 0) { + perror("socket(PF_PACKET, SOCK_DGRAM, ETH_P_PAE)"); + goto fail1; + } + + if (eloop_register_read_sock(drv->eapol_sock, handle_eapol, drv, NULL)) + { + printf("Could not register read socket for eapol\n"); + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, drv->iface, sizeof(ifr.ifr_name)); + if (ioctl(drv->ioctl_sock, SIOCGIFHWADDR, &ifr) != 0) { + perror("ioctl(SIOCGIFHWADDR)"); + goto fail1; + } + + if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { + printf("Invalid HW-addr family 0x%04x\n", + ifr.ifr_hwaddr.sa_family); + goto fail1; + } + memcpy(drv->hapd->own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); + + return 0; + +fail1: + nl80211_remove_iface(drv, drv->monitor_ifidx); + return -1; +} + + +static int i802_get_inact_sec(void *priv, const u8 *addr) +{ + struct hostap_sta_driver_data data; + int ret; + + data.inactive_msec = (unsigned long) -1; + ret = i802_read_sta_data(priv, &data, addr); + if (ret || data.inactive_msec == (unsigned long) -1) + return -1; + return data.inactive_msec / 1000; +} + + +static int i802_sta_clear_stats(void *priv, const u8 *addr) +{ +#if 0 + /* TODO */ +#endif + return 0; +} + + +static void +hostapd_wireless_event_wireless_custom(struct i802_driver_data *drv, + char *custom) +{ + wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom); + + if (strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) { + char *pos; + u8 addr[ETH_ALEN]; + pos = strstr(custom, "addr="); + if (pos == NULL) { + wpa_printf(MSG_DEBUG, + "MLME-MICHAELMICFAILURE.indication " + "without sender address ignored"); + return; + } + pos += 5; + if (hwaddr_aton(pos, addr) == 0) { + ieee80211_michael_mic_failure(drv->hapd, addr, 1); + } else { + wpa_printf(MSG_DEBUG, + "MLME-MICHAELMICFAILURE.indication " + "with invalid MAC address"); + } + } +} + + +static void hostapd_wireless_event_wireless(struct i802_driver_data *drv, + char *data, int len) +{ + struct iw_event iwe_buf, *iwe = &iwe_buf; + char *pos, *end, *custom, *buf; + + pos = data; + end = data + len; + + while (pos + IW_EV_LCP_LEN <= end) { + /* Event data may be unaligned, so make a local, aligned copy + * before processing. */ + memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); + wpa_printf(MSG_DEBUG, "Wireless event: cmd=0x%x len=%d", + iwe->cmd, iwe->len); + if (iwe->len <= IW_EV_LCP_LEN) + return; + + custom = pos + IW_EV_POINT_LEN; + if (drv->we_version > 18 && + (iwe->cmd == IWEVMICHAELMICFAILURE || + iwe->cmd == IWEVCUSTOM)) { + /* WE-19 removed the pointer from struct iw_point */ + char *dpos = (char *) &iwe_buf.u.data.length; + int dlen = dpos - (char *) &iwe_buf; + memcpy(dpos, pos + IW_EV_LCP_LEN, + sizeof(struct iw_event) - dlen); + } else { + memcpy(&iwe_buf, pos, sizeof(struct iw_event)); + custom += IW_EV_POINT_OFF; + } + + switch (iwe->cmd) { + case IWEVCUSTOM: + if (custom + iwe->u.data.length > end) + return; + buf = malloc(iwe->u.data.length + 1); + if (buf == NULL) + return; + memcpy(buf, custom, iwe->u.data.length); + buf[iwe->u.data.length] = '\0'; + hostapd_wireless_event_wireless_custom(drv, buf); + free(buf); + break; + } + + pos += iwe->len; + } +} + + +static void hostapd_wireless_event_rtm_newlink(struct i802_driver_data *drv, + struct nlmsghdr *h, int len) +{ + struct ifinfomsg *ifi; + int attrlen, _nlmsg_len, rta_len; + struct rtattr *attr; + + if (len < (int) sizeof(*ifi)) + return; + + ifi = NLMSG_DATA(h); + + /* TODO: use ifi->ifi_index to filter out wireless events from other + * interfaces */ + + _nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); + + attrlen = h->nlmsg_len - _nlmsg_len; + if (attrlen < 0) + return; + + attr = (struct rtattr *) (((char *) ifi) + _nlmsg_len); + + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + if (attr->rta_type == IFLA_WIRELESS) { + hostapd_wireless_event_wireless( + drv, ((char *) attr) + rta_len, + attr->rta_len - rta_len); + } + attr = RTA_NEXT(attr, attrlen); + } +} + + +static void hostapd_wireless_event_receive(int sock, void *eloop_ctx, + void *sock_ctx) +{ + char buf[256]; + int left; + struct sockaddr_nl from; + socklen_t fromlen; + struct nlmsghdr *h; + struct i802_driver_data *drv = eloop_ctx; + + fromlen = sizeof(from); + left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, + (struct sockaddr *) &from, &fromlen); + if (left < 0) { + if (errno != EINTR && errno != EAGAIN) + perror("recvfrom(netlink)"); + return; + } + + h = (struct nlmsghdr *) buf; + while (left >= (int) sizeof(*h)) { + int len, plen; + + len = h->nlmsg_len; + plen = len - sizeof(*h); + if (len > left || plen < 0) { + printf("Malformed netlink message: " + "len=%d left=%d plen=%d\n", + len, left, plen); + break; + } + + switch (h->nlmsg_type) { + case RTM_NEWLINK: + hostapd_wireless_event_rtm_newlink(drv, h, plen); + break; + } + + len = NLMSG_ALIGN(len); + left -= len; + h = (struct nlmsghdr *) ((char *) h + len); + } + + if (left > 0) { + printf("%d extra bytes in the end of netlink message\n", left); + } +} + + +static int hostap_get_we_version(struct i802_driver_data *drv) +{ + struct iw_range *range; + struct iwreq iwr; + int minlen; + size_t buflen; + + drv->we_version = 0; + + /* + * Use larger buffer than struct iw_range in order to allow the + * structure to grow in the future. + */ + buflen = sizeof(struct iw_range) + 500; + range = os_zalloc(buflen); + if (range == NULL) + return -1; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) range; + iwr.u.data.length = buflen; + + minlen = ((char *) &range->enc_capa) - (char *) range + + sizeof(range->enc_capa); + + if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) { + perror("ioctl[SIOCGIWRANGE]"); + free(range); + return -1; + } else if (iwr.u.data.length >= minlen && + range->we_version_compiled >= 18) { + wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d " + "WE(source)=%d enc_capa=0x%x", + range->we_version_compiled, + range->we_version_source, + range->enc_capa); + drv->we_version = range->we_version_compiled; + } + + free(range); + return 0; +} + + +static int i802_wireless_event_init(void *priv) +{ + struct i802_driver_data *drv = priv; + int s; + struct sockaddr_nl local; + + hostap_get_we_version(drv); + + drv->wext_sock = -1; + + s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (s < 0) { + perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)"); + return -1; + } + + memset(&local, 0, sizeof(local)); + local.nl_family = AF_NETLINK; + local.nl_groups = RTMGRP_LINK; + if (bind(s, (struct sockaddr *) &local, sizeof(local)) < 0) { + perror("bind(netlink)"); + close(s); + return -1; + } + + eloop_register_read_sock(s, hostapd_wireless_event_receive, drv, + NULL); + drv->wext_sock = s; + + return 0; +} + + +static void i802_wireless_event_deinit(void *priv) +{ + struct i802_driver_data *drv = priv; + if (drv->wext_sock < 0) + return; + eloop_unregister_read_sock(drv->wext_sock); + close(drv->wext_sock); +} + + +static int i802_sta_deauth(void *priv, const u8 *addr, int reason) +{ + struct i802_driver_data *drv = priv; + struct ieee80211_mgmt mgmt; + + memset(&mgmt, 0, sizeof(mgmt)); + mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_DEAUTH); + memcpy(mgmt.da, addr, ETH_ALEN); + memcpy(mgmt.sa, drv->hapd->own_addr, ETH_ALEN); + memcpy(mgmt.bssid, drv->hapd->own_addr, ETH_ALEN); + mgmt.u.deauth.reason_code = host_to_le16(reason); + return i802_send_mgmt_frame(drv, &mgmt, IEEE80211_HDRLEN + + sizeof(mgmt.u.deauth), 0); +} + + +static int i802_sta_disassoc(void *priv, const u8 *addr, int reason) +{ + struct i802_driver_data *drv = priv; + struct ieee80211_mgmt mgmt; + + memset(&mgmt, 0, sizeof(mgmt)); + mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_DISASSOC); + memcpy(mgmt.da, addr, ETH_ALEN); + memcpy(mgmt.sa, drv->hapd->own_addr, ETH_ALEN); + memcpy(mgmt.bssid, drv->hapd->own_addr, ETH_ALEN); + mgmt.u.disassoc.reason_code = host_to_le16(reason); + return i802_send_mgmt_frame(drv, &mgmt, IEEE80211_HDRLEN + + sizeof(mgmt.u.disassoc), 0); +} + + +static void *i802_init_bssid(struct hostapd_data *hapd, const u8 *bssid) +{ + struct i802_driver_data *drv; + + drv = os_zalloc(sizeof(struct i802_driver_data)); + if (drv == NULL) { + printf("Could not allocate memory for i802 driver data\n"); + return NULL; + } + + drv->hapd = hapd; + memcpy(drv->iface, hapd->conf->iface, sizeof(drv->iface)); + + drv->num_if_indices = sizeof(drv->default_if_indices) / sizeof(int); + drv->if_indices = drv->default_if_indices; + drv->bridge = if_nametoindex(hapd->conf->bridge); + + if (i802_init_sockets(drv, bssid)) + goto failed; + + return drv; + +failed: + free(drv); + return NULL; +} + + +static void *i802_init(struct hostapd_data *hapd) +{ + return i802_init_bssid(hapd, NULL); +} + + +static void i802_deinit(void *priv) +{ + struct i802_driver_data *drv = priv; + + if (drv->last_freq_ht) { + /* Clear HT flags from the driver */ + struct hostapd_freq_params freq; + os_memset(&freq, 0, sizeof(freq)); + freq.freq = drv->last_freq; + i802_set_freq2(priv, &freq); + } + + i802_del_beacon(drv); + + /* remove monitor interface */ + nl80211_remove_iface(drv, drv->monitor_ifidx); + + (void) hostapd_set_iface_flags(drv, drv->iface, 0); + + if (drv->monitor_sock >= 0) { + eloop_unregister_read_sock(drv->monitor_sock); + close(drv->monitor_sock); + } + if (drv->ioctl_sock >= 0) + close(drv->ioctl_sock); + if (drv->eapol_sock >= 0) { + eloop_unregister_read_sock(drv->eapol_sock); + close(drv->eapol_sock); + } + + genl_family_put(drv->nl80211); + nl_cache_free(drv->nl_cache); + nl_handle_destroy(drv->nl_handle); + nl_cb_put(drv->nl_cb); + + if (drv->if_indices != drv->default_if_indices) + free(drv->if_indices); + + free(drv); +} + + +const struct wpa_driver_ops wpa_driver_nl80211_ops = { + .name = "nl80211", + .init = i802_init, + .init_bssid = i802_init_bssid, + .deinit = i802_deinit, + .wireless_event_init = i802_wireless_event_init, + .wireless_event_deinit = i802_wireless_event_deinit, + .set_ieee8021x = i802_set_ieee8021x, + .set_privacy = i802_set_privacy, + .set_encryption = i802_set_encryption, + .get_seqnum = i802_get_seqnum, + .flush = i802_flush, + .read_sta_data = i802_read_sta_data, + .send_eapol = i802_send_eapol, + .sta_set_flags = i802_sta_set_flags, + .sta_deauth = i802_sta_deauth, + .sta_disassoc = i802_sta_disassoc, + .sta_remove = i802_sta_remove, + .send_mgmt_frame = i802_send_mgmt_frame, + .sta_add2 = i802_sta_add2, + .get_inact_sec = i802_get_inact_sec, + .sta_clear_stats = i802_sta_clear_stats, + .set_freq2 = i802_set_freq2, + .set_rts = i802_set_rts, + .get_rts = i802_get_rts, + .set_frag = i802_set_frag, + .get_frag = i802_get_frag, + .set_retry = i802_set_retry, + .get_retry = i802_get_retry, + .set_rate_sets = i802_set_rate_sets, + .set_regulatory_domain = i802_set_regulatory_domain, + .set_beacon = i802_set_beacon, + .set_internal_bridge = i802_set_internal_bridge, + .set_beacon_int = i802_set_beacon_int, + .set_dtim_period = i802_set_dtim_period, + .set_cts_protect = i802_set_cts_protect, + .set_preamble = i802_set_preamble, + .set_short_slot_time = i802_set_short_slot_time, + .set_tx_queue_params = i802_set_tx_queue_params, + .bss_add = i802_bss_add, + .bss_remove = i802_bss_remove, + .if_add = i802_if_add, + .if_update = i802_if_update, + .if_remove = i802_if_remove, + .get_hw_feature_data = i802_get_hw_feature_data, + .set_sta_vlan = i802_set_sta_vlan, + .set_country = i802_set_country, +}; diff --git a/hostapd/driver_none.c b/hostapd/driver_none.c new file mode 100644 index 0000000..96e7e64 --- /dev/null +++ b/hostapd/driver_none.c @@ -0,0 +1,62 @@ +/* + * hostapd / Driver interface for RADIUS server only (no driver) + * Copyright (c) 2008, Atheros Communications + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "hostapd.h" +#include "driver.h" + + +struct none_driver_data { + struct hostapd_data *hapd; +}; + + +static void * none_driver_init(struct hostapd_data *hapd) +{ + struct none_driver_data *drv; + + drv = os_zalloc(sizeof(struct none_driver_data)); + if (drv == NULL) { + wpa_printf(MSG_ERROR, "Could not allocate memory for none " + "driver data"); + return NULL; + } + drv->hapd = hapd; + + return drv; +} + + +static void none_driver_deinit(void *priv) +{ + struct none_driver_data *drv = priv; + + os_free(drv); +} + + +static int none_driver_send_ether(void *priv, const u8 *dst, const u8 *src, + u16 proto, const u8 *data, size_t data_len) +{ + return 0; +} + + +const struct wpa_driver_ops wpa_driver_none_ops = { + .name = "none", + .init = none_driver_init, + .deinit = none_driver_deinit, + .send_ether = none_driver_send_ether, +}; diff --git a/hostapd/driver_prism54.c b/hostapd/driver_prism54.c new file mode 100644 index 0000000..4e2189a --- /dev/null +++ b/hostapd/driver_prism54.c @@ -0,0 +1,1091 @@ +/* + * hostapd / Driver interaction with Prism54 PIMFOR interface + * Copyright (c) 2004, Bell Kin <bell_kin@pek.com.tw> + * based on hostap driver.c, ieee802_11.c + * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <sys/ioctl.h> +#include <sys/select.h> + +#ifdef USE_KERNEL_HEADERS +/* compat-wireless does not include linux/compiler.h to define __user, so + * define it here */ +#ifndef __user +#define __user +#endif /* __user */ +#include <asm/types.h> +#include <linux/if_packet.h> +#include <linux/if_ether.h> /* The L2 protocols */ +#include <linux/if_arp.h> +#include <linux/wireless.h> +#else /* USE_KERNEL_HEADERS */ +#include <net/if_arp.h> +#include <netpacket/packet.h> +#include "wireless_copy.h" +#endif /* USE_KERNEL_HEADERS */ + +#include "hostapd.h" +#include "driver.h" +#include "ieee802_1x.h" +#include "eloop.h" +#include "ieee802_11.h" +#include "prism54.h" +#include "wpa.h" +#include "radius/radius.h" +#include "sta_info.h" +#include "accounting.h" + +const int PIM_BUF_SIZE = 4096; + +struct prism54_driver_data { + struct hostapd_data *hapd; + char iface[IFNAMSIZ + 1]; + int sock; /* raw packet socket for 802.3 access */ + int pim_sock; /* socket for pimfor packet */ + char macs[2007][6]; +}; + + +static int mac_id_refresh(struct prism54_driver_data *data, int id, char *mac) +{ + if (id < 0 || id > 2006) { + return -1; + } + memcpy(&data->macs[id][0], mac, ETH_ALEN); + return 0; +} + + +static char * mac_id_get(struct prism54_driver_data *data, int id) +{ + if (id < 0 || id > 2006) { + return NULL; + } + return &data->macs[id][0]; +} + + +/* wait for a specific pimfor, timeout in 10ms resolution */ +/* pim_sock must be non-block to prevent dead lock from no response */ +/* or same response type in series */ +static int prism54_waitpim(void *priv, unsigned long oid, void *buf, int len, + int timeout) +{ + struct prism54_driver_data *drv = priv; + struct timeval tv, stv, ctv; + fd_set pfd; + int rlen; + pimdev_hdr *pkt; + + pkt = malloc(8192); + if (pkt == NULL) + return -1; + + FD_ZERO(&pfd); + gettimeofday(&stv, NULL); + do { + FD_SET(drv->pim_sock, &pfd); + tv.tv_sec = 0; + tv.tv_usec = 10000; + if (select(drv->pim_sock + 1, &pfd, NULL, NULL, &tv)) { + rlen = recv(drv->pim_sock, pkt, 8192, 0); + if (rlen > 0) { + if (pkt->oid == htonl(oid)) { + if (rlen <= len) { + if (buf != NULL) { + memcpy(buf, pkt, rlen); + } + free(pkt); + return rlen; + } else { + printf("buffer too small\n"); + free(pkt); + return -1; + } + } else { + gettimeofday(&ctv, NULL); + continue; + } + } + } + gettimeofday(&ctv, NULL); + } while (((ctv.tv_sec - stv.tv_sec) * 100 + + (ctv.tv_usec - stv.tv_usec) / 10000) > timeout); + free(pkt); + return 0; +} + + +/* send an eapol packet */ +static int prism54_send_eapol(void *priv, const u8 *addr, + const u8 *data, size_t data_len, int encrypt, + const u8 *own_addr) +{ + struct prism54_driver_data *drv = priv; + ieee802_3_hdr *hdr; + size_t len; + u8 *pos; + int res; + + len = sizeof(*hdr) + data_len; + hdr = os_zalloc(len); + if (hdr == NULL) { + printf("malloc() failed for prism54_send_data(len=%lu)\n", + (unsigned long) len); + return -1; + } + + memcpy(&hdr->da[0], addr, ETH_ALEN); + memcpy(&hdr->sa[0], own_addr, ETH_ALEN); + hdr->type = htons(ETH_P_PAE); + pos = (u8 *) (hdr + 1); + memcpy(pos, data, data_len); + + res = send(drv->sock, hdr, len, 0); + free(hdr); + + if (res < 0) { + perror("hostapd_send_eapol: send"); + printf("hostapd_send_eapol - packet len: %lu - failed\n", + (unsigned long) len); + } + + return res; +} + + +/* open data channel(auth-1) or eapol only(unauth-0) */ +static int prism54_set_sta_authorized(void *priv, const u8 *addr, + int authorized) +{ + struct prism54_driver_data *drv = priv; + pimdev_hdr *hdr; + char *pos; + + hdr = malloc(sizeof(*hdr) + ETH_ALEN); + if (hdr == NULL) + return -1; + hdr->op = htonl(PIMOP_SET); + if (authorized) { + hdr->oid = htonl(DOT11_OID_EAPAUTHSTA); + } else { + hdr->oid = htonl(DOT11_OID_EAPUNAUTHSTA); + } + pos = (char *) (hdr + 1); + memcpy(pos, addr, ETH_ALEN); + send(drv->pim_sock, hdr, sizeof(*hdr) + ETH_ALEN, 0); + prism54_waitpim(priv, hdr->oid, hdr, sizeof(*hdr) + ETH_ALEN, 10); + free(hdr); + return 0; +} + + +static int +prism54_sta_set_flags(void *priv, const u8 *addr, int total_flags, + int flags_or, int flags_and) +{ + /* For now, only support setting Authorized flag */ + if (flags_or & WLAN_STA_AUTHORIZED) + return prism54_set_sta_authorized(priv, addr, 1); + if (flags_and & WLAN_STA_AUTHORIZED) + return prism54_set_sta_authorized(priv, addr, 0); + return 0; +} + + +/* set per station key */ +static int prism54_set_encryption(const char *ifname, void *priv, + const char *alg, const u8 *addr, + int idx, const u8 *key, size_t key_len, + int txkey) +{ + struct prism54_driver_data *drv = priv; + pimdev_hdr *hdr; + struct obj_stakey *keys; + u8 *buf; + size_t blen; + int ret = 0; + + blen = sizeof(struct obj_stakey) + sizeof(pimdev_hdr); + hdr = malloc(blen); + if (hdr == NULL) { + printf("memory low\n"); + return -1; + } + keys = (struct obj_stakey *) &hdr[1]; + if (!addr) { + memset(&keys->address[0], 0xff, ETH_ALEN); + } else { + memcpy(&keys->address[0], addr, ETH_ALEN); + } + if (!strcmp(alg, "WEP")) { + keys->type = DOT11_PRIV_WEP; + } else if (!strcmp(alg, "TKIP")) { + keys->type = DOT11_PRIV_TKIP; + } else if (!strcmp(alg, "none")) { + /* the only way to clear the key is to deauth it */ + /* and prism54 is capable to receive unencrypted packet */ + /* so we do nothing here */ + free(hdr); + return 0; + } else { + printf("bad auth type: %s\n", alg); + } + buf = (u8 *) &keys->key[0]; + keys->length = key_len; + keys->keyid = idx; + keys->options = htons(DOT11_STAKEY_OPTION_DEFAULTKEY); + keys->reserved = 0; + + hdr->op = htonl(PIMOP_SET); + hdr->oid = htonl(DOT11_OID_STAKEY); + + memcpy(buf, key, key_len); + + ret = send(drv->pim_sock, hdr, blen, 0); + if (ret < 0) { + free(hdr); + return ret; + } + prism54_waitpim(priv, hdr->oid, hdr, blen, 10); + + free(hdr); + + return 0; +} + + +/* get TKIP station sequence counter, prism54 is only 6 bytes */ +static int prism54_get_seqnum(const char *ifname, void *priv, const u8 *addr, + int idx, u8 *seq) +{ + struct prism54_driver_data *drv = priv; + struct obj_stasc *stasc; + pimdev_hdr *hdr; + size_t blen; + int ret = 0; + + blen = sizeof(*stasc) + sizeof(*hdr); + hdr = malloc(blen); + if (hdr == NULL) + return -1; + + stasc = (struct obj_stasc *) &hdr[1]; + + if (addr == NULL) + memset(&stasc->address[0], 0xff, ETH_ALEN); + else + memcpy(&stasc->address[0], addr, ETH_ALEN); + + hdr->oid = htonl(DOT11_OID_STASC); + hdr->op = htonl(PIMOP_GET); + stasc->keyid = idx; + if (send(drv->pim_sock,hdr,blen,0) <= 0) { + free(hdr); + return -1; + } + if (prism54_waitpim(priv, DOT11_OID_STASC, hdr, blen, 10) <= 0) { + ret = -1; + } else { + if (hdr->op == (int) htonl(PIMOP_RESPONSE)) { + memcpy(seq + 2, &stasc->sc_high, ETH_ALEN); + memset(seq, 0, 2); + } else { + ret = -1; + } + } + free(hdr); + + return ret; +} + + +/* include unencrypted, set mlme autolevel to extended */ +static int prism54_init_1x(void *priv) +{ + struct prism54_driver_data *drv = priv; + pimdev_hdr *hdr; + unsigned long *ul; + int blen = sizeof(*hdr) + sizeof(*ul); + + hdr = malloc(blen); + if (hdr == NULL) + return -1; + + ul = (unsigned long *) &hdr[1]; + hdr->op = htonl(PIMOP_SET); + hdr->oid = htonl(DOT11_OID_EXUNENCRYPTED); + *ul = htonl(DOT11_BOOL_TRUE); /* not accept */ + send(drv->pim_sock, hdr, blen, 0); + prism54_waitpim(priv, DOT11_OID_EXUNENCRYPTED, hdr, blen, 10); + hdr->op = htonl(PIMOP_SET); + hdr->oid = htonl(DOT11_OID_MLMEAUTOLEVEL); + *ul = htonl(DOT11_MLME_EXTENDED); + send(drv->pim_sock, hdr, blen, 0); + prism54_waitpim(priv, DOT11_OID_MLMEAUTOLEVEL, hdr, blen, 10); + hdr->op = htonl(PIMOP_SET); + hdr->oid = htonl(DOT11_OID_DOT1XENABLE); + *ul = htonl(DOT11_BOOL_TRUE); + send(drv->pim_sock, hdr, blen, 0); + prism54_waitpim(priv, DOT11_OID_DOT1XENABLE, hdr, blen, 10); + hdr->op = htonl(PIMOP_SET); + hdr->oid = htonl(DOT11_OID_AUTHENABLE); + *ul = htonl(DOT11_AUTH_OS); /* OS */ + send(drv->pim_sock, hdr, blen, 0); + prism54_waitpim(priv, DOT11_OID_AUTHENABLE, hdr, blen, 10); + free(hdr); + return 0; +} + + +static int prism54_set_privacy_invoked(const char *ifname, void *priv, + int flag) +{ + struct prism54_driver_data *drv = priv; + pimdev_hdr *hdr; + unsigned long *ul; + int ret; + int blen = sizeof(*hdr) + sizeof(*ul); + hdr = malloc(blen); + if (hdr == NULL) + return -1; + ul = (unsigned long *) &hdr[1]; + hdr->op = htonl(PIMOP_SET); + hdr->oid = htonl(DOT11_OID_PRIVACYINVOKED); + if (flag) { + *ul = htonl(DOT11_BOOL_TRUE); /* has privacy */ + } else { + *ul = 0; + } + ret = send(drv->pim_sock, hdr, blen, 0); + if (ret >= 0) { + ret = prism54_waitpim(priv, DOT11_OID_PRIVACYINVOKED, hdr, + blen, 10); + } + free(hdr); + return ret; +} + + +static int prism54_ioctl_setiwessid(const char *ifname, void *priv, + const u8 *buf, int len) +{ +#if 0 + struct prism54_driver_data *drv = priv; + struct iwreq iwr; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.essid.flags = 1; /* SSID active */ + iwr.u.essid.pointer = (caddr_t) buf; + iwr.u.essid.length = len + 1; + + if (ioctl(drv->pim_sock, SIOCSIWESSID, &iwr) < 0) { + perror("ioctl[SIOCSIWESSID]"); + printf("len=%d\n", len); + return -1; + } +#endif + return 0; +} + + +/* kick all stations */ +/* does not work during init, but at least it won't crash firmware */ +static int prism54_flush(void *priv) +{ + struct prism54_driver_data *drv = priv; + struct obj_mlmeex *mlme; + pimdev_hdr *hdr; + int ret; + unsigned int i; + long *nsta; + int blen = sizeof(*hdr) + sizeof(*mlme); + char *mac_id; + + hdr = os_zalloc(blen); + if (hdr == NULL) + return -1; + + mlme = (struct obj_mlmeex *) &hdr[1]; + nsta = (long *) &hdr[1]; + hdr->op = htonl(PIMOP_GET); + hdr->oid = htonl(DOT11_OID_CLIENTS); + ret = send(drv->pim_sock, hdr, sizeof(*hdr) + sizeof(long), 0); + ret = prism54_waitpim(priv, DOT11_OID_CLIENTS, hdr, blen, 10); + if ((ret < 0) || (hdr->op != (int) htonl(PIMOP_RESPONSE)) || + (le_to_host32(*nsta) > 2007)) { + free(hdr); + return 0; + } + for (i = 0; i < le_to_host32(*nsta); i++) { + mlme->id = -1; + mac_id = mac_id_get(drv, i); + if (mac_id) + memcpy(&mlme->address[0], mac_id, ETH_ALEN); + mlme->code = host_to_le16(WLAN_REASON_UNSPECIFIED); + mlme->state = htons(DOT11_STATE_NONE); + mlme->size = 0; + hdr->op = htonl(PIMOP_SET); + hdr->oid = htonl(DOT11_OID_DISASSOCIATEEX); + ret = send(drv->pim_sock, hdr, blen, 0); + prism54_waitpim(priv, DOT11_OID_DISASSOCIATEEX, hdr, blen, + 100); + } + for (i = 0; i < le_to_host32(*nsta); i++) { + mlme->id = -1; + mac_id = mac_id_get(drv, i); + if (mac_id) + memcpy(&mlme->address[0], mac_id, ETH_ALEN); + mlme->code = host_to_le16(WLAN_REASON_UNSPECIFIED); + mlme->state = htons(DOT11_STATE_NONE); + mlme->size = 0; + hdr->op = htonl(PIMOP_SET); + hdr->oid = htonl(DOT11_OID_DEAUTHENTICATEEX); + ret = send(drv->pim_sock, hdr, blen, 0); + prism54_waitpim(priv, DOT11_OID_DEAUTHENTICATEEX, hdr, blen, + 100); + } + free(hdr); + return 0; +} + + +static int prism54_sta_deauth(void *priv, const u8 *addr, int reason) +{ + struct prism54_driver_data *drv = priv; + pimdev_hdr *hdr; + struct obj_mlmeex *mlme; + int ret; + int blen = sizeof(*hdr) + sizeof(*mlme); + hdr = malloc(blen); + if (hdr == NULL) + return -1; + mlme = (struct obj_mlmeex *) &hdr[1]; + hdr->op = htonl(PIMOP_SET); + hdr->oid = htonl(DOT11_OID_DEAUTHENTICATEEX); + memcpy(&mlme->address[0], addr, ETH_ALEN); + mlme->id = -1; + mlme->state = htons(DOT11_STATE_NONE); + mlme->code = host_to_le16(reason); + mlme->size = 0; + ret = send(drv->pim_sock, hdr, blen, 0); + prism54_waitpim(priv, DOT11_OID_DEAUTHENTICATEEX, hdr, blen, 10); + free(hdr); + return ret; +} + + +static int prism54_sta_disassoc(void *priv, const u8 *addr, int reason) +{ + struct prism54_driver_data *drv = priv; + pimdev_hdr *hdr; + struct obj_mlmeex *mlme; + int ret; + int blen = sizeof(*hdr) + sizeof(*mlme); + hdr = malloc(blen); + if (hdr == NULL) + return -1; + mlme = (struct obj_mlmeex *) &hdr[1]; + hdr->op = htonl(PIMOP_SET); + hdr->oid = htonl(DOT11_OID_DISASSOCIATEEX); + memcpy(&mlme->address[0], addr, ETH_ALEN); + mlme->id = -1; + mlme->state = htons(DOT11_STATE_NONE); + mlme->code = host_to_le16(reason); + mlme->size = 0; + ret = send(drv->pim_sock, hdr, blen, 0); + prism54_waitpim(priv, DOT11_OID_DISASSOCIATEEX, hdr, blen, 10); + free(hdr); + return ret; +} + + +static int prism54_get_inact_sec(void *priv, const u8 *addr) +{ + struct prism54_driver_data *drv = priv; + pimdev_hdr *hdr; + struct obj_sta *sta; + int blen = sizeof(*hdr) + sizeof(*sta); + int ret; + + hdr = malloc(blen); + if (hdr == NULL) + return -1; + hdr->op = htonl(PIMOP_GET); + hdr->oid = htonl(DOT11_OID_CLIENTFIND); + sta = (struct obj_sta *) &hdr[1]; + memcpy(&sta->address[0], addr, ETH_ALEN); + ret = send(drv->pim_sock, hdr, blen, 0); + ret = prism54_waitpim(priv, DOT11_OID_CLIENTFIND, hdr, blen, 10); + if (ret != blen) { + printf("get_inact_sec: bad return %d\n", ret); + free(hdr); + return -1; + } + if (hdr->op != (int) htonl(PIMOP_RESPONSE)) { + printf("get_inact_sec: bad resp\n"); + free(hdr); + return -1; + } + free(hdr); + return le_to_host16(sta->age); +} + + +/* set attachments */ +static int prism54_set_generic_elem(const char *ifname, void *priv, + const u8 *elem, size_t elem_len) +{ + struct prism54_driver_data *drv = priv; + pimdev_hdr *hdr; + char *pos; + struct obj_attachment_hdr *attach; + size_t blen = sizeof(*hdr) + sizeof(*attach) + elem_len; + hdr = os_zalloc(blen); + if (hdr == NULL) { + printf("%s: memory low\n", __func__); + return -1; + } + hdr->op = htonl(PIMOP_SET); + hdr->oid = htonl(DOT11_OID_ATTACHMENT); + attach = (struct obj_attachment_hdr *)&hdr[1]; + attach->type = DOT11_PKT_BEACON; + attach->id = -1; + attach->size = host_to_le16((short)elem_len); + pos = ((char*) attach) + sizeof(*attach); + if (elem) + memcpy(pos, elem, elem_len); + send(drv->pim_sock, hdr, blen, 0); + attach->type = DOT11_PKT_PROBE_RESP; + send(drv->pim_sock, hdr, blen, 0); + free(hdr); + return 0; +} + + +/* tell the card to auth the sta */ +static void prism54_handle_probe(struct prism54_driver_data *drv, + void *buf, size_t len) +{ + struct obj_mlmeex *mlme; + pimdev_hdr *hdr; + struct sta_info *sta; + hdr = (pimdev_hdr *)buf; + mlme = (struct obj_mlmeex *) &hdr[1]; + sta = ap_get_sta(drv->hapd, (u8 *) &mlme->address[0]); + if (sta != NULL) { + if (sta->flags & (WLAN_STA_AUTH | WLAN_STA_ASSOC)) + return; + } + if (len < sizeof(*mlme)) { + printf("bad probe packet\n"); + return; + } + mlme->state = htons(DOT11_STATE_AUTHING); + mlme->code = 0; + hdr->op = htonl(PIMOP_SET); + hdr->oid = htonl(DOT11_OID_AUTHENTICATEEX); + mlme->size = 0; + send(drv->pim_sock, hdr, sizeof(*hdr)+sizeof(*mlme), 0); +} + + +static void prism54_handle_deauth(struct prism54_driver_data *drv, + void *buf, size_t len) +{ + struct obj_mlme *mlme; + pimdev_hdr *hdr; + struct sta_info *sta; + char *mac_id; + + hdr = (pimdev_hdr *) buf; + mlme = (struct obj_mlme *) &hdr[1]; + sta = ap_get_sta(drv->hapd, (u8 *) &mlme->address[0]); + mac_id = mac_id_get(drv, mlme->id); + if (sta == NULL || mac_id == NULL) + return; + memcpy(&mlme->address[0], mac_id, ETH_ALEN); + sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); + wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH); + sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST; + ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); + ap_free_sta(drv->hapd, sta); +} + + +static void prism54_handle_disassoc(struct prism54_driver_data *drv, + void *buf, size_t len) +{ + struct obj_mlme *mlme; + pimdev_hdr *hdr; + struct sta_info *sta; + char *mac_id; + + hdr = (pimdev_hdr *) buf; + mlme = (struct obj_mlme *) &hdr[1]; + mac_id = mac_id_get(drv, mlme->id); + if (mac_id == NULL) + return; + memcpy(&mlme->address[0], mac_id, ETH_ALEN); + sta = ap_get_sta(drv->hapd, (u8 *) &mlme->address[0]); + if (sta == NULL) { + return; + } + sta->flags &= ~WLAN_STA_ASSOC; + wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC); + sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST; + ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); + accounting_sta_stop(drv->hapd, sta); + ieee802_1x_free_station(sta); +} + + +/* to auth it, just allow it now, later for os/sk */ +static void prism54_handle_auth(struct prism54_driver_data *drv, + void *buf, size_t len) +{ + struct obj_mlmeex *mlme; + pimdev_hdr *hdr; + struct sta_info *sta; + int resp; + + hdr = (pimdev_hdr *) buf; + mlme = (struct obj_mlmeex *) &hdr[1]; + if (len < sizeof(*mlme)) { + printf("bad auth packet\n"); + return; + } + + if (mlme->state == htons(DOT11_STATE_AUTHING)) { + sta = ap_sta_add(drv->hapd, (u8 *) &mlme->address[0]); + if (drv->hapd->tkip_countermeasures) { + resp = WLAN_REASON_MICHAEL_MIC_FAILURE; + goto fail; + } + mac_id_refresh(drv, mlme->id, &mlme->address[0]); + if (!sta) { + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + sta->flags &= ~WLAN_STA_PREAUTH; + + ieee802_1x_notify_pre_auth(sta->eapol_sm, 0); + sta->flags |= WLAN_STA_AUTH; + wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); + mlme->code = 0; + mlme->state=htons(DOT11_STATE_AUTH); + hdr->op = htonl(PIMOP_SET); + hdr->oid = htonl(DOT11_OID_AUTHENTICATEEX); + mlme->size = 0; + sta->timeout_next = STA_NULLFUNC; + send(drv->pim_sock, hdr, sizeof(*hdr) + sizeof(*mlme), 0); + } + return; + +fail: + printf("auth fail: %x\n", resp); + mlme->code = host_to_le16(resp); + mlme->size = 0; + if (sta) + sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); + hdr->oid = htonl(DOT11_OID_DEAUTHENTICATEEX); + hdr->op = htonl(PIMOP_SET); + send(drv->pim_sock, hdr, sizeof(*hdr)+sizeof(*mlme), 0); +} + + +/* do the wpa thing */ +static void prism54_handle_assoc(struct prism54_driver_data *drv, + void *buf, size_t len) +{ + pimdev_hdr *hdr; + struct obj_mlmeex *mlme; + struct ieee802_11_elems elems; + struct sta_info *sta; + u8 *wpa_ie; + u8 *cb; + int ieofs = 0; + size_t wpa_ie_len; + int resp, new_assoc; + char *mac_id; + + resp = 0; + hdr = (pimdev_hdr *) buf; + mlme = (struct obj_mlmeex *) &hdr[1]; + switch (ntohl(hdr->oid)) { + case DOT11_OID_ASSOCIATE: + case DOT11_OID_REASSOCIATE: + mlme->size = 0; + default: + break; + } + if ((mlme->state == (int) htonl(DOT11_STATE_ASSOCING)) || + (mlme->state == (int) htonl(DOT11_STATE_REASSOCING))) { + if (len < sizeof(pimdev_hdr) + sizeof(struct obj_mlme)) { + printf("bad assoc packet\n"); + return; + } + mac_id = mac_id_get(drv, mlme->id); + if (mac_id == NULL) + return; + memcpy(&mlme->address[0], mac_id, ETH_ALEN); + sta = ap_get_sta(drv->hapd, (u8 *) &mlme->address[0]); + if (sta == NULL) { + printf("cannot get sta\n"); + return; + } + cb = (u8 *) &mlme->data[0]; + if (hdr->oid == htonl(DOT11_OID_ASSOCIATEEX)) { + ieofs = 4; + } else if (hdr->oid == htonl(DOT11_OID_REASSOCIATEEX)) { + ieofs = 10; + } + if (le_to_host16(mlme->size) <= ieofs) { + printf("attach too small\n"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + if (ieee802_11_parse_elems(cb + ieofs, + le_to_host16(mlme->size) - ieofs, + &elems, 1) == ParseFailed) { + printf("STA " MACSTR " sent invalid association " + "request\n", MAC2STR(sta->addr)); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + if ((drv->hapd->conf->wpa & WPA_PROTO_RSN) && + elems.rsn_ie) { + wpa_ie = elems.rsn_ie; + wpa_ie_len = elems.rsn_ie_len; + } else if ((drv->hapd->conf->wpa & WPA_PROTO_WPA) && + elems.wpa_ie) { + wpa_ie = elems.wpa_ie; + wpa_ie_len = elems.wpa_ie_len; + } else { + wpa_ie = NULL; + wpa_ie_len = 0; + } + if (drv->hapd->conf->wpa && wpa_ie == NULL) { + printf("STA " MACSTR ": No WPA/RSN IE in association " + "request\n", MAC2STR(sta->addr)); + resp = WLAN_STATUS_INVALID_IE; + goto fail; + } + if (drv->hapd->conf->wpa) { + int res; + wpa_ie -= 2; + wpa_ie_len += 2; + if (sta->wpa_sm == NULL) + sta->wpa_sm = wpa_auth_sta_init( + drv->hapd->wpa_auth, sta->addr); + if (sta->wpa_sm == NULL) { + printf("Failed to initialize WPA state " + "machine\n"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + res = wpa_validate_wpa_ie(drv->hapd->wpa_auth, + sta->wpa_sm, + wpa_ie, wpa_ie_len, + NULL, 0); + if (res == WPA_INVALID_GROUP) + resp = WLAN_STATUS_GROUP_CIPHER_NOT_VALID; + else if (res == WPA_INVALID_PAIRWISE) + resp = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID; + else if (res == WPA_INVALID_AKMP) + resp = WLAN_STATUS_AKMP_NOT_VALID; + else if (res == WPA_ALLOC_FAIL) + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + else if (res != WPA_IE_OK) + resp = WLAN_STATUS_INVALID_IE; + if (resp != WLAN_STATUS_SUCCESS) + goto fail; + } + hdr->oid = (hdr->oid == htonl(DOT11_OID_ASSOCIATEEX)) ? + htonl(DOT11_OID_ASSOCIATEEX) : + htonl(DOT11_OID_REASSOCIATEEX); + hdr->op = htonl(PIMOP_SET); + mlme->code = 0; + mlme->state = htons(DOT11_STATE_ASSOC); + mlme->size = 0; + send(drv->pim_sock, hdr, sizeof(*hdr) + sizeof(*mlme), 0); + return; + } else if (mlme->state==htons(DOT11_STATE_ASSOC)) { + if (len < sizeof(pimdev_hdr) + sizeof(struct obj_mlme)) { + printf("bad assoc packet\n"); + return; + } + mac_id = mac_id_get(drv, mlme->id); + if (mac_id == NULL) + return; + memcpy(&mlme->address[0], mac_id, ETH_ALEN); + sta = ap_get_sta(drv->hapd, (u8 *) &mlme->address[0]); + if (sta == NULL) { + printf("cannot get sta\n"); + return; + } + new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0; + sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC; + wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC); + hostapd_new_assoc_sta(drv->hapd, sta, !new_assoc); + ieee802_1x_notify_port_enabled(sta->eapol_sm, 1); + sta->timeout_next = STA_NULLFUNC; + return; + } + return; + +fail: + printf("Prism54: assoc fail: %x\n", resp); + mlme->code = host_to_le16(resp); + mlme->size = 0; + mlme->state = htons(DOT11_STATE_ASSOCING); + hdr->oid = htonl(DOT11_OID_DISASSOCIATEEX); + hdr->op = htonl(PIMOP_SET); + sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); + send(drv->pim_sock, hdr, sizeof(*hdr) + sizeof(*mlme), 0); +} + + +static void handle_pim(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct prism54_driver_data *drv = eloop_ctx; + int len; + pimdev_hdr *hdr; + + hdr = malloc(PIM_BUF_SIZE); + if (hdr == NULL) + return; + len = recv(sock, hdr, PIM_BUF_SIZE, 0); + if (len < 0) { + perror("recv"); + free(hdr); + return; + } + if (len < 8) { + printf("handle_pim: too short (%d)\n", len); + free(hdr); + return; + } + + if (hdr->op != (int) htonl(PIMOP_TRAP)) { + free(hdr); + return; + } + switch (ntohl(hdr->oid)) { + case DOT11_OID_PROBE: + prism54_handle_probe(drv, hdr, len); + break; + case DOT11_OID_DEAUTHENTICATEEX: + case DOT11_OID_DEAUTHENTICATE: + prism54_handle_deauth(drv, hdr, len); + break; + case DOT11_OID_DISASSOCIATEEX: + case DOT11_OID_DISASSOCIATE: + prism54_handle_disassoc(drv, hdr, len); + break; + case DOT11_OID_AUTHENTICATEEX: + case DOT11_OID_AUTHENTICATE: + prism54_handle_auth(drv, hdr, len); + break; + case DOT11_OID_ASSOCIATEEX: + case DOT11_OID_REASSOCIATEEX: + case DOT11_OID_ASSOCIATE: + case DOT11_OID_REASSOCIATE: + prism54_handle_assoc(drv, hdr, len); + default: + break; + } + + free(hdr); +} + + +static void handle_802_3(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct hostapd_data *hapd = (struct hostapd_data *) eloop_ctx; + int len; + ieee802_3_hdr *hdr; + + hdr = malloc(PIM_BUF_SIZE); + if (hdr == NULL) + return; + len = recv(sock, hdr, PIM_BUF_SIZE, 0); + if (len < 0) { + perror("recv"); + free(hdr); + return; + } + if (len < 14) { + wpa_printf(MSG_MSGDUMP, "handle_802_3: too short (%d)", len); + free(hdr); + return; + } + if (hdr->type == htons(ETH_P_PAE)) { + ieee802_1x_receive(hapd, (u8 *) &hdr->sa[0], (u8 *) &hdr[1], + len - sizeof(*hdr)); + } + free(hdr); +} + + +static int prism54_init_sockets(struct prism54_driver_data *drv) +{ + struct hostapd_data *hapd = drv->hapd; + struct ifreq ifr; + struct sockaddr_ll addr; + + drv->sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PAE)); + if (drv->sock < 0) { + perror("socket[PF_PACKET,SOCK_RAW]"); + return -1; + } + + if (eloop_register_read_sock(drv->sock, handle_802_3, drv->hapd, NULL)) + { + printf("Could not register read socket\n"); + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + if (hapd->conf->bridge[0] != '\0') { + printf("opening bridge: %s\n", hapd->conf->bridge); + os_strlcpy(ifr.ifr_name, hapd->conf->bridge, + sizeof(ifr.ifr_name)); + } else { + os_strlcpy(ifr.ifr_name, drv->iface, sizeof(ifr.ifr_name)); + } + if (ioctl(drv->sock, SIOCGIFINDEX, &ifr) != 0) { + perror("ioctl(SIOCGIFINDEX)"); + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sll_family = AF_PACKET; + addr.sll_ifindex = ifr.ifr_ifindex; + addr.sll_protocol = htons(ETH_P_PAE); + wpa_printf(MSG_DEBUG, "Opening raw packet socket for ifindex %d", + addr.sll_ifindex); + + if (bind(drv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("bind"); + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, drv->iface, sizeof(ifr.ifr_name)); + if (ioctl(drv->sock, SIOCGIFHWADDR, &ifr) != 0) { + perror("ioctl(SIOCGIFHWADDR)"); + return -1; + } + + if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { + printf("Invalid HW-addr family 0x%04x\n", + ifr.ifr_hwaddr.sa_family); + return -1; + } + memcpy(drv->hapd->own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); + + drv->pim_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + if (drv->pim_sock < 0) { + perror("socket[PF_PACKET,SOCK_RAW]"); + return -1; + } + + if (eloop_register_read_sock(drv->pim_sock, handle_pim, drv, NULL)) { + printf("Could not register read socket\n"); + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%sap", drv->iface); + if (ioctl(drv->pim_sock, SIOCGIFINDEX, &ifr) != 0) { + perror("ioctl(SIOCGIFINDEX)"); + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sll_family = AF_PACKET; + addr.sll_ifindex = ifr.ifr_ifindex; + addr.sll_protocol = htons(ETH_P_ALL); + wpa_printf(MSG_DEBUG, "Opening raw packet socket for ifindex %d", + addr.sll_ifindex); + + if (bind(drv->pim_sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("bind"); + return -1; + } + + return 0; +} + + +static void * prism54_driver_init(struct hostapd_data *hapd) +{ + struct prism54_driver_data *drv; + + drv = os_zalloc(sizeof(struct prism54_driver_data)); + if (drv == NULL) { + printf("Could not allocate memory for hostapd Prism54 driver " + "data\n"); + return NULL; + } + + drv->hapd = hapd; + drv->pim_sock = drv->sock = -1; + memcpy(drv->iface, hapd->conf->iface, sizeof(drv->iface)); + + if (prism54_init_sockets(drv)) { + free(drv); + return NULL; + } + prism54_init_1x(drv); + /* must clean previous elems */ + prism54_set_generic_elem(drv->iface, drv, NULL, 0); + + return drv; +} + + +static void prism54_driver_deinit(void *priv) +{ + struct prism54_driver_data *drv = priv; + + if (drv->pim_sock >= 0) + close(drv->pim_sock); + + if (drv->sock >= 0) + close(drv->sock); + + free(drv); +} + + +const struct wpa_driver_ops wpa_driver_prism54_ops = { + .name = "prism54", + .init = prism54_driver_init, + .deinit = prism54_driver_deinit, + /* .set_ieee8021x = prism54_init_1x, */ + .set_privacy = prism54_set_privacy_invoked, + .set_encryption = prism54_set_encryption, + .get_seqnum = prism54_get_seqnum, + .flush = prism54_flush, + .set_generic_elem = prism54_set_generic_elem, + .send_eapol = prism54_send_eapol, + .sta_set_flags = prism54_sta_set_flags, + .sta_deauth = prism54_sta_deauth, + .sta_disassoc = prism54_sta_disassoc, + .set_ssid = prism54_ioctl_setiwessid, + .get_inact_sec = prism54_get_inact_sec, +}; diff --git a/hostapd/driver_test.c b/hostapd/driver_test.c new file mode 100644 index 0000000..9930a82 --- /dev/null +++ b/hostapd/driver_test.c @@ -0,0 +1,1300 @@ +/* + * hostapd / Driver interface for development testing + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <sys/un.h> +#include <dirent.h> + +#include "hostapd.h" +#include "driver.h" +#include "sha1.h" +#include "eloop.h" +#include "ieee802_1x.h" +#include "sta_info.h" +#include "wpa.h" +#include "accounting.h" +#include "radius/radius.h" +#include "l2_packet/l2_packet.h" +#include "ieee802_11.h" +#include "hw_features.h" +#include "wps_hostapd.h" + + +struct test_client_socket { + struct test_client_socket *next; + u8 addr[ETH_ALEN]; + struct sockaddr_un un; + socklen_t unlen; + struct test_driver_bss *bss; +}; + +struct test_driver_bss { + struct test_driver_bss *next; + char ifname[IFNAMSIZ + 1]; + u8 bssid[ETH_ALEN]; + u8 *ie; + size_t ielen; + u8 *wps_beacon_ie; + size_t wps_beacon_ie_len; + u8 *wps_probe_resp_ie; + size_t wps_probe_resp_ie_len; + u8 ssid[32]; + size_t ssid_len; + int privacy; +}; + +struct test_driver_data { + struct hostapd_data *hapd; + struct test_client_socket *cli; + int test_socket; + struct test_driver_bss *bss; + char *socket_dir; + char *own_socket_path; + int udp_port; +}; + + +static void test_driver_free_bss(struct test_driver_bss *bss) +{ + free(bss->ie); + free(bss->wps_beacon_ie); + free(bss->wps_probe_resp_ie); + free(bss); +} + + +static void test_driver_free_priv(struct test_driver_data *drv) +{ + struct test_driver_bss *bss, *prev; + + if (drv == NULL) + return; + + bss = drv->bss; + while (bss) { + prev = bss; + bss = bss->next; + test_driver_free_bss(prev); + } + free(drv->own_socket_path); + free(drv->socket_dir); + free(drv); +} + + +static struct test_client_socket * +test_driver_get_cli(struct test_driver_data *drv, struct sockaddr_un *from, + socklen_t fromlen) +{ + struct test_client_socket *cli = drv->cli; + + while (cli) { + if (cli->unlen == fromlen && + strncmp(cli->un.sun_path, from->sun_path, + fromlen - sizeof(cli->un.sun_family)) == 0) + return cli; + cli = cli->next; + } + + return NULL; +} + + +static int test_driver_send_eapol(void *priv, const u8 *addr, const u8 *data, + size_t data_len, int encrypt, + const u8 *own_addr) +{ + struct test_driver_data *drv = priv; + struct test_client_socket *cli; + struct msghdr msg; + struct iovec io[3]; + struct l2_ethhdr eth; + + if (drv->test_socket < 0) + return -1; + + cli = drv->cli; + while (cli) { + if (memcmp(cli->addr, addr, ETH_ALEN) == 0) + break; + cli = cli->next; + } + + if (!cli) { + wpa_printf(MSG_DEBUG, "%s: no destination client entry", + __func__); + return -1; + } + + memcpy(eth.h_dest, addr, ETH_ALEN); + memcpy(eth.h_source, own_addr, ETH_ALEN); + eth.h_proto = host_to_be16(ETH_P_EAPOL); + + io[0].iov_base = "EAPOL "; + io[0].iov_len = 6; + io[1].iov_base = ð + io[1].iov_len = sizeof(eth); + io[2].iov_base = (u8 *) data; + io[2].iov_len = data_len; + + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = io; + msg.msg_iovlen = 3; + msg.msg_name = &cli->un; + msg.msg_namelen = cli->unlen; + return sendmsg(drv->test_socket, &msg, 0); +} + + +static int test_driver_send_ether(void *priv, const u8 *dst, const u8 *src, + u16 proto, const u8 *data, size_t data_len) +{ + struct test_driver_data *drv = priv; + struct msghdr msg; + struct iovec io[3]; + struct l2_ethhdr eth; + char desttxt[30]; + struct sockaddr_un addr; + struct dirent *dent; + DIR *dir; + int ret = 0, broadcast = 0, count = 0; + + if (drv->test_socket < 0 || drv->socket_dir == NULL) { + wpa_printf(MSG_DEBUG, "%s: invalid parameters (sock=%d " + "socket_dir=%p)", + __func__, drv->test_socket, drv->socket_dir); + return -1; + } + + broadcast = memcmp(dst, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0; + snprintf(desttxt, sizeof(desttxt), MACSTR, MAC2STR(dst)); + + memcpy(eth.h_dest, dst, ETH_ALEN); + memcpy(eth.h_source, src, ETH_ALEN); + eth.h_proto = host_to_be16(proto); + + io[0].iov_base = "ETHER "; + io[0].iov_len = 6; + io[1].iov_base = ð + io[1].iov_len = sizeof(eth); + io[2].iov_base = (u8 *) data; + io[2].iov_len = data_len; + + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = io; + msg.msg_iovlen = 3; + + dir = opendir(drv->socket_dir); + if (dir == NULL) { + perror("test_driver: opendir"); + return -1; + } + 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; + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s", + drv->socket_dir, dent->d_name); + + if (strcmp(addr.sun_path, drv->own_socket_path) == 0) + continue; + if (!broadcast && strstr(dent->d_name, desttxt) == NULL) + continue; + + wpa_printf(MSG_DEBUG, "%s: Send ether frame to %s", + __func__, dent->d_name); + + msg.msg_name = &addr; + msg.msg_namelen = sizeof(addr); + ret = sendmsg(drv->test_socket, &msg, 0); + if (ret < 0) + perror("driver_test: sendmsg"); + count++; + } + closedir(dir); + + if (!broadcast && count == 0) { + wpa_printf(MSG_DEBUG, "%s: Destination " MACSTR " not found", + __func__, MAC2STR(dst)); + return -1; + } + + return ret; +} + + +static int test_driver_send_mgmt_frame(void *priv, const void *buf, + size_t len, int flags) +{ + struct test_driver_data *drv = priv; + struct msghdr msg; + struct iovec io[2]; + const u8 *dest; + int ret = 0, broadcast = 0; + char desttxt[30]; + struct sockaddr_un addr; + struct dirent *dent; + DIR *dir; + struct ieee80211_hdr *hdr; + u16 fc; + + if (drv->test_socket < 0 || len < 10 || drv->socket_dir == NULL) { + wpa_printf(MSG_DEBUG, "%s: invalid parameters (sock=%d len=%lu" + " socket_dir=%p)", + __func__, drv->test_socket, (unsigned long) len, + drv->socket_dir); + return -1; + } + + dest = buf; + dest += 4; + broadcast = memcmp(dest, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0; + snprintf(desttxt, sizeof(desttxt), MACSTR, MAC2STR(dest)); + + io[0].iov_base = "MLME "; + io[0].iov_len = 5; + io[1].iov_base = (void *) buf; + io[1].iov_len = len; + + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = io; + msg.msg_iovlen = 2; + + dir = opendir(drv->socket_dir); + if (dir == NULL) { + perror("test_driver: opendir"); + return -1; + } + 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; + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s", + drv->socket_dir, dent->d_name); + + if (strcmp(addr.sun_path, drv->own_socket_path) == 0) + continue; + if (!broadcast && strstr(dent->d_name, desttxt) == NULL) + continue; + + wpa_printf(MSG_DEBUG, "%s: Send management frame to %s", + __func__, dent->d_name); + + msg.msg_name = &addr; + msg.msg_namelen = sizeof(addr); + ret = sendmsg(drv->test_socket, &msg, 0); + if (ret < 0) + perror("driver_test: sendmsg"); + } + closedir(dir); + + hdr = (struct ieee80211_hdr *) buf; + fc = le_to_host16(hdr->frame_control); + ieee802_11_mgmt_cb(drv->hapd, (u8 *) buf, len, WLAN_FC_GET_STYPE(fc), + ret >= 0); + + return ret; +} + + +static void test_driver_scan(struct test_driver_data *drv, + struct sockaddr_un *from, socklen_t fromlen, + char *data) +{ + char buf[512], *pos, *end; + int ret; + struct test_driver_bss *bss; + u8 sa[ETH_ALEN]; + u8 ie[512]; + size_t ielen; + + /* data: optional [ ' ' | STA-addr | ' ' | IEs(hex) ] */ + + wpa_printf(MSG_DEBUG, "test_driver: SCAN"); + + if (*data) { + if (*data != ' ' || + hwaddr_aton(data + 1, sa)) { + wpa_printf(MSG_DEBUG, "test_driver: Unexpected SCAN " + "command format"); + return; + } + + data += 18; + while (*data == ' ') + data++; + ielen = os_strlen(data) / 2; + if (ielen > sizeof(ie)) + ielen = sizeof(ie); + if (hexstr2bin(data, ie, ielen) < 0) + ielen = 0; + + wpa_printf(MSG_DEBUG, "test_driver: Scan from " MACSTR, + MAC2STR(sa)); + wpa_hexdump(MSG_MSGDUMP, "test_driver: scan IEs", ie, ielen); + + hostapd_wps_probe_req_rx(drv->hapd, sa, ie, ielen); + } + + for (bss = drv->bss; bss; bss = bss->next) { + pos = buf; + end = buf + sizeof(buf); + + /* reply: SCANRESP BSSID SSID IEs */ + ret = snprintf(pos, end - pos, "SCANRESP " MACSTR " ", + MAC2STR(bss->bssid)); + if (ret < 0 || ret >= end - pos) + return; + pos += ret; + pos += wpa_snprintf_hex(pos, end - pos, + bss->ssid, bss->ssid_len); + ret = snprintf(pos, end - pos, " "); + if (ret < 0 || ret >= end - pos) + return; + pos += ret; + pos += wpa_snprintf_hex(pos, end - pos, bss->ie, bss->ielen); + pos += wpa_snprintf_hex(pos, end - pos, bss->wps_probe_resp_ie, + bss->wps_probe_resp_ie_len); + + if (bss->privacy) { + ret = snprintf(pos, end - pos, " PRIVACY"); + if (ret < 0 || ret >= end - pos) + return; + pos += ret; + } + + sendto(drv->test_socket, buf, pos - buf, 0, + (struct sockaddr *) from, fromlen); + } +} + + +static struct hostapd_data * test_driver_get_hapd(struct test_driver_data *drv, + struct test_driver_bss *bss) +{ + struct hostapd_iface *iface = drv->hapd->iface; + struct hostapd_data *hapd = NULL; + size_t i; + + if (bss == NULL) { + wpa_printf(MSG_DEBUG, "%s: bss == NULL", __func__); + return NULL; + } + + for (i = 0; i < iface->num_bss; i++) { + hapd = iface->bss[i]; + if (memcmp(hapd->own_addr, bss->bssid, ETH_ALEN) == 0) + break; + } + if (i == iface->num_bss) { + wpa_printf(MSG_DEBUG, "%s: no matching interface entry found " + "for BSSID " MACSTR, __func__, MAC2STR(bss->bssid)); + return NULL; + } + + return hapd; +} + + +static int test_driver_new_sta(struct test_driver_data *drv, + struct test_driver_bss *bss, const u8 *addr, + const u8 *ie, size_t ielen) +{ + struct hostapd_data *hapd; + struct sta_info *sta; + int new_assoc, res; + + hapd = test_driver_get_hapd(drv, bss); + if (hapd == NULL) + return -1; + + hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "associated"); + + sta = ap_get_sta(hapd, addr); + if (sta) { + accounting_sta_stop(hapd, sta); + } else { + sta = ap_sta_add(hapd, addr); + if (sta == NULL) + return -1; + } + sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS); + + if (hapd->conf->wpa) { + if (ie == NULL || ielen == 0) { + if (hapd->conf->wps_state) { + sta->flags |= WLAN_STA_WPS; + goto skip_wpa_check; + } + + printf("test_driver: no IE from STA\n"); + return -1; + } + if (hapd->conf->wps_state && ie[0] == 0xdd && ie[1] >= 4 && + os_memcmp(ie + 2, "\x00\x50\xf2\x04", 4) == 0) { + sta->flags |= WLAN_STA_WPS; + goto skip_wpa_check; + } + + if (sta->wpa_sm == NULL) + sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, + sta->addr); + if (sta->wpa_sm == NULL) { + printf("test_driver: Failed to initialize WPA state " + "machine\n"); + return -1; + } + res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, + ie, ielen, NULL, 0); + if (res != WPA_IE_OK) { + printf("WPA/RSN information element rejected? " + "(res %u)\n", res); + wpa_hexdump(MSG_DEBUG, "IE", ie, ielen); + return -1; + } + } +skip_wpa_check: + + new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0; + sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC; + wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC); + + hostapd_new_assoc_sta(hapd, sta, !new_assoc); + + ieee802_1x_notify_port_enabled(sta->eapol_sm, 1); + + return 0; +} + + +static void test_driver_assoc(struct test_driver_data *drv, + struct sockaddr_un *from, socklen_t fromlen, + char *data) +{ + struct test_client_socket *cli; + u8 ie[256], ssid[32]; + size_t ielen, ssid_len = 0; + char *pos, *pos2, cmd[50]; + struct test_driver_bss *bss; + + /* data: STA-addr SSID(hex) IEs(hex) */ + + cli = os_zalloc(sizeof(*cli)); + if (cli == NULL) + return; + + if (hwaddr_aton(data, cli->addr)) { + printf("test_socket: Invalid MAC address '%s' in ASSOC\n", + data); + free(cli); + return; + } + pos = data + 17; + while (*pos == ' ') + pos++; + pos2 = strchr(pos, ' '); + ielen = 0; + if (pos2) { + ssid_len = (pos2 - pos) / 2; + if (hexstr2bin(pos, ssid, ssid_len) < 0) { + wpa_printf(MSG_DEBUG, "%s: Invalid SSID", __func__); + free(cli); + return; + } + wpa_hexdump_ascii(MSG_DEBUG, "test_driver_assoc: SSID", + ssid, ssid_len); + + pos = pos2 + 1; + ielen = strlen(pos) / 2; + if (ielen > sizeof(ie)) + ielen = sizeof(ie); + if (hexstr2bin(pos, ie, ielen) < 0) + ielen = 0; + } + + for (bss = drv->bss; bss; bss = bss->next) { + if (bss->ssid_len == ssid_len && + memcmp(bss->ssid, ssid, ssid_len) == 0) + break; + } + if (bss == NULL) { + wpa_printf(MSG_DEBUG, "%s: No matching SSID found from " + "configured BSSes", __func__); + free(cli); + return; + } + + cli->bss = bss; + memcpy(&cli->un, from, sizeof(cli->un)); + cli->unlen = fromlen; + cli->next = drv->cli; + drv->cli = cli; + wpa_hexdump_ascii(MSG_DEBUG, "test_socket: ASSOC sun_path", + (const u8 *) cli->un.sun_path, + cli->unlen - sizeof(cli->un.sun_family)); + + snprintf(cmd, sizeof(cmd), "ASSOCRESP " MACSTR " 0", + MAC2STR(bss->bssid)); + sendto(drv->test_socket, cmd, strlen(cmd), 0, + (struct sockaddr *) from, fromlen); + + if (test_driver_new_sta(drv, bss, cli->addr, ie, ielen) < 0) { + wpa_printf(MSG_DEBUG, "test_driver: failed to add new STA"); + } +} + + +static void test_driver_disassoc(struct test_driver_data *drv, + struct sockaddr_un *from, socklen_t fromlen) +{ + struct test_client_socket *cli; + struct sta_info *sta; + + cli = test_driver_get_cli(drv, from, fromlen); + if (!cli) + return; + + hostapd_logger(drv->hapd, cli->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "disassociated"); + + sta = ap_get_sta(drv->hapd, cli->addr); + if (sta != NULL) { + sta->flags &= ~WLAN_STA_ASSOC; + wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC); + sta->acct_terminate_cause = + RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST; + ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); + ap_free_sta(drv->hapd, sta); + } +} + + +static void test_driver_eapol(struct test_driver_data *drv, + struct sockaddr_un *from, socklen_t fromlen, + u8 *data, size_t datalen) +{ + struct test_client_socket *cli; + if (datalen > 14) { + /* Skip Ethernet header */ + wpa_printf(MSG_DEBUG, "test_driver: dst=" MACSTR " src=" + MACSTR " proto=%04x", + MAC2STR(data), MAC2STR(data + ETH_ALEN), + WPA_GET_BE16(data + 2 * ETH_ALEN)); + data += 14; + datalen -= 14; + } + cli = test_driver_get_cli(drv, from, fromlen); + if (cli) { + struct hostapd_data *hapd; + hapd = test_driver_get_hapd(drv, cli->bss); + if (hapd == NULL) + return; + ieee802_1x_receive(hapd, cli->addr, data, datalen); + } else { + wpa_printf(MSG_DEBUG, "test_socket: EAPOL from unknown " + "client"); + } +} + + +static void test_driver_ether(struct test_driver_data *drv, + struct sockaddr_un *from, socklen_t fromlen, + u8 *data, size_t datalen) +{ + struct l2_ethhdr *eth; + + if (datalen < sizeof(*eth)) + return; + + eth = (struct l2_ethhdr *) data; + wpa_printf(MSG_DEBUG, "test_driver: RX ETHER dst=" MACSTR " src=" + MACSTR " proto=%04x", + MAC2STR(eth->h_dest), MAC2STR(eth->h_source), + be_to_host16(eth->h_proto)); + +#ifdef CONFIG_IEEE80211R + if (be_to_host16(eth->h_proto) == ETH_P_RRB) { + wpa_ft_rrb_rx(drv->hapd->wpa_auth, eth->h_source, + data + sizeof(*eth), datalen - sizeof(*eth)); + } +#endif /* CONFIG_IEEE80211R */ +} + + +static void test_driver_mlme(struct test_driver_data *drv, + struct sockaddr_un *from, socklen_t fromlen, + u8 *data, size_t datalen) +{ + struct ieee80211_hdr *hdr; + u16 fc; + + hdr = (struct ieee80211_hdr *) data; + + if (test_driver_get_cli(drv, from, fromlen) == NULL && datalen >= 16) { + struct test_client_socket *cli; + cli = os_zalloc(sizeof(*cli)); + if (cli == NULL) + return; + wpa_printf(MSG_DEBUG, "Adding client entry for " MACSTR, + MAC2STR(hdr->addr2)); + memcpy(cli->addr, hdr->addr2, ETH_ALEN); + memcpy(&cli->un, from, sizeof(cli->un)); + cli->unlen = fromlen; + cli->next = drv->cli; + drv->cli = cli; + } + + wpa_hexdump(MSG_MSGDUMP, "test_driver_mlme: received frame", + data, datalen); + fc = le_to_host16(hdr->frame_control); + if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT) { + wpa_printf(MSG_ERROR, "%s: received non-mgmt frame", + __func__); + return; + } + ieee802_11_mgmt(drv->hapd, data, datalen, WLAN_FC_GET_STYPE(fc), NULL); +} + + +static void test_driver_receive_unix(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct test_driver_data *drv = eloop_ctx; + char buf[2000]; + int res; + struct sockaddr_un from; + socklen_t fromlen = sizeof(from); + + res = recvfrom(sock, buf, sizeof(buf) - 1, 0, + (struct sockaddr *) &from, &fromlen); + if (res < 0) { + perror("recvfrom(test_socket)"); + return; + } + buf[res] = '\0'; + + wpa_printf(MSG_DEBUG, "test_driver: received %u bytes", res); + + if (strncmp(buf, "SCAN", 4) == 0) { + test_driver_scan(drv, &from, fromlen, buf + 4); + } else if (strncmp(buf, "ASSOC ", 6) == 0) { + test_driver_assoc(drv, &from, fromlen, buf + 6); + } else if (strcmp(buf, "DISASSOC") == 0) { + test_driver_disassoc(drv, &from, fromlen); + } else if (strncmp(buf, "EAPOL ", 6) == 0) { + test_driver_eapol(drv, &from, fromlen, (u8 *) buf + 6, + res - 6); + } else if (strncmp(buf, "ETHER ", 6) == 0) { + test_driver_ether(drv, &from, fromlen, (u8 *) buf + 6, + res - 6); + } else if (strncmp(buf, "MLME ", 5) == 0) { + test_driver_mlme(drv, &from, fromlen, (u8 *) buf + 5, res - 5); + } else { + wpa_hexdump_ascii(MSG_DEBUG, "Unknown test_socket command", + (u8 *) buf, res); + } +} + + +static struct test_driver_bss * +test_driver_get_bss(struct test_driver_data *drv, const char *ifname) +{ + struct test_driver_bss *bss; + + for (bss = drv->bss; bss; bss = bss->next) { + if (strcmp(bss->ifname, ifname) == 0) + return bss; + } + return NULL; +} + + +static int test_driver_set_generic_elem(const char *ifname, void *priv, + const u8 *elem, size_t elem_len) +{ + struct test_driver_data *drv = priv; + struct test_driver_bss *bss; + + bss = test_driver_get_bss(drv, ifname); + if (bss == NULL) + return -1; + + free(bss->ie); + + if (elem == NULL) { + bss->ie = NULL; + bss->ielen = 0; + return 0; + } + + bss->ie = malloc(elem_len); + if (bss->ie == NULL) { + bss->ielen = 0; + return -1; + } + + memcpy(bss->ie, elem, elem_len); + bss->ielen = elem_len; + return 0; +} + + +static int test_driver_set_wps_beacon_ie(const char *ifname, void *priv, + const u8 *ie, size_t len) +{ + struct test_driver_data *drv = priv; + struct test_driver_bss *bss; + + wpa_hexdump(MSG_DEBUG, "test_driver: Beacon WPS IE", ie, len); + bss = test_driver_get_bss(drv, ifname); + if (bss == NULL) + return -1; + + free(bss->wps_beacon_ie); + + if (ie == NULL) { + bss->wps_beacon_ie = NULL; + bss->wps_beacon_ie_len = 0; + return 0; + } + + bss->wps_beacon_ie = malloc(len); + if (bss->wps_beacon_ie == NULL) { + bss->wps_beacon_ie_len = 0; + return -1; + } + + memcpy(bss->wps_beacon_ie, ie, len); + bss->wps_beacon_ie_len = len; + return 0; +} + + +static int test_driver_set_wps_probe_resp_ie(const char *ifname, void *priv, + const u8 *ie, size_t len) +{ + struct test_driver_data *drv = priv; + struct test_driver_bss *bss; + + wpa_hexdump(MSG_DEBUG, "test_driver: ProbeResp WPS IE", ie, len); + bss = test_driver_get_bss(drv, ifname); + if (bss == NULL) + return -1; + + free(bss->wps_probe_resp_ie); + + if (ie == NULL) { + bss->wps_probe_resp_ie = NULL; + bss->wps_probe_resp_ie_len = 0; + return 0; + } + + bss->wps_probe_resp_ie = malloc(len); + if (bss->wps_probe_resp_ie == NULL) { + bss->wps_probe_resp_ie_len = 0; + return -1; + } + + memcpy(bss->wps_probe_resp_ie, ie, len); + bss->wps_probe_resp_ie_len = len; + return 0; +} + + +static int test_driver_sta_deauth(void *priv, const u8 *addr, int reason) +{ + struct test_driver_data *drv = priv; + struct test_client_socket *cli; + + if (drv->test_socket < 0) + return -1; + + cli = drv->cli; + while (cli) { + if (memcmp(cli->addr, addr, ETH_ALEN) == 0) + break; + cli = cli->next; + } + + if (!cli) + return -1; + + return sendto(drv->test_socket, "DEAUTH", 6, 0, + (struct sockaddr *) &cli->un, cli->unlen); +} + + +static int test_driver_sta_disassoc(void *priv, const u8 *addr, int reason) +{ + struct test_driver_data *drv = priv; + struct test_client_socket *cli; + + if (drv->test_socket < 0) + return -1; + + cli = drv->cli; + while (cli) { + if (memcmp(cli->addr, addr, ETH_ALEN) == 0) + break; + cli = cli->next; + } + + if (!cli) + return -1; + + return sendto(drv->test_socket, "DISASSOC", 8, 0, + (struct sockaddr *) &cli->un, cli->unlen); +} + + +static struct hostapd_hw_modes * +test_driver_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) +{ + struct hostapd_hw_modes *modes; + + *num_modes = 3; + *flags = 0; + modes = os_zalloc(*num_modes * sizeof(struct hostapd_hw_modes)); + if (modes == NULL) + return NULL; + modes[0].mode = HOSTAPD_MODE_IEEE80211G; + modes[0].num_channels = 1; + modes[0].num_rates = 1; + modes[0].channels = os_zalloc(sizeof(struct hostapd_channel_data)); + modes[0].rates = os_zalloc(sizeof(struct hostapd_rate_data)); + if (modes[0].channels == NULL || modes[0].rates == NULL) { + hostapd_free_hw_features(modes, *num_modes); + return NULL; + } + modes[0].channels[0].chan = 1; + modes[0].channels[0].freq = 2412; + modes[0].channels[0].flag = 0; + modes[0].rates[0].rate = 10; + modes[0].rates[0].flags = HOSTAPD_RATE_BASIC | HOSTAPD_RATE_SUPPORTED | + HOSTAPD_RATE_CCK | HOSTAPD_RATE_MANDATORY; + + modes[1].mode = HOSTAPD_MODE_IEEE80211B; + modes[1].num_channels = 1; + modes[1].num_rates = 1; + modes[1].channels = os_zalloc(sizeof(struct hostapd_channel_data)); + modes[1].rates = os_zalloc(sizeof(struct hostapd_rate_data)); + if (modes[1].channels == NULL || modes[1].rates == NULL) { + hostapd_free_hw_features(modes, *num_modes); + return NULL; + } + modes[1].channels[0].chan = 1; + modes[1].channels[0].freq = 2412; + modes[1].channels[0].flag = 0; + modes[1].rates[0].rate = 10; + modes[1].rates[0].flags = HOSTAPD_RATE_BASIC | HOSTAPD_RATE_SUPPORTED | + HOSTAPD_RATE_CCK | HOSTAPD_RATE_MANDATORY; + + modes[2].mode = HOSTAPD_MODE_IEEE80211A; + modes[2].num_channels = 1; + modes[2].num_rates = 1; + modes[2].channels = os_zalloc(sizeof(struct hostapd_channel_data)); + modes[2].rates = os_zalloc(sizeof(struct hostapd_rate_data)); + if (modes[2].channels == NULL || modes[2].rates == NULL) { + hostapd_free_hw_features(modes, *num_modes); + return NULL; + } + modes[2].channels[0].chan = 60; + modes[2].channels[0].freq = 5300; + modes[2].channels[0].flag = 0; + modes[2].rates[0].rate = 60; + modes[2].rates[0].flags = HOSTAPD_RATE_BASIC | HOSTAPD_RATE_SUPPORTED | + HOSTAPD_RATE_MANDATORY; + + return modes; +} + + +static int test_driver_bss_add(void *priv, const char *ifname, const u8 *bssid) +{ + struct test_driver_data *drv = priv; + struct test_driver_bss *bss; + + wpa_printf(MSG_DEBUG, "%s(ifname=%s bssid=" MACSTR ")", + __func__, ifname, MAC2STR(bssid)); + + bss = os_zalloc(sizeof(*bss)); + if (bss == NULL) + return -1; + + os_strlcpy(bss->ifname, ifname, IFNAMSIZ); + memcpy(bss->bssid, bssid, ETH_ALEN); + + bss->next = drv->bss; + drv->bss = bss; + + return 0; +} + + +static int test_driver_bss_remove(void *priv, const char *ifname) +{ + struct test_driver_data *drv = priv; + struct test_driver_bss *bss, *prev; + struct test_client_socket *cli, *prev_c; + + wpa_printf(MSG_DEBUG, "%s(ifname=%s)", __func__, ifname); + + for (prev = NULL, bss = drv->bss; bss; prev = bss, bss = bss->next) { + if (strcmp(bss->ifname, ifname) != 0) + continue; + + if (prev) + prev->next = bss->next; + else + drv->bss = bss->next; + + for (prev_c = NULL, cli = drv->cli; cli; + prev_c = cli, cli = cli->next) { + if (cli->bss != bss) + continue; + if (prev_c) + prev_c->next = cli->next; + else + drv->cli = cli->next; + free(cli); + break; + } + + test_driver_free_bss(bss); + return 0; + } + + return -1; +} + + +static int test_driver_if_add(const char *iface, void *priv, + enum hostapd_driver_if_type type, char *ifname, + const u8 *addr) +{ + wpa_printf(MSG_DEBUG, "%s(iface=%s type=%d ifname=%s)", + __func__, iface, type, ifname); + return 0; +} + + +static int test_driver_if_update(void *priv, enum hostapd_driver_if_type type, + char *ifname, const u8 *addr) +{ + wpa_printf(MSG_DEBUG, "%s(type=%d ifname=%s)", __func__, type, ifname); + return 0; +} + + +static int test_driver_if_remove(void *priv, enum hostapd_driver_if_type type, + const char *ifname, const u8 *addr) +{ + wpa_printf(MSG_DEBUG, "%s(type=%d ifname=%s)", __func__, type, ifname); + return 0; +} + + +static int test_driver_valid_bss_mask(void *priv, const u8 *addr, + const u8 *mask) +{ + return 0; +} + + +static int test_driver_set_ssid(const char *ifname, void *priv, const u8 *buf, + int len) +{ + struct test_driver_data *drv = priv; + struct test_driver_bss *bss; + + wpa_printf(MSG_DEBUG, "%s(ifname=%s)", __func__, ifname); + wpa_hexdump_ascii(MSG_DEBUG, "test_driver_set_ssid: SSID", buf, len); + + for (bss = drv->bss; bss; bss = bss->next) { + if (strcmp(bss->ifname, ifname) != 0) + continue; + + if (len < 0 || (size_t) len > sizeof(bss->ssid)) + return -1; + + memcpy(bss->ssid, buf, len); + bss->ssid_len = len; + + return 0; + } + + return -1; +} + + +static int test_driver_set_privacy(const char *ifname, void *priv, int enabled) +{ + struct test_driver_data *drv = priv; + struct test_driver_bss *bss; + + wpa_printf(MSG_DEBUG, "%s(ifname=%s enabled=%d)", + __func__, ifname, enabled); + + for (bss = drv->bss; bss; bss = bss->next) { + if (strcmp(bss->ifname, ifname) != 0) + continue; + + bss->privacy = enabled; + + return 0; + } + + return -1; +} + + +static int test_driver_set_encryption(const char *iface, void *priv, + const char *alg, const u8 *addr, int idx, + const u8 *key, size_t key_len, int txkey) +{ + wpa_printf(MSG_DEBUG, "%s(iface=%s alg=%s idx=%d txkey=%d)", + __func__, iface, alg, idx, txkey); + if (addr) + wpa_printf(MSG_DEBUG, " addr=" MACSTR, MAC2STR(addr)); + if (key) + wpa_hexdump_key(MSG_DEBUG, " key", key, key_len); + return 0; +} + + +static int test_driver_set_sta_vlan(void *priv, const u8 *addr, + const char *ifname, int vlan_id) +{ + wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " ifname=%s vlan_id=%d)", + __func__, MAC2STR(addr), ifname, vlan_id); + return 0; +} + + +static int test_driver_sta_add(const char *ifname, void *priv, const u8 *addr, + u16 aid, u16 capability, u8 *supp_rates, + size_t supp_rates_len, int flags, + u16 listen_interval) +{ + struct test_driver_data *drv = priv; + struct test_client_socket *cli; + struct test_driver_bss *bss; + + wpa_printf(MSG_DEBUG, "%s(ifname=%s addr=" MACSTR " aid=%d " + "capability=0x%x flags=0x%x listen_interval=%d)", + __func__, ifname, MAC2STR(addr), aid, capability, flags, + listen_interval); + wpa_hexdump(MSG_DEBUG, "test_driver_sta_add - supp_rates", + supp_rates, supp_rates_len); + + cli = drv->cli; + while (cli) { + if (memcmp(cli->addr, addr, ETH_ALEN) == 0) + break; + cli = cli->next; + } + if (!cli) { + wpa_printf(MSG_DEBUG, "%s: no matching client entry", + __func__); + return -1; + } + + for (bss = drv->bss; bss; bss = bss->next) { + if (strcmp(ifname, bss->ifname) == 0) + break; + } + if (bss == NULL) { + wpa_printf(MSG_DEBUG, "%s: No matching interface found from " + "configured BSSes", __func__); + return -1; + } + + cli->bss = bss; + + return 0; +} + + +static void * test_driver_init(struct hostapd_data *hapd) +{ + struct test_driver_data *drv; + struct sockaddr_un addr_un; + struct sockaddr_in addr_in; + struct sockaddr *addr; + socklen_t alen; + + drv = os_zalloc(sizeof(struct test_driver_data)); + if (drv == NULL) { + printf("Could not allocate memory for test driver data\n"); + return NULL; + } + drv->bss = os_zalloc(sizeof(*drv->bss)); + if (drv->bss == NULL) { + printf("Could not allocate memory for test driver BSS data\n"); + free(drv); + return NULL; + } + + drv->hapd = hapd; + + /* Generate a MAC address to help testing with multiple APs */ + hapd->own_addr[0] = 0x02; /* locally administered */ + sha1_prf((const u8 *) hapd->conf->iface, strlen(hapd->conf->iface), + "hostapd test bssid generation", + (const u8 *) hapd->conf->ssid.ssid, hapd->conf->ssid.ssid_len, + hapd->own_addr + 1, ETH_ALEN - 1); + + os_strlcpy(drv->bss->ifname, hapd->conf->iface, IFNAMSIZ); + memcpy(drv->bss->bssid, hapd->own_addr, ETH_ALEN); + + if (hapd->conf->test_socket) { + if (strlen(hapd->conf->test_socket) >= + sizeof(addr_un.sun_path)) { + printf("Too long test_socket path\n"); + test_driver_free_priv(drv); + return NULL; + } + if (strncmp(hapd->conf->test_socket, "DIR:", 4) == 0) { + size_t len = strlen(hapd->conf->test_socket) + 30; + drv->socket_dir = strdup(hapd->conf->test_socket + 4); + drv->own_socket_path = malloc(len); + if (drv->own_socket_path) { + snprintf(drv->own_socket_path, len, + "%s/AP-" MACSTR, + hapd->conf->test_socket + 4, + MAC2STR(hapd->own_addr)); + } + } else if (strncmp(hapd->conf->test_socket, "UDP:", 4) == 0) { + drv->udp_port = atoi(hapd->conf->test_socket + 4); + } else { + drv->own_socket_path = strdup(hapd->conf->test_socket); + } + if (drv->own_socket_path == NULL && drv->udp_port == 0) { + test_driver_free_priv(drv); + return NULL; + } + + drv->test_socket = socket(drv->udp_port ? PF_INET : PF_UNIX, + SOCK_DGRAM, 0); + if (drv->test_socket < 0) { + perror("socket"); + test_driver_free_priv(drv); + return NULL; + } + + if (drv->udp_port) { + os_memset(&addr_in, 0, sizeof(addr_in)); + addr_in.sin_family = AF_INET; + addr_in.sin_port = htons(drv->udp_port); + addr = (struct sockaddr *) &addr_in; + alen = sizeof(addr_in); + } else { + os_memset(&addr_un, 0, sizeof(addr_un)); + addr_un.sun_family = AF_UNIX; + os_strlcpy(addr_un.sun_path, drv->own_socket_path, + sizeof(addr_un.sun_path)); + addr = (struct sockaddr *) &addr_un; + alen = sizeof(addr_un); + } + if (bind(drv->test_socket, addr, alen) < 0) { + perror("bind(PF_UNIX)"); + close(drv->test_socket); + if (drv->own_socket_path) + unlink(drv->own_socket_path); + test_driver_free_priv(drv); + return NULL; + } + eloop_register_read_sock(drv->test_socket, + test_driver_receive_unix, drv, NULL); + } else + drv->test_socket = -1; + + return drv; +} + + +static void test_driver_deinit(void *priv) +{ + struct test_driver_data *drv = priv; + struct test_client_socket *cli, *prev; + + cli = drv->cli; + while (cli) { + prev = cli; + cli = cli->next; + free(prev); + } + + if (drv->test_socket >= 0) { + eloop_unregister_read_sock(drv->test_socket); + close(drv->test_socket); + if (drv->own_socket_path) + unlink(drv->own_socket_path); + } + + /* There should be only one BSS remaining at this point. */ + if (drv->bss == NULL) + wpa_printf(MSG_ERROR, "%s: drv->bss == NULL", __func__); + else if (drv->bss->next) + wpa_printf(MSG_ERROR, "%s: drv->bss->next != NULL", __func__); + + test_driver_free_priv(drv); +} + + +const struct wpa_driver_ops wpa_driver_test_ops = { + .name = "test", + .init = test_driver_init, + .deinit = test_driver_deinit, + .send_eapol = test_driver_send_eapol, + .send_mgmt_frame = test_driver_send_mgmt_frame, + .set_generic_elem = test_driver_set_generic_elem, + .sta_deauth = test_driver_sta_deauth, + .sta_disassoc = test_driver_sta_disassoc, + .get_hw_feature_data = test_driver_get_hw_feature_data, + .bss_add = test_driver_bss_add, + .bss_remove = test_driver_bss_remove, + .if_add = test_driver_if_add, + .if_update = test_driver_if_update, + .if_remove = test_driver_if_remove, + .valid_bss_mask = test_driver_valid_bss_mask, + .set_ssid = test_driver_set_ssid, + .set_privacy = test_driver_set_privacy, + .set_encryption = test_driver_set_encryption, + .set_sta_vlan = test_driver_set_sta_vlan, + .sta_add = test_driver_sta_add, + .send_ether = test_driver_send_ether, + .set_wps_beacon_ie = test_driver_set_wps_beacon_ie, + .set_wps_probe_resp_ie = test_driver_set_wps_probe_resp_ie, +}; diff --git a/hostapd/driver_wired.c b/hostapd/driver_wired.c new file mode 100644 index 0000000..61cb667 --- /dev/null +++ b/hostapd/driver_wired.c @@ -0,0 +1,372 @@ +/* + * hostapd / Kernel driver communication for wired (Ethernet) drivers + * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004, Gunter Burchardt <tira@isx.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <sys/ioctl.h> + +#ifdef USE_KERNEL_HEADERS +#include <asm/types.h> +#include <linux/if_packet.h> +#include <linux/if_ether.h> /* The L2 protocols */ +#include <linux/if_arp.h> +#include <linux/if.h> +#else /* USE_KERNEL_HEADERS */ +#include <net/if_arp.h> +#include <net/if.h> +#include <netpacket/packet.h> +#endif /* USE_KERNEL_HEADERS */ + +#include "hostapd.h" +#include "ieee802_1x.h" +#include "eloop.h" +#include "sta_info.h" +#include "driver.h" +#include "accounting.h" + + +struct wired_driver_data { + struct hostapd_data *hapd; + + int sock; /* raw packet socket for driver access */ + int dhcp_sock; /* socket for dhcp packets */ + int use_pae_group_addr; +}; + + +#define WIRED_EAPOL_MULTICAST_GROUP {0x01,0x80,0xc2,0x00,0x00,0x03} + + +/* TODO: detecting new devices should eventually be changed from using DHCP + * snooping to trigger on any packet from a new layer 2 MAC address, e.g., + * based on ebtables, etc. */ + +struct dhcp_message { + u_int8_t op; + u_int8_t htype; + u_int8_t hlen; + u_int8_t hops; + u_int32_t xid; + u_int16_t secs; + u_int16_t flags; + u_int32_t ciaddr; + u_int32_t yiaddr; + u_int32_t siaddr; + u_int32_t giaddr; + u_int8_t chaddr[16]; + u_int8_t sname[64]; + u_int8_t file[128]; + u_int32_t cookie; + u_int8_t options[308]; /* 312 - cookie */ +}; + + +static void wired_possible_new_sta(struct hostapd_data *hapd, u8 *addr) +{ + struct sta_info *sta; + + sta = ap_get_sta(hapd, addr); + if (sta) + return; + + wpa_printf(MSG_DEBUG, "Data frame from unknown STA " MACSTR + " - adding a new STA", MAC2STR(addr)); + sta = ap_sta_add(hapd, addr); + if (sta) { + hostapd_new_assoc_sta(hapd, sta, 0); + } else { + wpa_printf(MSG_DEBUG, "Failed to add STA entry for " MACSTR, + MAC2STR(addr)); + } +} + + +static void handle_data(struct hostapd_data *hapd, unsigned char *buf, + size_t len) +{ + struct ieee8023_hdr *hdr; + u8 *pos, *sa; + size_t left; + + /* must contain at least ieee8023_hdr 6 byte source, 6 byte dest, + * 2 byte ethertype */ + if (len < 14) { + wpa_printf(MSG_MSGDUMP, "handle_data: too short (%lu)", + (unsigned long) len); + return; + } + + hdr = (struct ieee8023_hdr *) buf; + + switch (ntohs(hdr->ethertype)) { + case ETH_P_PAE: + wpa_printf(MSG_MSGDUMP, "Received EAPOL packet"); + sa = hdr->src; + wired_possible_new_sta(hapd, sa); + + pos = (u8 *) (hdr + 1); + left = len - sizeof(*hdr); + + ieee802_1x_receive(hapd, sa, pos, left); + break; + + default: + wpa_printf(MSG_DEBUG, "Unknown ethertype 0x%04x in data frame", + ntohs(hdr->ethertype)); + break; + } +} + + +static void handle_read(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct hostapd_data *hapd = (struct hostapd_data *) eloop_ctx; + int len; + unsigned char buf[3000]; + + len = recv(sock, buf, sizeof(buf), 0); + if (len < 0) { + perror("recv"); + return; + } + + handle_data(hapd, buf, len); +} + + +static void handle_dhcp(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct hostapd_data *hapd = (struct hostapd_data *) eloop_ctx; + int len; + unsigned char buf[3000]; + struct dhcp_message *msg; + u8 *mac_address; + + len = recv(sock, buf, sizeof(buf), 0); + if (len < 0) { + perror("recv"); + return; + } + + /* must contain at least dhcp_message->chaddr */ + if (len < 44) { + wpa_printf(MSG_MSGDUMP, "handle_dhcp: too short (%d)", len); + return; + } + + msg = (struct dhcp_message *) buf; + mac_address = (u8 *) &(msg->chaddr); + + wpa_printf(MSG_MSGDUMP, "Got DHCP broadcast packet from " MACSTR, + MAC2STR(mac_address)); + + wired_possible_new_sta(hapd, mac_address); +} + + +static int wired_init_sockets(struct wired_driver_data *drv) +{ + struct hostapd_data *hapd = drv->hapd; + struct ifreq ifr; + struct sockaddr_ll addr; + struct sockaddr_in addr2; + struct packet_mreq mreq; + u8 multicastgroup_eapol[6] = WIRED_EAPOL_MULTICAST_GROUP; + int n = 1; + + drv->sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PAE)); + if (drv->sock < 0) { + perror("socket[PF_PACKET,SOCK_RAW]"); + return -1; + } + + if (eloop_register_read_sock(drv->sock, handle_read, hapd, NULL)) { + printf("Could not register read socket\n"); + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, hapd->conf->iface, sizeof(ifr.ifr_name)); + if (ioctl(drv->sock, SIOCGIFINDEX, &ifr) != 0) { + perror("ioctl(SIOCGIFINDEX)"); + return -1; + } + + + memset(&addr, 0, sizeof(addr)); + addr.sll_family = AF_PACKET; + addr.sll_ifindex = ifr.ifr_ifindex; + wpa_printf(MSG_DEBUG, "Opening raw packet socket for ifindex %d", + addr.sll_ifindex); + + if (bind(drv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("bind"); + return -1; + } + + /* filter multicast address */ + memset(&mreq, 0, sizeof(mreq)); + mreq.mr_ifindex = ifr.ifr_ifindex; + mreq.mr_type = PACKET_MR_MULTICAST; + mreq.mr_alen = 6; + memcpy(mreq.mr_address, multicastgroup_eapol, mreq.mr_alen); + + if (setsockopt(drv->sock, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, + sizeof(mreq)) < 0) { + perror("setsockopt[SOL_SOCKET,PACKET_ADD_MEMBERSHIP]"); + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, hapd->conf->iface, sizeof(ifr.ifr_name)); + if (ioctl(drv->sock, SIOCGIFHWADDR, &ifr) != 0) { + perror("ioctl(SIOCGIFHWADDR)"); + return -1; + } + + if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { + printf("Invalid HW-addr family 0x%04x\n", + ifr.ifr_hwaddr.sa_family); + return -1; + } + memcpy(hapd->own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); + + /* setup dhcp listen socket for sta detection */ + if ((drv->dhcp_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { + perror("socket call failed for dhcp"); + return -1; + } + + if (eloop_register_read_sock(drv->dhcp_sock, handle_dhcp, hapd, NULL)) + { + printf("Could not register read socket\n"); + return -1; + } + + memset(&addr2, 0, sizeof(addr2)); + addr2.sin_family = AF_INET; + addr2.sin_port = htons(67); + addr2.sin_addr.s_addr = INADDR_ANY; + + if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_REUSEADDR, (char *) &n, + sizeof(n)) == -1) { + perror("setsockopt[SOL_SOCKET,SO_REUSEADDR]"); + return -1; + } + if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_BROADCAST, (char *) &n, + sizeof(n)) == -1) { + perror("setsockopt[SOL_SOCKET,SO_BROADCAST]"); + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_ifrn.ifrn_name, hapd->conf->iface, IFNAMSIZ); + if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_BINDTODEVICE, + (char *) &ifr, sizeof(ifr)) < 0) { + perror("setsockopt[SOL_SOCKET,SO_BINDTODEVICE]"); + return -1; + } + + if (bind(drv->dhcp_sock, (struct sockaddr *) &addr2, + sizeof(struct sockaddr)) == -1) { + perror("bind"); + return -1; + } + + return 0; +} + + +static int wired_send_eapol(void *priv, const u8 *addr, + const u8 *data, size_t data_len, int encrypt, + const u8 *own_addr) +{ + struct wired_driver_data *drv = priv; + u8 pae_group_addr[ETH_ALEN] = WIRED_EAPOL_MULTICAST_GROUP; + struct ieee8023_hdr *hdr; + size_t len; + u8 *pos; + int res; + + len = sizeof(*hdr) + data_len; + hdr = os_zalloc(len); + if (hdr == NULL) { + printf("malloc() failed for wired_send_eapol(len=%lu)\n", + (unsigned long) len); + return -1; + } + + memcpy(hdr->dest, drv->use_pae_group_addr ? pae_group_addr : addr, + ETH_ALEN); + memcpy(hdr->src, own_addr, ETH_ALEN); + hdr->ethertype = htons(ETH_P_PAE); + + pos = (u8 *) (hdr + 1); + memcpy(pos, data, data_len); + + res = send(drv->sock, (u8 *) hdr, len, 0); + free(hdr); + + if (res < 0) { + perror("wired_send_eapol: send"); + printf("wired_send_eapol - packet len: %lu - failed\n", + (unsigned long) len); + } + + return res; +} + + +static void * wired_driver_init(struct hostapd_data *hapd) +{ + struct wired_driver_data *drv; + + drv = os_zalloc(sizeof(struct wired_driver_data)); + if (drv == NULL) { + printf("Could not allocate memory for wired driver data\n"); + return NULL; + } + + drv->hapd = hapd; + drv->use_pae_group_addr = hapd->conf->use_pae_group_addr; + + if (wired_init_sockets(drv)) { + free(drv); + return NULL; + } + + return drv; +} + + +static void wired_driver_deinit(void *priv) +{ + struct wired_driver_data *drv = priv; + + if (drv->sock >= 0) + close(drv->sock); + + if (drv->dhcp_sock >= 0) + close(drv->dhcp_sock); + + free(drv); +} + + +const struct wpa_driver_ops wpa_driver_wired_ops = { + .name = "wired", + .init = wired_driver_init, + .deinit = wired_driver_deinit, + .send_eapol = wired_send_eapol, +}; diff --git a/hostapd/drivers.c b/hostapd/drivers.c index 3006190..bde6e60 100644 --- a/hostapd/drivers.c +++ b/hostapd/drivers.c @@ -27,6 +27,9 @@ extern struct wpa_driver_ops wpa_driver_prism54_ops; /* driver_prism54.c */ #ifdef CONFIG_DRIVER_MADWIFI extern struct wpa_driver_ops wpa_driver_madwifi_ops; /* driver_madwifi.c */ #endif /* CONFIG_DRIVER_MADWIFI */ +#ifdef CONFIG_DRIVER_ATHEROS +extern struct wpa_driver_ops wpa_driver_atheros_ops; /* driver_atheros.c */ +#endif /* CONFIG_DRIVER_ATHEROS */ #ifdef CONFIG_DRIVER_BSD extern struct wpa_driver_ops wpa_driver_bsd_ops; /* driver_bsd.c */ #endif /* CONFIG_DRIVER_BSD */ @@ -55,6 +58,9 @@ struct wpa_driver_ops *hostapd_drivers[] = #ifdef CONFIG_DRIVER_MADWIFI &wpa_driver_madwifi_ops, #endif /* CONFIG_DRIVER_MADWIFI */ +#ifdef CONFIG_DRIVER_ATHEROS + &wpa_driver_atheros_ops, +#endif /* CONFIG_DRIVER_ATHEROS */ #ifdef CONFIG_DRIVER_BSD &wpa_driver_bsd_ops, #endif /* CONFIG_DRIVER_BSD */ diff --git a/hostapd/hostapd.8 b/hostapd/hostapd.8 index 9258512..a67a1bc 100644 --- a/hostapd/hostapd.8 +++ b/hostapd/hostapd.8 @@ -3,7 +3,7 @@ hostapd \- IEEE 802.11 AP, IEEE 802.1X/WPA/WPA2/EAP/RADIUS Authenticator .SH SYNOPSIS .B hostapd -[-hdBKtv] [-P <PID file>] <configuration file(s)> +[\-hdBKtv] [\-P <PID file>] <configuration file(s)> .SH DESCRIPTION This manual page documents briefly the .B hostapd diff --git a/hostapd/hostapd.c b/hostapd/hostapd.c index df6062b..3fbd3d0 100644 --- a/hostapd/hostapd.c +++ b/hostapd/hostapd.c @@ -249,7 +249,7 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta, if (!hapd->conf->ieee802_1x && !hapd->conf->wpa) accounting_sta_start(hapd, sta); - hostapd_wme_sta_config(hapd, sta); + hostapd_wmm_sta_config(hapd, sta); /* Start IEEE 802.1X authentication process for new stations */ ieee802_1x_new_station(hapd, sta); @@ -306,7 +306,7 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, wconf->rsn_preauth = conf->rsn_preauth; wconf->eapol_version = conf->eapol_version; wconf->peerkey = conf->peerkey; - wconf->wme_enabled = conf->wme_enabled; + wconf->wmm_enabled = conf->wmm_enabled; wconf->okc = conf->okc; #ifdef CONFIG_IEEE80211W wconf->ieee80211w = conf->ieee80211w; @@ -339,6 +339,7 @@ int hostapd_reload_config(struct hostapd_iface *iface) struct hostapd_data *hapd = iface->bss[0]; struct hostapd_config *newconf, *oldconf; struct wpa_auth_config wpa_auth_conf; + size_t j; newconf = hostapd_config_read(iface->config_fname); if (newconf == NULL) @@ -348,7 +349,8 @@ int hostapd_reload_config(struct hostapd_iface *iface) * Deauthenticate all stations since the new configuration may not * allow them to use the BSS anymore. */ - hostapd_flush_old_stations(hapd); + for (j = 0; j < iface->num_bss; j++) + hostapd_flush_old_stations(iface->bss[j]); /* TODO: update dynamic data based on changed configuration * items (e.g., open/close sockets, etc.) */ @@ -378,6 +380,16 @@ int hostapd_reload_config(struct hostapd_iface *iface) ieee802_11_set_beacon(hapd); + if (hapd->conf->ssid.ssid_set && + hostapd_set_ssid(hapd, (u8 *) hapd->conf->ssid.ssid, + hapd->conf->ssid.ssid_len)) { + wpa_printf(MSG_ERROR, "Could not set SSID for kernel driver"); + /* try to continue */ + } + + if (hapd->conf->ieee802_1x || hapd->conf->wpa) + hostapd_set_ieee8021x(hapd->conf->iface, hapd, 1); + hostapd_config_free(oldconf); wpa_printf(MSG_DEBUG, "Reconfigured interface %s", hapd->conf->iface); @@ -465,7 +477,7 @@ static void hostapd_dump_state(struct hostapd_data *hapd) (sta->flags & WLAN_STA_SHORT_PREAMBLE ? "[SHORT_PREAMBLE]" : ""), (sta->flags & WLAN_STA_PREAUTH ? "[PREAUTH]" : ""), - (sta->flags & WLAN_STA_WME ? "[WME]" : ""), + (sta->flags & WLAN_STA_WMM ? "[WMM]" : ""), (sta->flags & WLAN_STA_MFP ? "[MFP]" : ""), (sta->flags & WLAN_STA_WPS ? "[WPS]" : ""), (sta->flags & WLAN_STA_MAYBE_WPS ? "[MAYBE_WPS]" : ""), @@ -1308,6 +1320,13 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) } } + hostapd_flush_old_stations(hapd); + hostapd_set_privacy(hapd, 0); + + hostapd_broadcast_wep_clear(hapd); + if (hostapd_setup_encryption(hapd->conf->iface, hapd)) + return -1; + /* * Fetch the SSID from the system and use it or, * if one was specified in the config file, verify they @@ -1510,7 +1529,6 @@ static int setup_interface(struct hostapd_iface *iface) u8 *b = conf->bssid; int freq; size_t j; - int ret = 0; u8 *prev_addr; /* @@ -1582,9 +1600,6 @@ static int setup_interface(struct hostapd_iface *iface) } } - hostapd_flush_old_stations(hapd); - hostapd_set_privacy(hapd, 0); - if (hapd->iconf->channel) { freq = hostapd_hw_get_freq(hapd, hapd->iconf->channel); wpa_printf(MSG_DEBUG, "Mode: %s Channel: %d " @@ -1601,12 +1616,7 @@ static int setup_interface(struct hostapd_iface *iface) } } - hostapd_broadcast_wep_clear(hapd); - if (hostapd_setup_encryption(hapd->conf->iface, hapd)) - return -1; - hostapd_set_beacon_int(hapd, hapd->iconf->beacon_int); - ieee802_11_set_beacon(hapd); if (hapd->iconf->rts_threshold > -1 && hostapd_set_rts(hapd, hapd->iconf->rts_threshold)) { @@ -1644,7 +1654,7 @@ static int setup_interface(struct hostapd_iface *iface) return -1; } - return ret; + return 0; } @@ -1867,7 +1877,7 @@ int main(int argc, char *argv[]) int ret = 1, k; size_t i, j; int c, debug = 0, daemonize = 0, tnc = 0; - const char *pid_file = NULL; + char *pid_file = NULL; hostapd_logger_register_cb(hostapd_logger_cb); @@ -1891,7 +1901,8 @@ int main(int argc, char *argv[]) wpa_debug_show_keys++; break; case 'P': - pid_file = optarg; + os_free(pid_file); + pid_file = os_rel2abs_path(optarg); break; case 't': wpa_debug_timestamp++; @@ -2022,6 +2033,7 @@ int main(int argc, char *argv[]) eap_server_unregister_methods(); os_daemonize_terminate(pid_file); + os_free(pid_file); return ret; } diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index 0602cb0..703b646 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -245,14 +245,14 @@ ignore_broadcast_ssid=0 #tx_queue_beacon_cwmax=7 #tx_queue_beacon_burst=1.5 -# 802.1D Tag to AC mappings +# 802.1D Tag (= UP) to AC mappings # WMM specifies following mapping of data frames to different ACs. This mapping # can be configured using Linux QoS/tc and sch_pktpri.o module. # 802.1D Tag 802.1D Designation Access Category WMM Designation # 1 BK AC_BK Background # 2 - AC_BK Background # 0 BE AC_BE Best Effort -# 3 EE AC_VI Video +# 3 EE AC_BE Best Effort # 4 CL AC_VI Video # 5 VI AC_VI Video # 6 VO AC_VO Voice @@ -273,38 +273,38 @@ ignore_broadcast_ssid=0 # note - here cwMin and cmMax are in exponent form. the actual cw value used # will be (2^n)-1 where n is the value given here # -wme_enabled=1 +wmm_enabled=1 # # Low priority / AC_BK = background -wme_ac_bk_cwmin=4 -wme_ac_bk_cwmax=10 -wme_ac_bk_aifs=7 -wme_ac_bk_txop_limit=0 -wme_ac_bk_acm=0 +wmm_ac_bk_cwmin=4 +wmm_ac_bk_cwmax=10 +wmm_ac_bk_aifs=7 +wmm_ac_bk_txop_limit=0 +wmm_ac_bk_acm=0 # Note: for IEEE 802.11b mode: cWmin=5 cWmax=10 # # Normal priority / AC_BE = best effort -wme_ac_be_aifs=3 -wme_ac_be_cwmin=4 -wme_ac_be_cwmax=10 -wme_ac_be_txop_limit=0 -wme_ac_be_acm=0 +wmm_ac_be_aifs=3 +wmm_ac_be_cwmin=4 +wmm_ac_be_cwmax=10 +wmm_ac_be_txop_limit=0 +wmm_ac_be_acm=0 # Note: for IEEE 802.11b mode: cWmin=5 cWmax=7 # # High priority / AC_VI = video -wme_ac_vi_aifs=2 -wme_ac_vi_cwmin=3 -wme_ac_vi_cwmax=4 -wme_ac_vi_txop_limit=94 -wme_ac_vi_acm=0 +wmm_ac_vi_aifs=2 +wmm_ac_vi_cwmin=3 +wmm_ac_vi_cwmax=4 +wmm_ac_vi_txop_limit=94 +wmm_ac_vi_acm=0 # Note: for IEEE 802.11b mode: cWmin=4 cWmax=5 txop_limit=188 # # Highest priority / AC_VO = voice -wme_ac_vo_aifs=2 -wme_ac_vo_cwmin=2 -wme_ac_vo_cwmax=3 -wme_ac_vo_txop_limit=47 -wme_ac_vo_acm=0 +wmm_ac_vo_aifs=2 +wmm_ac_vo_cwmin=2 +wmm_ac_vo_cwmax=3 +wmm_ac_vo_txop_limit=47 +wmm_ac_vo_acm=0 # Note: for IEEE 802.11b mode: cWmin=3 cWmax=4 burst=102 # Static WEP key configuration @@ -375,6 +375,7 @@ wme_ac_vo_acm=0 # ieee80211n: Whether IEEE 802.11n (HT) is enabled # 0 = disabled (default) # 1 = enabled +# Note: You will also need to enable WMM for full HT functionality. #ieee80211n=1 # ht_capab: HT capabilities (list of flags) diff --git a/hostapd/hostapd_cli.1 b/hostapd/hostapd_cli.1 index 8d6587f..2fe4907 100644 --- a/hostapd/hostapd_cli.1 +++ b/hostapd/hostapd_cli.1 @@ -3,7 +3,7 @@ hostapd_cli \- hostapd command-line interface .SH SYNOPSIS .B hostapd_cli -[-p<path>] [-i<ifname>] [-hv] [command..] +[\-p<path>] [\-i<ifname>] [\-hv] [command..] .SH DESCRIPTION This manual page documents briefly the .B hostapd_cli diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c index 2614113..c2ecd4e 100644 --- a/hostapd/hostapd_cli.c +++ b/hostapd/hostapd_cli.c @@ -87,7 +87,7 @@ static const char *commands_help = " sa_query <addr> send SA Query to a station\n" #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_WPS -" wps_pin <uuid> <pin> add WPS Enrollee PIN (Device Password)\n" +" wps_pin <uuid> <pin> [timeout] add WPS Enrollee PIN (Device Password)\n" " wps_pbc indicate button pushed to initiate PBC\n" #endif /* CONFIG_WPS */ " help show this usage help\n" @@ -260,12 +260,16 @@ static int hostapd_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc, char *argv[]) { char buf[64]; - if (argc != 2) { - printf("Invalid 'wps_pin' command - exactly two arguments, " + if (argc < 2) { + printf("Invalid 'wps_pin' command - at least two arguments, " "UUID and PIN, are required.\n"); return -1; } - snprintf(buf, sizeof(buf), "WPS_PIN %s %s", argv[0], argv[1]); + if (argc > 2) + snprintf(buf, sizeof(buf), "WPS_PIN %s %s %s", + argv[0], argv[1], argv[2]); + else + snprintf(buf, sizeof(buf), "WPS_PIN %s %s", argv[0], argv[1]); return wpa_ctrl_command(ctrl, buf); } diff --git a/hostapd/hw_features.c b/hostapd/hw_features.c index f1288e0..1d6299e 100644 --- a/hostapd/hw_features.c +++ b/hostapd/hw_features.c @@ -382,6 +382,13 @@ int hostapd_select_hw_mode(struct hostapd_iface *iface) break; } } + if (iface->conf->channel == 0) { + /* TODO: could request a scan of neighboring BSSes and select + * the channel automatically */ + wpa_printf(MSG_ERROR, "Channel not configured " + "(hw_mode/channel in hostapd.conf)"); + return -1; + } if (ok == 0 && iface->conf->channel != 0) { hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, diff --git a/hostapd/ieee802_11.c b/hostapd/ieee802_11.c index 8399d88..70491b4 100644 --- a/hostapd/ieee802_11.c +++ b/hostapd/ieee802_11.c @@ -384,8 +384,8 @@ static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta, r = random(); os_memcpy(key, &now, 4); os_memcpy(key + 4, &r, 4); - rc4(sta->challenge, WLAN_AUTH_CHALLENGE_LEN, - key, sizeof(key)); + rc4_skip(key, sizeof(key), 0, + sta->challenge, WLAN_AUTH_CHALLENGE_LEN); } return 0; } @@ -585,7 +585,7 @@ static void handle_auth(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt, if (vlan_id > 0) { if (hostapd_get_vlan_id_ifname(hapd->conf->vlan, - sta->vlan_id) == NULL) { + vlan_id) == NULL) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, "Invalid VLAN ID " "%d received from RADIUS server", @@ -766,16 +766,16 @@ static void handle_assoc(struct hostapd_data *hapd, goto fail; } - sta->flags &= ~WLAN_STA_WME; - if (elems.wme && hapd->conf->wme_enabled) { - if (hostapd_eid_wme_valid(hapd, elems.wme, elems.wme_len)) + sta->flags &= ~WLAN_STA_WMM; + if (elems.wmm && hapd->conf->wmm_enabled) { + if (hostapd_eid_wmm_valid(hapd, elems.wmm, elems.wmm_len)) hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, HOSTAPD_LEVEL_DEBUG, - "invalid WME element in association " + "invalid WMM element in association " "request"); else - sta->flags |= WLAN_STA_WME; + sta->flags |= WLAN_STA_WMM; } if (!elems.supp_rates) { @@ -1124,8 +1124,8 @@ static void handle_assoc(struct hostapd_data *hapd, p = hostapd_eid_supp_rates(hapd, reply->u.assoc_resp.variable); /* Extended supported rates */ p = hostapd_eid_ext_supp_rates(hapd, p); - if (sta->flags & WLAN_STA_WME) - p = hostapd_eid_wme(hapd, p); + if (sta->flags & WLAN_STA_WMM) + p = hostapd_eid_wmm(hapd, p); p = hostapd_eid_ht_capabilities_info(hapd, p); p = hostapd_eid_ht_operation(hapd, p); @@ -1265,6 +1265,11 @@ void ieee802_11_send_sa_query_req(struct hostapd_data *hapd, struct ieee80211_mgmt mgmt; u8 *end; + wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Request to " + MACSTR, MAC2STR(addr)); + wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID", + trans_id, WLAN_SA_QUERY_TR_ID_LEN); + os_memset(&mgmt, 0, sizeof(mgmt)); mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_ACTION); @@ -1302,6 +1307,12 @@ static void hostapd_sa_query_action(struct hostapd_data *hapd, return; } + wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Response from " + MACSTR, MAC2STR(mgmt->sa)); + wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID", + mgmt->u.action.u.sa_query_resp.trans_id, + WLAN_SA_QUERY_TR_ID_LEN); + /* MLME-SAQuery.confirm */ sta = ap_get_sta(hapd, mgmt->sa); @@ -1330,12 +1341,21 @@ static void hostapd_sa_query_action(struct hostapd_data *hapd, "Reply to pending SA Query received"); ap_sta_stop_sa_query(hapd, sta); } + + +static int robust_action_frame(u8 category) +{ + return category != WLAN_ACTION_PUBLIC && + category != WLAN_ACTION_HT; +} #endif /* CONFIG_IEEE80211W */ static void handle_action(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt, size_t len) { + struct sta_info *sta; + if (len < IEEE80211_HDRLEN + 1) { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, @@ -1344,13 +1364,23 @@ static void handle_action(struct hostapd_data *hapd, return; } + sta = ap_get_sta(hapd, mgmt->sa); +#ifdef CONFIG_IEEE80211W + if (sta && (sta->flags & WLAN_STA_MFP) && + !(mgmt->frame_control & host_to_le16(WLAN_FC_ISWEP) && + robust_action_frame(mgmt->u.action.category))) { + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "Dropped unprotected Robust Action frame from " + "an MFP STA"); + return; + } +#endif /* CONFIG_IEEE80211W */ + switch (mgmt->u.action.category) { #ifdef CONFIG_IEEE80211R case WLAN_ACTION_FT: { - struct sta_info *sta; - - sta = ap_get_sta(hapd, mgmt->sa); if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) { wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignored FT Action " "frame from unassociated STA " MACSTR, @@ -1366,7 +1396,7 @@ static void handle_action(struct hostapd_data *hapd, } #endif /* CONFIG_IEEE80211R */ case WLAN_ACTION_WMM: - hostapd_wme_action(hapd, mgmt, len); + hostapd_wmm_action(hapd, mgmt, len); return; #ifdef CONFIG_IEEE80211W case WLAN_ACTION_SA_QUERY: @@ -1527,6 +1557,26 @@ static void handle_auth_cb(struct hostapd_data *hapd, } +#ifdef CONFIG_IEEE80211N +static void +hostapd_get_ht_capab(struct hostapd_data *hapd, + struct ht_cap_ie *ht_cap_ie, + struct ht_cap_ie *neg_ht_cap_ie) +{ + u16 cap; + + os_memcpy(neg_ht_cap_ie, ht_cap_ie, sizeof(struct ht_cap_ie)); + cap = le_to_host16(neg_ht_cap_ie->data.capabilities_info); + cap &= hapd->iconf->ht_capab; + cap |= (hapd->iconf->ht_capab & HT_CAP_INFO_SMPS_DISABLED); + + /* FIXME: Rx STBC needs to be handled specially */ + cap |= (hapd->iconf->ht_capab & HT_CAP_INFO_RX_STBC_MASK); + neg_ht_cap_ie->data.capabilities_info = host_to_le16(cap); +} +#endif /* CONFIG_IEEE80211N */ + + static void handle_assoc_cb(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt, size_t len, int reassoc, int ok) @@ -1534,7 +1584,11 @@ static void handle_assoc_cb(struct hostapd_data *hapd, u16 status; struct sta_info *sta; int new_assoc = 1; - struct ht_cap_ie *ht_cap = NULL; +#ifdef CONFIG_IEEE80211N + struct ht_cap_ie ht_cap; +#endif /* CONFIG_IEEE80211N */ + struct ht_cap_ie *ht_cap_ptr = NULL; + int set_flags, flags_and, flags_or; if (!ok) { hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211, @@ -1584,18 +1638,27 @@ static void handle_assoc_cb(struct hostapd_data *hapd, mlme_associate_indication(hapd, sta); #ifdef CONFIG_IEEE80211N - if (sta->flags & WLAN_STA_HT) - ht_cap = &sta->ht_capabilities; + if (sta->flags & WLAN_STA_HT) { + ht_cap_ptr = &ht_cap; + hostapd_get_ht_capab(hapd, &sta->ht_capabilities, ht_cap_ptr); + } #endif /* CONFIG_IEEE80211N */ #ifdef CONFIG_IEEE80211W sta->sa_query_timed_out = 0; #endif /* CONFIG_IEEE80211W */ + /* + * Remove the STA entry in order to make sure the STA PS state gets + * cleared and configuration gets updated in case of reassociation back + * to the same AP. + */ + hostapd_sta_remove(hapd, sta->addr); + if (hostapd_sta_add(hapd->conf->iface, hapd, sta->addr, sta->aid, sta->capability, sta->supported_rates, sta->supported_rates_len, 0, sta->listen_interval, - ht_cap)) + ht_cap_ptr)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE, @@ -1613,13 +1676,15 @@ static void handle_assoc_cb(struct hostapd_data *hapd, /* VLAN ID already set (e.g., by PMKSA caching), so bind STA */ ap_sta_bind_vlan(hapd, sta, 0); } - if (sta->flags & WLAN_STA_SHORT_PREAMBLE) { - hostapd_sta_set_flags(hapd, sta->addr, sta->flags, - WLAN_STA_SHORT_PREAMBLE, ~0); - } else { - hostapd_sta_set_flags(hapd, sta->addr, sta->flags, - 0, ~WLAN_STA_SHORT_PREAMBLE); - } + + set_flags = WLAN_STA_SHORT_PREAMBLE | WLAN_STA_WMM | WLAN_STA_MFP; + if (!hapd->conf->ieee802_1x && !hapd->conf->wpa && + sta->flags & WLAN_STA_AUTHORIZED) + set_flags |= WLAN_STA_AUTHORIZED; + flags_or = sta->flags & set_flags; + flags_and = sta->flags | ~set_flags; + hostapd_sta_set_flags(hapd, sta->addr, sta->flags, + flags_or, flags_and); if (sta->auth_alg == WLAN_AUTH_FT) wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT); diff --git a/hostapd/ieee802_1x.c b/hostapd/ieee802_1x.c index 9b096d4..7fd8028 100644 --- a/hostapd/ieee802_1x.c +++ b/hostapd/ieee802_1x.c @@ -164,7 +164,7 @@ static void ieee802_1x_tx_key_one(struct hostapd_data *hapd, } os_memcpy(ekey, key->key_iv, sizeof(key->key_iv)); os_memcpy(ekey + sizeof(key->key_iv), sm->eap_if->eapKeyData, 32); - rc4((u8 *) (key + 1), key_len, ekey, ekey_len); + rc4_skip(ekey, ekey_len, 0, (u8 *) (key + 1), key_len); os_free(ekey); /* This header is needed here for HMAC-MD5, but it will be regenerated diff --git a/hostapd/preauth.c b/hostapd/preauth.c index 36af4e3..9ab41ed 100644 --- a/hostapd/preauth.c +++ b/hostapd/preauth.c @@ -171,6 +171,7 @@ int rsn_preauth_iface_init(struct hostapd_data *hapd) if (rsn_preauth_iface_add(hapd, start)) { rsn_preauth_iface_deinit(hapd); + os_free(tmp); return -1; } diff --git a/hostapd/prism54.h b/hostapd/prism54.h new file mode 100644 index 0000000..cb0a9a1 --- /dev/null +++ b/hostapd/prism54.h @@ -0,0 +1,177 @@ +#ifndef PRISM54_H +#define PRISM54_H + +struct ieee802_3_hdr_s { + unsigned char da[6]; + unsigned char sa[6]; + unsigned short type; +} __attribute__ ((packed)); + +typedef struct ieee802_3_hdr_s ieee802_3_hdr; + +#define PIMOP_GET 0 +#define PIMOP_SET 1 +#define PIMOP_RESPONSE 2 +#define PIMOP_ERROR 3 +#define PIMOP_TRAP 4 + +struct pimdev_hdr_s { + int op; + unsigned long oid; +} __attribute__ ((packed)); + +typedef struct pimdev_hdr_s pimdev_hdr; + +#define DOT11_OID_ATTACHMENT 0x19000003 + +/* really need to check */ +#define DOT11_PKT_BEACON 0x80 +#define DOT11_PKT_ASSOC_RESP 0x10 +#define DOT11_PKT_REASSOC_RESP 0x30 +#define DOT11_PKT_PROBE_RESP 0x50 + +struct obj_attachment_hdr { + char type; + char reserved; + short id; + short size; +} __attribute__ ((packed)); + +struct obj_attachment { + char type; + char reserved; + short id; + short size; + char data[1]; +} __attribute__ ((packed)); + +#define DOT11_OID_MLMEAUTOLEVEL 0x19000001 +#define DOT11_MLME_AUTO 0 +#define DOT11_MLME_INTERMEDIATE 0x01000000 +#define DOT11_MLME_EXTENDED 0x02000000 + +#define DOT11_OID_DEAUTHENTICATE 0x18000000 +#define DOT11_OID_AUTHENTICATE 0x18000001 +#define DOT11_OID_DISASSOCIATE 0x18000002 +#define DOT11_OID_ASSOCIATE 0x18000003 +#define DOT11_OID_BEACON 0x18000005 +#define DOT11_OID_PROBE 0x18000006 +#define DOT11_OID_REASSOCIATE 0x1800000b + +struct obj_mlme { + char address[6]; + short id; + short state; + short code; +} __attribute__ ((packed)); + +#define DOT11_OID_DEAUTHENTICATEEX 0x18000007 +#define DOT11_OID_AUTHENTICATEEX 0x18000008 +#define DOT11_OID_DISASSOCIATEEX 0x18000009 +#define DOT11_OID_ASSOCIATEEX 0x1800000a +#define DOT11_OID_REASSOCIATEEX 0x1800000c + +struct obj_mlmeex { + char address[6]; + short id; + short state; + short code; + short size; + char data[1]; +} __attribute__ ((packed)); + +#define DOT11_OID_STAKEY 0x12000008 + +#define DOT11_PRIV_WEP 0 +#define DOT11_PRIV_TKIP 1 + +/* endian reversed to bigger endian */ +#define DOT11_STAKEY_OPTION_DEFAULTKEY 0x100 + +struct obj_stakey { + char address[6]; + char keyid; + char reserved; + short options; + char type; + char length; + char key[32]; +} __attribute__ ((packed)); + +#define DOT11_OID_DEFKEYID 0x12000003 +#define DOT11_OID_DEFKEY1 0x12000004 +#define DOT11_OID_DEFKEY2 0x12000005 +#define DOT11_OID_DEFKEY3 0x12000006 +#define DOT11_OID_DEFKEY4 0x12000007 + +struct obj_key { + char type; + char length; + char key[32]; +} __attribute__ ((packed)); + +#define DOT11_OID_STASC 0x1200000a + +struct obj_stasc { + char address[6]; + char keyid; + char tx_sc; + unsigned long sc_high; + unsigned short sc_low; +} __attribute__ ((packed)); + +#define DOT11_OID_CLIENTS 0x15000001 +#define DOT11_OID_CLIENTSASSOCIATED 0x15000002 +#define DOT11_OID_CLIENTST 0x15000003 +#define DOT11_OID_CLIENTEND 0x150007d9 +#define DOT11_OID_CLIENTFIND 0x150007db + +#define DOT11_NODE_UNKNOWN +#define DOT11_NODE_CLIENT +#define DOT11_NODE_AP + +/* endian reversed to bigger endian */ +#define DOT11_STATE_NONE 0 +#define DOT11_STATE_AUTHING 0x100 +#define DOT11_STATE_AUTH 0x200 +#define DOT11_STATE_ASSOCING 0x300 +#define DOT11_STATE_REASSOCING 0x400 +#define DOT11_STATE_ASSOC 0x500 +#define DOT11_STATE_WDS 0x600 + +struct obj_sta { + char address[6]; + char pad[2]; + char state; + char node; + short age; + char reserved1; + char rssi; + char rate; + char reserved2; +} __attribute__ ((packed)); + +#define DOT11_OID_SSID 0x10000002 +#define DOT11_OID_SSIDOVERRIDE 0x10000006 + +struct obj_ssid { + char length; + char octets[33]; +} __attribute__ ((packed)); + +#define DOT11_OID_EAPAUTHSTA 0x150007de +#define DOT11_OID_EAPUNAUTHSTA 0x150007df +/* not in 38801 datasheet??? */ +#define DOT11_OID_DOT1XENABLE 0x150007e0 +#define DOT11_OID_MICFAILURE 0x150007e1 +#define DOT11_OID_AUTHENABLE 0x12000000 +#define DOT11_OID_PRIVACYINVOKED 0x12000001 +#define DOT11_OID_EXUNENCRYPTED 0x12000002 + +#define DOT11_AUTH_OS 0x01000000 +#define DOT11_AUTH_SK 0x02000000 +#define DOT11_AUTH_BOTH 0x03000000 + +#define DOT11_BOOL_TRUE 0x01000000 + +#endif /* PRISM54_H */ diff --git a/hostapd/priv_netlink.h b/hostapd/priv_netlink.h new file mode 100644 index 0000000..d1f6f66 --- /dev/null +++ b/hostapd/priv_netlink.h @@ -0,0 +1,71 @@ +#ifndef PRIV_NETLINK_H +#define PRIV_NETLINK_H + +/* Private copy of needed Linux netlink/rtnetlink definitions. + * + * This should be replaced with user space header once one is available with C + * library, etc.. + */ + +#ifndef IFLA_IFNAME +#define IFLA_IFNAME 3 +#endif +#ifndef IFLA_WIRELESS +#define IFLA_WIRELESS 11 +#endif + +#define NETLINK_ROUTE 0 +#define RTMGRP_LINK 1 +#define RTM_BASE 0x10 +#define RTM_NEWLINK (RTM_BASE + 0) +#define RTM_DELLINK (RTM_BASE + 1) + +#define NLMSG_ALIGNTO 4 +#define NLMSG_ALIGN(len) (((len) + NLMSG_ALIGNTO - 1) & ~(NLMSG_ALIGNTO - 1)) +#define NLMSG_LENGTH(len) ((len) + NLMSG_ALIGN(sizeof(struct nlmsghdr))) +#define NLMSG_DATA(nlh) ((void*) (((char*) nlh) + NLMSG_LENGTH(0))) + +#define RTA_ALIGNTO 4 +#define RTA_ALIGN(len) (((len) + RTA_ALIGNTO - 1) & ~(RTA_ALIGNTO - 1)) +#define RTA_OK(rta,len) \ +((len) > 0 && (rta)->rta_len >= sizeof(struct rtattr) && \ +(rta)->rta_len <= (len)) +#define RTA_NEXT(rta,attrlen) \ +((attrlen) -= RTA_ALIGN((rta)->rta_len), \ +(struct rtattr *) (((char *)(rta)) + RTA_ALIGN((rta)->rta_len))) + + +struct sockaddr_nl +{ + sa_family_t nl_family; + unsigned short nl_pad; + u32 nl_pid; + u32 nl_groups; +}; + +struct nlmsghdr +{ + u32 nlmsg_len; + u16 nlmsg_type; + u16 nlmsg_flags; + u32 nlmsg_seq; + u32 nlmsg_pid; +}; + +struct ifinfomsg +{ + unsigned char ifi_family; + unsigned char __ifi_pad; + unsigned short ifi_type; + int ifi_index; + unsigned ifi_flags; + unsigned ifi_change; +}; + +struct rtattr +{ + unsigned short rta_len; + unsigned short rta_type; +}; + +#endif /* PRIV_NETLINK_H */ diff --git a/hostapd/radiotap.c b/hostapd/radiotap.c new file mode 100644 index 0000000..804473f --- /dev/null +++ b/hostapd/radiotap.c @@ -0,0 +1,287 @@ +/* + * Radiotap parser + * + * Copyright 2007 Andy Green <andy@warmcat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * + * Modified for userspace by Johannes Berg <johannes@sipsolutions.net> + * I only modified some things on top to ease syncing should bugs be found. + */ + +#include "includes.h" + +#include "common.h" +#include "radiotap_iter.h" + +#define le16_to_cpu le_to_host16 +#define le32_to_cpu le_to_host32 +#define __le32 uint32_t +#define ulong unsigned long +#define unlikely(cond) (cond) +#define get_unaligned(p) \ +({ \ + struct packed_dummy_struct { \ + typeof(*(p)) __val; \ + } __attribute__((packed)) *__ptr = (void *) (p); \ + \ + __ptr->__val; \ +}) + +/* function prototypes and related defs are in radiotap_iter.h */ + +/** + * ieee80211_radiotap_iterator_init - radiotap parser iterator initialization + * @iterator: radiotap_iterator to initialize + * @radiotap_header: radiotap header to parse + * @max_length: total length we can parse into (eg, whole packet length) + * + * Returns: 0 or a negative error code if there is a problem. + * + * This function initializes an opaque iterator struct which can then + * be passed to ieee80211_radiotap_iterator_next() to visit every radiotap + * argument which is present in the header. It knows about extended + * present headers and handles them. + * + * How to use: + * call __ieee80211_radiotap_iterator_init() to init a semi-opaque iterator + * struct ieee80211_radiotap_iterator (no need to init the struct beforehand) + * checking for a good 0 return code. Then loop calling + * __ieee80211_radiotap_iterator_next()... it returns either 0, + * -ENOENT if there are no more args to parse, or -EINVAL if there is a problem. + * The iterator's @this_arg member points to the start of the argument + * associated with the current argument index that is present, which can be + * found in the iterator's @this_arg_index member. This arg index corresponds + * to the IEEE80211_RADIOTAP_... defines. + * + * Radiotap header length: + * You can find the CPU-endian total radiotap header length in + * iterator->max_length after executing ieee80211_radiotap_iterator_init() + * successfully. + * + * Alignment Gotcha: + * You must take care when dereferencing iterator.this_arg + * for multibyte types... the pointer is not aligned. Use + * get_unaligned((type *)iterator.this_arg) to dereference + * iterator.this_arg for type "type" safely on all arches. + * + * Example code: + * See Documentation/networking/radiotap-headers.txt + */ + +int ieee80211_radiotap_iterator_init( + struct ieee80211_radiotap_iterator *iterator, + struct ieee80211_radiotap_header *radiotap_header, + int max_length) +{ + /* Linux only supports version 0 radiotap format */ + if (radiotap_header->it_version) + return -EINVAL; + + /* sanity check for allowed length and radiotap length field */ + if (max_length < le16_to_cpu(get_unaligned(&radiotap_header->it_len))) + return -EINVAL; + + iterator->rtheader = radiotap_header; + iterator->max_length = le16_to_cpu(get_unaligned( + &radiotap_header->it_len)); + iterator->arg_index = 0; + iterator->bitmap_shifter = le32_to_cpu(get_unaligned( + &radiotap_header->it_present)); + iterator->arg = (u8 *)radiotap_header + sizeof(*radiotap_header); + iterator->this_arg = NULL; + + /* find payload start allowing for extended bitmap(s) */ + + if (unlikely(iterator->bitmap_shifter & (1<<IEEE80211_RADIOTAP_EXT))) { + while (le32_to_cpu(get_unaligned((__le32 *)iterator->arg)) & + (1<<IEEE80211_RADIOTAP_EXT)) { + iterator->arg += sizeof(u32); + + /* + * check for insanity where the present bitmaps + * keep claiming to extend up to or even beyond the + * stated radiotap header length + */ + + if (((ulong)iterator->arg - (ulong)iterator->rtheader) + > (ulong)iterator->max_length) + return -EINVAL; + } + + iterator->arg += sizeof(u32); + + /* + * no need to check again for blowing past stated radiotap + * header length, because ieee80211_radiotap_iterator_next + * checks it before it is dereferenced + */ + } + + /* we are all initialized happily */ + + return 0; +} + + +/** + * ieee80211_radiotap_iterator_next - return next radiotap parser iterator arg + * @iterator: radiotap_iterator to move to next arg (if any) + * + * Returns: 0 if there is an argument to handle, + * -ENOENT if there are no more args or -EINVAL + * if there is something else wrong. + * + * This function provides the next radiotap arg index (IEEE80211_RADIOTAP_*) + * in @this_arg_index and sets @this_arg to point to the + * payload for the field. It takes care of alignment handling and extended + * present fields. @this_arg can be changed by the caller (eg, + * incremented to move inside a compound argument like + * IEEE80211_RADIOTAP_CHANNEL). The args pointed to are in + * little-endian format whatever the endianess of your CPU. + * + * Alignment Gotcha: + * You must take care when dereferencing iterator.this_arg + * for multibyte types... the pointer is not aligned. Use + * get_unaligned((type *)iterator.this_arg) to dereference + * iterator.this_arg for type "type" safely on all arches. + */ + +int ieee80211_radiotap_iterator_next( + struct ieee80211_radiotap_iterator *iterator) +{ + + /* + * small length lookup table for all radiotap types we heard of + * starting from b0 in the bitmap, so we can walk the payload + * area of the radiotap header + * + * There is a requirement to pad args, so that args + * of a given length must begin at a boundary of that length + * -- but note that compound args are allowed (eg, 2 x u16 + * for IEEE80211_RADIOTAP_CHANNEL) so total arg length is not + * a reliable indicator of alignment requirement. + * + * upper nybble: content alignment for arg + * lower nybble: content length for arg + */ + + static const u8 rt_sizes[] = { + [IEEE80211_RADIOTAP_TSFT] = 0x88, + [IEEE80211_RADIOTAP_FLAGS] = 0x11, + [IEEE80211_RADIOTAP_RATE] = 0x11, + [IEEE80211_RADIOTAP_CHANNEL] = 0x24, + [IEEE80211_RADIOTAP_FHSS] = 0x22, + [IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = 0x11, + [IEEE80211_RADIOTAP_DBM_ANTNOISE] = 0x11, + [IEEE80211_RADIOTAP_LOCK_QUALITY] = 0x22, + [IEEE80211_RADIOTAP_TX_ATTENUATION] = 0x22, + [IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = 0x22, + [IEEE80211_RADIOTAP_DBM_TX_POWER] = 0x11, + [IEEE80211_RADIOTAP_ANTENNA] = 0x11, + [IEEE80211_RADIOTAP_DB_ANTSIGNAL] = 0x11, + [IEEE80211_RADIOTAP_DB_ANTNOISE] = 0x11, + [IEEE80211_RADIOTAP_RX_FLAGS] = 0x22, + [IEEE80211_RADIOTAP_TX_FLAGS] = 0x22, + [IEEE80211_RADIOTAP_RTS_RETRIES] = 0x11, + [IEEE80211_RADIOTAP_DATA_RETRIES] = 0x11, + /* + * add more here as they are defined in + * include/net/ieee80211_radiotap.h + */ + }; + + /* + * for every radiotap entry we can at + * least skip (by knowing the length)... + */ + + while (iterator->arg_index < (int) sizeof(rt_sizes)) { + int hit = 0; + int pad; + + if (!(iterator->bitmap_shifter & 1)) + goto next_entry; /* arg not present */ + + /* + * arg is present, account for alignment padding + * 8-bit args can be at any alignment + * 16-bit args must start on 16-bit boundary + * 32-bit args must start on 32-bit boundary + * 64-bit args must start on 64-bit boundary + * + * note that total arg size can differ from alignment of + * elements inside arg, so we use upper nybble of length + * table to base alignment on + * + * also note: these alignments are ** relative to the + * start of the radiotap header **. There is no guarantee + * that the radiotap header itself is aligned on any + * kind of boundary. + * + * the above is why get_unaligned() is used to dereference + * multibyte elements from the radiotap area + */ + + pad = (((ulong)iterator->arg) - + ((ulong)iterator->rtheader)) & + ((rt_sizes[iterator->arg_index] >> 4) - 1); + + if (pad) + iterator->arg += + (rt_sizes[iterator->arg_index] >> 4) - pad; + + /* + * this is what we will return to user, but we need to + * move on first so next call has something fresh to test + */ + iterator->this_arg_index = iterator->arg_index; + iterator->this_arg = iterator->arg; + hit = 1; + + /* internally move on the size of this arg */ + iterator->arg += rt_sizes[iterator->arg_index] & 0x0f; + + /* + * check for insanity where we are given a bitmap that + * claims to have more arg content than the length of the + * radiotap section. We will normally end up equalling this + * max_length on the last arg, never exceeding it. + */ + + if (((ulong)iterator->arg - (ulong)iterator->rtheader) > + (ulong) iterator->max_length) + return -EINVAL; + + next_entry: + iterator->arg_index++; + if (unlikely((iterator->arg_index & 31) == 0)) { + /* completed current u32 bitmap */ + if (iterator->bitmap_shifter & 1) { + /* b31 was set, there is more */ + /* move to next u32 bitmap */ + iterator->bitmap_shifter = le32_to_cpu( + get_unaligned(iterator->next_bitmap)); + iterator->next_bitmap++; + } else + /* no more bitmaps: end */ + iterator->arg_index = sizeof(rt_sizes); + } else /* just try the next bit */ + iterator->bitmap_shifter >>= 1; + + /* if we found a valid arg earlier, return it now */ + if (hit) + return 0; + } + + /* we don't know how to handle any more args, we're done */ + return -ENOENT; +} diff --git a/hostapd/radiotap.h b/hostapd/radiotap.h new file mode 100644 index 0000000..508264c --- /dev/null +++ b/hostapd/radiotap.h @@ -0,0 +1,242 @@ +/* $FreeBSD: src/sys/net80211/ieee80211_radiotap.h,v 1.5 2005/01/22 20:12:05 sam Exp $ */ +/* $NetBSD: ieee80211_radiotap.h,v 1.11 2005/06/22 06:16:02 dyoung Exp $ */ + +/*- + * Copyright (c) 2003, 2004 David Young. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of David Young may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY DAVID YOUNG ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DAVID + * YOUNG BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +/* + * Modifications to fit into the linux IEEE 802.11 stack, + * Mike Kershaw (dragorn@kismetwireless.net) + */ + +#ifndef IEEE80211RADIOTAP_H +#define IEEE80211RADIOTAP_H + +#include <stdint.h> + +/* Base version of the radiotap packet header data */ +#define PKTHDR_RADIOTAP_VERSION 0 + +/* A generic radio capture format is desirable. There is one for + * Linux, but it is neither rigidly defined (there were not even + * units given for some fields) nor easily extensible. + * + * I suggest the following extensible radio capture format. It is + * based on a bitmap indicating which fields are present. + * + * I am trying to describe precisely what the application programmer + * should expect in the following, and for that reason I tell the + * units and origin of each measurement (where it applies), or else I + * use sufficiently weaselly language ("is a monotonically nondecreasing + * function of...") that I cannot set false expectations for lawyerly + * readers. + */ + +/* The radio capture header precedes the 802.11 header. + * All data in the header is little endian on all platforms. + */ +struct ieee80211_radiotap_header { + uint8_t it_version; /* Version 0. Only increases + * for drastic changes, + * introduction of compatible + * new fields does not count. + */ + uint8_t it_pad; + uint16_t it_len; /* length of the whole + * header in bytes, including + * it_version, it_pad, + * it_len, and data fields. + */ + uint32_t it_present; /* A bitmap telling which + * fields are present. Set bit 31 + * (0x80000000) to extend the + * bitmap by another 32 bits. + * Additional extensions are made + * by setting bit 31. + */ +}; + +/* Name Data type Units + * ---- --------- ----- + * + * IEEE80211_RADIOTAP_TSFT __le64 microseconds + * + * Value in microseconds of the MAC's 64-bit 802.11 Time + * Synchronization Function timer when the first bit of the + * MPDU arrived at the MAC. For received frames, only. + * + * IEEE80211_RADIOTAP_CHANNEL 2 x uint16_t MHz, bitmap + * + * Tx/Rx frequency in MHz, followed by flags (see below). + * + * IEEE80211_RADIOTAP_FHSS uint16_t see below + * + * For frequency-hopping radios, the hop set (first byte) + * and pattern (second byte). + * + * IEEE80211_RADIOTAP_RATE u8 500kb/s + * + * Tx/Rx data rate + * + * IEEE80211_RADIOTAP_DBM_ANTSIGNAL s8 decibels from + * one milliwatt (dBm) + * + * RF signal power at the antenna, decibel difference from + * one milliwatt. + * + * IEEE80211_RADIOTAP_DBM_ANTNOISE s8 decibels from + * one milliwatt (dBm) + * + * RF noise power at the antenna, decibel difference from one + * milliwatt. + * + * IEEE80211_RADIOTAP_DB_ANTSIGNAL u8 decibel (dB) + * + * RF signal power at the antenna, decibel difference from an + * arbitrary, fixed reference. + * + * IEEE80211_RADIOTAP_DB_ANTNOISE u8 decibel (dB) + * + * RF noise power at the antenna, decibel difference from an + * arbitrary, fixed reference point. + * + * IEEE80211_RADIOTAP_LOCK_QUALITY uint16_t unitless + * + * Quality of Barker code lock. Unitless. Monotonically + * nondecreasing with "better" lock strength. Called "Signal + * Quality" in datasheets. (Is there a standard way to measure + * this?) + * + * IEEE80211_RADIOTAP_TX_ATTENUATION uint16_t unitless + * + * Transmit power expressed as unitless distance from max + * power set at factory calibration. 0 is max power. + * Monotonically nondecreasing with lower power levels. + * + * IEEE80211_RADIOTAP_DB_TX_ATTENUATION uint16_t decibels (dB) + * + * Transmit power expressed as decibel distance from max power + * set at factory calibration. 0 is max power. Monotonically + * nondecreasing with lower power levels. + * + * IEEE80211_RADIOTAP_DBM_TX_POWER s8 decibels from + * one milliwatt (dBm) + * + * Transmit power expressed as dBm (decibels from a 1 milliwatt + * reference). This is the absolute power level measured at + * the antenna port. + * + * IEEE80211_RADIOTAP_FLAGS u8 bitmap + * + * Properties of transmitted and received frames. See flags + * defined below. + * + * IEEE80211_RADIOTAP_ANTENNA u8 antenna index + * + * Unitless indication of the Rx/Tx antenna for this packet. + * The first antenna is antenna 0. + * + * IEEE80211_RADIOTAP_RX_FLAGS uint16_t bitmap + * + * Properties of received frames. See flags defined below. + * + * IEEE80211_RADIOTAP_TX_FLAGS uint16_t bitmap + * + * Properties of transmitted frames. See flags defined below. + * + * IEEE80211_RADIOTAP_RTS_RETRIES u8 data + * + * Number of rts retries a transmitted frame used. + * + * IEEE80211_RADIOTAP_DATA_RETRIES u8 data + * + * Number of unicast retries a transmitted frame used. + * + */ +enum ieee80211_radiotap_type { + IEEE80211_RADIOTAP_TSFT = 0, + IEEE80211_RADIOTAP_FLAGS = 1, + IEEE80211_RADIOTAP_RATE = 2, + IEEE80211_RADIOTAP_CHANNEL = 3, + IEEE80211_RADIOTAP_FHSS = 4, + IEEE80211_RADIOTAP_DBM_ANTSIGNAL = 5, + IEEE80211_RADIOTAP_DBM_ANTNOISE = 6, + IEEE80211_RADIOTAP_LOCK_QUALITY = 7, + IEEE80211_RADIOTAP_TX_ATTENUATION = 8, + IEEE80211_RADIOTAP_DB_TX_ATTENUATION = 9, + IEEE80211_RADIOTAP_DBM_TX_POWER = 10, + IEEE80211_RADIOTAP_ANTENNA = 11, + IEEE80211_RADIOTAP_DB_ANTSIGNAL = 12, + IEEE80211_RADIOTAP_DB_ANTNOISE = 13, + IEEE80211_RADIOTAP_RX_FLAGS = 14, + IEEE80211_RADIOTAP_TX_FLAGS = 15, + IEEE80211_RADIOTAP_RTS_RETRIES = 16, + IEEE80211_RADIOTAP_DATA_RETRIES = 17, + IEEE80211_RADIOTAP_EXT = 31 +}; + +/* Channel flags. */ +#define IEEE80211_CHAN_TURBO 0x0010 /* Turbo channel */ +#define IEEE80211_CHAN_CCK 0x0020 /* CCK channel */ +#define IEEE80211_CHAN_OFDM 0x0040 /* OFDM channel */ +#define IEEE80211_CHAN_2GHZ 0x0080 /* 2 GHz spectrum channel. */ +#define IEEE80211_CHAN_5GHZ 0x0100 /* 5 GHz spectrum channel */ +#define IEEE80211_CHAN_PASSIVE 0x0200 /* Only passive scan allowed */ +#define IEEE80211_CHAN_DYN 0x0400 /* Dynamic CCK-OFDM channel */ +#define IEEE80211_CHAN_GFSK 0x0800 /* GFSK channel (FHSS PHY) */ + +/* For IEEE80211_RADIOTAP_FLAGS */ +#define IEEE80211_RADIOTAP_F_CFP 0x01 /* sent/received + * during CFP + */ +#define IEEE80211_RADIOTAP_F_SHORTPRE 0x02 /* sent/received + * with short + * preamble + */ +#define IEEE80211_RADIOTAP_F_WEP 0x04 /* sent/received + * with WEP encryption + */ +#define IEEE80211_RADIOTAP_F_FRAG 0x08 /* sent/received + * with fragmentation + */ +#define IEEE80211_RADIOTAP_F_FCS 0x10 /* frame includes FCS */ +#define IEEE80211_RADIOTAP_F_DATAPAD 0x20 /* frame has padding between + * 802.11 header and payload + * (to 32-bit boundary) + */ +/* For IEEE80211_RADIOTAP_RX_FLAGS */ +#define IEEE80211_RADIOTAP_F_RX_BADFCS 0x0001 /* frame failed crc check */ + +/* For IEEE80211_RADIOTAP_TX_FLAGS */ +#define IEEE80211_RADIOTAP_F_TX_FAIL 0x0001 /* failed due to excessive + * retries */ +#define IEEE80211_RADIOTAP_F_TX_CTS 0x0002 /* used cts 'protection' */ +#define IEEE80211_RADIOTAP_F_TX_RTS 0x0004 /* used rts/cts handshake */ + +#endif /* IEEE80211_RADIOTAP_H */ diff --git a/hostapd/radiotap_iter.h b/hostapd/radiotap_iter.h new file mode 100644 index 0000000..92a798a --- /dev/null +++ b/hostapd/radiotap_iter.h @@ -0,0 +1,41 @@ +#ifndef __RADIOTAP_ITER_H +#define __RADIOTAP_ITER_H + +#include "radiotap.h" + +/* Radiotap header iteration + * implemented in radiotap.c + */ +/** + * struct ieee80211_radiotap_iterator - tracks walk thru present radiotap args + * @rtheader: pointer to the radiotap header we are walking through + * @max_length: length of radiotap header in cpu byte ordering + * @this_arg_index: IEEE80211_RADIOTAP_... index of current arg + * @this_arg: pointer to current radiotap arg + * @arg_index: internal next argument index + * @arg: internal next argument pointer + * @next_bitmap: internal pointer to next present u32 + * @bitmap_shifter: internal shifter for curr u32 bitmap, b0 set == arg present + */ + +struct ieee80211_radiotap_iterator { + struct ieee80211_radiotap_header *rtheader; + int max_length; + int this_arg_index; + unsigned char *this_arg; + + int arg_index; + unsigned char *arg; + uint32_t *next_bitmap; + uint32_t bitmap_shifter; +}; + +extern int ieee80211_radiotap_iterator_init( + struct ieee80211_radiotap_iterator *iterator, + struct ieee80211_radiotap_header *radiotap_header, + int max_length); + +extern int ieee80211_radiotap_iterator_next( + struct ieee80211_radiotap_iterator *iterator); + +#endif /* __RADIOTAP_ITER_H */ diff --git a/hostapd/wme.c b/hostapd/wme.c index 727ee7e..f2bbbd9 100644 --- a/hostapd/wme.c +++ b/hostapd/wme.c @@ -24,47 +24,63 @@ /* TODO: maintain separate sequence and fragment numbers for each AC * TODO: IGMP snooping to track which multicasts to forward - and use QOS-DATA - * if only WME stations are receiving a certain group */ + * if only WMM stations are receiving a certain group */ -static u8 wme_oui[3] = { 0x00, 0x50, 0xf2 }; +static inline u8 wmm_aci_aifsn(int aifsn, int acm, int aci) +{ + u8 ret; + ret = (aifsn << WMM_AC_AIFNS_SHIFT) & WMM_AC_AIFSN_MASK; + if (acm) + ret |= WMM_AC_ACM; + ret |= (aci << WMM_AC_ACI_SHIFT) & WMM_AC_ACI_MASK; + return ret; +} + + +static inline u8 wmm_ecw(int ecwmin, int ecwmax) +{ + return ((ecwmin << WMM_AC_ECWMIN_SHIFT) & WMM_AC_ECWMIN_MASK) | + ((ecwmax << WMM_AC_ECWMAX_SHIFT) & WMM_AC_ECWMAX_MASK); +} -/* Add WME Parameter Element to Beacon and Probe Response frames. */ -u8 * hostapd_eid_wme(struct hostapd_data *hapd, u8 *eid) +/* + * Add WMM Parameter Element to Beacon, Probe Response, and (Re)Association + * Response frames. + */ +u8 * hostapd_eid_wmm(struct hostapd_data *hapd, u8 *eid) { u8 *pos = eid; - struct wme_parameter_element *wme = - (struct wme_parameter_element *) (pos + 2); + struct wmm_parameter_element *wmm = + (struct wmm_parameter_element *) (pos + 2); int e; - if (!hapd->conf->wme_enabled) + if (!hapd->conf->wmm_enabled) return eid; eid[0] = WLAN_EID_VENDOR_SPECIFIC; - wme->oui[0] = 0x00; - wme->oui[1] = 0x50; - wme->oui[2] = 0xf2; - wme->oui_type = WME_OUI_TYPE; - wme->oui_subtype = WME_OUI_SUBTYPE_PARAMETER_ELEMENT; - wme->version = WME_VERSION; - wme->acInfo = hapd->parameter_set_count & 0xf; + wmm->oui[0] = 0x00; + wmm->oui[1] = 0x50; + wmm->oui[2] = 0xf2; + wmm->oui_type = WMM_OUI_TYPE; + wmm->oui_subtype = WMM_OUI_SUBTYPE_PARAMETER_ELEMENT; + wmm->version = WMM_VERSION; + wmm->qos_info = hapd->parameter_set_count & 0xf; /* fill in a parameter set record for each AC */ for (e = 0; e < 4; e++) { - struct wme_ac_parameter *ac = &wme->ac[e]; - struct hostapd_wme_ac_params *acp = - &hapd->iconf->wme_ac_params[e]; - - ac->aifsn = acp->aifs; - ac->acm = acp->admission_control_mandatory; - ac->aci = e; - ac->reserved = 0; - ac->eCWmin = acp->cwmin; - ac->eCWmax = acp->cwmax; - ac->txopLimit = host_to_le16(acp->txopLimit); + struct wmm_ac_parameter *ac = &wmm->ac[e]; + struct hostapd_wmm_ac_params *acp = + &hapd->iconf->wmm_ac_params[e]; + + ac->aci_aifsn = wmm_aci_aifsn(acp->aifs, + acp->admission_control_mandatory, + e); + ac->cw = wmm_ecw(acp->cwmin, acp->cwmax); + ac->txop_limit = host_to_le16(acp->txop_limit); } - pos = (u8 *) (wme + 1); + pos = (u8 *) (wmm + 1); eid[1] = pos - eid - 2; /* element length */ return pos; @@ -72,31 +88,28 @@ u8 * hostapd_eid_wme(struct hostapd_data *hapd, u8 *eid) /* This function is called when a station sends an association request with - * WME info element. The function returns zero on success or non-zero on any - * error in WME element. eid does not include Element ID and Length octets. */ -int hostapd_eid_wme_valid(struct hostapd_data *hapd, u8 *eid, size_t len) + * WMM info element. The function returns zero on success or non-zero on any + * error in WMM element. eid does not include Element ID and Length octets. */ +int hostapd_eid_wmm_valid(struct hostapd_data *hapd, u8 *eid, size_t len) { - struct wme_information_element *wme; + struct wmm_information_element *wmm; - wpa_hexdump(MSG_MSGDUMP, "WME IE", eid, len); + wpa_hexdump(MSG_MSGDUMP, "WMM IE", eid, len); - if (len < sizeof(struct wme_information_element)) { - wpa_printf(MSG_DEBUG, "Too short WME IE (len=%lu)", + if (len < sizeof(struct wmm_information_element)) { + wpa_printf(MSG_DEBUG, "Too short WMM IE (len=%lu)", (unsigned long) len); return -1; } - wme = (struct wme_information_element *) eid; - wpa_printf(MSG_DEBUG, "Validating WME IE: OUI %02x:%02x:%02x " - "OUI type %d OUI sub-type %d version %d", - wme->oui[0], wme->oui[1], wme->oui[2], wme->oui_type, - wme->oui_subtype, wme->version); - if (os_memcmp(wme->oui, wme_oui, sizeof(wme_oui)) != 0 || - wme->oui_type != WME_OUI_TYPE || - wme->oui_subtype != WME_OUI_SUBTYPE_INFORMATION_ELEMENT || - wme->version != WME_VERSION) { - wpa_printf(MSG_DEBUG, "Unsupported WME IE OUI/Type/Subtype/" - "Version"); + wmm = (struct wmm_information_element *) eid; + wpa_printf(MSG_DEBUG, "Validating WMM IE: OUI %02x:%02x:%02x " + "OUI type %d OUI sub-type %d version %d QoS info 0x%x", + wmm->oui[0], wmm->oui[1], wmm->oui[2], wmm->oui_type, + wmm->oui_subtype, wmm->version, wmm->qos_info); + if (wmm->oui_subtype != WMM_OUI_SUBTYPE_INFORMATION_ELEMENT || + wmm->version != WMM_VERSION) { + wpa_printf(MSG_DEBUG, "Unsupported WMM IE Subtype/Version"); return -1; } @@ -105,31 +118,30 @@ int hostapd_eid_wme_valid(struct hostapd_data *hapd, u8 *eid, size_t len) /* This function is called when a station sends an ACK frame for an AssocResp - * frame (status=success) and the matching AssocReq contained a WME element. + * frame (status=success) and the matching AssocReq contained a WMM element. */ -int hostapd_wme_sta_config(struct hostapd_data *hapd, struct sta_info *sta) +int hostapd_wmm_sta_config(struct hostapd_data *hapd, struct sta_info *sta) { - /* update kernel STA data for WME related items (WLAN_STA_WPA flag) */ - if (sta->flags & WLAN_STA_WME) + /* update kernel STA data for WMM related items (WLAN_STA_WPA flag) */ + if (sta->flags & WLAN_STA_WMM) hostapd_sta_set_flags(hapd, sta->addr, sta->flags, - WLAN_STA_WME, ~0); + WLAN_STA_WMM, ~0); else hostapd_sta_set_flags(hapd, sta->addr, sta->flags, - 0, ~WLAN_STA_WME); + 0, ~WLAN_STA_WMM); return 0; } -static void wme_send_action(struct hostapd_data *hapd, const u8 *addr, - const struct wme_tspec_info_element *tspec, +static void wmm_send_action(struct hostapd_data *hapd, const u8 *addr, + const struct wmm_tspec_element *tspec, u8 action_code, u8 dialogue_token, u8 status_code) { u8 buf[256]; struct ieee80211_mgmt *m = (struct ieee80211_mgmt *) buf; - struct wme_tspec_info_element *t = - (struct wme_tspec_info_element *) - m->u.action.u.wme_action.variable; + struct wmm_tspec_element *t = (struct wmm_tspec_element *) + m->u.action.u.wmm_action.variable; int len; hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, @@ -142,55 +154,117 @@ static void wme_send_action(struct hostapd_data *hapd, const u8 *addr, os_memcpy(m->sa, hapd->own_addr, ETH_ALEN); os_memcpy(m->bssid, hapd->own_addr, ETH_ALEN); m->u.action.category = WLAN_ACTION_WMM; - m->u.action.u.wme_action.action_code = action_code; - m->u.action.u.wme_action.dialog_token = dialogue_token; - m->u.action.u.wme_action.status_code = status_code; - os_memcpy(t, tspec, sizeof(struct wme_tspec_info_element)); + m->u.action.u.wmm_action.action_code = action_code; + m->u.action.u.wmm_action.dialog_token = dialogue_token; + m->u.action.u.wmm_action.status_code = status_code; + os_memcpy(t, tspec, sizeof(struct wmm_tspec_element)); len = ((u8 *) (t + 1)) - buf; if (hostapd_send_mgmt_frame(hapd, m, len, 0) < 0) - perror("wme_send_action: send"); + perror("wmm_send_action: send"); } -/* given frame data payload size in bytes, and data_rate in bits per second - * returns time to complete frame exchange */ -/* FIX: should not use floating point types */ -static double wme_frame_exchange_time(int bytes, int data_rate, int encryption, - int cts_protection) +static void wmm_addts_req(struct hostapd_data *hapd, + struct ieee80211_mgmt *mgmt, + struct wmm_tspec_element *tspec, size_t len) { - /* TODO: account for MAC/PHY headers correctly */ - /* TODO: account for encryption headers */ - /* TODO: account for WDS headers */ - /* TODO: account for CTS protection */ - /* TODO: account for SIFS + ACK at minimum PHY rate */ - return (bytes + 400) * 8.0 / data_rate; -} + u8 *end = ((u8 *) mgmt) + len; + int medium_time, pps, duration; + int up, psb, dir, tid; + u16 val, surplus; + if ((u8 *) (tspec + 1) > end) { + wpa_printf(MSG_DEBUG, "WMM: TSPEC overflow in ADDTS Request"); + return; + } -static void wme_setup_request(struct hostapd_data *hapd, - struct ieee80211_mgmt *mgmt, - struct wme_tspec_info_element *tspec, size_t len) -{ - /* FIX: should not use floating point types */ - double medium_time, pps; + wpa_printf(MSG_DEBUG, "WMM: ADDTS Request (Dialog Token %d) for TSPEC " + "from " MACSTR, + mgmt->u.action.u.wmm_action.dialog_token, + MAC2STR(mgmt->sa)); + + up = (tspec->ts_info[1] >> 3) & 0x07; + psb = (tspec->ts_info[1] >> 2) & 0x01; + dir = (tspec->ts_info[0] >> 5) & 0x03; + tid = (tspec->ts_info[0] >> 1) & 0x0f; + wpa_printf(MSG_DEBUG, "WMM: TS Info: UP=%d PSB=%d Direction=%d TID=%d", + up, psb, dir, tid); + val = le_to_host16(tspec->nominal_msdu_size); + wpa_printf(MSG_DEBUG, "WMM: Nominal MSDU Size: %d%s", + val & 0x7fff, val & 0x8000 ? " (fixed)" : ""); + wpa_printf(MSG_DEBUG, "WMM: Mean Data Rate: %u bps", + le_to_host32(tspec->mean_data_rate)); + wpa_printf(MSG_DEBUG, "WMM: Minimum PHY Rate: %u bps", + le_to_host32(tspec->minimum_phy_rate)); + val = le_to_host16(tspec->surplus_bandwidth_allowance); + wpa_printf(MSG_DEBUG, "WMM: Surplus Bandwidth Allowance: %u.%04u", + val >> 13, 10000 * (val & 0x1fff) / 0x2000); + + val = le_to_host16(tspec->nominal_msdu_size); + if (val == 0) { + wpa_printf(MSG_DEBUG, "WMM: Invalid Nominal MSDU Size (0)"); + goto invalid; + } + /* pps = Ceiling((Mean Data Rate / 8) / Nominal MSDU Size) */ + pps = ((le_to_host32(tspec->mean_data_rate) / 8) + val - 1) / val; + wpa_printf(MSG_DEBUG, "WMM: Packets-per-second estimate for TSPEC: %d", + pps); + + if (le_to_host32(tspec->minimum_phy_rate) < 1000000) { + wpa_printf(MSG_DEBUG, "WMM: Too small Minimum PHY Rate"); + goto invalid; + } + + duration = (le_to_host16(tspec->nominal_msdu_size) & 0x7fff) * 8 / + (le_to_host32(tspec->minimum_phy_rate) / 1000000) + + 50 /* FIX: proper SIFS + ACK duration */; + + /* unsigned binary number with an implicit binary point after the + * leftmost 3 bits, i.e., 0x2000 = 1.0 */ + surplus = le_to_host16(tspec->surplus_bandwidth_allowance); + if (surplus <= 0x2000) { + wpa_printf(MSG_DEBUG, "WMM: Surplus Bandwidth Allowance not " + "greater than unity"); + goto invalid; + } + + medium_time = surplus * pps * duration / 0x2000; + wpa_printf(MSG_DEBUG, "WMM: Estimated medium time: %u", medium_time); + + /* + * TODO: store list of granted (and still active) TSPECs and check + * whether there is available medium time for this request. For now, + * just refuse requests that would by themselves take very large + * portion of the available bandwidth. + */ + if (medium_time > 750000) { + wpa_printf(MSG_DEBUG, "WMM: Refuse TSPEC request for over " + "75%% of available bandwidth"); + wmm_send_action(hapd, mgmt->sa, tspec, + WMM_ACTION_CODE_ADDTS_RESP, + mgmt->u.action.u.wmm_action.dialog_token, + WMM_ADDTS_STATUS_REFUSED); + return; + } - /* TODO: account for airtime and answer no to tspec setup requests - * when none left!! */ + /* Convert to 32 microseconds per second unit */ + tspec->medium_time = host_to_le16(medium_time / 32); - pps = (tspec->mean_data_rate / 8.0) / tspec->nominal_msdu_size; - medium_time = (tspec->surplus_bandwidth_allowance / 8) * pps * - wme_frame_exchange_time(tspec->nominal_msdu_size, - tspec->minimum_phy_rate, 0, 0); - tspec->medium_time = medium_time * 1000000.0 / 32.0; + wmm_send_action(hapd, mgmt->sa, tspec, WMM_ACTION_CODE_ADDTS_RESP, + mgmt->u.action.u.wmm_action.dialog_token, + WMM_ADDTS_STATUS_ADMISSION_ACCEPTED); + return; - wme_send_action(hapd, mgmt->sa, tspec, WME_ACTION_CODE_SETUP_RESPONSE, - mgmt->u.action.u.wme_action.dialog_token, - WME_SETUP_RESPONSE_STATUS_ADMISSION_ACCEPTED); +invalid: + wmm_send_action(hapd, mgmt->sa, tspec, + WMM_ACTION_CODE_ADDTS_RESP, + mgmt->u.action.u.wmm_action.dialog_token, + WMM_ADDTS_STATUS_INVALID_PARAMETERS); } -void hostapd_wme_action(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt, +void hostapd_wmm_action(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt, size_t len) { int action_code; @@ -201,11 +275,11 @@ void hostapd_wme_action(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt, /* check that the request comes from a valid station */ if (!sta || - (sta->flags & (WLAN_STA_ASSOC | WLAN_STA_WME)) != - (WLAN_STA_ASSOC | WLAN_STA_WME)) { + (sta->flags & (WLAN_STA_ASSOC | WLAN_STA_WMM)) != + (WLAN_STA_ASSOC | WLAN_STA_WMM)) { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, - "wme action received is not from associated wme" + "wmm action received is not from associated wmm" " station"); /* TODO: respond with action frame refused status code */ return; @@ -215,19 +289,18 @@ void hostapd_wme_action(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt, if (ieee802_11_parse_elems(pos, left, &elems, 1) == ParseFailed) { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, - "hostapd_wme_action - could not parse wme " + "hostapd_wmm_action - could not parse wmm " "action"); /* TODO: respond with action frame invalid parameters status * code */ return; } - if (!elems.wme_tspec || - elems.wme_tspec_len != (sizeof(struct wme_tspec_info_element) - 2)) - { + if (!elems.wmm_tspec || + elems.wmm_tspec_len != (sizeof(struct wmm_tspec_element) - 2)) { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, - "hostapd_wme_action - missing or wrong length " + "hostapd_wmm_action - missing or wrong length " "tspec"); /* TODO: respond with action frame invalid parameters status * code */ @@ -237,26 +310,26 @@ void hostapd_wme_action(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt, /* TODO: check the request is for an AC with ACM set, if not, refuse * request */ - action_code = mgmt->u.action.u.wme_action.action_code; + action_code = mgmt->u.action.u.wmm_action.action_code; switch (action_code) { - case WME_ACTION_CODE_SETUP_REQUEST: - wme_setup_request(hapd, mgmt, (struct wme_tspec_info_element *) - elems.wme_tspec, len); + case WMM_ACTION_CODE_ADDTS_REQ: + wmm_addts_req(hapd, mgmt, (struct wmm_tspec_element *) + (elems.wmm_tspec - 2), len); return; #if 0 /* TODO: needed for client implementation */ - case WME_ACTION_CODE_SETUP_RESPONSE: - wme_setup_request(hapd, mgmt, len); + case WMM_ACTION_CODE_ADDTS_RESP: + wmm_setup_request(hapd, mgmt, len); return; /* TODO: handle station teardown requests */ - case WME_ACTION_CODE_TEARDOWN: - wme_teardown(hapd, mgmt, len); + case WMM_ACTION_CODE_DELTS: + wmm_teardown(hapd, mgmt, len); return; #endif } hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, - "hostapd_wme_action - unknown action code %d", + "hostapd_wmm_action - unknown action code %d", action_code); } diff --git a/hostapd/wme.h b/hostapd/wme.h index 4ee281a..e06b5bc 100644 --- a/hostapd/wme.h +++ b/hostapd/wme.h @@ -16,114 +16,97 @@ #ifndef WME_H #define WME_H -#ifdef __linux__ -#include <endian.h> -#endif /* __linux__ */ - -#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) -#include <sys/types.h> -#include <sys/endian.h> -#endif /* defined(__FreeBSD__) || defined(__NetBSD__) || - * defined(__DragonFly__) */ - - -extern inline u16 tsinfo(int tag1d, int contention_based, int direction) -{ - return (tag1d << 11) | (contention_based << 7) | (direction << 5) | - (tag1d << 1); -} - - -struct wme_information_element { - /* required fields for WME version 1 */ - u8 oui[3]; - u8 oui_type; - u8 oui_subtype; - u8 version; - u8 acInfo; +/* + * WMM Information Element (used in (Re)Association Request frames; may also be + * used in Beacon frames) + */ +struct wmm_information_element { + /* Element ID: 221 (0xdd); Length: 7 */ + /* required fields for WMM version 1 */ + u8 oui[3]; /* 00:50:f2 */ + u8 oui_type; /* 2 */ + u8 oui_subtype; /* 0 */ + u8 version; /* 1 for WMM version 1.0 */ + u8 qos_info; /* AP/STA specific QoS info */ } __attribute__ ((packed)); -struct wme_ac_parameter { -#if __BYTE_ORDER == __LITTLE_ENDIAN - /* byte 1 */ - u8 aifsn:4, - acm:1, - aci:2, - reserved:1; - - /* byte 2 */ - u8 eCWmin:4, - eCWmax:4; -#elif __BYTE_ORDER == __BIG_ENDIAN - /* byte 1 */ - u8 reserved:1, - aci:2, - acm:1, - aifsn:4; - - /* byte 2 */ - u8 eCWmax:4, - eCWmin:4; -#else -#error "Please fix <endian.h>" -#endif - - /* bytes 3 & 4 */ - le16 txopLimit; +#define WMM_AC_AIFSN_MASK 0x0f +#define WMM_AC_AIFNS_SHIFT 0 +#define WMM_AC_ACM 0x10 +#define WMM_AC_ACI_MASK 0x60 +#define WMM_AC_ACI_SHIFT 5 + +#define WMM_AC_ECWMIN_MASK 0x0f +#define WMM_AC_ECWMIN_SHIFT 0 +#define WMM_AC_ECWMAX_MASK 0xf0 +#define WMM_AC_ECWMAX_SHIFT 4 + +struct wmm_ac_parameter { + u8 aci_aifsn; /* AIFSN, ACM, ACI */ + u8 cw; /* ECWmin, ECWmax (CW = 2^ECW - 1) */ + le16 txop_limit; } __attribute__ ((packed)); -struct wme_parameter_element { - /* required fields for WME version 1 */ - u8 oui[3]; - u8 oui_type; - u8 oui_subtype; - u8 version; - u8 acInfo; - u8 reserved; - struct wme_ac_parameter ac[4]; +/* + * WMM Parameter Element (used in Beacon, Probe Response, and (Re)Association + * Response frmaes) + */ +struct wmm_parameter_element { + /* Element ID: 221 (0xdd); Length: 24 */ + /* required fields for WMM version 1 */ + u8 oui[3]; /* 00:50:f2 */ + u8 oui_type; /* 2 */ + u8 oui_subtype; /* 1 */ + u8 version; /* 1 for WMM version 1.0 */ + u8 qos_info; /* AP/STA specif QoS info */ + u8 reserved; /* 0 */ + struct wmm_ac_parameter ac[4]; /* AC_BE, AC_BK, AC_VI, AC_VO */ } __attribute__ ((packed)); -struct wme_tspec_info_element { - u8 eid; - u8 length; - u8 oui[3]; - u8 oui_type; - u8 oui_subtype; - u8 version; - u16 ts_info; - u16 nominal_msdu_size; - u16 maximum_msdu_size; - u32 minimum_service_interval; - u32 maximum_service_interval; - u32 inactivity_interval; - u32 start_time; - u32 minimum_data_rate; - u32 mean_data_rate; - u32 maximum_burst_size; - u32 minimum_phy_rate; - u32 peak_data_rate; - u32 delay_bound; - u16 surplus_bandwidth_allowance; - u16 medium_time; +/* WMM TSPEC Element */ +struct wmm_tspec_element { + u8 eid; /* 221 = 0xdd */ + u8 length; /* 6 + 55 = 61 */ + u8 oui[3]; /* 00:50:f2 */ + u8 oui_type; /* 2 */ + u8 oui_subtype; /* 2 */ + u8 version; /* 1 */ + /* WMM TSPEC body (55 octets): */ + u8 ts_info[3]; + le16 nominal_msdu_size; + le16 maximum_msdu_size; + le32 minimum_service_interval; + le32 maximum_service_interval; + le32 inactivity_interval; + le32 suspension_interval; + le32 service_start_time; + le32 minimum_data_rate; + le32 mean_data_rate; + le32 peak_data_rate; + le32 maximum_burst_size; + le32 delay_bound; + le32 minimum_phy_rate; + le16 surplus_bandwidth_allowance; + le16 medium_time; } __attribute__ ((packed)); -/* Access Categories */ +/* Access Categories / ACI to AC coding */ enum { - WME_AC_BK = 1, - WME_AC_BE = 0, - WME_AC_VI = 2, - WME_AC_VO = 3 + WMM_AC_BE = 0 /* Best Effort */, + WMM_AC_BK = 1 /* Background */, + WMM_AC_VI = 2 /* Video */, + WMM_AC_VO = 3 /* Voice */ }; struct ieee80211_mgmt; -u8 * hostapd_eid_wme(struct hostapd_data *hapd, u8 *eid); -int hostapd_eid_wme_valid(struct hostapd_data *hapd, u8 *eid, size_t len); -int hostapd_wme_sta_config(struct hostapd_data *hapd, struct sta_info *sta); -void hostapd_wme_action(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt, +u8 * hostapd_eid_wmm(struct hostapd_data *hapd, u8 *eid); +int hostapd_eid_wmm_valid(struct hostapd_data *hapd, u8 *eid, size_t len); +int hostapd_wmm_sta_config(struct hostapd_data *hapd, struct sta_info *sta); +void hostapd_wmm_action(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt, size_t len); #endif /* WME_H */ diff --git a/hostapd/wpa.c b/hostapd/wpa.c index cf285b6..19b11d5 100644 --- a/hostapd/wpa.c +++ b/hostapd/wpa.c @@ -1,6 +1,6 @@ /* * hostapd - IEEE 802.11i-2004 / WPA Authenticator - * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -44,6 +44,8 @@ static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx); static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth, struct wpa_group *group); static void wpa_request_new_ptk(struct wpa_state_machine *sm); +static int wpa_gtk_update(struct wpa_authenticator *wpa_auth, + struct wpa_group *group); static const u32 dot11RSNAConfigGroupUpdateCount = 4; static const u32 dot11RSNAConfigPairwiseUpdateCount = 4; @@ -286,21 +288,9 @@ static void wpa_auth_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry, } -static struct wpa_group * wpa_group_init(struct wpa_authenticator *wpa_auth, - int vlan_id) +static void wpa_group_set_key_len(struct wpa_group *group, int cipher) { - struct wpa_group *group; - u8 buf[ETH_ALEN + 8 + sizeof(group)]; - u8 rkey[32]; - - group = os_zalloc(sizeof(struct wpa_group)); - if (group == NULL) - return NULL; - - group->GTKAuthenticator = TRUE; - group->vlan_id = vlan_id; - - switch (wpa_auth->conf.wpa_group) { + switch (cipher) { case WPA_CIPHER_CCMP: group->GTK_len = 16; break; @@ -314,6 +304,24 @@ static struct wpa_group * wpa_group_init(struct wpa_authenticator *wpa_auth, group->GTK_len = 5; break; } +} + + +static struct wpa_group * wpa_group_init(struct wpa_authenticator *wpa_auth, + int vlan_id) +{ + struct wpa_group *group; + u8 buf[ETH_ALEN + 8 + sizeof(group)]; + u8 rkey[32]; + + group = os_zalloc(sizeof(struct wpa_group)); + if (group == NULL) + return NULL; + + group->GTKAuthenticator = TRUE; + group->vlan_id = vlan_id; + + wpa_group_set_key_len(group, wpa_auth->conf.wpa_group); /* Counter = PRF-256(Random number, "Init Counter", * Local MAC Address || Time) @@ -451,6 +459,7 @@ void wpa_deinit(struct wpa_authenticator *wpa_auth) int wpa_reconfig(struct wpa_authenticator *wpa_auth, struct wpa_auth_config *conf) { + struct wpa_group *group; if (wpa_auth == NULL) return 0; @@ -460,6 +469,17 @@ int wpa_reconfig(struct wpa_authenticator *wpa_auth, return -1; } + /* + * Reinitialize GTK to make sure it is suitable for the new + * configuration. + */ + group = wpa_auth->group; + wpa_group_set_key_len(group, wpa_auth->conf.wpa_group); + group->GInit = TRUE; + wpa_group_sm_step(wpa_auth, group); + group->GInit = FALSE; + wpa_group_sm_step(wpa_auth, group); + return 0; } @@ -619,6 +639,22 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, return; } + if (sm->wpa == WPA_VERSION_WPA2) { + if (key->type != EAPOL_KEY_TYPE_RSN) { + wpa_printf(MSG_DEBUG, "Ignore EAPOL-Key with " + "unexpected type %d in RSN mode", + key->type); + return; + } + } else { + if (key->type != EAPOL_KEY_TYPE_WPA) { + wpa_printf(MSG_DEBUG, "Ignore EAPOL-Key with " + "unexpected type %d in WPA mode", + key->type); + return; + } + } + /* FIX: verify that the EAPOL-Key frame was encrypted if pairwise keys * are set */ @@ -1403,14 +1439,15 @@ SM_STATE(WPA_PTK, PTKSTART) static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *pmk, struct wpa_ptk *ptk) { + size_t ptk_len = sm->pairwise == WPA_CIPHER_CCMP ? 48 : 64; #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) - return wpa_auth_derive_ptk_ft(sm, pmk, ptk); + return wpa_auth_derive_ptk_ft(sm, pmk, ptk, ptk_len); #endif /* CONFIG_IEEE80211R */ wpa_pmk_to_ptk(pmk, PMK_LEN, "Pairwise key expansion", sm->wpa_auth->addr, sm->addr, sm->ANonce, sm->SNonce, - (u8 *) ptk, sizeof(*ptk), + (u8 *) ptk, ptk_len, wpa_key_mgmt_sha256(sm->wpa_key_mgmt)); return 0; diff --git a/hostapd/wpa.h b/hostapd/wpa.h index d353844..7d9b3d3 100644 --- a/hostapd/wpa.h +++ b/hostapd/wpa.h @@ -141,7 +141,7 @@ struct wpa_auth_config { int rsn_preauth; int eapol_version; int peerkey; - int wme_enabled; + int wmm_enabled; int okc; #ifdef CONFIG_IEEE80211W enum { diff --git a/hostapd/wpa_auth_i.h b/hostapd/wpa_auth_i.h index bcaeda5..925d3ee 100644 --- a/hostapd/wpa_auth_i.h +++ b/hostapd/wpa_auth_i.h @@ -213,7 +213,7 @@ void wpa_smk_m3(struct wpa_authenticator *wpa_auth, #ifdef CONFIG_IEEE80211R int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len); int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk, - struct wpa_ptk *ptk); + struct wpa_ptk *ptk, size_t ptk_len); struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void); void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache); #endif /* CONFIG_IEEE80211R */ diff --git a/hostapd/wpa_auth_ie.c b/hostapd/wpa_auth_ie.c index 3ac9d67..7e01635 100644 --- a/hostapd/wpa_auth_ie.c +++ b/hostapd/wpa_auth_ie.c @@ -215,8 +215,8 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, capab |= WPA_CAPABILITY_PREAUTH; if (conf->peerkey) capab |= WPA_CAPABILITY_PEERKEY_ENABLED; - if (conf->wme_enabled) { - /* 4 PTKSA replay counters when using WME */ + if (conf->wmm_enabled) { + /* 4 PTKSA replay counters when using WMM */ capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2); } #ifdef CONFIG_IEEE80211W diff --git a/hostapd/wpa_ft.c b/hostapd/wpa_ft.c index 9cf6713..3139105 100644 --- a/hostapd/wpa_ft.c +++ b/hostapd/wpa_ft.c @@ -321,7 +321,7 @@ static int wpa_ft_pull_pmk_r1(struct wpa_authenticator *wpa_auth, int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk, - struct wpa_ptk *ptk) + struct wpa_ptk *ptk, size_t ptk_len) { u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN]; u8 pmk_r1[PMK_LEN], pmk_r1_name[WPA_PMK_NAME_LEN]; @@ -354,8 +354,8 @@ int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk, wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr, sm->wpa_auth->addr, pmk_r1_name, - (u8 *) ptk, sizeof(*ptk), ptk_name); - wpa_hexdump_key(MSG_DEBUG, "FT: PTK", (u8 *) ptk, sizeof(*ptk)); + (u8 *) ptk, ptk_len, ptk_name); + wpa_hexdump_key(MSG_DEBUG, "FT: PTK", (u8 *) ptk, ptk_len); wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN); return 0; @@ -714,7 +714,7 @@ static u16 wpa_ft_process_auth_req(struct wpa_state_machine *sm, u8 ptk_name[WPA_PMK_NAME_LEN]; struct wpa_auth_config *conf; struct wpa_ft_ies parse; - size_t buflen; + size_t buflen, ptk_len; int ret; u8 *pos, *end; @@ -804,11 +804,12 @@ static u16 wpa_ft_process_auth_req(struct wpa_state_machine *sm, wpa_hexdump(MSG_DEBUG, "FT: Generated ANonce", sm->ANonce, WPA_NONCE_LEN); + ptk_len = sm->pairwise == WPA_CIPHER_CCMP ? 48 : 64; wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr, sm->wpa_auth->addr, pmk_r1_name, - (u8 *) &sm->PTK, sizeof(sm->PTK), ptk_name); + (u8 *) &sm->PTK, ptk_len, ptk_name); wpa_hexdump_key(MSG_DEBUG, "FT: PTK", - (u8 *) &sm->PTK, sizeof(sm->PTK)); + (u8 *) &sm->PTK, ptk_len); wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN); wpa_ft_install_ptk(sm); diff --git a/hostapd/wps_hostapd.c b/hostapd/wps_hostapd.c index a824c16..818767e 100644 --- a/hostapd/wps_hostapd.c +++ b/hostapd/wps_hostapd.c @@ -219,13 +219,13 @@ static int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred) if ((hapd->conf->wps_cred_processing == 1 || hapd->conf->wps_cred_processing == 2) && cred->cred_attr) { size_t blen = cred->cred_attr_len * 2 + 1; - char *buf = os_malloc(blen); - if (buf) { - wpa_snprintf_hex(buf, blen, + char *_buf = os_malloc(blen); + if (_buf) { + wpa_snprintf_hex(_buf, blen, cred->cred_attr, cred->cred_attr_len); wpa_msg(hapd, MSG_INFO, "%s%s", - WPS_EVENT_NEW_AP_SETTINGS, buf); - os_free(buf); + WPS_EVENT_NEW_AP_SETTINGS, _buf); + os_free(_buf); } } else wpa_msg(hapd, MSG_INFO, WPS_EVENT_NEW_AP_SETTINGS); @@ -233,6 +233,28 @@ static int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred) if (hapd->conf->wps_cred_processing == 1) return 0; + os_memcpy(hapd->wps->ssid, cred->ssid, cred->ssid_len); + hapd->wps->ssid_len = cred->ssid_len; + hapd->wps->encr_types = cred->encr_type; + hapd->wps->auth_types = cred->auth_type; + if (cred->key_len == 0) { + os_free(hapd->wps->network_key); + hapd->wps->network_key = NULL; + hapd->wps->network_key_len = 0; + } else { + if (hapd->wps->network_key == NULL || + hapd->wps->network_key_len < cred->key_len) { + hapd->wps->network_key_len = 0; + os_free(hapd->wps->network_key); + hapd->wps->network_key = os_malloc(cred->key_len); + if (hapd->wps->network_key == NULL) + return -1; + } + hapd->wps->network_key_len = cred->key_len; + os_memcpy(hapd->wps->network_key, cred->key, cred->key_len); + } + hapd->wps->wps_state = WPS_STATE_CONFIGURED; + len = os_strlen(hapd->iface->config_fname) + 5; tmp_fname = os_malloc(len); if (tmp_fname == NULL) @@ -326,15 +348,21 @@ static int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred) else fprintf(nconf, "auth_algs=1\n"); - if (cred->encr_type & WPS_ENCR_WEP && cred->key_idx < 4) { - fprintf(nconf, "wep_default_key=%d\n", cred->key_idx); - fprintf(nconf, "wep_key%d=", cred->key_idx); - if (cred->key_len != 10 && cred->key_len != 26) - fputc('"', nconf); - for (i = 0; i < cred->key_len; i++) - fputc(cred->key[i], nconf); - if (cred->key_len != 10 && cred->key_len != 26) - fputc('"', nconf); + if (cred->encr_type & WPS_ENCR_WEP && cred->key_idx <= 4) { + int key_idx = cred->key_idx; + if (key_idx) + key_idx--; + fprintf(nconf, "wep_default_key=%d\n", key_idx); + fprintf(nconf, "wep_key%d=", key_idx); + if (cred->key_len == 10 || cred->key_len == 26) { + /* WEP key as a hex string */ + for (i = 0; i < cred->key_len; i++) + fputc(cred->key[i], nconf); + } else { + /* Raw WEP key; convert to hex */ + for (i = 0; i < cred->key_len; i++) + fprintf(nconf, "%02x", cred->key[i]); + } fprintf(nconf, "\n"); } } @@ -620,6 +648,8 @@ int hostapd_init_wps(struct hostapd_data *hapd, cfg.extra_cred_len = conf->extra_cred_len; cfg.disable_auto_conf = (hapd->conf->wps_cred_processing == 1) && conf->skip_cred_build; + if (conf->ssid.security_policy == SECURITY_STATIC_WEP) + cfg.static_wep_only = 1; wps->registrar = wps_registrar_init(wps, &cfg); if (wps->registrar == NULL) { @@ -669,7 +699,7 @@ void hostapd_deinit_wps(struct hostapd_data *hapd) int hostapd_wps_add_pin(struct hostapd_data *hapd, const char *uuid, - const char *pin) + const char *pin, int timeout) { u8 u[UUID_LEN]; int any = 0; @@ -681,7 +711,8 @@ int hostapd_wps_add_pin(struct hostapd_data *hapd, const char *uuid, else if (uuid_str2bin(uuid, u)) return -1; return wps_registrar_add_pin(hapd->wps->registrar, any ? NULL : u, - (const u8 *) pin, os_strlen(pin)); + (const u8 *) pin, os_strlen(pin), + timeout); } @@ -864,8 +895,8 @@ static int hostapd_rx_req_put_wlan_response( wpa_printf(MSG_DEBUG, "WPS UPnP: PutWLANResponse ev_type=%d mac_addr=" MACSTR, ev_type, MAC2STR(mac_addr)); - wpa_hexdump_ascii(MSG_MSGDUMP, "WPS UPnP: PutWLANResponse NewMessage", - wpabuf_head(msg), wpabuf_len(msg)); + wpa_hexdump(MSG_MSGDUMP, "WPS UPnP: PutWLANResponse NewMessage", + wpabuf_head(msg), wpabuf_len(msg)); if (ev_type != UPNP_WPS_WLANEVENT_TYPE_EAP) { wpa_printf(MSG_DEBUG, "WPS UPnP: Ignored unexpected " "PutWLANResponse WLANEventType %d", ev_type); diff --git a/hostapd/wps_hostapd.h b/hostapd/wps_hostapd.h index 6615c62a..e949bee 100644 --- a/hostapd/wps_hostapd.h +++ b/hostapd/wps_hostapd.h @@ -21,7 +21,7 @@ int hostapd_init_wps(struct hostapd_data *hapd, struct hostapd_bss_config *conf); void hostapd_deinit_wps(struct hostapd_data *hapd); int hostapd_wps_add_pin(struct hostapd_data *hapd, const char *uuid, - const char *pin); + const char *pin, int timeout); int hostapd_wps_button_pushed(struct hostapd_data *hapd); void hostapd_wps_probe_req_rx(struct hostapd_data *hapd, const u8 *addr, const u8 *ie, size_t ie_len); diff --git a/src/common/.gitignore b/src/common/.gitignore deleted file mode 100644 index a438335..0000000 --- a/src/common/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.d diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c index 991c989..242f933 100644 --- a/src/common/ieee802_11_common.c +++ b/src/common/ieee802_11_common.c @@ -49,26 +49,33 @@ static int ieee802_11_parse_vendor_specific(u8 *pos, size_t elen, elems->wpa_ie = pos; elems->wpa_ie_len = elen; break; - case WME_OUI_TYPE: /* this is a Wi-Fi WME info. element */ + case WMM_OUI_TYPE: + /* WMM information element */ if (elen < 5) { - wpa_printf(MSG_MSGDUMP, "short WME " + wpa_printf(MSG_MSGDUMP, "short WMM " "information element ignored " "(len=%lu)", (unsigned long) elen); return -1; } switch (pos[4]) { - case WME_OUI_SUBTYPE_INFORMATION_ELEMENT: - case WME_OUI_SUBTYPE_PARAMETER_ELEMENT: - elems->wme = pos; - elems->wme_len = elen; + case WMM_OUI_SUBTYPE_INFORMATION_ELEMENT: + case WMM_OUI_SUBTYPE_PARAMETER_ELEMENT: + /* + * Share same pointer since only one of these + * is used and they start with same data. + * Length field can be used to distinguish the + * IEs. + */ + elems->wmm = pos; + elems->wmm_len = elen; break; - case WME_OUI_SUBTYPE_TSPEC_ELEMENT: - elems->wme_tspec = pos; - elems->wme_tspec_len = elen; + case WMM_OUI_SUBTYPE_TSPEC_ELEMENT: + elems->wmm_tspec = pos; + elems->wmm_tspec_len = elen; break; default: - wpa_printf(MSG_MSGDUMP, "unknown WME " + wpa_printf(MSG_MSGDUMP, "unknown WMM " "information element ignored " "(subtype=%d len=%lu)", pos[4], (unsigned long) elen); diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h index 2aff993..b7e497b 100644 --- a/src/common/ieee802_11_common.h +++ b/src/common/ieee802_11_common.h @@ -41,10 +41,10 @@ struct ieee802_11_elems { u8 wpa_ie_len; u8 *rsn_ie; u8 rsn_ie_len; - u8 *wme; - u8 wme_len; - u8 *wme_tspec; - u8 wme_tspec_len; + u8 *wmm; /* WMM Information or Parameter Element */ + u8 wmm_len; /* 7 = WMM Information; 24 = WMM Parameter */ + u8 *wmm_tspec; + u8 wmm_tspec_len; u8 *wps_ie; u8 wps_ie_len; u8 *power_cap; diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index 18220b0..d9e54a9 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -134,10 +134,9 @@ #define WLAN_STATUS_ASSOC_DENIED_LISTEN_INT_TOO_LARGE 51 /* IEEE 802.11r */ #define WLAN_STATUS_INVALID_FT_ACTION_FRAME_COUNT 52 -#define WLAN_STATUS_EXPECTED_RESOURCE_REQ_FT 53 -#define WLAN_STATUS_INVALID_PMKID 54 -#define WLAN_STATUS_INVALID_MDIE 55 -#define WLAN_STATUS_INVALID_FTIE 56 +#define WLAN_STATUS_INVALID_PMKID 53 +#define WLAN_STATUS_INVALID_MDIE 54 +#define WLAN_STATUS_INVALID_FTIE 55 /* Reason codes (IEEE 802.11-2007, 7.3.1.7, Table 7-22) */ #define WLAN_REASON_UNSPECIFIED 1 @@ -211,16 +210,18 @@ #define WLAN_ACTION_QOS 1 #define WLAN_ACTION_DLS 2 #define WLAN_ACTION_BLOCK_ACK 3 +#define WLAN_ACTION_PUBLIC 4 #define WLAN_ACTION_RADIO_MEASUREMENT 5 #define WLAN_ACTION_FT 6 +#define WLAN_ACTION_HT 7 #define WLAN_ACTION_SA_QUERY 8 -#define WLAN_ACTION_WMM 17 +#define WLAN_ACTION_WMM 17 /* WMM Specification 1.1 */ -/* SA Query Action frame (IEEE 802.11w/D7.0, 7.4.9) */ +/* SA Query Action frame (IEEE 802.11w/D8.0, 7.4.9) */ #define WLAN_SA_QUERY_REQUEST 0 #define WLAN_SA_QUERY_RESPONSE 1 -#define WLAN_SA_QUERY_TR_ID_LEN 16 +#define WLAN_SA_QUERY_TR_ID_LEN 2 /* Timeout Interval Type */ #define WLAN_TIMEOUT_REASSOC_DEADLINE 1 @@ -301,7 +302,7 @@ struct ieee80211_mgmt { u8 dialog_token; u8 status_code; u8 variable[0]; - } STRUCT_PACKED wme_action; + } STRUCT_PACKED wmm_action; struct{ u8 action_code; u8 element_id; @@ -562,23 +563,27 @@ struct mimo_pwr_save_action { #define OUI_MICROSOFT 0x0050f2 /* Microsoft (also used in Wi-Fi specs) * 00:50:F2 */ -#define WME_OUI_TYPE 2 -#define WME_OUI_SUBTYPE_INFORMATION_ELEMENT 0 -#define WME_OUI_SUBTYPE_PARAMETER_ELEMENT 1 -#define WME_OUI_SUBTYPE_TSPEC_ELEMENT 2 -#define WME_VERSION 1 - -#define WME_ACTION_CODE_SETUP_REQUEST 0 -#define WME_ACTION_CODE_SETUP_RESPONSE 1 -#define WME_ACTION_CODE_TEARDOWN 2 - -#define WME_SETUP_RESPONSE_STATUS_ADMISSION_ACCEPTED 0 -#define WME_SETUP_RESPONSE_STATUS_INVALID_PARAMETERS 1 -#define WME_SETUP_RESPONSE_STATUS_REFUSED 3 - -#define WME_TSPEC_DIRECTION_UPLINK 0 -#define WME_TSPEC_DIRECTION_DOWNLINK 1 -#define WME_TSPEC_DIRECTION_BI_DIRECTIONAL 3 +#define WMM_OUI_TYPE 2 +#define WMM_OUI_SUBTYPE_INFORMATION_ELEMENT 0 +#define WMM_OUI_SUBTYPE_PARAMETER_ELEMENT 1 +#define WMM_OUI_SUBTYPE_TSPEC_ELEMENT 2 +#define WMM_VERSION 1 + +#define WMM_ACTION_CODE_ADDTS_REQ 0 +#define WMM_ACTION_CODE_ADDTS_RESP 1 +#define WMM_ACTION_CODE_DELTS 2 + +#define WMM_ADDTS_STATUS_ADMISSION_ACCEPTED 0 +#define WMM_ADDTS_STATUS_INVALID_PARAMETERS 1 +/* 2 - Reserved */ +#define WMM_ADDTS_STATUS_REFUSED 3 +/* 4-255 - Reserved */ + +/* WMM TSPEC Direction Field Values */ +#define WMM_TSPEC_DIRECTION_UPLINK 0 +#define WMM_TSPEC_DIRECTION_DOWNLINK 1 +/* 2 - Reserved */ +#define WMM_TSPEC_DIRECTION_BI_DIRECTIONAL 3 #define OUI_BROADCOM 0x00904c /* Broadcom (Epigram) */ diff --git a/src/common/nl80211_copy.h b/src/common/nl80211_copy.h new file mode 100644 index 0000000..45db17f --- /dev/null +++ b/src/common/nl80211_copy.h @@ -0,0 +1,1434 @@ +#ifndef __LINUX_NL80211_H +#define __LINUX_NL80211_H +/* + * 802.11 netlink interface public header + * + * Copyright 2006, 2007, 2008 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2008 Michael Wu <flamingice@sourmilk.net> + * Copyright 2008 Luis Carlos Cobo <luisca@cozybit.com> + * Copyright 2008 Michael Buesch <mb@bu3sch.de> + * Copyright 2008, 2009 Luis R. Rodriguez <lrodriguez@atheros.com> + * Copyright 2008 Jouni Malinen <jouni.malinen@atheros.com> + * Copyright 2008 Colin McCabe <colin@cozybit.com> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include <linux/types.h> + +/** + * DOC: Station handling + * + * Stations are added per interface, but a special case exists with VLAN + * interfaces. When a station is bound to an AP interface, it may be moved + * into a VLAN identified by a VLAN interface index (%NL80211_ATTR_STA_VLAN). + * The station is still assumed to belong to the AP interface it was added + * to. + * + * TODO: need more info? + */ + +/** + * enum nl80211_commands - supported nl80211 commands + * + * @NL80211_CMD_UNSPEC: unspecified command to catch errors + * + * @NL80211_CMD_GET_WIPHY: request information about a wiphy or dump request + * to get a list of all present wiphys. + * @NL80211_CMD_SET_WIPHY: set wiphy parameters, needs %NL80211_ATTR_WIPHY or + * %NL80211_ATTR_IFINDEX; can be used to set %NL80211_ATTR_WIPHY_NAME, + * %NL80211_ATTR_WIPHY_TXQ_PARAMS, %NL80211_ATTR_WIPHY_FREQ, + * %NL80211_ATTR_WIPHY_CHANNEL_TYPE, %NL80211_ATTR_WIPHY_RETRY_SHORT, + * %NL80211_ATTR_WIPHY_RETRY_LONG, %NL80211_ATTR_WIPHY_FRAG_THRESHOLD, + * and/or %NL80211_ATTR_WIPHY_RTS_THRESHOLD. + * @NL80211_CMD_NEW_WIPHY: Newly created wiphy, response to get request + * or rename notification. Has attributes %NL80211_ATTR_WIPHY and + * %NL80211_ATTR_WIPHY_NAME. + * @NL80211_CMD_DEL_WIPHY: Wiphy deleted. Has attributes + * %NL80211_ATTR_WIPHY and %NL80211_ATTR_WIPHY_NAME. + * + * @NL80211_CMD_GET_INTERFACE: Request an interface's configuration; + * either a dump request on a %NL80211_ATTR_WIPHY or a specific get + * on an %NL80211_ATTR_IFINDEX is supported. + * @NL80211_CMD_SET_INTERFACE: Set type of a virtual interface, requires + * %NL80211_ATTR_IFINDEX and %NL80211_ATTR_IFTYPE. + * @NL80211_CMD_NEW_INTERFACE: Newly created virtual interface or response + * to %NL80211_CMD_GET_INTERFACE. Has %NL80211_ATTR_IFINDEX, + * %NL80211_ATTR_WIPHY and %NL80211_ATTR_IFTYPE attributes. Can also + * be sent from userspace to request creation of a new virtual interface, + * then requires attributes %NL80211_ATTR_WIPHY, %NL80211_ATTR_IFTYPE and + * %NL80211_ATTR_IFNAME. + * @NL80211_CMD_DEL_INTERFACE: Virtual interface was deleted, has attributes + * %NL80211_ATTR_IFINDEX and %NL80211_ATTR_WIPHY. Can also be sent from + * userspace to request deletion of a virtual interface, then requires + * attribute %NL80211_ATTR_IFINDEX. + * + * @NL80211_CMD_GET_KEY: Get sequence counter information for a key specified + * by %NL80211_ATTR_KEY_IDX and/or %NL80211_ATTR_MAC. + * @NL80211_CMD_SET_KEY: Set key attributes %NL80211_ATTR_KEY_DEFAULT, + * %NL80211_ATTR_KEY_DEFAULT_MGMT, or %NL80211_ATTR_KEY_THRESHOLD. + * @NL80211_CMD_NEW_KEY: add a key with given %NL80211_ATTR_KEY_DATA, + * %NL80211_ATTR_KEY_IDX, %NL80211_ATTR_MAC, %NL80211_ATTR_KEY_CIPHER, + * and %NL80211_ATTR_KEY_SEQ attributes. + * @NL80211_CMD_DEL_KEY: delete a key identified by %NL80211_ATTR_KEY_IDX + * or %NL80211_ATTR_MAC. + * + * @NL80211_CMD_GET_BEACON: retrieve beacon information (returned in a + * %NL80222_CMD_NEW_BEACON message) + * @NL80211_CMD_SET_BEACON: set the beacon on an access point interface + * using the %NL80211_ATTR_BEACON_INTERVAL, %NL80211_ATTR_DTIM_PERIOD, + * %NL80211_ATTR_BEACON_HEAD and %NL80211_ATTR_BEACON_TAIL attributes. + * @NL80211_CMD_NEW_BEACON: add a new beacon to an access point interface, + * parameters are like for %NL80211_CMD_SET_BEACON. + * @NL80211_CMD_DEL_BEACON: remove the beacon, stop sending it + * + * @NL80211_CMD_GET_STATION: Get station attributes for station identified by + * %NL80211_ATTR_MAC on the interface identified by %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_SET_STATION: Set station attributes for station identified by + * %NL80211_ATTR_MAC on the interface identified by %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_NEW_STATION: Add a station with given attributes to the + * the interface identified by %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_DEL_STATION: Remove a station identified by %NL80211_ATTR_MAC + * or, if no MAC address given, all stations, on the interface identified + * by %NL80211_ATTR_IFINDEX. + * + * @NL80211_CMD_GET_MPATH: Get mesh path attributes for mesh path to + * destination %NL80211_ATTR_MAC on the interface identified by + * %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_SET_MPATH: Set mesh path attributes for mesh path to + * destination %NL80211_ATTR_MAC on the interface identified by + * %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_NEW_PATH: Add a mesh path with given attributes to the + * the interface identified by %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_DEL_PATH: Remove a mesh path identified by %NL80211_ATTR_MAC + * or, if no MAC address given, all mesh paths, on the interface identified + * by %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_SET_BSS: Set BSS attributes for BSS identified by + * %NL80211_ATTR_IFINDEX. + * + * @NL80211_CMD_GET_REG: ask the wireless core to send us its currently set + * regulatory domain. + * @NL80211_CMD_SET_REG: Set current regulatory domain. CRDA sends this command + * after being queried by the kernel. CRDA replies by sending a regulatory + * domain structure which consists of %NL80211_ATTR_REG_ALPHA set to our + * current alpha2 if it found a match. It also provides + * NL80211_ATTR_REG_RULE_FLAGS, and a set of regulatory rules. Each + * regulatory rule is a nested set of attributes given by + * %NL80211_ATTR_REG_RULE_FREQ_[START|END] and + * %NL80211_ATTR_FREQ_RANGE_MAX_BW with an attached power rule given by + * %NL80211_ATTR_REG_RULE_POWER_MAX_ANT_GAIN and + * %NL80211_ATTR_REG_RULE_POWER_MAX_EIRP. + * @NL80211_CMD_REQ_SET_REG: ask the wireless core to set the regulatory domain + * to the the specified ISO/IEC 3166-1 alpha2 country code. The core will + * store this as a valid request and then query userspace for it. + * + * @NL80211_CMD_GET_MESH_PARAMS: Get mesh networking properties for the + * interface identified by %NL80211_ATTR_IFINDEX + * + * @NL80211_CMD_SET_MESH_PARAMS: Set mesh networking properties for the + * interface identified by %NL80211_ATTR_IFINDEX + * + * @NL80211_CMD_SET_MGMT_EXTRA_IE: Set extra IEs for management frames. The + * interface is identified with %NL80211_ATTR_IFINDEX and the management + * frame subtype with %NL80211_ATTR_MGMT_SUBTYPE. The extra IE data to be + * added to the end of the specified management frame is specified with + * %NL80211_ATTR_IE. If the command succeeds, the requested data will be + * added to all specified management frames generated by + * kernel/firmware/driver. + * Note: This command has been removed and it is only reserved at this + * point to avoid re-using existing command number. The functionality this + * command was planned for has been provided with cleaner design with the + * option to specify additional IEs in NL80211_CMD_TRIGGER_SCAN, + * NL80211_CMD_AUTHENTICATE, NL80211_CMD_ASSOCIATE, + * NL80211_CMD_DEAUTHENTICATE, and NL80211_CMD_DISASSOCIATE. + * + * @NL80211_CMD_GET_SCAN: get scan results + * @NL80211_CMD_TRIGGER_SCAN: trigger a new scan with the given parameters + * @NL80211_CMD_NEW_SCAN_RESULTS: scan notification (as a reply to + * NL80211_CMD_GET_SCAN and on the "scan" multicast group) + * @NL80211_CMD_SCAN_ABORTED: scan was aborted, for unspecified reasons, + * partial scan results may be available + * + * @NL80211_CMD_GET_SURVEY: get survey resuls, e.g. channel occupation + * or noise level + * @NL80211_CMD_NEW_SURVEY_RESULTS: survey data notification (as a reply to + * NL80211_CMD_GET_SURVEY and on the "scan" multicast group) + * + * @NL80211_CMD_REG_CHANGE: indicates to userspace the regulatory domain + * has been changed and provides details of the request information + * that caused the change such as who initiated the regulatory request + * (%NL80211_ATTR_REG_INITIATOR), the wiphy_idx + * (%NL80211_ATTR_REG_ALPHA2) on which the request was made from if + * the initiator was %NL80211_REGDOM_SET_BY_COUNTRY_IE or + * %NL80211_REGDOM_SET_BY_DRIVER, the type of regulatory domain + * set (%NL80211_ATTR_REG_TYPE), if the type of regulatory domain is + * %NL80211_REG_TYPE_COUNTRY the alpha2 to which we have moved on + * to (%NL80211_ATTR_REG_ALPHA2). + * @NL80211_CMD_REG_BEACON_HINT: indicates to userspace that an AP beacon + * has been found while world roaming thus enabling active scan or + * any mode of operation that initiates TX (beacons) on a channel + * where we would not have been able to do either before. As an example + * if you are world roaming (regulatory domain set to world or if your + * driver is using a custom world roaming regulatory domain) and while + * doing a passive scan on the 5 GHz band you find an AP there (if not + * on a DFS channel) you will now be able to actively scan for that AP + * or use AP mode on your card on that same channel. Note that this will + * never be used for channels 1-11 on the 2 GHz band as they are always + * enabled world wide. This beacon hint is only sent if your device had + * either disabled active scanning or beaconing on a channel. We send to + * userspace the wiphy on which we removed a restriction from + * (%NL80211_ATTR_WIPHY) and the channel on which this occurred + * before (%NL80211_ATTR_FREQ_BEFORE) and after (%NL80211_ATTR_FREQ_AFTER) + * the beacon hint was processed. + * + * @NL80211_CMD_AUTHENTICATE: authentication request and notification. + * This command is used both as a command (request to authenticate) and + * as an event on the "mlme" multicast group indicating completion of the + * authentication process. + * When used as a command, %NL80211_ATTR_IFINDEX is used to identify the + * interface. %NL80211_ATTR_MAC is used to specify PeerSTAAddress (and + * BSSID in case of station mode). %NL80211_ATTR_SSID is used to specify + * the SSID (mainly for association, but is included in authentication + * request, too, to help BSS selection. %NL80211_ATTR_WIPHY_FREQ is used + * to specify the frequence of the channel in MHz. %NL80211_ATTR_AUTH_TYPE + * is used to specify the authentication type. %NL80211_ATTR_IE is used to + * define IEs (VendorSpecificInfo, but also including RSN IE and FT IEs) + * to be added to the frame. + * When used as an event, this reports reception of an Authentication + * frame in station and IBSS modes when the local MLME processed the + * frame, i.e., it was for the local STA and was received in correct + * state. This is similar to MLME-AUTHENTICATE.confirm primitive in the + * MLME SAP interface (kernel providing MLME, userspace SME). The + * included %NL80211_ATTR_FRAME attribute contains the management frame + * (including both the header and frame body, but not FCS). This event is + * also used to indicate if the authentication attempt timed out. In that + * case the %NL80211_ATTR_FRAME attribute is replaced with a + * %NL80211_ATTR_TIMED_OUT flag (and %NL80211_ATTR_MAC to indicate which + * pending authentication timed out). + * @NL80211_CMD_ASSOCIATE: association request and notification; like + * NL80211_CMD_AUTHENTICATE but for Association and Reassociation + * (similar to MLME-ASSOCIATE.request, MLME-REASSOCIATE.request, + * MLME-ASSOCIATE.confirm or MLME-REASSOCIATE.confirm primitives). + * @NL80211_CMD_DEAUTHENTICATE: deauthentication request and notification; like + * NL80211_CMD_AUTHENTICATE but for Deauthentication frames (similar to + * MLME-DEAUTHENTICATION.request and MLME-DEAUTHENTICATE.indication + * primitives). + * @NL80211_CMD_DISASSOCIATE: disassociation request and notification; like + * NL80211_CMD_AUTHENTICATE but for Disassociation frames (similar to + * MLME-DISASSOCIATE.request and MLME-DISASSOCIATE.indication primitives). + * + * @NL80211_CMD_MICHAEL_MIC_FAILURE: notification of a locally detected Michael + * MIC (part of TKIP) failure; sent on the "mlme" multicast group; the + * event includes %NL80211_ATTR_MAC to describe the source MAC address of + * the frame with invalid MIC, %NL80211_ATTR_KEY_TYPE to show the key + * type, %NL80211_ATTR_KEY_IDX to indicate the key identifier, and + * %NL80211_ATTR_KEY_SEQ to indicate the TSC value of the frame; this + * event matches with MLME-MICHAELMICFAILURE.indication() primitive + * + * @NL80211_CMD_JOIN_IBSS: Join a new IBSS -- given at least an SSID and a + * FREQ attribute (for the initial frequency if no peer can be found) + * and optionally a MAC (as BSSID) and FREQ_FIXED attribute if those + * should be fixed rather than automatically determined. Can only be + * executed on a network interface that is UP, and fixed BSSID/FREQ + * may be rejected. Another optional parameter is the beacon interval, + * given in the %NL80211_ATTR_BEACON_INTERVAL attribute, which if not + * given defaults to 100 TU (102.4ms). + * @NL80211_CMD_LEAVE_IBSS: Leave the IBSS -- no special arguments, the IBSS is + * determined by the network interface. + * + * @NL80211_CMD_TESTMODE: testmode command, takes a wiphy (or ifindex) attribute + * to identify the device, and the TESTDATA blob attribute to pass through + * to the driver. + * + * @NL80211_CMD_CONNECT: connection request and notification; this command + * requests to connect to a specified network but without separating + * auth and assoc steps. For this, you need to specify the SSID in a + * %NL80211_ATTR_SSID attribute, and can optionally specify the association + * IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_MAC, + * %NL80211_ATTR_WIPHY_FREQ and %NL80211_ATTR_CONTROL_PORT. + * It is also sent as an event, with the BSSID and response IEs when the + * connection is established or failed to be established. This can be + * determined by the STATUS_CODE attribute. + * @NL80211_CMD_ROAM: request that the card roam (currently not implemented), + * sent as an event when the card/driver roamed by itself. + * @NL80211_CMD_DISCONNECT: drop a given connection; also used to notify + * userspace that a connection was dropped by the AP or due to other + * reasons, for this the %NL80211_ATTR_DISCONNECTED_BY_AP and + * %NL80211_ATTR_REASON_CODE attributes are used. + * + * @NL80211_CMD_SET_WIPHY_NETNS: Set a wiphy's netns. Note that all devices + * associated with this wiphy must be down and will follow. + * + * @NL80211_CMD_MAX: highest used command number + * @__NL80211_CMD_AFTER_LAST: internal use + */ +enum nl80211_commands { +/* don't change the order or add anything inbetween, this is ABI! */ + NL80211_CMD_UNSPEC, + + NL80211_CMD_GET_WIPHY, /* can dump */ + NL80211_CMD_SET_WIPHY, + NL80211_CMD_NEW_WIPHY, + NL80211_CMD_DEL_WIPHY, + + NL80211_CMD_GET_INTERFACE, /* can dump */ + NL80211_CMD_SET_INTERFACE, + NL80211_CMD_NEW_INTERFACE, + NL80211_CMD_DEL_INTERFACE, + + NL80211_CMD_GET_KEY, + NL80211_CMD_SET_KEY, + NL80211_CMD_NEW_KEY, + NL80211_CMD_DEL_KEY, + + NL80211_CMD_GET_BEACON, + NL80211_CMD_SET_BEACON, + NL80211_CMD_NEW_BEACON, + NL80211_CMD_DEL_BEACON, + + NL80211_CMD_GET_STATION, + NL80211_CMD_SET_STATION, + NL80211_CMD_NEW_STATION, + NL80211_CMD_DEL_STATION, + + NL80211_CMD_GET_MPATH, + NL80211_CMD_SET_MPATH, + NL80211_CMD_NEW_MPATH, + NL80211_CMD_DEL_MPATH, + + NL80211_CMD_SET_BSS, + + NL80211_CMD_SET_REG, + NL80211_CMD_REQ_SET_REG, + + NL80211_CMD_GET_MESH_PARAMS, + NL80211_CMD_SET_MESH_PARAMS, + + NL80211_CMD_SET_MGMT_EXTRA_IE /* reserved; not used */, + + NL80211_CMD_GET_REG, + + NL80211_CMD_GET_SCAN, + NL80211_CMD_TRIGGER_SCAN, + NL80211_CMD_NEW_SCAN_RESULTS, + NL80211_CMD_SCAN_ABORTED, + + NL80211_CMD_REG_CHANGE, + + NL80211_CMD_AUTHENTICATE, + NL80211_CMD_ASSOCIATE, + NL80211_CMD_DEAUTHENTICATE, + NL80211_CMD_DISASSOCIATE, + + NL80211_CMD_MICHAEL_MIC_FAILURE, + + NL80211_CMD_REG_BEACON_HINT, + + NL80211_CMD_JOIN_IBSS, + NL80211_CMD_LEAVE_IBSS, + + NL80211_CMD_TESTMODE, + + NL80211_CMD_CONNECT, + NL80211_CMD_ROAM, + NL80211_CMD_DISCONNECT, + + NL80211_CMD_SET_WIPHY_NETNS, + + NL80211_CMD_GET_SURVEY, + NL80211_CMD_NEW_SURVEY_RESULTS, + + /* add new commands above here */ + + /* used to define NL80211_CMD_MAX below */ + __NL80211_CMD_AFTER_LAST, + NL80211_CMD_MAX = __NL80211_CMD_AFTER_LAST - 1 +}; + +/* + * Allow user space programs to use #ifdef on new commands by defining them + * here + */ +#define NL80211_CMD_SET_BSS NL80211_CMD_SET_BSS +#define NL80211_CMD_SET_MGMT_EXTRA_IE NL80211_CMD_SET_MGMT_EXTRA_IE +#define NL80211_CMD_REG_CHANGE NL80211_CMD_REG_CHANGE +#define NL80211_CMD_AUTHENTICATE NL80211_CMD_AUTHENTICATE +#define NL80211_CMD_ASSOCIATE NL80211_CMD_ASSOCIATE +#define NL80211_CMD_DEAUTHENTICATE NL80211_CMD_DEAUTHENTICATE +#define NL80211_CMD_DISASSOCIATE NL80211_CMD_DISASSOCIATE +#define NL80211_CMD_REG_BEACON_HINT NL80211_CMD_REG_BEACON_HINT + +/** + * enum nl80211_attrs - nl80211 netlink attributes + * + * @NL80211_ATTR_UNSPEC: unspecified attribute to catch errors + * + * @NL80211_ATTR_WIPHY: index of wiphy to operate on, cf. + * /sys/class/ieee80211/<phyname>/index + * @NL80211_ATTR_WIPHY_NAME: wiphy name (used for renaming) + * @NL80211_ATTR_WIPHY_TXQ_PARAMS: a nested array of TX queue parameters + * @NL80211_ATTR_WIPHY_FREQ: frequency of the selected channel in MHz + * @NL80211_ATTR_WIPHY_CHANNEL_TYPE: included with NL80211_ATTR_WIPHY_FREQ + * if HT20 or HT40 are allowed (i.e., 802.11n disabled if not included): + * NL80211_CHAN_NO_HT = HT not allowed (i.e., same as not including + * this attribute) + * NL80211_CHAN_HT20 = HT20 only + * NL80211_CHAN_HT40MINUS = secondary channel is below the primary channel + * NL80211_CHAN_HT40PLUS = secondary channel is above the primary channel + * @NL80211_ATTR_WIPHY_RETRY_SHORT: TX retry limit for frames whose length is + * less than or equal to the RTS threshold; allowed range: 1..255; + * dot11ShortRetryLimit; u8 + * @NL80211_ATTR_WIPHY_RETRY_LONG: TX retry limit for frames whose length is + * greater than the RTS threshold; allowed range: 1..255; + * dot11ShortLongLimit; u8 + * @NL80211_ATTR_WIPHY_FRAG_THRESHOLD: fragmentation threshold, i.e., maximum + * length in octets for frames; allowed range: 256..8000, disable + * fragmentation with (u32)-1; dot11FragmentationThreshold; u32 + * @NL80211_ATTR_WIPHY_RTS_THRESHOLD: RTS threshold (TX frames with length + * larger than or equal to this use RTS/CTS handshake); allowed range: + * 0..65536, disable with (u32)-1; dot11RTSThreshold; u32 + * + * @NL80211_ATTR_IFINDEX: network interface index of the device to operate on + * @NL80211_ATTR_IFNAME: network interface name + * @NL80211_ATTR_IFTYPE: type of virtual interface, see &enum nl80211_iftype + * + * @NL80211_ATTR_MAC: MAC address (various uses) + * + * @NL80211_ATTR_KEY_DATA: (temporal) key data; for TKIP this consists of + * 16 bytes encryption key followed by 8 bytes each for TX and RX MIC + * keys + * @NL80211_ATTR_KEY_IDX: key ID (u8, 0-3) + * @NL80211_ATTR_KEY_CIPHER: key cipher suite (u32, as defined by IEEE 802.11 + * section 7.3.2.25.1, e.g. 0x000FAC04) + * @NL80211_ATTR_KEY_SEQ: transmit key sequence number (IV/PN) for TKIP and + * CCMP keys, each six bytes in little endian + * + * @NL80211_ATTR_BEACON_INTERVAL: beacon interval in TU + * @NL80211_ATTR_DTIM_PERIOD: DTIM period for beaconing + * @NL80211_ATTR_BEACON_HEAD: portion of the beacon before the TIM IE + * @NL80211_ATTR_BEACON_TAIL: portion of the beacon after the TIM IE + * + * @NL80211_ATTR_STA_AID: Association ID for the station (u16) + * @NL80211_ATTR_STA_FLAGS: flags, nested element with NLA_FLAG attributes of + * &enum nl80211_sta_flags (deprecated, use %NL80211_ATTR_STA_FLAGS2) + * @NL80211_ATTR_STA_LISTEN_INTERVAL: listen interval as defined by + * IEEE 802.11 7.3.1.6 (u16). + * @NL80211_ATTR_STA_SUPPORTED_RATES: supported rates, array of supported + * rates as defined by IEEE 802.11 7.3.2.2 but without the length + * restriction (at most %NL80211_MAX_SUPP_RATES). + * @NL80211_ATTR_STA_VLAN: interface index of VLAN interface to move station + * to, or the AP interface the station was originally added to to. + * @NL80211_ATTR_STA_INFO: information about a station, part of station info + * given for %NL80211_CMD_GET_STATION, nested attribute containing + * info as possible, see &enum nl80211_sta_info. + * + * @NL80211_ATTR_WIPHY_BANDS: Information about an operating bands, + * consisting of a nested array. + * + * @NL80211_ATTR_MESH_ID: mesh id (1-32 bytes). + * @NL80211_ATTR_PLINK_ACTION: action to perform on the mesh peer link. + * @NL80211_ATTR_MPATH_NEXT_HOP: MAC address of the next hop for a mesh path. + * @NL80211_ATTR_MPATH_INFO: information about a mesh_path, part of mesh path + * info given for %NL80211_CMD_GET_MPATH, nested attribute described at + * &enum nl80211_mpath_info. + * + * @NL80211_ATTR_MNTR_FLAGS: flags, nested element with NLA_FLAG attributes of + * &enum nl80211_mntr_flags. + * + * @NL80211_ATTR_REG_ALPHA2: an ISO-3166-alpha2 country code for which the + * current regulatory domain should be set to or is already set to. + * For example, 'CR', for Costa Rica. This attribute is used by the kernel + * to query the CRDA to retrieve one regulatory domain. This attribute can + * also be used by userspace to query the kernel for the currently set + * regulatory domain. We chose an alpha2 as that is also used by the + * IEEE-802.11d country information element to identify a country. + * Users can also simply ask the wireless core to set regulatory domain + * to a specific alpha2. + * @NL80211_ATTR_REG_RULES: a nested array of regulatory domain regulatory + * rules. + * + * @NL80211_ATTR_BSS_CTS_PROT: whether CTS protection is enabled (u8, 0 or 1) + * @NL80211_ATTR_BSS_SHORT_PREAMBLE: whether short preamble is enabled + * (u8, 0 or 1) + * @NL80211_ATTR_BSS_SHORT_SLOT_TIME: whether short slot time enabled + * (u8, 0 or 1) + * @NL80211_ATTR_BSS_BASIC_RATES: basic rates, array of basic + * rates in format defined by IEEE 802.11 7.3.2.2 but without the length + * restriction (at most %NL80211_MAX_SUPP_RATES). + * + * @NL80211_ATTR_HT_CAPABILITY: HT Capability information element (from + * association request when used with NL80211_CMD_NEW_STATION) + * + * @NL80211_ATTR_SUPPORTED_IFTYPES: nested attribute containing all + * supported interface types, each a flag attribute with the number + * of the interface mode. + * + * @NL80211_ATTR_MGMT_SUBTYPE: Management frame subtype for + * %NL80211_CMD_SET_MGMT_EXTRA_IE. + * + * @NL80211_ATTR_IE: Information element(s) data (used, e.g., with + * %NL80211_CMD_SET_MGMT_EXTRA_IE). + * + * @NL80211_ATTR_MAX_NUM_SCAN_SSIDS: number of SSIDs you can scan with + * a single scan request, a wiphy attribute. + * @NL80211_ATTR_MAX_SCAN_IE_LEN: maximum length of information elements + * that can be added to a scan request + * + * @NL80211_ATTR_SCAN_FREQUENCIES: nested attribute with frequencies (in MHz) + * @NL80211_ATTR_SCAN_SSIDS: nested attribute with SSIDs, leave out for passive + * scanning and include a zero-length SSID (wildcard) for wildcard scan + * @NL80211_ATTR_BSS: scan result BSS + * + * @NL80211_ATTR_REG_INITIATOR: indicates who requested the regulatory domain + * currently in effect. This could be any of the %NL80211_REGDOM_SET_BY_* + * @NL80211_ATTR_REG_TYPE: indicates the type of the regulatory domain currently + * set. This can be one of the nl80211_reg_type (%NL80211_REGDOM_TYPE_*) + * + * @NL80211_ATTR_SUPPORTED_COMMANDS: wiphy attribute that specifies + * an array of command numbers (i.e. a mapping index to command number) + * that the driver for the given wiphy supports. + * + * @NL80211_ATTR_FRAME: frame data (binary attribute), including frame header + * and body, but not FCS; used, e.g., with NL80211_CMD_AUTHENTICATE and + * NL80211_CMD_ASSOCIATE events + * @NL80211_ATTR_SSID: SSID (binary attribute, 0..32 octets) + * @NL80211_ATTR_AUTH_TYPE: AuthenticationType, see &enum nl80211_auth_type, + * represented as a u32 + * @NL80211_ATTR_REASON_CODE: ReasonCode for %NL80211_CMD_DEAUTHENTICATE and + * %NL80211_CMD_DISASSOCIATE, u16 + * + * @NL80211_ATTR_KEY_TYPE: Key Type, see &enum nl80211_key_type, represented as + * a u32 + * + * @NL80211_ATTR_FREQ_BEFORE: A channel which has suffered a regulatory change + * due to considerations from a beacon hint. This attribute reflects + * the state of the channel _before_ the beacon hint processing. This + * attributes consists of a nested attribute containing + * NL80211_FREQUENCY_ATTR_* + * @NL80211_ATTR_FREQ_AFTER: A channel which has suffered a regulatory change + * due to considerations from a beacon hint. This attribute reflects + * the state of the channel _after_ the beacon hint processing. This + * attributes consists of a nested attribute containing + * NL80211_FREQUENCY_ATTR_* + * + * @NL80211_ATTR_CIPHER_SUITES: a set of u32 values indicating the supported + * cipher suites + * + * @NL80211_ATTR_FREQ_FIXED: a flag indicating the IBSS should not try to look + * for other networks on different channels + * + * @NL80211_ATTR_TIMED_OUT: a flag indicating than an operation timed out; this + * is used, e.g., with %NL80211_CMD_AUTHENTICATE event + * + * @NL80211_ATTR_USE_MFP: Whether management frame protection (IEEE 802.11w) is + * used for the association (&enum nl80211_mfp, represented as a u32); + * this attribute can be used + * with %NL80211_CMD_ASSOCIATE request + * + * @NL80211_ATTR_STA_FLAGS2: Attribute containing a + * &struct nl80211_sta_flag_update. + * + * @NL80211_ATTR_CONTROL_PORT: A flag indicating whether user space controls + * IEEE 802.1X port, i.e., sets/clears %NL80211_STA_FLAG_AUTHORIZED, in + * station mode. If the flag is included in %NL80211_CMD_ASSOCIATE + * request, the driver will assume that the port is unauthorized until + * authorized by user space. Otherwise, port is marked authorized by + * default in station mode. + * + * @NL80211_ATTR_TESTDATA: Testmode data blob, passed through to the driver. + * We recommend using nested, driver-specific attributes within this. + * + * @NL80211_ATTR_DISCONNECTED_BY_AP: A flag indicating that the DISCONNECT + * event was due to the AP disconnecting the station, and not due to + * a local disconnect request. + * @NL80211_ATTR_STATUS_CODE: StatusCode for the %NL80211_CMD_CONNECT + * event (u16) + * @NL80211_ATTR_PRIVACY: Flag attribute, used with connect(), indicating + * that protected APs should be used. + * + * @NL80211_ATTR_CIPHERS_PAIRWISE: Used with CONNECT and ASSOCIATE to + * indicate which unicast key ciphers will be used with the connection + * (an array of u32). + * @NL80211_ATTR_CIPHER_GROUP: Used with CONNECT and ASSOCIATE to indicate + * which group key cipher will be used with the connection (a u32). + * @NL80211_ATTR_WPA_VERSIONS: Used with CONNECT and ASSOCIATE to indicate + * which WPA version(s) the AP we want to associate with is using + * (a u32 with flags from &enum nl80211_wpa_versions). + * @NL80211_ATTR_AKM_SUITES: Used with CONNECT and ASSOCIATE to indicate + * which key management algorithm(s) to use (an array of u32). + * + * @NL80211_ATTR_REQ_IE: (Re)association request information elements as + * sent out by the card, for ROAM and successful CONNECT events. + * @NL80211_ATTR_RESP_IE: (Re)association response information elements as + * sent by peer, for ROAM and successful CONNECT events. + * + * @NL80211_ATTR_PREV_BSSID: previous BSSID, to be used by in ASSOCIATE + * commands to specify using a reassociate frame + * + * @NL80211_ATTR_KEY: key information in a nested attribute with + * %NL80211_KEY_* sub-attributes + * @NL80211_ATTR_KEYS: array of keys for static WEP keys for connect() + * and join_ibss(), key information is in a nested attribute each + * with %NL80211_KEY_* sub-attributes + * + * @NL80211_ATTR_PID: Process ID of a network namespace. + * + * @NL80211_ATTR_GENERATION: Used to indicate consistent snapshots for + * dumps. This number increases whenever the object list being + * dumped changes, and as such userspace can verify that it has + * obtained a complete and consistent snapshot by verifying that + * all dump messages contain the same generation number. If it + * changed then the list changed and the dump should be repeated + * completely from scratch. + * + * @NL80211_ATTR_4ADDR: Use 4-address frames on a virtual interface + * + * @NL80211_ATTR_SURVEY_INFO: survey information about a channel, part of + * the survey response for %NL80211_CMD_GET_SURVEY, nested attribute + * containing info as possible, see &enum survey_info. + * + * @NL80211_ATTR_MAX: highest attribute number currently defined + * @__NL80211_ATTR_AFTER_LAST: internal use + */ +enum nl80211_attrs { +/* don't change the order or add anything inbetween, this is ABI! */ + NL80211_ATTR_UNSPEC, + + NL80211_ATTR_WIPHY, + NL80211_ATTR_WIPHY_NAME, + + NL80211_ATTR_IFINDEX, + NL80211_ATTR_IFNAME, + NL80211_ATTR_IFTYPE, + + NL80211_ATTR_MAC, + + NL80211_ATTR_KEY_DATA, + NL80211_ATTR_KEY_IDX, + NL80211_ATTR_KEY_CIPHER, + NL80211_ATTR_KEY_SEQ, + NL80211_ATTR_KEY_DEFAULT, + + NL80211_ATTR_BEACON_INTERVAL, + NL80211_ATTR_DTIM_PERIOD, + NL80211_ATTR_BEACON_HEAD, + NL80211_ATTR_BEACON_TAIL, + + NL80211_ATTR_STA_AID, + NL80211_ATTR_STA_FLAGS, + NL80211_ATTR_STA_LISTEN_INTERVAL, + NL80211_ATTR_STA_SUPPORTED_RATES, + NL80211_ATTR_STA_VLAN, + NL80211_ATTR_STA_INFO, + + NL80211_ATTR_WIPHY_BANDS, + + NL80211_ATTR_MNTR_FLAGS, + + NL80211_ATTR_MESH_ID, + NL80211_ATTR_STA_PLINK_ACTION, + NL80211_ATTR_MPATH_NEXT_HOP, + NL80211_ATTR_MPATH_INFO, + + NL80211_ATTR_BSS_CTS_PROT, + NL80211_ATTR_BSS_SHORT_PREAMBLE, + NL80211_ATTR_BSS_SHORT_SLOT_TIME, + + NL80211_ATTR_HT_CAPABILITY, + + NL80211_ATTR_SUPPORTED_IFTYPES, + + NL80211_ATTR_REG_ALPHA2, + NL80211_ATTR_REG_RULES, + + NL80211_ATTR_MESH_PARAMS, + + NL80211_ATTR_BSS_BASIC_RATES, + + NL80211_ATTR_WIPHY_TXQ_PARAMS, + NL80211_ATTR_WIPHY_FREQ, + NL80211_ATTR_WIPHY_CHANNEL_TYPE, + + NL80211_ATTR_KEY_DEFAULT_MGMT, + + NL80211_ATTR_MGMT_SUBTYPE, + NL80211_ATTR_IE, + + NL80211_ATTR_MAX_NUM_SCAN_SSIDS, + + NL80211_ATTR_SCAN_FREQUENCIES, + NL80211_ATTR_SCAN_SSIDS, + NL80211_ATTR_GENERATION, /* replaces old SCAN_GENERATION */ + NL80211_ATTR_BSS, + + NL80211_ATTR_REG_INITIATOR, + NL80211_ATTR_REG_TYPE, + + NL80211_ATTR_SUPPORTED_COMMANDS, + + NL80211_ATTR_FRAME, + NL80211_ATTR_SSID, + NL80211_ATTR_AUTH_TYPE, + NL80211_ATTR_REASON_CODE, + + NL80211_ATTR_KEY_TYPE, + + NL80211_ATTR_MAX_SCAN_IE_LEN, + NL80211_ATTR_CIPHER_SUITES, + + NL80211_ATTR_FREQ_BEFORE, + NL80211_ATTR_FREQ_AFTER, + + NL80211_ATTR_FREQ_FIXED, + + + NL80211_ATTR_WIPHY_RETRY_SHORT, + NL80211_ATTR_WIPHY_RETRY_LONG, + NL80211_ATTR_WIPHY_FRAG_THRESHOLD, + NL80211_ATTR_WIPHY_RTS_THRESHOLD, + + NL80211_ATTR_TIMED_OUT, + + NL80211_ATTR_USE_MFP, + + NL80211_ATTR_STA_FLAGS2, + + NL80211_ATTR_CONTROL_PORT, + + NL80211_ATTR_TESTDATA, + + NL80211_ATTR_PRIVACY, + + NL80211_ATTR_DISCONNECTED_BY_AP, + NL80211_ATTR_STATUS_CODE, + + NL80211_ATTR_CIPHER_SUITES_PAIRWISE, + NL80211_ATTR_CIPHER_SUITE_GROUP, + NL80211_ATTR_WPA_VERSIONS, + NL80211_ATTR_AKM_SUITES, + + NL80211_ATTR_REQ_IE, + NL80211_ATTR_RESP_IE, + + NL80211_ATTR_PREV_BSSID, + + NL80211_ATTR_KEY, + NL80211_ATTR_KEYS, + + NL80211_ATTR_PID, + + NL80211_ATTR_4ADDR, + + NL80211_ATTR_SURVEY_INFO, + + /* add attributes here, update the policy in nl80211.c */ + + __NL80211_ATTR_AFTER_LAST, + NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1 +}; + +/* source-level API compatibility */ +#define NL80211_ATTR_SCAN_GENERATION NL80211_ATTR_GENERATION + +/* + * Allow user space programs to use #ifdef on new attributes by defining them + * here + */ +#define NL80211_CMD_CONNECT NL80211_CMD_CONNECT +#define NL80211_ATTR_HT_CAPABILITY NL80211_ATTR_HT_CAPABILITY +#define NL80211_ATTR_BSS_BASIC_RATES NL80211_ATTR_BSS_BASIC_RATES +#define NL80211_ATTR_WIPHY_TXQ_PARAMS NL80211_ATTR_WIPHY_TXQ_PARAMS +#define NL80211_ATTR_WIPHY_FREQ NL80211_ATTR_WIPHY_FREQ +#define NL80211_ATTR_WIPHY_CHANNEL_TYPE NL80211_ATTR_WIPHY_CHANNEL_TYPE +#define NL80211_ATTR_MGMT_SUBTYPE NL80211_ATTR_MGMT_SUBTYPE +#define NL80211_ATTR_IE NL80211_ATTR_IE +#define NL80211_ATTR_REG_INITIATOR NL80211_ATTR_REG_INITIATOR +#define NL80211_ATTR_REG_TYPE NL80211_ATTR_REG_TYPE +#define NL80211_ATTR_FRAME NL80211_ATTR_FRAME +#define NL80211_ATTR_SSID NL80211_ATTR_SSID +#define NL80211_ATTR_AUTH_TYPE NL80211_ATTR_AUTH_TYPE +#define NL80211_ATTR_REASON_CODE NL80211_ATTR_REASON_CODE +#define NL80211_ATTR_CIPHER_SUITES_PAIRWISE NL80211_ATTR_CIPHER_SUITES_PAIRWISE +#define NL80211_ATTR_CIPHER_SUITE_GROUP NL80211_ATTR_CIPHER_SUITE_GROUP +#define NL80211_ATTR_WPA_VERSIONS NL80211_ATTR_WPA_VERSIONS +#define NL80211_ATTR_AKM_SUITES NL80211_ATTR_AKM_SUITES +#define NL80211_ATTR_KEY NL80211_ATTR_KEY +#define NL80211_ATTR_KEYS NL80211_ATTR_KEYS + +#define NL80211_MAX_SUPP_RATES 32 +#define NL80211_MAX_SUPP_REG_RULES 32 +#define NL80211_TKIP_DATA_OFFSET_ENCR_KEY 0 +#define NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY 16 +#define NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY 24 +#define NL80211_HT_CAPABILITY_LEN 26 + +#define NL80211_MAX_NR_CIPHER_SUITES 5 +#define NL80211_MAX_NR_AKM_SUITES 2 + +/** + * enum nl80211_iftype - (virtual) interface types + * + * @NL80211_IFTYPE_UNSPECIFIED: unspecified type, driver decides + * @NL80211_IFTYPE_ADHOC: independent BSS member + * @NL80211_IFTYPE_STATION: managed BSS member + * @NL80211_IFTYPE_AP: access point + * @NL80211_IFTYPE_AP_VLAN: VLAN interface for access points + * @NL80211_IFTYPE_WDS: wireless distribution interface + * @NL80211_IFTYPE_MONITOR: monitor interface receiving all frames + * @NL80211_IFTYPE_MESH_POINT: mesh point + * @NL80211_IFTYPE_MAX: highest interface type number currently defined + * @__NL80211_IFTYPE_AFTER_LAST: internal use + * + * These values are used with the %NL80211_ATTR_IFTYPE + * to set the type of an interface. + * + */ +enum nl80211_iftype { + NL80211_IFTYPE_UNSPECIFIED, + NL80211_IFTYPE_ADHOC, + NL80211_IFTYPE_STATION, + NL80211_IFTYPE_AP, + NL80211_IFTYPE_AP_VLAN, + NL80211_IFTYPE_WDS, + NL80211_IFTYPE_MONITOR, + NL80211_IFTYPE_MESH_POINT, + + /* keep last */ + __NL80211_IFTYPE_AFTER_LAST, + NL80211_IFTYPE_MAX = __NL80211_IFTYPE_AFTER_LAST - 1 +}; + +/** + * enum nl80211_sta_flags - station flags + * + * Station flags. When a station is added to an AP interface, it is + * assumed to be already associated (and hence authenticated.) + * + * @NL80211_STA_FLAG_AUTHORIZED: station is authorized (802.1X) + * @NL80211_STA_FLAG_SHORT_PREAMBLE: station is capable of receiving frames + * with short barker preamble + * @NL80211_STA_FLAG_WME: station is WME/QoS capable + * @NL80211_STA_FLAG_MFP: station uses management frame protection + */ +enum nl80211_sta_flags { + __NL80211_STA_FLAG_INVALID, + NL80211_STA_FLAG_AUTHORIZED, + NL80211_STA_FLAG_SHORT_PREAMBLE, + NL80211_STA_FLAG_WME, + NL80211_STA_FLAG_MFP, + + /* keep last */ + __NL80211_STA_FLAG_AFTER_LAST, + NL80211_STA_FLAG_MAX = __NL80211_STA_FLAG_AFTER_LAST - 1 +}; + +/** + * struct nl80211_sta_flag_update - station flags mask/set + * @mask: mask of station flags to set + * @set: which values to set them to + * + * Both mask and set contain bits as per &enum nl80211_sta_flags. + */ +struct nl80211_sta_flag_update { + __u32 mask; + __u32 set; +} __attribute__((packed)); + +/** + * enum nl80211_rate_info - bitrate information + * + * These attribute types are used with %NL80211_STA_INFO_TXRATE + * when getting information about the bitrate of a station. + * + * @__NL80211_RATE_INFO_INVALID: attribute number 0 is reserved + * @NL80211_RATE_INFO_BITRATE: total bitrate (u16, 100kbit/s) + * @NL80211_RATE_INFO_MCS: mcs index for 802.11n (u8) + * @NL80211_RATE_INFO_40_MHZ_WIDTH: 40 Mhz dualchannel bitrate + * @NL80211_RATE_INFO_SHORT_GI: 400ns guard interval + * @NL80211_RATE_INFO_MAX: highest rate_info number currently defined + * @__NL80211_RATE_INFO_AFTER_LAST: internal use + */ +enum nl80211_rate_info { + __NL80211_RATE_INFO_INVALID, + NL80211_RATE_INFO_BITRATE, + NL80211_RATE_INFO_MCS, + NL80211_RATE_INFO_40_MHZ_WIDTH, + NL80211_RATE_INFO_SHORT_GI, + + /* keep last */ + __NL80211_RATE_INFO_AFTER_LAST, + NL80211_RATE_INFO_MAX = __NL80211_RATE_INFO_AFTER_LAST - 1 +}; + +/** + * enum nl80211_sta_info - station information + * + * These attribute types are used with %NL80211_ATTR_STA_INFO + * when getting information about a station. + * + * @__NL80211_STA_INFO_INVALID: attribute number 0 is reserved + * @NL80211_STA_INFO_INACTIVE_TIME: time since last activity (u32, msecs) + * @NL80211_STA_INFO_RX_BYTES: total received bytes (u32, from this station) + * @NL80211_STA_INFO_TX_BYTES: total transmitted bytes (u32, to this station) + * @__NL80211_STA_INFO_AFTER_LAST: internal + * @NL80211_STA_INFO_MAX: highest possible station info attribute + * @NL80211_STA_INFO_SIGNAL: signal strength of last received PPDU (u8, dBm) + * @NL80211_STA_INFO_TX_BITRATE: current unicast tx rate, nested attribute + * containing info as possible, see &enum nl80211_sta_info_txrate. + * @NL80211_STA_INFO_RX_PACKETS: total received packet (u32, from this station) + * @NL80211_STA_INFO_TX_PACKETS: total transmitted packets (u32, to this + * station) + */ +enum nl80211_sta_info { + __NL80211_STA_INFO_INVALID, + NL80211_STA_INFO_INACTIVE_TIME, + NL80211_STA_INFO_RX_BYTES, + NL80211_STA_INFO_TX_BYTES, + NL80211_STA_INFO_LLID, + NL80211_STA_INFO_PLID, + NL80211_STA_INFO_PLINK_STATE, + NL80211_STA_INFO_SIGNAL, + NL80211_STA_INFO_TX_BITRATE, + NL80211_STA_INFO_RX_PACKETS, + NL80211_STA_INFO_TX_PACKETS, + + /* keep last */ + __NL80211_STA_INFO_AFTER_LAST, + NL80211_STA_INFO_MAX = __NL80211_STA_INFO_AFTER_LAST - 1 +}; + +/** + * enum nl80211_mpath_flags - nl80211 mesh path flags + * + * @NL80211_MPATH_FLAG_ACTIVE: the mesh path is active + * @NL80211_MPATH_FLAG_RESOLVING: the mesh path discovery process is running + * @NL80211_MPATH_FLAG_SN_VALID: the mesh path contains a valid SN + * @NL80211_MPATH_FLAG_FIXED: the mesh path has been manually set + * @NL80211_MPATH_FLAG_RESOLVED: the mesh path discovery process succeeded + */ +enum nl80211_mpath_flags { + NL80211_MPATH_FLAG_ACTIVE = 1<<0, + NL80211_MPATH_FLAG_RESOLVING = 1<<1, + NL80211_MPATH_FLAG_SN_VALID = 1<<2, + NL80211_MPATH_FLAG_FIXED = 1<<3, + NL80211_MPATH_FLAG_RESOLVED = 1<<4, +}; + +/** + * enum nl80211_mpath_info - mesh path information + * + * These attribute types are used with %NL80211_ATTR_MPATH_INFO when getting + * information about a mesh path. + * + * @__NL80211_MPATH_INFO_INVALID: attribute number 0 is reserved + * @NL80211_ATTR_MPATH_FRAME_QLEN: number of queued frames for this destination + * @NL80211_ATTR_MPATH_SN: destination sequence number + * @NL80211_ATTR_MPATH_METRIC: metric (cost) of this mesh path + * @NL80211_ATTR_MPATH_EXPTIME: expiration time for the path, in msec from now + * @NL80211_ATTR_MPATH_FLAGS: mesh path flags, enumerated in + * &enum nl80211_mpath_flags; + * @NL80211_ATTR_MPATH_DISCOVERY_TIMEOUT: total path discovery timeout, in msec + * @NL80211_ATTR_MPATH_DISCOVERY_RETRIES: mesh path discovery retries + */ +enum nl80211_mpath_info { + __NL80211_MPATH_INFO_INVALID, + NL80211_MPATH_INFO_FRAME_QLEN, + NL80211_MPATH_INFO_SN, + NL80211_MPATH_INFO_METRIC, + NL80211_MPATH_INFO_EXPTIME, + NL80211_MPATH_INFO_FLAGS, + NL80211_MPATH_INFO_DISCOVERY_TIMEOUT, + NL80211_MPATH_INFO_DISCOVERY_RETRIES, + + /* keep last */ + __NL80211_MPATH_INFO_AFTER_LAST, + NL80211_MPATH_INFO_MAX = __NL80211_MPATH_INFO_AFTER_LAST - 1 +}; + +/** + * enum nl80211_band_attr - band attributes + * @__NL80211_BAND_ATTR_INVALID: attribute number 0 is reserved + * @NL80211_BAND_ATTR_FREQS: supported frequencies in this band, + * an array of nested frequency attributes + * @NL80211_BAND_ATTR_RATES: supported bitrates in this band, + * an array of nested bitrate attributes + * @NL80211_BAND_ATTR_HT_MCS_SET: 16-byte attribute containing the MCS set as + * defined in 802.11n + * @NL80211_BAND_ATTR_HT_CAPA: HT capabilities, as in the HT information IE + * @NL80211_BAND_ATTR_HT_AMPDU_FACTOR: A-MPDU factor, as in 11n + * @NL80211_BAND_ATTR_HT_AMPDU_DENSITY: A-MPDU density, as in 11n + */ +enum nl80211_band_attr { + __NL80211_BAND_ATTR_INVALID, + NL80211_BAND_ATTR_FREQS, + NL80211_BAND_ATTR_RATES, + + NL80211_BAND_ATTR_HT_MCS_SET, + NL80211_BAND_ATTR_HT_CAPA, + NL80211_BAND_ATTR_HT_AMPDU_FACTOR, + NL80211_BAND_ATTR_HT_AMPDU_DENSITY, + + /* keep last */ + __NL80211_BAND_ATTR_AFTER_LAST, + NL80211_BAND_ATTR_MAX = __NL80211_BAND_ATTR_AFTER_LAST - 1 +}; + +#define NL80211_BAND_ATTR_HT_CAPA NL80211_BAND_ATTR_HT_CAPA + +/** + * enum nl80211_frequency_attr - frequency attributes + * @NL80211_FREQUENCY_ATTR_FREQ: Frequency in MHz + * @NL80211_FREQUENCY_ATTR_DISABLED: Channel is disabled in current + * regulatory domain. + * @NL80211_FREQUENCY_ATTR_PASSIVE_SCAN: Only passive scanning is + * permitted on this channel in current regulatory domain. + * @NL80211_FREQUENCY_ATTR_NO_IBSS: IBSS networks are not permitted + * on this channel in current regulatory domain. + * @NL80211_FREQUENCY_ATTR_RADAR: Radar detection is mandatory + * on this channel in current regulatory domain. + * @NL80211_FREQUENCY_ATTR_MAX_TX_POWER: Maximum transmission power in mBm + * (100 * dBm). + */ +enum nl80211_frequency_attr { + __NL80211_FREQUENCY_ATTR_INVALID, + NL80211_FREQUENCY_ATTR_FREQ, + NL80211_FREQUENCY_ATTR_DISABLED, + NL80211_FREQUENCY_ATTR_PASSIVE_SCAN, + NL80211_FREQUENCY_ATTR_NO_IBSS, + NL80211_FREQUENCY_ATTR_RADAR, + NL80211_FREQUENCY_ATTR_MAX_TX_POWER, + + /* keep last */ + __NL80211_FREQUENCY_ATTR_AFTER_LAST, + NL80211_FREQUENCY_ATTR_MAX = __NL80211_FREQUENCY_ATTR_AFTER_LAST - 1 +}; + +#define NL80211_FREQUENCY_ATTR_MAX_TX_POWER NL80211_FREQUENCY_ATTR_MAX_TX_POWER + +/** + * enum nl80211_bitrate_attr - bitrate attributes + * @NL80211_BITRATE_ATTR_RATE: Bitrate in units of 100 kbps + * @NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE: Short preamble supported + * in 2.4 GHz band. + */ +enum nl80211_bitrate_attr { + __NL80211_BITRATE_ATTR_INVALID, + NL80211_BITRATE_ATTR_RATE, + NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE, + + /* keep last */ + __NL80211_BITRATE_ATTR_AFTER_LAST, + NL80211_BITRATE_ATTR_MAX = __NL80211_BITRATE_ATTR_AFTER_LAST - 1 +}; + +/** + * enum nl80211_initiator - Indicates the initiator of a reg domain request + * @NL80211_REGDOM_SET_BY_CORE: Core queried CRDA for a dynamic world + * regulatory domain. + * @NL80211_REGDOM_SET_BY_USER: User asked the wireless core to set the + * regulatory domain. + * @NL80211_REGDOM_SET_BY_DRIVER: a wireless drivers has hinted to the + * wireless core it thinks its knows the regulatory domain we should be in. + * @NL80211_REGDOM_SET_BY_COUNTRY_IE: the wireless core has received an + * 802.11 country information element with regulatory information it + * thinks we should consider. + */ +enum nl80211_reg_initiator { + NL80211_REGDOM_SET_BY_CORE, + NL80211_REGDOM_SET_BY_USER, + NL80211_REGDOM_SET_BY_DRIVER, + NL80211_REGDOM_SET_BY_COUNTRY_IE, +}; + +/** + * enum nl80211_reg_type - specifies the type of regulatory domain + * @NL80211_REGDOM_TYPE_COUNTRY: the regulatory domain set is one that pertains + * to a specific country. When this is set you can count on the + * ISO / IEC 3166 alpha2 country code being valid. + * @NL80211_REGDOM_TYPE_WORLD: the regulatory set domain is the world regulatory + * domain. + * @NL80211_REGDOM_TYPE_CUSTOM_WORLD: the regulatory domain set is a custom + * driver specific world regulatory domain. These do not apply system-wide + * and are only applicable to the individual devices which have requested + * them to be applied. + * @NL80211_REGDOM_TYPE_INTERSECTION: the regulatory domain set is the product + * of an intersection between two regulatory domains -- the previously + * set regulatory domain on the system and the last accepted regulatory + * domain request to be processed. + */ +enum nl80211_reg_type { + NL80211_REGDOM_TYPE_COUNTRY, + NL80211_REGDOM_TYPE_WORLD, + NL80211_REGDOM_TYPE_CUSTOM_WORLD, + NL80211_REGDOM_TYPE_INTERSECTION, +}; + +/** + * enum nl80211_reg_rule_attr - regulatory rule attributes + * @NL80211_ATTR_REG_RULE_FLAGS: a set of flags which specify additional + * considerations for a given frequency range. These are the + * &enum nl80211_reg_rule_flags. + * @NL80211_ATTR_FREQ_RANGE_START: starting frequencry for the regulatory + * rule in KHz. This is not a center of frequency but an actual regulatory + * band edge. + * @NL80211_ATTR_FREQ_RANGE_END: ending frequency for the regulatory rule + * in KHz. This is not a center a frequency but an actual regulatory + * band edge. + * @NL80211_ATTR_FREQ_RANGE_MAX_BW: maximum allowed bandwidth for this + * frequency range, in KHz. + * @NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN: the maximum allowed antenna gain + * for a given frequency range. The value is in mBi (100 * dBi). + * If you don't have one then don't send this. + * @NL80211_ATTR_POWER_RULE_MAX_EIRP: the maximum allowed EIRP for + * a given frequency range. The value is in mBm (100 * dBm). + */ +enum nl80211_reg_rule_attr { + __NL80211_REG_RULE_ATTR_INVALID, + NL80211_ATTR_REG_RULE_FLAGS, + + NL80211_ATTR_FREQ_RANGE_START, + NL80211_ATTR_FREQ_RANGE_END, + NL80211_ATTR_FREQ_RANGE_MAX_BW, + + NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN, + NL80211_ATTR_POWER_RULE_MAX_EIRP, + + /* keep last */ + __NL80211_REG_RULE_ATTR_AFTER_LAST, + NL80211_REG_RULE_ATTR_MAX = __NL80211_REG_RULE_ATTR_AFTER_LAST - 1 +}; + +/** + * enum nl80211_reg_rule_flags - regulatory rule flags + * + * @NL80211_RRF_NO_OFDM: OFDM modulation not allowed + * @NL80211_RRF_NO_CCK: CCK modulation not allowed + * @NL80211_RRF_NO_INDOOR: indoor operation not allowed + * @NL80211_RRF_NO_OUTDOOR: outdoor operation not allowed + * @NL80211_RRF_DFS: DFS support is required to be used + * @NL80211_RRF_PTP_ONLY: this is only for Point To Point links + * @NL80211_RRF_PTMP_ONLY: this is only for Point To Multi Point links + * @NL80211_RRF_PASSIVE_SCAN: passive scan is required + * @NL80211_RRF_NO_IBSS: no IBSS is allowed + */ +enum nl80211_reg_rule_flags { + NL80211_RRF_NO_OFDM = 1<<0, + NL80211_RRF_NO_CCK = 1<<1, + NL80211_RRF_NO_INDOOR = 1<<2, + NL80211_RRF_NO_OUTDOOR = 1<<3, + NL80211_RRF_DFS = 1<<4, + NL80211_RRF_PTP_ONLY = 1<<5, + NL80211_RRF_PTMP_ONLY = 1<<6, + NL80211_RRF_PASSIVE_SCAN = 1<<7, + NL80211_RRF_NO_IBSS = 1<<8, +}; + +/** + * enum nl80211_survey_info - survey information + * + * These attribute types are used with %NL80211_ATTR_SURVEY_INFO + * when getting information about a survey. + * + * @__NL80211_SURVEY_INFO_INVALID: attribute number 0 is reserved + * @NL80211_SURVEY_INFO_FREQUENCY: center frequency of channel + * @NL80211_SURVEY_INFO_NOISE: noise level of channel (u8, dBm) + */ +enum nl80211_survey_info { + __NL80211_SURVEY_INFO_INVALID, + NL80211_SURVEY_INFO_FREQUENCY, + NL80211_SURVEY_INFO_NOISE, + + /* keep last */ + __NL80211_SURVEY_INFO_AFTER_LAST, + NL80211_SURVEY_INFO_MAX = __NL80211_SURVEY_INFO_AFTER_LAST - 1 +}; + +/** + * enum nl80211_mntr_flags - monitor configuration flags + * + * Monitor configuration flags. + * + * @__NL80211_MNTR_FLAG_INVALID: reserved + * + * @NL80211_MNTR_FLAG_FCSFAIL: pass frames with bad FCS + * @NL80211_MNTR_FLAG_PLCPFAIL: pass frames with bad PLCP + * @NL80211_MNTR_FLAG_CONTROL: pass control frames + * @NL80211_MNTR_FLAG_OTHER_BSS: disable BSSID filtering + * @NL80211_MNTR_FLAG_COOK_FRAMES: report frames after processing. + * overrides all other flags. + * + * @__NL80211_MNTR_FLAG_AFTER_LAST: internal use + * @NL80211_MNTR_FLAG_MAX: highest possible monitor flag + */ +enum nl80211_mntr_flags { + __NL80211_MNTR_FLAG_INVALID, + NL80211_MNTR_FLAG_FCSFAIL, + NL80211_MNTR_FLAG_PLCPFAIL, + NL80211_MNTR_FLAG_CONTROL, + NL80211_MNTR_FLAG_OTHER_BSS, + NL80211_MNTR_FLAG_COOK_FRAMES, + + /* keep last */ + __NL80211_MNTR_FLAG_AFTER_LAST, + NL80211_MNTR_FLAG_MAX = __NL80211_MNTR_FLAG_AFTER_LAST - 1 +}; + +/** + * enum nl80211_meshconf_params - mesh configuration parameters + * + * Mesh configuration parameters + * + * @__NL80211_MESHCONF_INVALID: internal use + * + * @NL80211_MESHCONF_RETRY_TIMEOUT: specifies the initial retry timeout in + * millisecond units, used by the Peer Link Open message + * + * @NL80211_MESHCONF_CONFIRM_TIMEOUT: specifies the inital confirm timeout, in + * millisecond units, used by the peer link management to close a peer link + * + * @NL80211_MESHCONF_HOLDING_TIMEOUT: specifies the holding timeout, in + * millisecond units + * + * @NL80211_MESHCONF_MAX_PEER_LINKS: maximum number of peer links allowed + * on this mesh interface + * + * @NL80211_MESHCONF_MAX_RETRIES: specifies the maximum number of peer link + * open retries that can be sent to establish a new peer link instance in a + * mesh + * + * @NL80211_MESHCONF_TTL: specifies the value of TTL field set at a source mesh + * point. + * + * @NL80211_MESHCONF_AUTO_OPEN_PLINKS: whether we should automatically + * open peer links when we detect compatible mesh peers. + * + * @NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES: the number of action frames + * containing a PREQ that an MP can send to a particular destination (path + * target) + * + * @NL80211_MESHCONF_PATH_REFRESH_TIME: how frequently to refresh mesh paths + * (in milliseconds) + * + * @NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT: minimum length of time to wait + * until giving up on a path discovery (in milliseconds) + * + * @NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT: The time (in TUs) for which mesh + * points receiving a PREQ shall consider the forwarding information from the + * root to be valid. (TU = time unit) + * + * @NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL: The minimum interval of time (in + * TUs) during which an MP can send only one action frame containing a PREQ + * reference element + * + * @NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME: The interval of time (in TUs) + * that it takes for an HWMP information element to propagate across the mesh + * + * @NL80211_MESHCONF_ROOTMODE: whether root mode is enabled or not + * + * @NL80211_MESHCONF_ATTR_MAX: highest possible mesh configuration attribute + * + * @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use + */ +enum nl80211_meshconf_params { + __NL80211_MESHCONF_INVALID, + NL80211_MESHCONF_RETRY_TIMEOUT, + NL80211_MESHCONF_CONFIRM_TIMEOUT, + NL80211_MESHCONF_HOLDING_TIMEOUT, + NL80211_MESHCONF_MAX_PEER_LINKS, + NL80211_MESHCONF_MAX_RETRIES, + NL80211_MESHCONF_TTL, + NL80211_MESHCONF_AUTO_OPEN_PLINKS, + NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, + NL80211_MESHCONF_PATH_REFRESH_TIME, + NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, + NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, + NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, + NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, + NL80211_MESHCONF_HWMP_ROOTMODE, + + /* keep last */ + __NL80211_MESHCONF_ATTR_AFTER_LAST, + NL80211_MESHCONF_ATTR_MAX = __NL80211_MESHCONF_ATTR_AFTER_LAST - 1 +}; + +/** + * enum nl80211_txq_attr - TX queue parameter attributes + * @__NL80211_TXQ_ATTR_INVALID: Attribute number 0 is reserved + * @NL80211_TXQ_ATTR_QUEUE: TX queue identifier (NL80211_TXQ_Q_*) + * @NL80211_TXQ_ATTR_TXOP: Maximum burst time in units of 32 usecs, 0 meaning + * disabled + * @NL80211_TXQ_ATTR_CWMIN: Minimum contention window [a value of the form + * 2^n-1 in the range 1..32767] + * @NL80211_TXQ_ATTR_CWMAX: Maximum contention window [a value of the form + * 2^n-1 in the range 1..32767] + * @NL80211_TXQ_ATTR_AIFS: Arbitration interframe space [0..255] + * @__NL80211_TXQ_ATTR_AFTER_LAST: Internal + * @NL80211_TXQ_ATTR_MAX: Maximum TXQ attribute number + */ +enum nl80211_txq_attr { + __NL80211_TXQ_ATTR_INVALID, + NL80211_TXQ_ATTR_QUEUE, + NL80211_TXQ_ATTR_TXOP, + NL80211_TXQ_ATTR_CWMIN, + NL80211_TXQ_ATTR_CWMAX, + NL80211_TXQ_ATTR_AIFS, + + /* keep last */ + __NL80211_TXQ_ATTR_AFTER_LAST, + NL80211_TXQ_ATTR_MAX = __NL80211_TXQ_ATTR_AFTER_LAST - 1 +}; + +enum nl80211_txq_q { + NL80211_TXQ_Q_VO, + NL80211_TXQ_Q_VI, + NL80211_TXQ_Q_BE, + NL80211_TXQ_Q_BK +}; + +enum nl80211_channel_type { + NL80211_CHAN_NO_HT, + NL80211_CHAN_HT20, + NL80211_CHAN_HT40MINUS, + NL80211_CHAN_HT40PLUS +}; + +/** + * enum nl80211_bss - netlink attributes for a BSS + * + * @__NL80211_BSS_INVALID: invalid + * @NL80211_BSS_FREQUENCY: frequency in MHz (u32) + * @NL80211_BSS_TSF: TSF of the received probe response/beacon (u64) + * @NL80211_BSS_BEACON_INTERVAL: beacon interval of the (I)BSS (u16) + * @NL80211_BSS_CAPABILITY: capability field (CPU order, u16) + * @NL80211_BSS_INFORMATION_ELEMENTS: binary attribute containing the + * raw information elements from the probe response/beacon (bin) + * @NL80211_BSS_SIGNAL_MBM: signal strength of probe response/beacon + * in mBm (100 * dBm) (s32) + * @NL80211_BSS_SIGNAL_UNSPEC: signal strength of the probe response/beacon + * in unspecified units, scaled to 0..100 (u8) + * @NL80211_BSS_STATUS: status, if this BSS is "used" + * @NL80211_BSS_SEEN_MS_AGO: age of this BSS entry in ms + * @__NL80211_BSS_AFTER_LAST: internal + * @NL80211_BSS_MAX: highest BSS attribute + */ +enum nl80211_bss { + __NL80211_BSS_INVALID, + NL80211_BSS_BSSID, + NL80211_BSS_FREQUENCY, + NL80211_BSS_TSF, + NL80211_BSS_BEACON_INTERVAL, + NL80211_BSS_CAPABILITY, + NL80211_BSS_INFORMATION_ELEMENTS, + NL80211_BSS_SIGNAL_MBM, + NL80211_BSS_SIGNAL_UNSPEC, + NL80211_BSS_STATUS, + NL80211_BSS_SEEN_MS_AGO, + + /* keep last */ + __NL80211_BSS_AFTER_LAST, + NL80211_BSS_MAX = __NL80211_BSS_AFTER_LAST - 1 +}; + +/** + * enum nl80211_bss_status - BSS "status" + */ +enum nl80211_bss_status { + NL80211_BSS_STATUS_AUTHENTICATED, + NL80211_BSS_STATUS_ASSOCIATED, + NL80211_BSS_STATUS_IBSS_JOINED, +}; + +/** + * enum nl80211_auth_type - AuthenticationType + * + * @NL80211_AUTHTYPE_OPEN_SYSTEM: Open System authentication + * @NL80211_AUTHTYPE_SHARED_KEY: Shared Key authentication (WEP only) + * @NL80211_AUTHTYPE_FT: Fast BSS Transition (IEEE 802.11r) + * @NL80211_AUTHTYPE_NETWORK_EAP: Network EAP (some Cisco APs and mainly LEAP) + * @__NL80211_AUTHTYPE_NUM: internal + * @NL80211_AUTHTYPE_MAX: maximum valid auth algorithm + * @NL80211_AUTHTYPE_AUTOMATIC: determine automatically (if necessary by + * trying multiple times); this is invalid in netlink -- leave out + * the attribute for this on CONNECT commands. + */ +enum nl80211_auth_type { + NL80211_AUTHTYPE_OPEN_SYSTEM, + NL80211_AUTHTYPE_SHARED_KEY, + NL80211_AUTHTYPE_FT, + NL80211_AUTHTYPE_NETWORK_EAP, + + /* keep last */ + __NL80211_AUTHTYPE_NUM, + NL80211_AUTHTYPE_MAX = __NL80211_AUTHTYPE_NUM - 1, + NL80211_AUTHTYPE_AUTOMATIC +}; + +/** + * enum nl80211_key_type - Key Type + * @NL80211_KEYTYPE_GROUP: Group (broadcast/multicast) key + * @NL80211_KEYTYPE_PAIRWISE: Pairwise (unicast/individual) key + * @NL80211_KEYTYPE_PEERKEY: PeerKey (DLS) + */ +enum nl80211_key_type { + NL80211_KEYTYPE_GROUP, + NL80211_KEYTYPE_PAIRWISE, + NL80211_KEYTYPE_PEERKEY, +}; + +/** + * enum nl80211_mfp - Management frame protection state + * @NL80211_MFP_NO: Management frame protection not used + * @NL80211_MFP_REQUIRED: Management frame protection required + */ +enum nl80211_mfp { + NL80211_MFP_NO, + NL80211_MFP_REQUIRED, +}; + +enum nl80211_wpa_versions { + NL80211_WPA_VERSION_1 = 1 << 0, + NL80211_WPA_VERSION_2 = 1 << 1, +}; + +/** + * enum nl80211_key_attributes - key attributes + * @__NL80211_KEY_INVALID: invalid + * @NL80211_KEY_DATA: (temporal) key data; for TKIP this consists of + * 16 bytes encryption key followed by 8 bytes each for TX and RX MIC + * keys + * @NL80211_KEY_IDX: key ID (u8, 0-3) + * @NL80211_KEY_CIPHER: key cipher suite (u32, as defined by IEEE 802.11 + * section 7.3.2.25.1, e.g. 0x000FAC04) + * @NL80211_KEY_SEQ: transmit key sequence number (IV/PN) for TKIP and + * CCMP keys, each six bytes in little endian + * @NL80211_KEY_DEFAULT: flag indicating default key + * @NL80211_KEY_DEFAULT_MGMT: flag indicating default management key + * @__NL80211_KEY_AFTER_LAST: internal + * @NL80211_KEY_MAX: highest key attribute + */ +enum nl80211_key_attributes { + __NL80211_KEY_INVALID, + NL80211_KEY_DATA, + NL80211_KEY_IDX, + NL80211_KEY_CIPHER, + NL80211_KEY_SEQ, + NL80211_KEY_DEFAULT, + NL80211_KEY_DEFAULT_MGMT, + + /* keep last */ + __NL80211_KEY_AFTER_LAST, + NL80211_KEY_MAX = __NL80211_KEY_AFTER_LAST - 1 +}; + +#endif /* __LINUX_NL80211_H */ diff --git a/src/common/version.h b/src/common/version.h index 9c4c6ca..b79c494 100644 --- a/src/common/version.h +++ b/src/common/version.h @@ -1,6 +1,6 @@ #ifndef VERSION_H #define VERSION_H -#define VERSION_STR "0.6.8" +#define VERSION_STR "0.6.10" #endif /* VERSION_H */ diff --git a/src/common/wireless_copy.h b/src/common/wireless_copy.h new file mode 100644 index 0000000..ad76466 --- /dev/null +++ b/src/common/wireless_copy.h @@ -0,0 +1,1099 @@ +/* This is based on Linux Wireless Extensions header file from WIRELESS_EXT 18. + * I have just removed kernel related headers and added some typedefs etc. to + * make this easier to include into user space programs. + * Jouni Malinen, 2005-03-12. + */ + + +/* + * This file define a set of standard wireless extensions + * + * Version : 19 18.3.05 + * + * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> + * Copyright (c) 1997-2005 Jean Tourrilhes, All Rights Reserved. + */ + +#ifndef _LINUX_WIRELESS_H +#define _LINUX_WIRELESS_H + +/************************** DOCUMENTATION **************************/ +/* + * Initial APIs (1996 -> onward) : + * ----------------------------- + * Basically, the wireless extensions are for now a set of standard ioctl + * call + /proc/net/wireless + * + * The entry /proc/net/wireless give statistics and information on the + * driver. + * This is better than having each driver having its entry because + * its centralised and we may remove the driver module safely. + * + * Ioctl are used to configure the driver and issue commands. This is + * better than command line options of insmod because we may want to + * change dynamically (while the driver is running) some parameters. + * + * The ioctl mechanimsm are copied from standard devices ioctl. + * We have the list of command plus a structure descibing the + * data exchanged... + * Note that to add these ioctl, I was obliged to modify : + * # net/core/dev.c (two place + add include) + * # net/ipv4/af_inet.c (one place + add include) + * + * /proc/net/wireless is a copy of /proc/net/dev. + * We have a structure for data passed from the driver to /proc/net/wireless + * Too add this, I've modified : + * # net/core/dev.c (two other places) + * # include/linux/netdevice.h (one place) + * # include/linux/proc_fs.h (one place) + * + * New driver API (2002 -> onward) : + * ------------------------------- + * This file is only concerned with the user space API and common definitions. + * The new driver API is defined and documented in : + * # include/net/iw_handler.h + * + * Note as well that /proc/net/wireless implementation has now moved in : + * # net/core/wireless.c + * + * Wireless Events (2002 -> onward) : + * -------------------------------- + * Events are defined at the end of this file, and implemented in : + * # net/core/wireless.c + * + * Other comments : + * -------------- + * Do not add here things that are redundant with other mechanisms + * (drivers init, ifconfig, /proc/net/dev, ...) and with are not + * wireless specific. + * + * These wireless extensions are not magic : each driver has to provide + * support for them... + * + * IMPORTANT NOTE : As everything in the kernel, this is very much a + * work in progress. Contact me if you have ideas of improvements... + */ + +/***************************** INCLUDES *****************************/ + + /* jkm - replaced linux headers with C library headers, added typedefs */ +#if 0 +/* To minimise problems in user space, I might remove those headers + * at some point. Jean II */ +#include <linux/types.h> /* for "caddr_t" et al */ +#include <linux/socket.h> /* for "struct sockaddr" et al */ +#include <linux/if.h> /* for IFNAMSIZ and co... */ +#else +#include <sys/types.h> +#include <net/if.h> +typedef __uint32_t __u32; +typedef __int32_t __s32; +typedef __uint16_t __u16; +typedef __int16_t __s16; +typedef __uint8_t __u8; +#ifndef __user +#define __user +#endif /* __user */ +#endif + +/***************************** VERSION *****************************/ +/* + * This constant is used to know the availability of the wireless + * extensions and to know which version of wireless extensions it is + * (there is some stuff that will be added in the future...) + * I just plan to increment with each new version. + */ +#define WIRELESS_EXT 19 + +/* + * Changes : + * + * V2 to V3 + * -------- + * Alan Cox start some incompatibles changes. I've integrated a bit more. + * - Encryption renamed to Encode to avoid US regulation problems + * - Frequency changed from float to struct to avoid problems on old 386 + * + * V3 to V4 + * -------- + * - Add sensitivity + * + * V4 to V5 + * -------- + * - Missing encoding definitions in range + * - Access points stuff + * + * V5 to V6 + * -------- + * - 802.11 support (ESSID ioctls) + * + * V6 to V7 + * -------- + * - define IW_ESSID_MAX_SIZE and IW_MAX_AP + * + * V7 to V8 + * -------- + * - Changed my e-mail address + * - More 802.11 support (nickname, rate, rts, frag) + * - List index in frequencies + * + * V8 to V9 + * -------- + * - Support for 'mode of operation' (ad-hoc, managed...) + * - Support for unicast and multicast power saving + * - Change encoding to support larger tokens (>64 bits) + * - Updated iw_params (disable, flags) and use it for NWID + * - Extracted iw_point from iwreq for clarity + * + * V9 to V10 + * --------- + * - Add PM capability to range structure + * - Add PM modifier : MAX/MIN/RELATIVE + * - Add encoding option : IW_ENCODE_NOKEY + * - Add TxPower ioctls (work like TxRate) + * + * V10 to V11 + * ---------- + * - Add WE version in range (help backward/forward compatibility) + * - Add retry ioctls (work like PM) + * + * V11 to V12 + * ---------- + * - Add SIOCSIWSTATS to get /proc/net/wireless programatically + * - Add DEV PRIVATE IOCTL to avoid collisions in SIOCDEVPRIVATE space + * - Add new statistics (frag, retry, beacon) + * - Add average quality (for user space calibration) + * + * V12 to V13 + * ---------- + * - Document creation of new driver API. + * - Extract union iwreq_data from struct iwreq (for new driver API). + * - Rename SIOCSIWNAME as SIOCSIWCOMMIT + * + * V13 to V14 + * ---------- + * - Wireless Events support : define struct iw_event + * - Define additional specific event numbers + * - Add "addr" and "param" fields in union iwreq_data + * - AP scanning stuff (SIOCSIWSCAN and friends) + * + * V14 to V15 + * ---------- + * - Add IW_PRIV_TYPE_ADDR for struct sockaddr private arg + * - Make struct iw_freq signed (both m & e), add explicit padding + * - Add IWEVCUSTOM for driver specific event/scanning token + * - Add IW_MAX_GET_SPY for driver returning a lot of addresses + * - Add IW_TXPOW_RANGE for range of Tx Powers + * - Add IWEVREGISTERED & IWEVEXPIRED events for Access Points + * - Add IW_MODE_MONITOR for passive monitor + * + * V15 to V16 + * ---------- + * - Increase the number of bitrates in iw_range to 32 (for 802.11g) + * - Increase the number of frequencies in iw_range to 32 (for 802.11b+a) + * - Reshuffle struct iw_range for increases, add filler + * - Increase IW_MAX_AP to 64 for driver returning a lot of addresses + * - Remove IW_MAX_GET_SPY because conflict with enhanced spy support + * - Add SIOCSIWTHRSPY/SIOCGIWTHRSPY and "struct iw_thrspy" + * - Add IW_ENCODE_TEMP and iw_range->encoding_login_index + * + * V16 to V17 + * ---------- + * - Add flags to frequency -> auto/fixed + * - Document (struct iw_quality *)->updated, add new flags (INVALID) + * - Wireless Event capability in struct iw_range + * - Add support for relative TxPower (yick !) + * + * V17 to V18 (From Jouni Malinen <j@w1.fi>) + * ---------- + * - Add support for WPA/WPA2 + * - Add extended encoding configuration (SIOCSIWENCODEEXT and + * SIOCGIWENCODEEXT) + * - Add SIOCSIWGENIE/SIOCGIWGENIE + * - Add SIOCSIWMLME + * - Add SIOCSIWPMKSA + * - Add struct iw_range bit field for supported encoding capabilities + * - Add optional scan request parameters for SIOCSIWSCAN + * - Add SIOCSIWAUTH/SIOCGIWAUTH for setting authentication and WPA + * related parameters (extensible up to 4096 parameter values) + * - Add wireless events: IWEVGENIE, IWEVMICHAELMICFAILURE, + * IWEVASSOCREQIE, IWEVASSOCRESPIE, IWEVPMKIDCAND + * + * V18 to V19 + * ---------- + * - Remove (struct iw_point *)->pointer from events and streams + * - Remove header includes to help user space + * - Increase IW_ENCODING_TOKEN_MAX from 32 to 64 + * - Add IW_QUAL_ALL_UPDATED and IW_QUAL_ALL_INVALID macros + * - Add explicit flag to tell stats are in dBm : IW_QUAL_DBM + * - Add IW_IOCTL_IDX() and IW_EVENT_IDX() macros + */ + +/**************************** CONSTANTS ****************************/ + +/* -------------------------- IOCTL LIST -------------------------- */ + +/* Wireless Identification */ +#define SIOCSIWCOMMIT 0x8B00 /* Commit pending changes to driver */ +#define SIOCGIWNAME 0x8B01 /* get name == wireless protocol */ +/* SIOCGIWNAME is used to verify the presence of Wireless Extensions. + * Common values : "IEEE 802.11-DS", "IEEE 802.11-FH", "IEEE 802.11b"... + * Don't put the name of your driver there, it's useless. */ + +/* Basic operations */ +#define SIOCSIWNWID 0x8B02 /* set network id (pre-802.11) */ +#define SIOCGIWNWID 0x8B03 /* get network id (the cell) */ +#define SIOCSIWFREQ 0x8B04 /* set channel/frequency (Hz) */ +#define SIOCGIWFREQ 0x8B05 /* get channel/frequency (Hz) */ +#define SIOCSIWMODE 0x8B06 /* set operation mode */ +#define SIOCGIWMODE 0x8B07 /* get operation mode */ +#define SIOCSIWSENS 0x8B08 /* set sensitivity (dBm) */ +#define SIOCGIWSENS 0x8B09 /* get sensitivity (dBm) */ + +/* Informative stuff */ +#define SIOCSIWRANGE 0x8B0A /* Unused */ +#define SIOCGIWRANGE 0x8B0B /* Get range of parameters */ +#define SIOCSIWPRIV 0x8B0C /* Unused */ +#define SIOCGIWPRIV 0x8B0D /* get private ioctl interface info */ +#define SIOCSIWSTATS 0x8B0E /* Unused */ +#define SIOCGIWSTATS 0x8B0F /* Get /proc/net/wireless stats */ +/* SIOCGIWSTATS is strictly used between user space and the kernel, and + * is never passed to the driver (i.e. the driver will never see it). */ + +/* Spy support (statistics per MAC address - used for Mobile IP support) */ +#define SIOCSIWSPY 0x8B10 /* set spy addresses */ +#define SIOCGIWSPY 0x8B11 /* get spy info (quality of link) */ +#define SIOCSIWTHRSPY 0x8B12 /* set spy threshold (spy event) */ +#define SIOCGIWTHRSPY 0x8B13 /* get spy threshold */ + +/* Access Point manipulation */ +#define SIOCSIWAP 0x8B14 /* set access point MAC addresses */ +#define SIOCGIWAP 0x8B15 /* get access point MAC addresses */ +#define SIOCGIWAPLIST 0x8B17 /* Deprecated in favor of scanning */ +#define SIOCSIWSCAN 0x8B18 /* trigger scanning (list cells) */ +#define SIOCGIWSCAN 0x8B19 /* get scanning results */ + +/* 802.11 specific support */ +#define SIOCSIWESSID 0x8B1A /* set ESSID (network name) */ +#define SIOCGIWESSID 0x8B1B /* get ESSID */ +#define SIOCSIWNICKN 0x8B1C /* set node name/nickname */ +#define SIOCGIWNICKN 0x8B1D /* get node name/nickname */ +/* As the ESSID and NICKN are strings up to 32 bytes long, it doesn't fit + * within the 'iwreq' structure, so we need to use the 'data' member to + * point to a string in user space, like it is done for RANGE... */ + +/* Other parameters useful in 802.11 and some other devices */ +#define SIOCSIWRATE 0x8B20 /* set default bit rate (bps) */ +#define SIOCGIWRATE 0x8B21 /* get default bit rate (bps) */ +#define SIOCSIWRTS 0x8B22 /* set RTS/CTS threshold (bytes) */ +#define SIOCGIWRTS 0x8B23 /* get RTS/CTS threshold (bytes) */ +#define SIOCSIWFRAG 0x8B24 /* set fragmentation thr (bytes) */ +#define SIOCGIWFRAG 0x8B25 /* get fragmentation thr (bytes) */ +#define SIOCSIWTXPOW 0x8B26 /* set transmit power (dBm) */ +#define SIOCGIWTXPOW 0x8B27 /* get transmit power (dBm) */ +#define SIOCSIWRETRY 0x8B28 /* set retry limits and lifetime */ +#define SIOCGIWRETRY 0x8B29 /* get retry limits and lifetime */ + +/* Encoding stuff (scrambling, hardware security, WEP...) */ +#define SIOCSIWENCODE 0x8B2A /* set encoding token & mode */ +#define SIOCGIWENCODE 0x8B2B /* get encoding token & mode */ +/* Power saving stuff (power management, unicast and multicast) */ +#define SIOCSIWPOWER 0x8B2C /* set Power Management settings */ +#define SIOCGIWPOWER 0x8B2D /* get Power Management settings */ + +/* WPA : Generic IEEE 802.11 informatiom element (e.g., for WPA/RSN/WMM). + * This ioctl uses struct iw_point and data buffer that includes IE id and len + * fields. More than one IE may be included in the request. Setting the generic + * IE to empty buffer (len=0) removes the generic IE from the driver. Drivers + * are allowed to generate their own WPA/RSN IEs, but in these cases, drivers + * are required to report the used IE as a wireless event, e.g., when + * associating with an AP. */ +#define SIOCSIWGENIE 0x8B30 /* set generic IE */ +#define SIOCGIWGENIE 0x8B31 /* get generic IE */ + +/* WPA : IEEE 802.11 MLME requests */ +#define SIOCSIWMLME 0x8B16 /* request MLME operation; uses + * struct iw_mlme */ +/* WPA : Authentication mode parameters */ +#define SIOCSIWAUTH 0x8B32 /* set authentication mode params */ +#define SIOCGIWAUTH 0x8B33 /* get authentication mode params */ + +/* WPA : Extended version of encoding configuration */ +#define SIOCSIWENCODEEXT 0x8B34 /* set encoding token & mode */ +#define SIOCGIWENCODEEXT 0x8B35 /* get encoding token & mode */ + +/* WPA2 : PMKSA cache management */ +#define SIOCSIWPMKSA 0x8B36 /* PMKSA cache operation */ + +/* -------------------- DEV PRIVATE IOCTL LIST -------------------- */ + +/* These 32 ioctl are wireless device private, for 16 commands. + * Each driver is free to use them for whatever purpose it chooses, + * however the driver *must* export the description of those ioctls + * with SIOCGIWPRIV and *must* use arguments as defined below. + * If you don't follow those rules, DaveM is going to hate you (reason : + * it make mixed 32/64bit operation impossible). + */ +#define SIOCIWFIRSTPRIV 0x8BE0 +#define SIOCIWLASTPRIV 0x8BFF +/* Previously, we were using SIOCDEVPRIVATE, but we now have our + * separate range because of collisions with other tools such as + * 'mii-tool'. + * We now have 32 commands, so a bit more space ;-). + * Also, all 'odd' commands are only usable by root and don't return the + * content of ifr/iwr to user (but you are not obliged to use the set/get + * convention, just use every other two command). More details in iwpriv.c. + * And I repeat : you are not forced to use them with iwpriv, but you + * must be compliant with it. + */ + +/* ------------------------- IOCTL STUFF ------------------------- */ + +/* The first and the last (range) */ +#define SIOCIWFIRST 0x8B00 +#define SIOCIWLAST SIOCIWLASTPRIV /* 0x8BFF */ +#define IW_IOCTL_IDX(cmd) ((cmd) - SIOCIWFIRST) + +/* Even : get (world access), odd : set (root access) */ +#define IW_IS_SET(cmd) (!((cmd) & 0x1)) +#define IW_IS_GET(cmd) ((cmd) & 0x1) + +/* ----------------------- WIRELESS EVENTS ----------------------- */ +/* Those are *NOT* ioctls, do not issue request on them !!! */ +/* Most events use the same identifier as ioctl requests */ + +#define IWEVTXDROP 0x8C00 /* Packet dropped to excessive retry */ +#define IWEVQUAL 0x8C01 /* Quality part of statistics (scan) */ +#define IWEVCUSTOM 0x8C02 /* Driver specific ascii string */ +#define IWEVREGISTERED 0x8C03 /* Discovered a new node (AP mode) */ +#define IWEVEXPIRED 0x8C04 /* Expired a node (AP mode) */ +#define IWEVGENIE 0x8C05 /* Generic IE (WPA, RSN, WMM, ..) + * (scan results); This includes id and + * length fields. One IWEVGENIE may + * contain more than one IE. Scan + * results may contain one or more + * IWEVGENIE events. */ +#define IWEVMICHAELMICFAILURE 0x8C06 /* Michael MIC failure + * (struct iw_michaelmicfailure) + */ +#define IWEVASSOCREQIE 0x8C07 /* IEs used in (Re)Association Request. + * The data includes id and length + * fields and may contain more than one + * IE. This event is required in + * Managed mode if the driver + * generates its own WPA/RSN IE. This + * should be sent just before + * IWEVREGISTERED event for the + * association. */ +#define IWEVASSOCRESPIE 0x8C08 /* IEs used in (Re)Association + * Response. The data includes id and + * length fields and may contain more + * than one IE. This may be sent + * between IWEVASSOCREQIE and + * IWEVREGISTERED events for the + * association. */ +#define IWEVPMKIDCAND 0x8C09 /* PMKID candidate for RSN + * pre-authentication + * (struct iw_pmkid_cand) */ + +#define IWEVFIRST 0x8C00 +#define IW_EVENT_IDX(cmd) ((cmd) - IWEVFIRST) + +/* ------------------------- PRIVATE INFO ------------------------- */ +/* + * The following is used with SIOCGIWPRIV. It allow a driver to define + * the interface (name, type of data) for its private ioctl. + * Privates ioctl are SIOCIWFIRSTPRIV -> SIOCIWLASTPRIV + */ + +#define IW_PRIV_TYPE_MASK 0x7000 /* Type of arguments */ +#define IW_PRIV_TYPE_NONE 0x0000 +#define IW_PRIV_TYPE_BYTE 0x1000 /* Char as number */ +#define IW_PRIV_TYPE_CHAR 0x2000 /* Char as character */ +#define IW_PRIV_TYPE_INT 0x4000 /* 32 bits int */ +#define IW_PRIV_TYPE_FLOAT 0x5000 /* struct iw_freq */ +#define IW_PRIV_TYPE_ADDR 0x6000 /* struct sockaddr */ + +#define IW_PRIV_SIZE_FIXED 0x0800 /* Variable or fixed number of args */ + +#define IW_PRIV_SIZE_MASK 0x07FF /* Max number of those args */ + +/* + * Note : if the number of args is fixed and the size < 16 octets, + * instead of passing a pointer we will put args in the iwreq struct... + */ + +/* ----------------------- OTHER CONSTANTS ----------------------- */ + +/* Maximum frequencies in the range struct */ +#define IW_MAX_FREQUENCIES 32 +/* Note : if you have something like 80 frequencies, + * don't increase this constant and don't fill the frequency list. + * The user will be able to set by channel anyway... */ + +/* Maximum bit rates in the range struct */ +#define IW_MAX_BITRATES 32 + +/* Maximum tx powers in the range struct */ +#define IW_MAX_TXPOWER 8 +/* Note : if you more than 8 TXPowers, just set the max and min or + * a few of them in the struct iw_range. */ + +/* Maximum of address that you may set with SPY */ +#define IW_MAX_SPY 8 + +/* Maximum of address that you may get in the + list of access points in range */ +#define IW_MAX_AP 64 + +/* Maximum size of the ESSID and NICKN strings */ +#define IW_ESSID_MAX_SIZE 32 + +/* Modes of operation */ +#define IW_MODE_AUTO 0 /* Let the driver decides */ +#define IW_MODE_ADHOC 1 /* Single cell network */ +#define IW_MODE_INFRA 2 /* Multi cell network, roaming, ... */ +#define IW_MODE_MASTER 3 /* Synchronisation master or Access Point */ +#define IW_MODE_REPEAT 4 /* Wireless Repeater (forwarder) */ +#define IW_MODE_SECOND 5 /* Secondary master/repeater (backup) */ +#define IW_MODE_MONITOR 6 /* Passive monitor (listen only) */ + +/* Statistics flags (bitmask in updated) */ +#define IW_QUAL_QUAL_UPDATED 0x01 /* Value was updated since last read */ +#define IW_QUAL_LEVEL_UPDATED 0x02 +#define IW_QUAL_NOISE_UPDATED 0x04 +#define IW_QUAL_ALL_UPDATED 0x07 +#define IW_QUAL_DBM 0x08 /* Level + Noise are dBm */ +#define IW_QUAL_QUAL_INVALID 0x10 /* Driver doesn't provide value */ +#define IW_QUAL_LEVEL_INVALID 0x20 +#define IW_QUAL_NOISE_INVALID 0x40 +#define IW_QUAL_ALL_INVALID 0x70 + +/* Frequency flags */ +#define IW_FREQ_AUTO 0x00 /* Let the driver decides */ +#define IW_FREQ_FIXED 0x01 /* Force a specific value */ + +/* Maximum number of size of encoding token available + * they are listed in the range structure */ +#define IW_MAX_ENCODING_SIZES 8 + +/* Maximum size of the encoding token in bytes */ +#define IW_ENCODING_TOKEN_MAX 64 /* 512 bits (for now) */ + +/* Flags for encoding (along with the token) */ +#define IW_ENCODE_INDEX 0x00FF /* Token index (if needed) */ +#define IW_ENCODE_FLAGS 0xFF00 /* Flags defined below */ +#define IW_ENCODE_MODE 0xF000 /* Modes defined below */ +#define IW_ENCODE_DISABLED 0x8000 /* Encoding disabled */ +#define IW_ENCODE_ENABLED 0x0000 /* Encoding enabled */ +#define IW_ENCODE_RESTRICTED 0x4000 /* Refuse non-encoded packets */ +#define IW_ENCODE_OPEN 0x2000 /* Accept non-encoded packets */ +#define IW_ENCODE_NOKEY 0x0800 /* Key is write only, so not present */ +#define IW_ENCODE_TEMP 0x0400 /* Temporary key */ + +/* Power management flags available (along with the value, if any) */ +#define IW_POWER_ON 0x0000 /* No details... */ +#define IW_POWER_TYPE 0xF000 /* Type of parameter */ +#define IW_POWER_PERIOD 0x1000 /* Value is a period/duration of */ +#define IW_POWER_TIMEOUT 0x2000 /* Value is a timeout (to go asleep) */ +#define IW_POWER_MODE 0x0F00 /* Power Management mode */ +#define IW_POWER_UNICAST_R 0x0100 /* Receive only unicast messages */ +#define IW_POWER_MULTICAST_R 0x0200 /* Receive only multicast messages */ +#define IW_POWER_ALL_R 0x0300 /* Receive all messages though PM */ +#define IW_POWER_FORCE_S 0x0400 /* Force PM procedure for sending unicast */ +#define IW_POWER_REPEATER 0x0800 /* Repeat broadcast messages in PM period */ +#define IW_POWER_MODIFIER 0x000F /* Modify a parameter */ +#define IW_POWER_MIN 0x0001 /* Value is a minimum */ +#define IW_POWER_MAX 0x0002 /* Value is a maximum */ +#define IW_POWER_RELATIVE 0x0004 /* Value is not in seconds/ms/us */ + +/* Transmit Power flags available */ +#define IW_TXPOW_TYPE 0x00FF /* Type of value */ +#define IW_TXPOW_DBM 0x0000 /* Value is in dBm */ +#define IW_TXPOW_MWATT 0x0001 /* Value is in mW */ +#define IW_TXPOW_RELATIVE 0x0002 /* Value is in arbitrary units */ +#define IW_TXPOW_RANGE 0x1000 /* Range of value between min/max */ + +/* Retry limits and lifetime flags available */ +#define IW_RETRY_ON 0x0000 /* No details... */ +#define IW_RETRY_TYPE 0xF000 /* Type of parameter */ +#define IW_RETRY_LIMIT 0x1000 /* Maximum number of retries*/ +#define IW_RETRY_LIFETIME 0x2000 /* Maximum duration of retries in us */ +#define IW_RETRY_MODIFIER 0x000F /* Modify a parameter */ +#define IW_RETRY_MIN 0x0001 /* Value is a minimum */ +#define IW_RETRY_MAX 0x0002 /* Value is a maximum */ +#define IW_RETRY_RELATIVE 0x0004 /* Value is not in seconds/ms/us */ + +/* Scanning request flags */ +#define IW_SCAN_DEFAULT 0x0000 /* Default scan of the driver */ +#define IW_SCAN_ALL_ESSID 0x0001 /* Scan all ESSIDs */ +#define IW_SCAN_THIS_ESSID 0x0002 /* Scan only this ESSID */ +#define IW_SCAN_ALL_FREQ 0x0004 /* Scan all Frequencies */ +#define IW_SCAN_THIS_FREQ 0x0008 /* Scan only this Frequency */ +#define IW_SCAN_ALL_MODE 0x0010 /* Scan all Modes */ +#define IW_SCAN_THIS_MODE 0x0020 /* Scan only this Mode */ +#define IW_SCAN_ALL_RATE 0x0040 /* Scan all Bit-Rates */ +#define IW_SCAN_THIS_RATE 0x0080 /* Scan only this Bit-Rate */ +/* struct iw_scan_req scan_type */ +#define IW_SCAN_TYPE_ACTIVE 0 +#define IW_SCAN_TYPE_PASSIVE 1 +/* Maximum size of returned data */ +#define IW_SCAN_MAX_DATA 4096 /* In bytes */ + +/* Max number of char in custom event - use multiple of them if needed */ +#define IW_CUSTOM_MAX 256 /* In bytes */ + +/* Generic information element */ +#define IW_GENERIC_IE_MAX 1024 + +/* MLME requests (SIOCSIWMLME / struct iw_mlme) */ +#define IW_MLME_DEAUTH 0 +#define IW_MLME_DISASSOC 1 + +/* SIOCSIWAUTH/SIOCGIWAUTH struct iw_param flags */ +#define IW_AUTH_INDEX 0x0FFF +#define IW_AUTH_FLAGS 0xF000 +/* SIOCSIWAUTH/SIOCGIWAUTH parameters (0 .. 4095) + * (IW_AUTH_INDEX mask in struct iw_param flags; this is the index of the + * parameter that is being set/get to; value will be read/written to + * struct iw_param value field) */ +#define IW_AUTH_WPA_VERSION 0 +#define IW_AUTH_CIPHER_PAIRWISE 1 +#define IW_AUTH_CIPHER_GROUP 2 +#define IW_AUTH_KEY_MGMT 3 +#define IW_AUTH_TKIP_COUNTERMEASURES 4 +#define IW_AUTH_DROP_UNENCRYPTED 5 +#define IW_AUTH_80211_AUTH_ALG 6 +#define IW_AUTH_WPA_ENABLED 7 +#define IW_AUTH_RX_UNENCRYPTED_EAPOL 8 +#define IW_AUTH_ROAMING_CONTROL 9 +#define IW_AUTH_PRIVACY_INVOKED 10 +#define IW_AUTH_CIPHER_GROUP_MGMT 11 +#define IW_AUTH_MFP 12 + +/* IW_AUTH_WPA_VERSION values (bit field) */ +#define IW_AUTH_WPA_VERSION_DISABLED 0x00000001 +#define IW_AUTH_WPA_VERSION_WPA 0x00000002 +#define IW_AUTH_WPA_VERSION_WPA2 0x00000004 + +/* IW_AUTH_PAIRWISE_CIPHER and IW_AUTH_GROUP_CIPHER values (bit field) */ +#define IW_AUTH_CIPHER_NONE 0x00000001 +#define IW_AUTH_CIPHER_WEP40 0x00000002 +#define IW_AUTH_CIPHER_TKIP 0x00000004 +#define IW_AUTH_CIPHER_CCMP 0x00000008 +#define IW_AUTH_CIPHER_WEP104 0x00000010 + +/* IW_AUTH_KEY_MGMT values (bit field) */ +#define IW_AUTH_KEY_MGMT_802_1X 1 +#define IW_AUTH_KEY_MGMT_PSK 2 + +/* IW_AUTH_80211_AUTH_ALG values (bit field) */ +#define IW_AUTH_ALG_OPEN_SYSTEM 0x00000001 +#define IW_AUTH_ALG_SHARED_KEY 0x00000002 +#define IW_AUTH_ALG_LEAP 0x00000004 + +/* IW_AUTH_ROAMING_CONTROL values */ +#define IW_AUTH_ROAMING_ENABLE 0 /* driver/firmware based roaming */ +#define IW_AUTH_ROAMING_DISABLE 1 /* user space program used for roaming + * control */ + +/* IW_AUTH_MFP (management frame protection) values */ +#define IW_AUTH_MFP_DISABLED 0 /* MFP disabled */ +#define IW_AUTH_MFP_OPTIONAL 1 /* MFP optional */ +#define IW_AUTH_MFP_REQUIRED 2 /* MFP required */ + +/* SIOCSIWENCODEEXT definitions */ +#define IW_ENCODE_SEQ_MAX_SIZE 8 +/* struct iw_encode_ext ->alg */ +#define IW_ENCODE_ALG_NONE 0 +#define IW_ENCODE_ALG_WEP 1 +#define IW_ENCODE_ALG_TKIP 2 +#define IW_ENCODE_ALG_CCMP 3 +#define IW_ENCODE_ALG_PMK 4 +#define IW_ENCODE_ALG_AES_CMAC 5 +/* struct iw_encode_ext ->ext_flags */ +#define IW_ENCODE_EXT_TX_SEQ_VALID 0x00000001 +#define IW_ENCODE_EXT_RX_SEQ_VALID 0x00000002 +#define IW_ENCODE_EXT_GROUP_KEY 0x00000004 +#define IW_ENCODE_EXT_SET_TX_KEY 0x00000008 + +/* IWEVMICHAELMICFAILURE : struct iw_michaelmicfailure ->flags */ +#define IW_MICFAILURE_KEY_ID 0x00000003 /* Key ID 0..3 */ +#define IW_MICFAILURE_GROUP 0x00000004 +#define IW_MICFAILURE_PAIRWISE 0x00000008 +#define IW_MICFAILURE_STAKEY 0x00000010 +#define IW_MICFAILURE_COUNT 0x00000060 /* 1 or 2 (0 = count not supported) + */ + +/* Bit field values for enc_capa in struct iw_range */ +#define IW_ENC_CAPA_WPA 0x00000001 +#define IW_ENC_CAPA_WPA2 0x00000002 +#define IW_ENC_CAPA_CIPHER_TKIP 0x00000004 +#define IW_ENC_CAPA_CIPHER_CCMP 0x00000008 +#define IW_ENC_CAPA_4WAY_HANDSHAKE 0x00000010 + +/* Event capability macros - in (struct iw_range *)->event_capa + * Because we have more than 32 possible events, we use an array of + * 32 bit bitmasks. Note : 32 bits = 0x20 = 2^5. */ +#define IW_EVENT_CAPA_BASE(cmd) ((cmd >= SIOCIWFIRSTPRIV) ? \ + (cmd - SIOCIWFIRSTPRIV + 0x60) : \ + (cmd - SIOCSIWCOMMIT)) +#define IW_EVENT_CAPA_INDEX(cmd) (IW_EVENT_CAPA_BASE(cmd) >> 5) +#define IW_EVENT_CAPA_MASK(cmd) (1 << (IW_EVENT_CAPA_BASE(cmd) & 0x1F)) +/* Event capability constants - event autogenerated by the kernel + * This list is valid for most 802.11 devices, customise as needed... */ +#define IW_EVENT_CAPA_K_0 (IW_EVENT_CAPA_MASK(0x8B04) | \ + IW_EVENT_CAPA_MASK(0x8B06) | \ + IW_EVENT_CAPA_MASK(0x8B1A)) +#define IW_EVENT_CAPA_K_1 (IW_EVENT_CAPA_MASK(0x8B2A)) +/* "Easy" macro to set events in iw_range (less efficient) */ +#define IW_EVENT_CAPA_SET(event_capa, cmd) (event_capa[IW_EVENT_CAPA_INDEX(cmd)] |= IW_EVENT_CAPA_MASK(cmd)) +#define IW_EVENT_CAPA_SET_KERNEL(event_capa) {event_capa[0] |= IW_EVENT_CAPA_K_0; event_capa[1] |= IW_EVENT_CAPA_K_1; } + + +/****************************** TYPES ******************************/ + +/* --------------------------- SUBTYPES --------------------------- */ +/* + * Generic format for most parameters that fit in an int + */ +struct iw_param +{ + __s32 value; /* The value of the parameter itself */ + __u8 fixed; /* Hardware should not use auto select */ + __u8 disabled; /* Disable the feature */ + __u16 flags; /* Various specifc flags (if any) */ +}; + +/* + * For all data larger than 16 octets, we need to use a + * pointer to memory allocated in user space. + */ +struct iw_point +{ + void __user *pointer; /* Pointer to the data (in user space) */ + __u16 length; /* number of fields or size in bytes */ + __u16 flags; /* Optional params */ +}; + +/* + * A frequency + * For numbers lower than 10^9, we encode the number in 'm' and + * set 'e' to 0 + * For number greater than 10^9, we divide it by the lowest power + * of 10 to get 'm' lower than 10^9, with 'm'= f / (10^'e')... + * The power of 10 is in 'e', the result of the division is in 'm'. + */ +struct iw_freq +{ + __s32 m; /* Mantissa */ + __s16 e; /* Exponent */ + __u8 i; /* List index (when in range struct) */ + __u8 flags; /* Flags (fixed/auto) */ +}; + +/* + * Quality of the link + */ +struct iw_quality +{ + __u8 qual; /* link quality (%retries, SNR, + %missed beacons or better...) */ + __u8 level; /* signal level (dBm) */ + __u8 noise; /* noise level (dBm) */ + __u8 updated; /* Flags to know if updated */ +}; + +/* + * Packet discarded in the wireless adapter due to + * "wireless" specific problems... + * Note : the list of counter and statistics in net_device_stats + * is already pretty exhaustive, and you should use that first. + * This is only additional stats... + */ +struct iw_discarded +{ + __u32 nwid; /* Rx : Wrong nwid/essid */ + __u32 code; /* Rx : Unable to code/decode (WEP) */ + __u32 fragment; /* Rx : Can't perform MAC reassembly */ + __u32 retries; /* Tx : Max MAC retries num reached */ + __u32 misc; /* Others cases */ +}; + +/* + * Packet/Time period missed in the wireless adapter due to + * "wireless" specific problems... + */ +struct iw_missed +{ + __u32 beacon; /* Missed beacons/superframe */ +}; + +/* + * Quality range (for spy threshold) + */ +struct iw_thrspy +{ + struct sockaddr addr; /* Source address (hw/mac) */ + struct iw_quality qual; /* Quality of the link */ + struct iw_quality low; /* Low threshold */ + struct iw_quality high; /* High threshold */ +}; + +/* + * Optional data for scan request + * + * Note: these optional parameters are controlling parameters for the + * scanning behavior, these do not apply to getting scan results + * (SIOCGIWSCAN). Drivers are expected to keep a local BSS table and + * provide a merged results with all BSSes even if the previous scan + * request limited scanning to a subset, e.g., by specifying an SSID. + * Especially, scan results are required to include an entry for the + * current BSS if the driver is in Managed mode and associated with an AP. + */ +struct iw_scan_req +{ + __u8 scan_type; /* IW_SCAN_TYPE_{ACTIVE,PASSIVE} */ + __u8 essid_len; + __u8 num_channels; /* num entries in channel_list; + * 0 = scan all allowed channels */ + __u8 flags; /* reserved as padding; use zero, this may + * be used in the future for adding flags + * to request different scan behavior */ + struct sockaddr bssid; /* ff:ff:ff:ff:ff:ff for broadcast BSSID or + * individual address of a specific BSS */ + + /* + * Use this ESSID if IW_SCAN_THIS_ESSID flag is used instead of using + * the current ESSID. This allows scan requests for specific ESSID + * without having to change the current ESSID and potentially breaking + * the current association. + */ + __u8 essid[IW_ESSID_MAX_SIZE]; + + /* + * Optional parameters for changing the default scanning behavior. + * These are based on the MLME-SCAN.request from IEEE Std 802.11. + * TU is 1.024 ms. If these are set to 0, driver is expected to use + * reasonable default values. min_channel_time defines the time that + * will be used to wait for the first reply on each channel. If no + * replies are received, next channel will be scanned after this. If + * replies are received, total time waited on the channel is defined by + * max_channel_time. + */ + __u32 min_channel_time; /* in TU */ + __u32 max_channel_time; /* in TU */ + + struct iw_freq channel_list[IW_MAX_FREQUENCIES]; +}; + +/* ------------------------- WPA SUPPORT ------------------------- */ + +/* + * Extended data structure for get/set encoding (this is used with + * SIOCSIWENCODEEXT/SIOCGIWENCODEEXT. struct iw_point and IW_ENCODE_* + * flags are used in the same way as with SIOCSIWENCODE/SIOCGIWENCODE and + * only the data contents changes (key data -> this structure, including + * key data). + * + * If the new key is the first group key, it will be set as the default + * TX key. Otherwise, default TX key index is only changed if + * IW_ENCODE_EXT_SET_TX_KEY flag is set. + * + * Key will be changed with SIOCSIWENCODEEXT in all cases except for + * special "change TX key index" operation which is indicated by setting + * key_len = 0 and ext_flags |= IW_ENCODE_EXT_SET_TX_KEY. + * + * tx_seq/rx_seq are only used when respective + * IW_ENCODE_EXT_{TX,RX}_SEQ_VALID flag is set in ext_flags. Normal + * TKIP/CCMP operation is to set RX seq with SIOCSIWENCODEEXT and start + * TX seq from zero whenever key is changed. SIOCGIWENCODEEXT is normally + * used only by an Authenticator (AP or an IBSS station) to get the + * current TX sequence number. Using TX_SEQ_VALID for SIOCSIWENCODEEXT and + * RX_SEQ_VALID for SIOCGIWENCODEEXT are optional, but can be useful for + * debugging/testing. + */ +struct iw_encode_ext +{ + __u32 ext_flags; /* IW_ENCODE_EXT_* */ + __u8 tx_seq[IW_ENCODE_SEQ_MAX_SIZE]; /* LSB first */ + __u8 rx_seq[IW_ENCODE_SEQ_MAX_SIZE]; /* LSB first */ + struct sockaddr addr; /* ff:ff:ff:ff:ff:ff for broadcast/multicast + * (group) keys or unicast address for + * individual keys */ + __u16 alg; /* IW_ENCODE_ALG_* */ + __u16 key_len; + __u8 key[0]; +}; + +/* SIOCSIWMLME data */ +struct iw_mlme +{ + __u16 cmd; /* IW_MLME_* */ + __u16 reason_code; + struct sockaddr addr; +}; + +/* SIOCSIWPMKSA data */ +#define IW_PMKSA_ADD 1 +#define IW_PMKSA_REMOVE 2 +#define IW_PMKSA_FLUSH 3 + +#define IW_PMKID_LEN 16 + +struct iw_pmksa +{ + __u32 cmd; /* IW_PMKSA_* */ + struct sockaddr bssid; + __u8 pmkid[IW_PMKID_LEN]; +}; + +/* IWEVMICHAELMICFAILURE data */ +struct iw_michaelmicfailure +{ + __u32 flags; + struct sockaddr src_addr; + __u8 tsc[IW_ENCODE_SEQ_MAX_SIZE]; /* LSB first */ +}; + +/* IWEVPMKIDCAND data */ +#define IW_PMKID_CAND_PREAUTH 0x00000001 /* RNS pre-authentication enabled */ +struct iw_pmkid_cand +{ + __u32 flags; /* IW_PMKID_CAND_* */ + __u32 index; /* the smaller the index, the higher the + * priority */ + struct sockaddr bssid; +}; + +/* ------------------------ WIRELESS STATS ------------------------ */ +/* + * Wireless statistics (used for /proc/net/wireless) + */ +struct iw_statistics +{ + __u16 status; /* Status + * - device dependent for now */ + + struct iw_quality qual; /* Quality of the link + * (instant/mean/max) */ + struct iw_discarded discard; /* Packet discarded counts */ + struct iw_missed miss; /* Packet missed counts */ +}; + +/* ------------------------ IOCTL REQUEST ------------------------ */ +/* + * This structure defines the payload of an ioctl, and is used + * below. + * + * Note that this structure should fit on the memory footprint + * of iwreq (which is the same as ifreq), which mean a max size of + * 16 octets = 128 bits. Warning, pointers might be 64 bits wide... + * You should check this when increasing the structures defined + * above in this file... + */ +union iwreq_data +{ + /* Config - generic */ + char name[IFNAMSIZ]; + /* Name : used to verify the presence of wireless extensions. + * Name of the protocol/provider... */ + + struct iw_point essid; /* Extended network name */ + struct iw_param nwid; /* network id (or domain - the cell) */ + struct iw_freq freq; /* frequency or channel : + * 0-1000 = channel + * > 1000 = frequency in Hz */ + + struct iw_param sens; /* signal level threshold */ + struct iw_param bitrate; /* default bit rate */ + struct iw_param txpower; /* default transmit power */ + struct iw_param rts; /* RTS threshold threshold */ + struct iw_param frag; /* Fragmentation threshold */ + __u32 mode; /* Operation mode */ + struct iw_param retry; /* Retry limits & lifetime */ + + struct iw_point encoding; /* Encoding stuff : tokens */ + struct iw_param power; /* PM duration/timeout */ + struct iw_quality qual; /* Quality part of statistics */ + + struct sockaddr ap_addr; /* Access point address */ + struct sockaddr addr; /* Destination address (hw/mac) */ + + struct iw_param param; /* Other small parameters */ + struct iw_point data; /* Other large parameters */ +}; + +/* + * The structure to exchange data for ioctl. + * This structure is the same as 'struct ifreq', but (re)defined for + * convenience... + * Do I need to remind you about structure size (32 octets) ? + */ +struct iwreq +{ + union + { + char ifrn_name[IFNAMSIZ]; /* if name, e.g. "eth0" */ + } ifr_ifrn; + + /* Data part (defined just above) */ + union iwreq_data u; +}; + +/* -------------------------- IOCTL DATA -------------------------- */ +/* + * For those ioctl which want to exchange mode data that what could + * fit in the above structure... + */ + +/* + * Range of parameters + */ + +struct iw_range +{ + /* Informative stuff (to choose between different interface) */ + __u32 throughput; /* To give an idea... */ + /* In theory this value should be the maximum benchmarked + * TCP/IP throughput, because with most of these devices the + * bit rate is meaningless (overhead an co) to estimate how + * fast the connection will go and pick the fastest one. + * I suggest people to play with Netperf or any benchmark... + */ + + /* NWID (or domain id) */ + __u32 min_nwid; /* Minimal NWID we are able to set */ + __u32 max_nwid; /* Maximal NWID we are able to set */ + + /* Old Frequency (backward compat - moved lower ) */ + __u16 old_num_channels; + __u8 old_num_frequency; + + /* Wireless event capability bitmasks */ + __u32 event_capa[6]; + + /* signal level threshold range */ + __s32 sensitivity; + + /* Quality of link & SNR stuff */ + /* Quality range (link, level, noise) + * If the quality is absolute, it will be in the range [0 ; max_qual], + * if the quality is dBm, it will be in the range [max_qual ; 0]. + * Don't forget that we use 8 bit arithmetics... */ + struct iw_quality max_qual; /* Quality of the link */ + /* This should contain the average/typical values of the quality + * indicator. This should be the threshold between a "good" and + * a "bad" link (example : monitor going from green to orange). + * Currently, user space apps like quality monitors don't have any + * way to calibrate the measurement. With this, they can split + * the range between 0 and max_qual in different quality level + * (using a geometric subdivision centered on the average). + * I expect that people doing the user space apps will feedback + * us on which value we need to put in each driver... */ + struct iw_quality avg_qual; /* Quality of the link */ + + /* Rates */ + __u8 num_bitrates; /* Number of entries in the list */ + __s32 bitrate[IW_MAX_BITRATES]; /* list, in bps */ + + /* RTS threshold */ + __s32 min_rts; /* Minimal RTS threshold */ + __s32 max_rts; /* Maximal RTS threshold */ + + /* Frag threshold */ + __s32 min_frag; /* Minimal frag threshold */ + __s32 max_frag; /* Maximal frag threshold */ + + /* Power Management duration & timeout */ + __s32 min_pmp; /* Minimal PM period */ + __s32 max_pmp; /* Maximal PM period */ + __s32 min_pmt; /* Minimal PM timeout */ + __s32 max_pmt; /* Maximal PM timeout */ + __u16 pmp_flags; /* How to decode max/min PM period */ + __u16 pmt_flags; /* How to decode max/min PM timeout */ + __u16 pm_capa; /* What PM options are supported */ + + /* Encoder stuff */ + __u16 encoding_size[IW_MAX_ENCODING_SIZES]; /* Different token sizes */ + __u8 num_encoding_sizes; /* Number of entry in the list */ + __u8 max_encoding_tokens; /* Max number of tokens */ + /* For drivers that need a "login/passwd" form */ + __u8 encoding_login_index; /* token index for login token */ + + /* Transmit power */ + __u16 txpower_capa; /* What options are supported */ + __u8 num_txpower; /* Number of entries in the list */ + __s32 txpower[IW_MAX_TXPOWER]; /* list, in bps */ + + /* Wireless Extension version info */ + __u8 we_version_compiled; /* Must be WIRELESS_EXT */ + __u8 we_version_source; /* Last update of source */ + + /* Retry limits and lifetime */ + __u16 retry_capa; /* What retry options are supported */ + __u16 retry_flags; /* How to decode max/min retry limit */ + __u16 r_time_flags; /* How to decode max/min retry life */ + __s32 min_retry; /* Minimal number of retries */ + __s32 max_retry; /* Maximal number of retries */ + __s32 min_r_time; /* Minimal retry lifetime */ + __s32 max_r_time; /* Maximal retry lifetime */ + + /* Frequency */ + __u16 num_channels; /* Number of channels [0; num - 1] */ + __u8 num_frequency; /* Number of entry in the list */ + struct iw_freq freq[IW_MAX_FREQUENCIES]; /* list */ + /* Note : this frequency list doesn't need to fit channel numbers, + * because each entry contain its channel index */ + + __u32 enc_capa; /* IW_ENC_CAPA_* bit field */ +}; + +/* + * Private ioctl interface information + */ + +struct iw_priv_args +{ + __u32 cmd; /* Number of the ioctl to issue */ + __u16 set_args; /* Type and number of args */ + __u16 get_args; /* Type and number of args */ + char name[IFNAMSIZ]; /* Name of the extension */ +}; + +/* ----------------------- WIRELESS EVENTS ----------------------- */ +/* + * Wireless events are carried through the rtnetlink socket to user + * space. They are encapsulated in the IFLA_WIRELESS field of + * a RTM_NEWLINK message. + */ + +/* + * A Wireless Event. Contains basically the same data as the ioctl... + */ +struct iw_event +{ + __u16 len; /* Real lenght of this stuff */ + __u16 cmd; /* Wireless IOCTL */ + union iwreq_data u; /* IOCTL fixed payload */ +}; + +/* Size of the Event prefix (including padding and alignement junk) */ +#define IW_EV_LCP_LEN (sizeof(struct iw_event) - sizeof(union iwreq_data)) +/* Size of the various events */ +#define IW_EV_CHAR_LEN (IW_EV_LCP_LEN + IFNAMSIZ) +#define IW_EV_UINT_LEN (IW_EV_LCP_LEN + sizeof(__u32)) +#define IW_EV_FREQ_LEN (IW_EV_LCP_LEN + sizeof(struct iw_freq)) +#define IW_EV_PARAM_LEN (IW_EV_LCP_LEN + sizeof(struct iw_param)) +#define IW_EV_ADDR_LEN (IW_EV_LCP_LEN + sizeof(struct sockaddr)) +#define IW_EV_QUAL_LEN (IW_EV_LCP_LEN + sizeof(struct iw_quality)) + +/* iw_point events are special. First, the payload (extra data) come at + * the end of the event, so they are bigger than IW_EV_POINT_LEN. Second, + * we omit the pointer, so start at an offset. */ +#define IW_EV_POINT_OFF (((char *) &(((struct iw_point *) NULL)->length)) - \ + (char *) NULL) +#define IW_EV_POINT_LEN (IW_EV_LCP_LEN + sizeof(struct iw_point) - \ + IW_EV_POINT_OFF) + +#endif /* _LINUX_WIRELESS_H */ diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c index c9ff319..074cb80 100644 --- a/src/common/wpa_common.c +++ b/src/common/wpa_common.c @@ -56,10 +56,10 @@ int wpa_eapol_key_mic(const u8 *key, int ver, const u8 *buf, size_t len, hmac_sha1(key, 16, buf, len, hash); os_memcpy(mic, hash, MD5_MAC_LEN); break; -#ifdef CONFIG_IEEE80211R +#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) case WPA_KEY_INFO_TYPE_AES_128_CMAC: return omac1_aes_128(key, buf, len, mic); -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */ default: return -1; } diff --git a/src/crypto/.gitignore b/src/crypto/.gitignore deleted file mode 100644 index a438335..0000000 --- a/src/crypto/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.d diff --git a/src/crypto/crypto_cryptoapi.c b/src/crypto/crypto_cryptoapi.c index bb05730..45333dd 100644 --- a/src/crypto/crypto_cryptoapi.c +++ b/src/crypto/crypto_cryptoapi.c @@ -1,6 +1,6 @@ /* - * WPA Supplicant / Crypto wrapper for Microsoft CryptoAPI - * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * Crypto wrapper for Microsoft CryptoAPI + * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -40,12 +40,6 @@ L"Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)" * define here whatever extra is needed. */ -static PCCERT_CONTEXT WINAPI -(*CertCreateCertificateContext)(DWORD dwCertEncodingType, - const BYTE *pbCertEncoded, - DWORD cbCertEncoded) -= NULL; /* to be loaded from crypt32.dll */ - static BOOL WINAPI (*CryptImportPublicKeyInfo)(HCRYPTPROV hCryptProv, DWORD dwCertEncodingType, PCERT_PUBLIC_KEY_INFO pInfo, HCRYPTKEY *phKey) @@ -59,7 +53,7 @@ static int mingw_load_crypto_func(void) /* MinGW does not yet have full CryptoAPI support, so load the needed * function here. */ - if (CertCreateCertificateContext) + if (CryptImportPublicKeyInfo) return 0; dll = LoadLibrary("crypt32"); @@ -69,15 +63,6 @@ static int mingw_load_crypto_func(void) return -1; } - CertCreateCertificateContext = (void *) GetProcAddress( - dll, "CertCreateCertificateContext"); - if (CertCreateCertificateContext == NULL) { - wpa_printf(MSG_DEBUG, "CryptoAPI: Could not get " - "CertCreateCertificateContext() address from " - "crypt32 library"); - return -1; - } - CryptImportPublicKeyInfo = GetProcAddress( dll, "CryptImportPublicKeyInfo"); if (CryptImportPublicKeyInfo == NULL) { diff --git a/src/crypto/crypto_gnutls.c b/src/crypto/crypto_gnutls.c index 8023965..8f8611c 100644 --- a/src/crypto/crypto_gnutls.c +++ b/src/crypto/crypto_gnutls.c @@ -57,7 +57,6 @@ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) } -#ifdef EAP_TLS_FUNCS void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { gcry_md_hd_t hd; @@ -162,7 +161,6 @@ void aes_decrypt_deinit(void *ctx) gcry_cipher_hd_t hd = ctx; gcry_cipher_close(hd); } -#endif /* EAP_TLS_FUNCS */ int crypto_mod_exp(const u8 *base, size_t base_len, diff --git a/src/crypto/crypto_internal.c b/src/crypto/crypto_internal.c index a601cbf..cddfb4d 100644 --- a/src/crypto/crypto_internal.c +++ b/src/crypto/crypto_internal.c @@ -25,7 +25,7 @@ #include "tls/asn1.h" -#ifdef EAP_TLS_FUNCS +#ifdef CONFIG_CRYPTO_INTERNAL #ifdef CONFIG_TLS_INTERNAL @@ -435,6 +435,7 @@ struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len) } +#ifdef EAP_TLS_FUNCS static struct crypto_private_key * crypto_pkcs8_key_import(const u8 *buf, size_t len) { @@ -536,6 +537,7 @@ crypto_pkcs8_key_import(const u8 *buf, size_t len) return (struct crypto_private_key *) crypto_rsa_import_private_key(hdr.payload, hdr.length); } +#endif /* EAP_TLS_FUNCS */ struct crypto_private_key * crypto_private_key_import(const u8 *key, @@ -788,6 +790,7 @@ int crypto_global_init(void) void crypto_global_deinit(void) { } +#endif /* CONFIG_TLS_INTERNAL */ #if defined(EAP_FAST) || defined(CONFIG_WPS) @@ -830,6 +833,4 @@ error: #endif /* EAP_FAST || CONFIG_WPS */ -#endif /* CONFIG_TLS_INTERNAL */ - -#endif /* EAP_TLS_FUNCS */ +#endif /* CONFIG_CRYPTO_INTERNAL */ diff --git a/src/crypto/dh_groups.c b/src/crypto/dh_groups.c index e351632..5f6008a 100644 --- a/src/crypto/dh_groups.c +++ b/src/crypto/dh_groups.c @@ -19,6 +19,8 @@ #include "dh_groups.h" +#ifdef ALL_DH_GROUPS + /* RFC 4306, B.1. Group 1 - 768 Bit MODP * Generator: 2 * Prime: 2^768 - 2 ^704 - 1 + 2^64 * { [2^638 pi] + 149686 } @@ -63,6 +65,8 @@ static const u8 dh_group2_prime[128] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +#endif /* ALL_DH_GROUPS */ + /* RFC 3526, 2. Group 5 - 1536 Bit MODP * Generator: 2 * Prime: 2^1536 - 2^1472 - 1 + 2^64 * { [2^1406 pi] + 741804 } @@ -95,6 +99,8 @@ static const u8 dh_group5_prime[192] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +#ifdef ALL_DH_GROUPS + /* RFC 3526, 3. Group 14 - 2048 Bit MODP * Generator: 2 * Prime: 2^2048 - 2^1984 - 1 + 2^64 * { [2^1918 pi] + 124476 } @@ -503,6 +509,8 @@ static const u8 dh_group18_prime[1024] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +#endif /* ALL_DH_GROUPS */ + #define DH_GROUP(id) \ { id, dh_group ## id ## _generator, sizeof(dh_group ## id ## _generator), \ @@ -510,14 +518,16 @@ dh_group ## id ## _prime, sizeof(dh_group ## id ## _prime) } static struct dh_group dh_groups[] = { + DH_GROUP(5), +#ifdef ALL_DH_GROUPS DH_GROUP(1), DH_GROUP(2), - DH_GROUP(5), DH_GROUP(14), DH_GROUP(15), DH_GROUP(16), DH_GROUP(17), DH_GROUP(18) +#endif /* ALL_DH_GROUPS */ }; #define NUM_DH_GROUPS (sizeof(dh_groups) / sizeof(dh_groups[0])) diff --git a/src/crypto/ms_funcs.c b/src/crypto/ms_funcs.c index c14af64..7e2f0fa 100644 --- a/src/crypto/ms_funcs.c +++ b/src/crypto/ms_funcs.c @@ -379,7 +379,7 @@ int encrypt_pw_block_with_password_hash( */ pos = &pw_block[2 * 256]; WPA_PUT_LE16(pos, password_len * 2); - rc4(pw_block, PWBLOCK_LEN, password_hash, 16); + rc4_skip(password_hash, 16, 0, pw_block, PWBLOCK_LEN); return 0; } diff --git a/src/crypto/rc4.c b/src/crypto/rc4.c index 8480cc5..70c790e 100644 --- a/src/crypto/rc4.c +++ b/src/crypto/rc4.c @@ -68,19 +68,3 @@ void rc4_skip(const u8 *key, size_t keylen, size_t skip, *pos++ ^= S[(S[i] + S[j]) & 0xff]; } } - - -/** - * rc4 - XOR RC4 stream to given data - * @buf: data to be XOR'ed with RC4 stream - * @len: buf length - * @key: RC4 key - * @key_len: RC4 key length - * - * Generate RC4 pseudo random stream for the given key and XOR this with the - * data buffer to perform RC4 encryption/decryption. - */ -void rc4(u8 *buf, size_t len, const u8 *key, size_t key_len) -{ - rc4_skip(key, key_len, 0, buf, len); -} diff --git a/src/crypto/rc4.h b/src/crypto/rc4.h index 01f1383..35c7e41 100644 --- a/src/crypto/rc4.h +++ b/src/crypto/rc4.h @@ -17,6 +17,5 @@ void rc4_skip(const u8 *key, size_t keylen, size_t skip, u8 *data, size_t data_len); -void rc4(u8 *buf, size_t len, const u8 *key, size_t key_len); #endif /* RC4_H */ diff --git a/src/crypto/sha1.c b/src/crypto/sha1.c index ceec1a4..141e4f4 100644 --- a/src/crypto/sha1.c +++ b/src/crypto/sha1.c @@ -606,8 +606,8 @@ static void SHA1Transform(u32 state[5], const unsigned char buffer[64]) } CHAR64LONG16; CHAR64LONG16* block; #ifdef SHA1HANDSOFF - u32 workspace[16]; - block = (CHAR64LONG16 *) workspace; + CHAR64LONG16 workspace; + block = &workspace; os_memcpy(block, buffer, 64); #else block = (CHAR64LONG16 *) buffer; diff --git a/src/crypto/sha256.c b/src/crypto/sha256.c index 3d3958f..96dac0e 100644 --- a/src/crypto/sha256.c +++ b/src/crypto/sha256.c @@ -122,7 +122,7 @@ void hmac_sha256(const u8 *key, size_t key_len, const u8 *data, void sha256_prf(const u8 *key, size_t key_len, const char *label, const u8 *data, size_t data_len, u8 *buf, size_t buf_len) { - u16 counter = 0; + u16 counter = 1; size_t pos, plen; u8 hash[SHA256_MAC_LEN]; const u8 *addr[4]; diff --git a/src/crypto/tls.h b/src/crypto/tls.h index dafe8bb..aafb7999 100644 --- a/src/crypto/tls.h +++ b/src/crypto/tls.h @@ -34,6 +34,9 @@ struct tls_config { const char *pkcs11_module_path; }; +#define TLS_CONN_ALLOW_SIGN_RSA_MD5 BIT(0) +#define TLS_CONN_DISABLE_TIME_CHECKS BIT(1) + /** * struct tls_connection_params - Parameters for TLS connection * @ca_cert: File or reference name for CA X.509 certificate in PEM or DER @@ -68,6 +71,7 @@ struct tls_config { * @cert_id: the certificate's id when using engine * @ca_cert_id: the CA certificate's id when using engine * @tls_ia: Whether to enable TLS/IA (for EAP-TTLSv1) + * @flags: Parameter options (TLS_CONN_*) * * TLS connection parameters to be configured with tls_connection_set_params() * and tls_global_set_params(). @@ -103,6 +107,8 @@ struct tls_connection_params { const char *key_id; const char *cert_id; const char *ca_cert_id; + + unsigned int flags; }; diff --git a/src/crypto/tls_gnutls.c b/src/crypto/tls_gnutls.c index db66ae1..2c5c5a2 100644 --- a/src/crypto/tls_gnutls.c +++ b/src/crypto/tls_gnutls.c @@ -35,8 +35,12 @@ int gnutls_ia_verify_endphase(gnutls_session_t session, char *checksum); #include "tls.h" +#ifndef TLS_RANDOM_SIZE #define TLS_RANDOM_SIZE 32 +#endif +#ifndef TLS_MASTER_SIZE #define TLS_MASTER_SIZE 48 +#endif #if LIBGNUTLS_VERSION_NUMBER < 0x010302 @@ -591,6 +595,17 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, return -1; } } + + if (params->flags & TLS_CONN_ALLOW_SIGN_RSA_MD5) { + gnutls_certificate_set_verify_flags( + conn->xcred, GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5); + } + + if (params->flags & TLS_CONN_DISABLE_TIME_CHECKS) { + gnutls_certificate_set_verify_flags( + conn->xcred, + GNUTLS_VERIFY_DISABLE_TIME_CHECKS); + } } if (params->client_cert && params->private_key) { @@ -711,6 +726,18 @@ int tls_global_set_params(void *tls_ctx, goto fail; } } + + if (params->flags & TLS_CONN_ALLOW_SIGN_RSA_MD5) { + gnutls_certificate_set_verify_flags( + global->xcred, + GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5); + } + + if (params->flags & TLS_CONN_DISABLE_TIME_CHECKS) { + gnutls_certificate_set_verify_flags( + global->xcred, + GNUTLS_VERIFY_DISABLE_TIME_CHECKS); + } } if (params->client_cert && params->private_key) { @@ -843,7 +870,8 @@ int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, } -static int tls_connection_verify_peer(struct tls_connection *conn) +static int tls_connection_verify_peer(struct tls_connection *conn, + gnutls_alert_description_t *err) { unsigned int status, num_certs, i; struct os_time now; @@ -853,22 +881,39 @@ static int tls_connection_verify_peer(struct tls_connection *conn) if (gnutls_certificate_verify_peers2(conn->session, &status) < 0) { wpa_printf(MSG_INFO, "TLS: Failed to verify peer " "certificate chain"); + *err = GNUTLS_A_INTERNAL_ERROR; return -1; } if (conn->verify_peer && (status & GNUTLS_CERT_INVALID)) { wpa_printf(MSG_INFO, "TLS: Peer certificate not trusted"); + if (status & GNUTLS_CERT_INSECURE_ALGORITHM) { + wpa_printf(MSG_INFO, "TLS: Certificate uses insecure " + "algorithm"); + *err = GNUTLS_A_INSUFFICIENT_SECURITY; + } + if (status & GNUTLS_CERT_NOT_ACTIVATED) { + wpa_printf(MSG_INFO, "TLS: Certificate not yet " + "activated"); + *err = GNUTLS_A_CERTIFICATE_EXPIRED; + } + if (status & GNUTLS_CERT_EXPIRED) { + wpa_printf(MSG_INFO, "TLS: Certificate expired"); + *err = GNUTLS_A_CERTIFICATE_EXPIRED; + } return -1; } if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) { wpa_printf(MSG_INFO, "TLS: Peer certificate does not have a " "known issuer"); + *err = GNUTLS_A_UNKNOWN_CA; return -1; } if (status & GNUTLS_CERT_REVOKED) { wpa_printf(MSG_INFO, "TLS: Peer certificate has been revoked"); + *err = GNUTLS_A_CERTIFICATE_REVOKED; return -1; } @@ -878,6 +923,7 @@ static int tls_connection_verify_peer(struct tls_connection *conn) if (certs == NULL) { wpa_printf(MSG_INFO, "TLS: No peer certificate chain " "received"); + *err = GNUTLS_A_UNKNOWN_CA; return -1; } @@ -887,6 +933,7 @@ static int tls_connection_verify_peer(struct tls_connection *conn) if (gnutls_x509_crt_init(&cert) < 0) { wpa_printf(MSG_INFO, "TLS: Certificate initialization " "failed"); + *err = GNUTLS_A_BAD_CERTIFICATE; return -1; } @@ -895,6 +942,7 @@ static int tls_connection_verify_peer(struct tls_connection *conn) wpa_printf(MSG_INFO, "TLS: Could not parse peer " "certificate %d/%d", i + 1, num_certs); gnutls_x509_crt_deinit(cert); + *err = GNUTLS_A_BAD_CERTIFICATE; return -1; } @@ -920,6 +968,7 @@ static int tls_connection_verify_peer(struct tls_connection *conn) "not valid at this time", i + 1, num_certs); gnutls_x509_crt_deinit(cert); + *err = GNUTLS_A_CERTIFICATE_EXPIRED; return -1; } @@ -981,19 +1030,24 @@ u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, } } else { size_t size; + gnutls_alert_description_t err; - if (conn->verify_peer && tls_connection_verify_peer(conn)) { + if (conn->verify_peer && + tls_connection_verify_peer(conn, &err)) { wpa_printf(MSG_INFO, "TLS: Peer certificate chain " "failed validation"); conn->failed++; - return NULL; + gnutls_alert_send(conn->session, GNUTLS_AL_FATAL, err); + goto out; } +#ifdef CONFIG_GNUTLS_EXTRA if (conn->tls_ia && !gnutls_ia_handshake_p(conn->session)) { wpa_printf(MSG_INFO, "TLS: No TLS/IA negotiation"); conn->failed++; return NULL; } +#endif /* CONFIG_GNUTLS_EXTRA */ if (conn->tls_ia) wpa_printf(MSG_DEBUG, "TLS: Start TLS/IA handshake"); @@ -1021,6 +1075,7 @@ u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, } } +out: out_data = conn->push_buf; *out_len = conn->push_buf_len; conn->push_buf = NULL; diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c index f290a39..b5a1d64 100644 --- a/src/crypto/tls_openssl.c +++ b/src/crypto/tls_openssl.c @@ -108,71 +108,9 @@ static void tls_show_errors(int level, const char *func, const char *txt) * MinGW does not yet include all the needed definitions for CryptoAPI, so * define here whatever extra is needed. */ -#define CALG_SSL3_SHAMD5 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SSL3SHAMD5) #define CERT_SYSTEM_STORE_CURRENT_USER (1 << 16) #define CERT_STORE_READONLY_FLAG 0x00008000 #define CERT_STORE_OPEN_EXISTING_FLAG 0x00004000 -#define CRYPT_ACQUIRE_COMPARE_KEY_FLAG 0x00000004 - -static BOOL WINAPI -(*CryptAcquireCertificatePrivateKey)(PCCERT_CONTEXT pCert, DWORD dwFlags, - void *pvReserved, HCRYPTPROV *phCryptProv, - DWORD *pdwKeySpec, BOOL *pfCallerFreeProv) -= NULL; /* to be loaded from crypt32.dll */ - -#ifdef CONFIG_MINGW32_LOAD_CERTENUM -static PCCERT_CONTEXT WINAPI -(*CertEnumCertificatesInStore)(HCERTSTORE hCertStore, - PCCERT_CONTEXT pPrevCertContext) -= NULL; /* to be loaded from crypt32.dll */ -#endif /* CONFIG_MINGW32_LOAD_CERTENUM */ - -static int mingw_load_crypto_func(void) -{ - HINSTANCE dll; - - /* MinGW does not yet have full CryptoAPI support, so load the needed - * function here. */ - - if (CryptAcquireCertificatePrivateKey) - return 0; - - dll = LoadLibrary("crypt32"); - if (dll == NULL) { - wpa_printf(MSG_DEBUG, "CryptoAPI: Could not load crypt32 " - "library"); - return -1; - } - - CryptAcquireCertificatePrivateKey = GetProcAddress( - dll, "CryptAcquireCertificatePrivateKey"); - if (CryptAcquireCertificatePrivateKey == NULL) { - wpa_printf(MSG_DEBUG, "CryptoAPI: Could not get " - "CryptAcquireCertificatePrivateKey() address from " - "crypt32 library"); - return -1; - } - -#ifdef CONFIG_MINGW32_LOAD_CERTENUM - CertEnumCertificatesInStore = (void *) GetProcAddress( - dll, "CertEnumCertificatesInStore"); - if (CertEnumCertificatesInStore == NULL) { - wpa_printf(MSG_DEBUG, "CryptoAPI: Could not get " - "CertEnumCertificatesInStore() address from " - "crypt32 library"); - return -1; - } -#endif /* CONFIG_MINGW32_LOAD_CERTENUM */ - - return 0; -} - -#else /* __MINGW32_VERSION */ - -static int mingw_load_crypto_func(void) -{ - return 0; -} #endif /* __MINGW32_VERSION */ @@ -403,9 +341,6 @@ static int tls_cryptoapi_cert(SSL *ssl, const char *name) goto err; } - if (mingw_load_crypto_func()) - goto err; - if (!CryptAcquireCertificatePrivateKey(priv->cert, CRYPT_ACQUIRE_COMPARE_KEY_FLAG, NULL, &priv->crypt_prov, @@ -476,9 +411,6 @@ static int tls_cryptoapi_ca_cert(SSL_CTX *ssl_ctx, SSL *ssl, const char *name) WCHAR *wstore; #endif /* UNICODE */ - if (mingw_load_crypto_func()) - return -1; - if (name == NULL || strncmp(name, "cert_store://", 13) != 0) return -1; @@ -735,11 +667,23 @@ void * tls_init(const struct tls_config *conf) if (tls_openssl_ref_count == 0) { SSL_load_error_strings(); SSL_library_init(); +#ifndef OPENSSL_NO_SHA256 + EVP_add_digest(EVP_sha256()); +#endif /* OPENSSL_NO_SHA256 */ /* TODO: if /dev/urandom is available, PRNG is seeded * automatically. If this is not the case, random data should * be added here. */ #ifdef PKCS12_FUNCS +#ifndef OPENSSL_NO_RC2 + /* + * 40-bit RC2 is commonly used in PKCS#12 files, so enable it. + * This is enabled by PKCS12_PBE_add() in OpenSSL 0.9.8 + * versions, but it looks like OpenSSL 1.0.0 does not do that + * anymore. + */ + EVP_add_cipher(EVP_rc2_40_cbc()); +#endif /* OPENSSL_NO_RC2 */ PKCS12_PBE_add(); #endif /* PKCS12_FUNCS */ } @@ -2105,9 +2049,18 @@ u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, if (*appl_data) { res = SSL_read(conn->ssl, *appl_data, in_len); if (res < 0) { - tls_show_errors(MSG_INFO, __func__, - "Failed to read possible " - "Application Data"); + int err = SSL_get_error(conn->ssl, res); + if (err == SSL_ERROR_WANT_READ || + err == SSL_ERROR_WANT_WRITE) { + wpa_printf(MSG_DEBUG, + "SSL: No Application Data " + "included"); + } else { + tls_show_errors(MSG_INFO, __func__, + "Failed to read " + "possible " + "Application Data"); + } os_free(*appl_data); *appl_data = NULL; } else { diff --git a/src/drivers/Apple80211.h b/src/drivers/Apple80211.h new file mode 100644 index 0000000..2a612e7 --- /dev/null +++ b/src/drivers/Apple80211.h @@ -0,0 +1,156 @@ +#ifndef APPLE80211_H +#define APPLE80211_H + +/* + * Apple80211 framework definitions + * This is an undocumented interface and the definitions here are based on + * information from MacStumbler (http://www.macstumbler.com/Apple80211.h) and + * whatever related information can be found with google and experiments ;-). + */ + +typedef struct __WirelessRef *WirelessRef; +typedef SInt32 WirelessError; +#define errWirelessNoError 0 + +typedef struct WirelessInfo { + UInt16 link_qual; + UInt16 comms_qual; + UInt16 signal; + UInt16 noise; + UInt16 port_stat; + UInt16 client_mode; + UInt16 res1; + UInt16 power; + UInt16 res2; + UInt8 bssID[6]; + UInt8 ssid[34]; +} WirelessInfo; + +typedef struct WirelessInfo2 { + /* TODO - these are probably not in correct order or complete */ + WirelessInfo info1; + UInt8 macAddress[6]; +} WirelessInfo2; + +typedef struct WirelessNetworkInfo { + UInt16 channel; + UInt16 noise; + UInt16 signal; + UInt8 bssid[6]; + UInt16 beacon_int; + UInt16 capability; + UInt16 ssid_len; + UInt8 ssid[32]; +} WirelessNetworkInfo; + +typedef int wirelessKeyType; /* TODO */ + +int WirelessIsAvailable(void); +WirelessError WirelessAttach(WirelessRef *ref, UInt32 res); +WirelessError WirelessDetach(WirelessRef ref); +WirelessError WirelessPrivate(WirelessRef ref, void *in_ptr, int in_bytes, + void *out_ptr, int out_bytes); +WirelessError WirelessSetEnabled(WirelessRef ref, UInt8 enabled); +WirelessError WirelessGetEnabled(WirelessRef ref, UInt8 *enabled); +WirelessError WirelessSetPower(WirelessRef ref, UInt8 power); +WirelessError WirelessGetPower(WirelessRef ref, UInt8 *power); +WirelessError WirelessGetInfo(WirelessRef ref, WirelessInfo *info); +WirelessError WirelessGetInfo2(WirelessRef ref, WirelessInfo2 *info); +WirelessError WirelessScan(WirelessRef ref, CFArrayRef *results, + UInt32 strip_dups); +WirelessError WirelessScanSplit(WirelessRef ref, CFArrayRef *ap_results, + CFArrayRef *ibss_results, UInt32 strip_dups); +WirelessError WirelessDirectedScan(WirelessRef ref, CFArrayRef *results, + UInt32 strip_dups, CFStringRef ssid); +WirelessError WirelessDirectedScan2(WirelessRef ref, CFDataRef ssid, + UInt32 strip_dups, CFArrayRef *results); +WirelessError WirelessJoin(WirelessRef ref, CFStringRef ssid); +WirelessError WirelessJoinWEP(WirelessRef ref, CFStringRef ssid, + CFStringRef passwd); +WirelessError WirelessJoin8021x(WirelessRef ref, CFStringRef ssid); +/* + * Set WEP key + * ref: wireless reference from WirelessAttach() + * type: ? + * key_idx: 0..3 + * key_len: 13 for WEP-104 or 0 for clearing the key + * key: Pointer to the key or %NULL if key_len = 0 + */ +WirelessError WirelessSetKey(WirelessRef ref, wirelessKeyType type, + int key_idx, int key_len, + const unsigned char *key); +/* + * Set WPA key (e.g., PMK for 4-way handshake) + * ref: wireless reference from WirelessAttach() + * type: 0..4; 1 = PMK + * key_len: 16, 32, or 0 + * key: Pointer to the key or %NULL if key_len = 0 + */ +WirelessError WirelessSetWPAKey(WirelessRef ref, wirelessKeyType type, + int key_len, const unsigned char *key); +WirelessError WirelessAssociate(WirelessRef ref, int type, CFDataRef ssid, + CFStringRef key); +WirelessError WirelessAssociate2(WirelessRef ref, CFDictionaryRef scan_res, + CFStringRef key); +WirelessError WirelessDisassociate(WirelessRef ref); + +/* + * Get a copy of scan results for the given SSID + * The returned dictionary includes following entries: + * beaconInterval: CFNumber(kCFNumberSInt32Type) + * SSID: CFData buffer of the SSID + * isWPA: CFNumber(kCFNumberSInt32Type); 0 = not used, 1 = WPA, -128 = WPA2 + * name: Name of the network (SSID string) + * BSSID: CFData buffer of the BSSID + * channel: CFNumber(kCFNumberSInt32Type) + * signal: CFNumber(kCFNumberSInt32Type) + * appleIE: CFData + * WPSNOPINRequired: CFBoolean + * noise: CFNumber(kCFNumberSInt32Type) + * capability: CFNumber(kCFNumberSInt32Type) + * uniCipher: CFArray of CFNumber(kCFNumberSInt32Type) + * appleIE_Version: CFNumber(kCFNumberSInt32Type) + * appleIE_Robust: CFBoolean + * WPSConfigured: CFBoolean + * scanWasDirected: CFBoolean + * appleIE_Product: CFNumber(kCFNumberSInt32Type) + * authModes: CFArray of CFNumber(kCFNumberSInt32Type) + * multiCipher: CFNumber(kCFNumberSInt32Type) + */ +CFDictionaryRef WirelessSafeDirectedScanCopy(WirelessRef ref, CFDataRef ssid); + +/* + * Get information about the current association + * The returned dictionary includes following entries: + * keyData: CFData buffer of the key (e.g., 32-octet PSK) + * multiCipher: CFNumber(kCFNumberSInt32Type); 0 = none, 5 = CCMP? + * channel: CFNumber(kCFNumberSInt32Type) + * isIBSS: CFBoolean + * authMode: CFNumber(kCFNumberSInt32Type); 2 = WPA-Personal; 3 = open, + * 129 = WPA2-Enterprise + * isWPA: CFNumber(kCFNumberSInt32Type); 0 = not used, 1 = WPA, -128 == WPA2 + * SSID: CFData buffer of the SSID + * cipherMode: CFNumber(kCFNumberSInt32Type); 0 = none, 4 = CCMP? + */ +CFDictionaryRef WirelessGetAssociationInfo(WirelessRef ref); + +WirelessError WirelessConfigure(WirelessRef ref); + +/* + * Get ASP information + * The returned dictionary includes following entries: + * Version: version number (e.g., 3.0) + * Channel: channel (e.g., 1) + * Vendor: vendor (e.g., 2) + */ +CFDictionaryRef WirelessGetInfoASP(void); + +/* + * Get a copy of the interface dictionary + * The returned dictionary has a key,value pairs for wireless interfaces. + * The key is the interface name and the value is the driver identifier, e.g., + * en1: com.apple.driver.AirPort.Atheros + */ +CFDictionaryRef WirelessCopyInterfaceDict(void); + +#endif /* APPLE80211_H */ diff --git a/src/drivers/Makefile b/src/drivers/Makefile new file mode 100644 index 0000000..cffba62 --- /dev/null +++ b/src/drivers/Makefile @@ -0,0 +1,9 @@ +all: + @echo Nothing to be made. + +clean: + for d in $(SUBDIRS); do make -C $$d clean; done + rm -f *~ *.o *.d + +install: + @echo Nothing to be made. diff --git a/src/drivers/MobileApple80211.c b/src/drivers/MobileApple80211.c new file mode 100644 index 0000000..ce004fe --- /dev/null +++ b/src/drivers/MobileApple80211.c @@ -0,0 +1,189 @@ +#include "includes.h" +#include <dlfcn.h> + +#include "common.h" + +#include <CoreFoundation/CoreFoundation.h> +#include "MobileApple80211.h" + +/* + * Code for dynamically loading Apple80211 functions from Aeropuerto to avoid + * having to link with full Preferences.framework. + */ + +static void *aeropuerto = NULL; + + +int _Apple80211Initialized(void) +{ + return aeropuerto ? 1 : 0; +} + + +static int (*__Apple80211Open)(Apple80211Ref *ctx) = NULL; + +int Apple80211Open(Apple80211Ref *ctx) +{ + return __Apple80211Open(ctx); +} + + +static int (*__Apple80211Close)(Apple80211Ref ctx) = NULL; + +int Apple80211Close(Apple80211Ref ctx) +{ + return __Apple80211Close(ctx); +} + + +static int (*__Apple80211GetIfListCopy)(Apple80211Ref handle, CFArrayRef *list) + = NULL; + +int Apple80211GetIfListCopy(Apple80211Ref handle, CFArrayRef *list) +{ + return __Apple80211GetIfListCopy(handle, list); +} + + +static int (*__Apple80211BindToInterface)(Apple80211Ref handle, + CFStringRef interface) = NULL; + +int Apple80211BindToInterface(Apple80211Ref handle, + CFStringRef interface) +{ + return __Apple80211BindToInterface(handle, interface); +} + + +static int (*__Apple80211GetInterfaceNameCopy)(Apple80211Ref handle, + CFStringRef *name) = NULL; + +int Apple80211GetInterfaceNameCopy(Apple80211Ref handle, + CFStringRef *name) +{ + return __Apple80211GetInterfaceNameCopy(handle, name); +} + + +static int (*__Apple80211GetInfoCopy)(Apple80211Ref handle, + CFDictionaryRef *info) = NULL; + +int Apple80211GetInfoCopy(Apple80211Ref handle, + CFDictionaryRef *info) +{ + return __Apple80211GetInfoCopy(handle, info); +} + + +static int (*__Apple80211GetPower)(Apple80211Ref handle, char *pwr) = NULL; + +int Apple80211GetPower(Apple80211Ref handle, char *pwr) +{ + return __Apple80211GetPower(handle, pwr); +} + + +static int (*__Apple80211SetPower)(Apple80211Ref handle, char pwr) = NULL; + +int Apple80211SetPower(Apple80211Ref handle, char pwr) +{ + return __Apple80211SetPower(handle, pwr); +} + + +static int (*__Apple80211Scan)(Apple80211Ref handle, CFArrayRef *list, + CFDictionaryRef parameters) = NULL; + +int Apple80211Scan(Apple80211Ref handle, CFArrayRef *list, + CFDictionaryRef parameters) +{ + return __Apple80211Scan(handle, list, parameters); +} + + +static int (*__Apple80211Associate)(Apple80211Ref handle, CFDictionaryRef bss, + CFStringRef password) = NULL; + +int Apple80211Associate(Apple80211Ref handle, CFDictionaryRef bss, + CFStringRef password) +{ + return __Apple80211Associate(handle, bss, password); +} + + +static int (*__Apple80211AssociateAndCopyInfo)(Apple80211Ref handle, + CFDictionaryRef bss, + CFStringRef password, + CFDictionaryRef *info) = + NULL; + +int Apple80211AssociateAndCopyInfo(Apple80211Ref handle, CFDictionaryRef bss, + CFStringRef password, CFDictionaryRef *info) +{ + return __Apple80211AssociateAndCopyInfo(handle, bss, password, info); +} + + +static int (*__Apple80211CopyValue)(Apple80211Ref handle, int field, + CFDictionaryRef arg2, void *value) = NULL; + +int Apple80211CopyValue(Apple80211Ref handle, int field, CFDictionaryRef arg2, + void *value) +{ + return __Apple80211CopyValue(handle, field, arg2, value); +} + + +#define DLSYM(s) \ +do { \ + __ ## s = dlsym(aeropuerto, #s); \ + if (__ ## s == NULL) { \ + wpa_printf(MSG_ERROR, "MobileApple80211: Could not resolve " \ + "symbol '" #s "' (%s)", dlerror()); \ + err = 1; \ + } \ +} while (0) + + +__attribute__ ((constructor)) +void _Apple80211_constructor(void) +{ + const char *fname = "/System/Library/SystemConfiguration/" + "Aeropuerto.bundle/Aeropuerto"; + int err = 0; + + aeropuerto = dlopen(fname, RTLD_LAZY); + if (!aeropuerto) { + wpa_printf(MSG_ERROR, "MobileApple80211: Failed to open %s " + "for symbols", fname); + return; + } + + DLSYM(Apple80211Open); + DLSYM(Apple80211Close); + DLSYM(Apple80211GetIfListCopy); + DLSYM(Apple80211BindToInterface); + DLSYM(Apple80211GetInterfaceNameCopy); + DLSYM(Apple80211GetInfoCopy); + DLSYM(Apple80211GetPower); + DLSYM(Apple80211SetPower); + DLSYM(Apple80211Scan); + DLSYM(Apple80211Associate); + DLSYM(Apple80211AssociateAndCopyInfo); + DLSYM(Apple80211CopyValue); + + if (err) { + dlclose(aeropuerto); + aeropuerto = NULL; + } +} + + +__attribute__ ((destructor)) +void _Apple80211_destructor(void) +{ + if (aeropuerto) { + dlclose(aeropuerto); + aeropuerto = NULL; + } +} diff --git a/src/drivers/MobileApple80211.h b/src/drivers/MobileApple80211.h new file mode 100644 index 0000000..64d439d --- /dev/null +++ b/src/drivers/MobileApple80211.h @@ -0,0 +1,43 @@ +#ifndef MOBILEAPPLE80211_H +#define MOBILEAPPLE80211_H + +/* + * MobileApple80211 interface for iPhone/iPod touch + * These functions are available from Aeropuerto. + */ + +struct Apple80211; +typedef struct Apple80211 *Apple80211Ref; + +int Apple80211Open(Apple80211Ref *ctx); +int Apple80211Close(Apple80211Ref ctx); +int Apple80211GetIfListCopy(Apple80211Ref handle, CFArrayRef *list); +int Apple80211BindToInterface(Apple80211Ref handle, + CFStringRef interface); +int Apple80211GetInterfaceNameCopy(Apple80211Ref handle, + CFStringRef *name); +int Apple80211GetInfoCopy(Apple80211Ref handle, + CFDictionaryRef *info); +int Apple80211GetPower(Apple80211Ref handle, char *pwr); +int Apple80211SetPower(Apple80211Ref handle, char pwr); + +/* parameters can be NULL; returns scan results in CFArrayRef *list; + * caller will need to free with CFRelease() */ +int Apple80211Scan(Apple80211Ref handle, CFArrayRef *list, + CFDictionaryRef parameters); + +int Apple80211Associate(Apple80211Ref handle, CFDictionaryRef bss, + CFStringRef password); +int Apple80211AssociateAndCopyInfo(Apple80211Ref handle, CFDictionaryRef bss, + CFStringRef password, + CFDictionaryRef *info); + +enum { + APPLE80211_VALUE_SSID = 1, + APPLE80211_VALUE_BSSID = 9 +}; + +int Apple80211CopyValue(Apple80211Ref handle, int field, CFDictionaryRef arg2, + void *value); + +#endif /* MOBILEAPPLE80211_H */ diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 9bce6c6..c2975d2 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -131,7 +131,7 @@ struct wpa_scan_results { * @ifname: Interface name that can be used with init() or init2() * @desc: Human readable adapter description (e.g., vendor/model) or NULL if * not available - * @drv_bame: struct wpa_driver_ops::name (note: unlike other strings, this one + * @drv_name: struct wpa_driver_ops::name (note: unlike other strings, this one * is not an allocated copy, i.e., get_interfaces() caller will not free * this) */ @@ -702,7 +702,7 @@ struct wpa_driver_ops { int (*flush_pmkid)(void *priv); /** - * flush_pmkid - Flush PMKSA cache + * get_capa - Get driver capabilities * @priv: private driver interface data * * Returns: 0 on success, -1 on failure diff --git a/src/drivers/driver_atmel.c b/src/drivers/driver_atmel.c new file mode 100644 index 0000000..0a7a66d --- /dev/null +++ b/src/drivers/driver_atmel.c @@ -0,0 +1,506 @@ +/* + * WPA Supplicant - Driver interaction with Atmel Wireless LAN drivers + * Copyright (c) 2000-2005, ATMEL Corporation + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +/****************************************************************************** + Copyright 2000-2001 ATMEL Corporation. + + WPA Supplicant - driver interaction with Atmel Wireless lan drivers. + + This is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Atmel wireless lan drivers; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +******************************************************************************/ + +/* + * Alternatively, this software may be distributed under the terms of BSD + * license. + */ + +#include "includes.h" +#include <sys/ioctl.h> + +#include "wireless_copy.h" +#include "common.h" +#include "driver.h" +#include "driver_wext.h" + +struct wpa_driver_atmel_data { + void *wext; /* private data for driver_wext */ + void *ctx; + char ifname[IFNAMSIZ + 1]; + int sock; +}; + + +#define ATMEL_WPA_IOCTL (SIOCIWFIRSTPRIV + 2) +#define ATMEL_WPA_IOCTL_PARAM (SIOCIWFIRSTPRIV + 3) +#define ATMEL_WPA_IOCTL_GET_PARAM (SIOCIWFIRSTPRIV + 4) + + +/* ATMEL_WPA_IOCTL ioctl() cmd: */ +enum { + SET_WPA_ENCRYPTION = 1, + SET_CIPHER_SUITES = 2, + MLME_STA_DEAUTH = 3, + MLME_STA_DISASSOC = 4 +}; + +/* ATMEL_WPA_IOCTL_PARAM ioctl() cmd: */ +enum { + ATMEL_PARAM_WPA = 1, + ATMEL_PARAM_PRIVACY_INVOKED = 2, + ATMEL_PARAM_WPA_TYPE = 3 +}; + +#define MAX_KEY_LENGTH 40 + +struct atmel_param{ + unsigned char sta_addr[6]; + int cmd; + u8 alg; + u8 key_idx; + u8 set_tx; + u8 seq[8]; + u8 seq_len; + u16 key_len; + u8 key[MAX_KEY_LENGTH]; + struct{ + int reason_code; + u8 state; + }mlme; + u8 pairwise_suite; + u8 group_suite; + u8 key_mgmt_suite; +}; + + + +static int atmel_ioctl(struct wpa_driver_atmel_data *drv, + struct atmel_param *param, + int len, int show_err) +{ + struct iwreq iwr; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) param; + iwr.u.data.length = len; + + if (ioctl(drv->sock, ATMEL_WPA_IOCTL, &iwr) < 0) { + int ret; + ret = errno; + if (show_err) + perror("ioctl[ATMEL_WPA_IOCTL]"); + return ret; + } + + return 0; +} + + +static int atmel2param(struct wpa_driver_atmel_data *drv, int param, int value) +{ + struct iwreq iwr; + int *i, ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + i = (int *) iwr.u.name; + *i++ = param; + *i++ = value; + + if (ioctl(drv->sock, ATMEL_WPA_IOCTL_PARAM, &iwr) < 0) { + perror("ioctl[ATMEL_WPA_IOCTL_PARAM]"); + ret = -1; + } + return ret; +} + + +#if 0 +static int wpa_driver_atmel_set_wpa_ie(struct wpa_driver_atmel_data *drv, + const char *wpa_ie, size_t wpa_ie_len) +{ + struct atmel_param *param; + int res; + size_t blen = ATMEL_GENERIC_ELEMENT_HDR_LEN + wpa_ie_len; + if (blen < sizeof(*param)) + blen = sizeof(*param); + + param = os_zalloc(blen); + if (param == NULL) + return -1; + + param->cmd = ATMEL_SET_GENERIC_ELEMENT; + param->u.generic_elem.len = wpa_ie_len; + os_memcpy(param->u.generic_elem.data, wpa_ie, wpa_ie_len); + res = atmel_ioctl(drv, param, blen, 1); + + os_free(param); + + return res; +} +#endif + + +static int wpa_driver_atmel_set_wpa(void *priv, int enabled) +{ + struct wpa_driver_atmel_data *drv = priv; + int ret = 0; + + printf("wpa_driver_atmel_set_wpa %s\n", drv->ifname); + + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + +#if 0 + if (!enabled && wpa_driver_atmel_set_wpa_ie(drv, NULL, 0) < 0) + ret = -1; +#endif + if (atmel2param(drv, ATMEL_PARAM_PRIVACY_INVOKED, enabled) < 0) + ret = -1; + if (atmel2param(drv, ATMEL_PARAM_WPA, enabled) < 0) + ret = -1; + + return ret; +} + + +static int wpa_driver_atmel_set_key(void *priv, wpa_alg alg, + const u8 *addr, int key_idx, + int set_tx, const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + struct wpa_driver_atmel_data *drv = priv; + int ret = 0; + struct atmel_param *param; + u8 *buf; + u8 alg_type; + + size_t blen; + char *alg_name; + + switch (alg) { + case WPA_ALG_NONE: + alg_name = "none"; + alg_type = 0; + break; + case WPA_ALG_WEP: + alg_name = "WEP"; + alg_type = 1; + break; + case WPA_ALG_TKIP: + alg_name = "TKIP"; + alg_type = 2; + break; + case WPA_ALG_CCMP: + alg_name = "CCMP"; + alg_type = 3; + break; + default: + return -1; + } + + wpa_printf(MSG_DEBUG, "%s: alg=%s key_idx=%d set_tx=%d seq_len=%lu " + "key_len=%lu", __FUNCTION__, alg_name, key_idx, set_tx, + (unsigned long) seq_len, (unsigned long) key_len); + + if (seq_len > 8) + return -2; + + blen = sizeof(*param) + key_len; + buf = os_zalloc(blen); + if (buf == NULL) + return -1; + + param = (struct atmel_param *) buf; + + param->cmd = SET_WPA_ENCRYPTION; + + if (addr == NULL) + os_memset(param->sta_addr, 0xff, ETH_ALEN); + else + os_memcpy(param->sta_addr, addr, ETH_ALEN); + + param->alg = alg_type; + param->key_idx = key_idx; + param->set_tx = set_tx; + os_memcpy(param->seq, seq, seq_len); + param->seq_len = seq_len; + param->key_len = key_len; + os_memcpy((u8 *)param->key, key, key_len); + + if (atmel_ioctl(drv, param, blen, 1)) { + wpa_printf(MSG_WARNING, "Failed to set encryption."); + /* TODO: show key error*/ + ret = -1; + } + os_free(buf); + + return ret; +} + + +static int wpa_driver_atmel_set_countermeasures(void *priv, + int enabled) +{ + /* FIX */ + printf("wpa_driver_atmel_set_countermeasures - not yet " + "implemented\n"); + return 0; +} + + +static int wpa_driver_atmel_set_drop_unencrypted(void *priv, + int enabled) +{ + /* FIX */ + printf("wpa_driver_atmel_set_drop_unencrypted - not yet " + "implemented\n"); + return 0; +} + + +static int wpa_driver_atmel_mlme(void *priv, const u8 *addr, int cmd, + int reason_code) +{ + struct wpa_driver_atmel_data *drv = priv; + struct atmel_param param; + int ret; + int mgmt_error = 0xaa; + + os_memset(¶m, 0, sizeof(param)); + os_memcpy(param.sta_addr, addr, ETH_ALEN); + param.cmd = cmd; + param.mlme.reason_code = reason_code; + param.mlme.state = mgmt_error; + ret = atmel_ioctl(drv, ¶m, sizeof(param), 1); + return ret; +} + + +#if 0 +static int wpa_driver_atmel_set_suites(struct wpa_driver_atmel_data *drv, + u8 pairwise_suite, u8 group_suite, + u8 key_mgmt_suite) +{ + struct atmel_param param; + int ret; + + os_memset(¶m, 0, sizeof(param)); + param.cmd = SET_CIPHER_SUITES; + param.pairwise_suite = pairwise_suite; + param.group_suite = group_suite; + param.key_mgmt_suite = key_mgmt_suite; + + ret = atmel_ioctl(drv, ¶m, sizeof(param), 1); + return ret; +} +#endif + + +static int wpa_driver_atmel_deauthenticate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_atmel_data *drv = priv; + printf("wpa_driver_atmel_deauthenticate\n"); + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + return wpa_driver_atmel_mlme(drv, addr, MLME_STA_DEAUTH, + reason_code); + +} + + +static int wpa_driver_atmel_disassociate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_atmel_data *drv = priv; + printf("wpa_driver_atmel_disassociate\n"); + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + return wpa_driver_atmel_mlme(drv, addr, MLME_STA_DISASSOC, + reason_code); + +} + + +#if 0 +/* Atmel driver uses specific values for each cipher suite */ +static int convertSuiteToDriver(wpa_cipher suite) +{ + u8 suite_type; + + switch(suite) { + case CIPHER_NONE: + suite_type = 0; + break; + case CIPHER_WEP40: + suite_type = 1; + break; + case CIPHER_TKIP: + suite_type = 2; + break; + case CIPHER_WEP104: + suite_type = 5; + break; + case CIPHER_CCMP: + suite_type = 3; + break; + default: + suite_type = 2; + } + + return suite_type; + +} +#endif + +static int +wpa_driver_atmel_associate(void *priv, + struct wpa_driver_associate_params *params) +{ + struct wpa_driver_atmel_data *drv = priv; + int ret = 0; +#if 0 + u8 pairwise_suite_driver; + u8 group_suite_driver; + u8 key_mgmt_suite_driver; + + pairwise_suite_driver = convertSuiteToDriver(params->pairwise_suite); + group_suite_driver = convertSuiteToDriver(params->group_suite); + key_mgmt_suite_driver = convertSuiteToDriver(params->key_mgmt_suite); + + if (wpa_driver_atmel_set_suites(drv, pairwise_suite_driver, + group_suite_driver, + key_mgmt_suite_driver) < 0){ + printf("wpa_driver_atmel_set_suites.\n"); + ret = -1; + } + if (wpa_driver_wext_set_freq(drv->wext, params->freq) < 0) { + printf("wpa_driver_atmel_set_freq.\n"); + ret = -1; + } +#endif + if (wpa_driver_wext_set_ssid(drv->wext, params->ssid, params->ssid_len) + < 0) { + printf("FAILED : wpa_driver_atmel_set_ssid.\n"); + ret = -1; + } + if (wpa_driver_wext_set_bssid(drv->wext, params->bssid) < 0) { + printf("FAILED : wpa_driver_atmel_set_bssid.\n"); + ret = -1; + } + + return ret; +} + + +static int wpa_driver_atmel_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_atmel_data *drv = priv; + return wpa_driver_wext_get_bssid(drv->wext, bssid); +} + + +static int wpa_driver_atmel_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_atmel_data *drv = priv; + return wpa_driver_wext_get_ssid(drv->wext, ssid); +} + + +static int wpa_driver_atmel_scan(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_atmel_data *drv = priv; + return wpa_driver_wext_scan(drv->wext, ssid, ssid_len); +} + + +static struct wpa_scan_results * wpa_driver_atmel_get_scan_results(void *priv) +{ + struct wpa_driver_atmel_data *drv = priv; + return wpa_driver_wext_get_scan_results(drv->wext); +} + + +static int wpa_driver_atmel_set_operstate(void *priv, int state) +{ + struct wpa_driver_atmel_data *drv = priv; + return wpa_driver_wext_set_operstate(drv->wext, state); +} + + +static void * wpa_driver_atmel_init(void *ctx, const char *ifname) +{ + struct wpa_driver_atmel_data *drv; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->wext = wpa_driver_wext_init(ctx, ifname); + if (drv->wext == NULL) { + os_free(drv); + return NULL; + } + + drv->ctx = ctx; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + drv->sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->sock < 0) { + wpa_driver_wext_deinit(drv->wext); + os_free(drv); + return NULL; + } + + return drv; +} + + +static void wpa_driver_atmel_deinit(void *priv) +{ + struct wpa_driver_atmel_data *drv = priv; + wpa_driver_wext_deinit(drv->wext); + close(drv->sock); + os_free(drv); +} + + +const struct wpa_driver_ops wpa_driver_atmel_ops = { + .name = "atmel", + .desc = "ATMEL AT76C5XXx (USB, PCMCIA)", + .get_bssid = wpa_driver_atmel_get_bssid, + .get_ssid = wpa_driver_atmel_get_ssid, + .set_wpa = wpa_driver_atmel_set_wpa, + .set_key = wpa_driver_atmel_set_key, + .init = wpa_driver_atmel_init, + .deinit = wpa_driver_atmel_deinit, + .set_countermeasures = wpa_driver_atmel_set_countermeasures, + .set_drop_unencrypted = wpa_driver_atmel_set_drop_unencrypted, + .scan = wpa_driver_atmel_scan, + .get_scan_results2 = wpa_driver_atmel_get_scan_results, + .deauthenticate = wpa_driver_atmel_deauthenticate, + .disassociate = wpa_driver_atmel_disassociate, + .associate = wpa_driver_atmel_associate, + .set_operstate = wpa_driver_atmel_set_operstate, +}; diff --git a/src/drivers/driver_broadcom.c b/src/drivers/driver_broadcom.c new file mode 100644 index 0000000..3600dae --- /dev/null +++ b/src/drivers/driver_broadcom.c @@ -0,0 +1,604 @@ +/* + * WPA Supplicant - driver interaction with old Broadcom wl.o driver + * Copyright (c) 2004, Nikki Chumkov <nikki@gattaca.ru> + * Copyright (c) 2004, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * Please note that the newer Broadcom driver ("hybrid Linux driver") supports + * Linux wireless extensions and does not need (or even work) with this old + * driver wrapper. Use driver_wext.c with that driver. + */ + +#include "includes.h" + +#include <sys/ioctl.h> + +#include "common.h" + +#if 0 +#include <netpacket/packet.h> +#include <net/ethernet.h> /* the L2 protocols */ +#else +#include <linux/if_packet.h> +#include <linux/if_ether.h> /* The L2 protocols */ +#endif +#include <net/if.h> +#include <typedefs.h> + +/* wlioctl.h is a Broadcom header file and it is available, e.g., from Linksys + * WRT54G GPL tarball. */ +#include <wlioctl.h> + +#include "driver.h" +#include "eloop.h" + +struct wpa_driver_broadcom_data { + void *ctx; + int ioctl_sock; + int event_sock; + char ifname[IFNAMSIZ + 1]; +}; + + +#ifndef WLC_DEAUTHENTICATE +#define WLC_DEAUTHENTICATE 143 +#endif +#ifndef WLC_DEAUTHENTICATE_WITH_REASON +#define WLC_DEAUTHENTICATE_WITH_REASON 201 +#endif +#ifndef WLC_SET_TKIP_COUNTERMEASURES +#define WLC_SET_TKIP_COUNTERMEASURES 202 +#endif + +#if !defined(PSK_ENABLED) /* NEW driver interface */ +#define WL_VERSION 360130 +/* wireless authentication bit vector */ +#define WPA_ENABLED 1 +#define PSK_ENABLED 2 + +#define WAUTH_WPA_ENABLED(wauth) ((wauth) & WPA_ENABLED) +#define WAUTH_PSK_ENABLED(wauth) ((wauth) & PSK_ENABLED) +#define WAUTH_ENABLED(wauth) ((wauth) & (WPA_ENABLED | PSK_ENABLED)) + +#define WSEC_PRIMARY_KEY WL_PRIMARY_KEY + +typedef wl_wsec_key_t wsec_key_t; +#endif + +typedef struct { + uint32 val; + struct ether_addr ea; + uint16 res; +} wlc_deauth_t; + + +static void wpa_driver_broadcom_scan_timeout(void *eloop_ctx, + void *timeout_ctx); + +static int broadcom_ioctl(struct wpa_driver_broadcom_data *drv, int cmd, + void *buf, int len) +{ + struct ifreq ifr; + wl_ioctl_t ioc; + int ret = 0; + + wpa_printf(MSG_MSGDUMP, "BROADCOM: wlioctl(%s,%d,len=%d,val=%p)", + drv->ifname, cmd, len, buf); + /* wpa_hexdump(MSG_MSGDUMP, "BROADCOM: wlioctl buf", buf, len); */ + + ioc.cmd = cmd; + ioc.buf = buf; + ioc.len = len; + os_strlcpy(ifr.ifr_name, drv->ifname, IFNAMSIZ); + ifr.ifr_data = (caddr_t) &ioc; + if ((ret = ioctl(drv->ioctl_sock, SIOCDEVPRIVATE, &ifr)) < 0) { + if (cmd != WLC_GET_MAGIC) + perror(ifr.ifr_name); + wpa_printf(MSG_MSGDUMP, "BROADCOM: wlioctl cmd=%d res=%d", + cmd, ret); + } + + return ret; +} + +static int wpa_driver_broadcom_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_broadcom_data *drv = priv; + if (broadcom_ioctl(drv, WLC_GET_BSSID, bssid, ETH_ALEN) == 0) + return 0; + + os_memset(bssid, 0, ETH_ALEN); + return -1; +} + +static int wpa_driver_broadcom_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_broadcom_data *drv = priv; + wlc_ssid_t s; + + if (broadcom_ioctl(drv, WLC_GET_SSID, &s, sizeof(s)) == -1) + return -1; + + os_memcpy(ssid, s.SSID, s.SSID_len); + return s.SSID_len; +} + +static int wpa_driver_broadcom_set_wpa(void *priv, int enable) +{ + struct wpa_driver_broadcom_data *drv = priv; + unsigned int wauth, wsec; + struct ether_addr ea; + + os_memset(&ea, enable ? 0xff : 0, sizeof(ea)); + if (broadcom_ioctl(drv, WLC_GET_WPA_AUTH, &wauth, sizeof(wauth)) == + -1 || + broadcom_ioctl(drv, WLC_GET_WSEC, &wsec, sizeof(wsec)) == -1) + return -1; + + if (enable) { + wauth = PSK_ENABLED; + wsec = TKIP_ENABLED; + } else { + wauth = 255; + wsec &= ~(TKIP_ENABLED | AES_ENABLED); + } + + if (broadcom_ioctl(drv, WLC_SET_WPA_AUTH, &wauth, sizeof(wauth)) == + -1 || + broadcom_ioctl(drv, WLC_SET_WSEC, &wsec, sizeof(wsec)) == -1) + return -1; + + /* FIX: magic number / error handling? */ + broadcom_ioctl(drv, 122, &ea, sizeof(ea)); + + return 0; +} + +static int wpa_driver_broadcom_set_key(void *priv, wpa_alg alg, + const u8 *addr, int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + struct wpa_driver_broadcom_data *drv = priv; + int ret; + wsec_key_t wkt; + + os_memset(&wkt, 0, sizeof wkt); + wpa_printf(MSG_MSGDUMP, "BROADCOM: SET %sKEY[%d] alg=%d", + set_tx ? "PRIMARY " : "", key_idx, alg); + if (key && key_len > 0) + wpa_hexdump_key(MSG_MSGDUMP, "BROADCOM: key", key, key_len); + + switch (alg) { + case WPA_ALG_NONE: + wkt.algo = CRYPTO_ALGO_OFF; + break; + case WPA_ALG_WEP: + wkt.algo = CRYPTO_ALGO_WEP128; /* CRYPTO_ALGO_WEP1? */ + break; + case WPA_ALG_TKIP: + wkt.algo = 0; /* CRYPTO_ALGO_TKIP? */ + break; + case WPA_ALG_CCMP: + wkt.algo = 0; /* CRYPTO_ALGO_AES_CCM; + * AES_OCB_MSDU, AES_OCB_MPDU? */ + break; + default: + wkt.algo = CRYPTO_ALGO_NALG; + break; + } + + if (seq && seq_len > 0) + wpa_hexdump(MSG_MSGDUMP, "BROADCOM: SEQ", seq, seq_len); + + if (addr) + wpa_hexdump(MSG_MSGDUMP, "BROADCOM: addr", addr, ETH_ALEN); + + wkt.index = key_idx; + wkt.len = key_len; + if (key && key_len > 0) { + os_memcpy(wkt.data, key, key_len); + if (key_len == 32) { + /* hack hack hack XXX */ + os_memcpy(&wkt.data[16], &key[24], 8); + os_memcpy(&wkt.data[24], &key[16], 8); + } + } + /* wkt.algo = CRYPTO_ALGO_...; */ + wkt.flags = set_tx ? 0 : WSEC_PRIMARY_KEY; + if (addr && set_tx) + os_memcpy(&wkt.ea, addr, sizeof(wkt.ea)); + ret = broadcom_ioctl(drv, WLC_SET_KEY, &wkt, sizeof(wkt)); + if (addr && set_tx) { + /* FIX: magic number / error handling? */ + broadcom_ioctl(drv, 121, &wkt.ea, sizeof(wkt.ea)); + } + return ret; +} + + +static void wpa_driver_broadcom_event_receive(int sock, void *ctx, + void *sock_ctx) +{ + char buf[8192]; + int left; + wl_wpa_header_t *wwh; + union wpa_event_data data; + + if ((left = recv(sock, buf, sizeof buf, 0)) < 0) + return; + + wpa_hexdump(MSG_DEBUG, "RECEIVE EVENT", (u8 *) buf, left); + + if ((size_t) left < sizeof(wl_wpa_header_t)) + return; + + wwh = (wl_wpa_header_t *) buf; + + if (wwh->snap.type != WL_WPA_ETHER_TYPE) + return; + if (os_memcmp(&wwh->snap, wl_wpa_snap_template, 6) != 0) + return; + + os_memset(&data, 0, sizeof(data)); + + switch (wwh->type) { + case WLC_ASSOC_MSG: + left -= WL_WPA_HEADER_LEN; + wpa_printf(MSG_DEBUG, "BROADCOM: ASSOC MESSAGE (left: %d)", + left); + if (left > 0) { + data.assoc_info.resp_ies = os_malloc(left); + if (data.assoc_info.resp_ies == NULL) + return; + os_memcpy(data.assoc_info.resp_ies, + buf + WL_WPA_HEADER_LEN, left); + data.assoc_info.resp_ies_len = left; + wpa_hexdump(MSG_MSGDUMP, "BROADCOM: copying %d bytes " + "into resp_ies", + data.assoc_info.resp_ies, left); + } + /* data.assoc_info.req_ies = NULL; */ + /* data.assoc_info.req_ies_len = 0; */ + + wpa_supplicant_event(ctx, EVENT_ASSOCINFO, &data); + wpa_supplicant_event(ctx, EVENT_ASSOC, NULL); + break; + case WLC_DISASSOC_MSG: + wpa_printf(MSG_DEBUG, "BROADCOM: DISASSOC MESSAGE"); + wpa_supplicant_event(ctx, EVENT_DISASSOC, NULL); + break; + case WLC_PTK_MIC_MSG: + wpa_printf(MSG_DEBUG, "BROADCOM: PTK MIC MSG MESSAGE"); + data.michael_mic_failure.unicast = 1; + wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); + break; + case WLC_GTK_MIC_MSG: + wpa_printf(MSG_DEBUG, "BROADCOM: GTK MIC MSG MESSAGE"); + data.michael_mic_failure.unicast = 0; + wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); + break; + default: + wpa_printf(MSG_DEBUG, "BROADCOM: UNKNOWN MESSAGE (%d)", + wwh->type); + break; + } + os_free(data.assoc_info.resp_ies); +} + +static void * wpa_driver_broadcom_init(void *ctx, const char *ifname) +{ + int s; + struct sockaddr_ll ll; + struct wpa_driver_broadcom_data *drv; + struct ifreq ifr; + + /* open socket to kernel */ + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + perror("socket"); + return NULL; + } + /* do it */ + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) { + perror(ifr.ifr_name); + return NULL; + } + + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->ctx = ctx; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + drv->ioctl_sock = s; + + s = socket(PF_PACKET, SOCK_RAW, ntohs(ETH_P_802_2)); + if (s < 0) { + perror("socket(PF_PACKET, SOCK_RAW, ntohs(ETH_P_802_2))"); + close(drv->ioctl_sock); + os_free(drv); + return NULL; + } + + os_memset(&ll, 0, sizeof(ll)); + ll.sll_family = AF_PACKET; + ll.sll_protocol = ntohs(ETH_P_802_2); + ll.sll_ifindex = ifr.ifr_ifindex; + ll.sll_hatype = 0; + ll.sll_pkttype = PACKET_HOST; + ll.sll_halen = 0; + + if (bind(s, (struct sockaddr *) &ll, sizeof(ll)) < 0) { + perror("bind(netlink)"); + close(s); + close(drv->ioctl_sock); + os_free(drv); + return NULL; + } + + eloop_register_read_sock(s, wpa_driver_broadcom_event_receive, ctx, + NULL); + drv->event_sock = s; + + return drv; +} + +static void wpa_driver_broadcom_deinit(void *priv) +{ + struct wpa_driver_broadcom_data *drv = priv; + eloop_cancel_timeout(wpa_driver_broadcom_scan_timeout, drv, drv->ctx); + eloop_unregister_read_sock(drv->event_sock); + close(drv->event_sock); + close(drv->ioctl_sock); + os_free(drv); +} + +static int wpa_driver_broadcom_set_countermeasures(void *priv, + int enabled) +{ +#if 0 + struct wpa_driver_broadcom_data *drv = priv; + /* FIX: ? */ + return broadcom_ioctl(drv, WLC_SET_TKIP_COUNTERMEASURES, &enabled, + sizeof(enabled)); +#else + return 0; +#endif +} + +static int wpa_driver_broadcom_set_drop_unencrypted(void *priv, int enabled) +{ + struct wpa_driver_broadcom_data *drv = priv; + /* SET_EAP_RESTRICT, SET_WEP_RESTRICT */ + int restrict = (enabled ? 1 : 0); + + if (broadcom_ioctl(drv, WLC_SET_WEP_RESTRICT, + &restrict, sizeof(restrict)) < 0 || + broadcom_ioctl(drv, WLC_SET_EAP_RESTRICT, + &restrict, sizeof(restrict)) < 0) + return -1; + + return 0; +} + +static void wpa_driver_broadcom_scan_timeout(void *eloop_ctx, + void *timeout_ctx) +{ + wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); + wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); +} + +static int wpa_driver_broadcom_scan(void *priv, const u8 *ssid, + size_t ssid_len) +{ + struct wpa_driver_broadcom_data *drv = priv; + wlc_ssid_t wst = { 0, "" }; + + if (ssid && ssid_len > 0 && ssid_len <= sizeof(wst.SSID)) { + wst.SSID_len = ssid_len; + os_memcpy(wst.SSID, ssid, ssid_len); + } + + if (broadcom_ioctl(drv, WLC_SCAN, &wst, sizeof(wst)) < 0) + return -1; + + eloop_cancel_timeout(wpa_driver_broadcom_scan_timeout, drv, drv->ctx); + eloop_register_timeout(3, 0, wpa_driver_broadcom_scan_timeout, drv, + drv->ctx); + return 0; +} + + +static const int frequency_list[] = { + 2412, 2417, 2422, 2427, 2432, 2437, 2442, + 2447, 2452, 2457, 2462, 2467, 2472, 2484 +}; + +struct bss_ie_hdr { + u8 elem_id; + u8 len; + u8 oui[3]; + /* u8 oui_type; */ + /* u16 version; */ +} __attribute__ ((packed)); + +static int +wpa_driver_broadcom_get_scan_results(void *priv, + struct wpa_scan_result *results, + size_t max_size) +{ + struct wpa_driver_broadcom_data *drv = priv; + char *buf; + wl_scan_results_t *wsr; + wl_bss_info_t *wbi; + size_t ap_num; + + buf = os_malloc(WLC_IOCTL_MAXLEN); + if (buf == NULL) + return -1; + + wsr = (wl_scan_results_t *) buf; + + wsr->buflen = WLC_IOCTL_MAXLEN - sizeof(wsr); + wsr->version = 107; + wsr->count = 0; + + if (broadcom_ioctl(drv, WLC_SCAN_RESULTS, buf, WLC_IOCTL_MAXLEN) < 0) { + os_free(buf); + return -1; + } + + os_memset(results, 0, max_size * sizeof(struct wpa_scan_result)); + + for (ap_num = 0, wbi = wsr->bss_info; ap_num < wsr->count; ++ap_num) { + int left; + struct bss_ie_hdr *ie; + + os_memcpy(results[ap_num].bssid, &wbi->BSSID, ETH_ALEN); + os_memcpy(results[ap_num].ssid, wbi->SSID, wbi->SSID_len); + results[ap_num].ssid_len = wbi->SSID_len; + results[ap_num].freq = frequency_list[wbi->channel - 1]; + /* get ie's */ + wpa_hexdump(MSG_MSGDUMP, "BROADCOM: AP IEs", + (u8 *) wbi + sizeof(*wbi), wbi->ie_length); + ie = (struct bss_ie_hdr *) ((u8 *) wbi + sizeof(*wbi)); + for (left = wbi->ie_length; left > 0; + left -= (ie->len + 2), ie = (struct bss_ie_hdr *) + ((u8 *) ie + 2 + ie->len)) { + wpa_printf(MSG_MSGDUMP, "BROADCOM: IE: id:%x, len:%d", + ie->elem_id, ie->len); + if (ie->len >= 3) + wpa_printf(MSG_MSGDUMP, + "BROADCOM: oui:%02x%02x%02x", + ie->oui[0], ie->oui[1], ie->oui[2]); + if (ie->elem_id != 0xdd || + ie->len < 6 || + os_memcmp(ie->oui, WPA_OUI, 3) != 0) + continue; + os_memcpy(results[ap_num].wpa_ie, ie, ie->len + 2); + results[ap_num].wpa_ie_len = ie->len + 2; + break; + } + + wbi = (wl_bss_info_t *) ((u8 *) wbi + wbi->length); + } + + wpa_printf(MSG_MSGDUMP, "Received %d bytes of scan results (%lu " + "BSSes)", + wsr->buflen, (unsigned long) ap_num); + + os_free(buf); + return ap_num; +} + +static int wpa_driver_broadcom_deauthenticate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_broadcom_data *drv = priv; + wlc_deauth_t wdt; + wdt.val = reason_code; + os_memcpy(&wdt.ea, addr, sizeof wdt.ea); + wdt.res = 0x7fff; + return broadcom_ioctl(drv, WLC_DEAUTHENTICATE_WITH_REASON, &wdt, + sizeof(wdt)); +} + +static int wpa_driver_broadcom_disassociate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_broadcom_data *drv = priv; + return broadcom_ioctl(drv, WLC_DISASSOC, 0, 0); +} + +static int +wpa_driver_broadcom_associate(void *priv, + struct wpa_driver_associate_params *params) +{ + struct wpa_driver_broadcom_data *drv = priv; + wlc_ssid_t s; + int infra = 1; + int auth = 0; + int wsec = 4; + int dummy; + int wpa_auth; + + s.SSID_len = params->ssid_len; + os_memcpy(s.SSID, params->ssid, params->ssid_len); + + switch (params->pairwise_suite) { + case CIPHER_WEP40: + case CIPHER_WEP104: + wsec = 1; + break; + + case CIPHER_TKIP: + wsec = 2; + break; + + case CIPHER_CCMP: + wsec = 4; + break; + + default: + wsec = 0; + break; + } + + switch (params->key_mgmt_suite) { + case KEY_MGMT_802_1X: + wpa_auth = 1; + break; + + case KEY_MGMT_PSK: + wpa_auth = 2; + break; + + default: + wpa_auth = 255; + break; + } + + /* printf("broadcom_associate: %u %u %u\n", pairwise_suite, + * group_suite, key_mgmt_suite); + * broadcom_ioctl(ifname, WLC_GET_WSEC, &wsec, sizeof(wsec)); + * wl join uses wlc_sec_wep here, not wlc_set_wsec */ + + if (broadcom_ioctl(drv, WLC_SET_WSEC, &wsec, sizeof(wsec)) < 0 || + broadcom_ioctl(drv, WLC_SET_WPA_AUTH, &wpa_auth, + sizeof(wpa_auth)) < 0 || + broadcom_ioctl(drv, WLC_GET_WEP, &dummy, sizeof(dummy)) < 0 || + broadcom_ioctl(drv, WLC_SET_INFRA, &infra, sizeof(infra)) < 0 || + broadcom_ioctl(drv, WLC_SET_AUTH, &auth, sizeof(auth)) < 0 || + broadcom_ioctl(drv, WLC_SET_WEP, &wsec, sizeof(wsec)) < 0 || + broadcom_ioctl(drv, WLC_SET_SSID, &s, sizeof(s)) < 0) + return -1; + + return 0; +} + +const struct wpa_driver_ops wpa_driver_broadcom_ops = { + .name = "broadcom", + .desc = "Broadcom wl.o driver", + .get_bssid = wpa_driver_broadcom_get_bssid, + .get_ssid = wpa_driver_broadcom_get_ssid, + .set_wpa = wpa_driver_broadcom_set_wpa, + .set_key = wpa_driver_broadcom_set_key, + .init = wpa_driver_broadcom_init, + .deinit = wpa_driver_broadcom_deinit, + .set_countermeasures = wpa_driver_broadcom_set_countermeasures, + .set_drop_unencrypted = wpa_driver_broadcom_set_drop_unencrypted, + .scan = wpa_driver_broadcom_scan, + .get_scan_results = wpa_driver_broadcom_get_scan_results, + .deauthenticate = wpa_driver_broadcom_deauthenticate, + .disassociate = wpa_driver_broadcom_disassociate, + .associate = wpa_driver_broadcom_associate, +}; diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c new file mode 100644 index 0000000..218d913 --- /dev/null +++ b/src/drivers/driver_bsd.c @@ -0,0 +1,794 @@ +/* + * WPA Supplicant - driver interaction with BSD net80211 layer + * Copyright (c) 2004, Sam Leffler <sam@errno.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <sys/ioctl.h> + +#include "common.h" +#include "driver.h" +#include "eloop.h" +#include "ieee802_11_defs.h" + +#include <net/if.h> + +#ifdef __NetBSD__ +#include <net/if_ether.h> +#define COMPAT_FREEBSD_NET80211 +#else +#include <net/ethernet.h> +#endif + +#include <net80211/ieee80211.h> +#include <net80211/ieee80211_crypto.h> +#include <net80211/ieee80211_ioctl.h> + +struct wpa_driver_bsd_data { + int sock; /* open socket for 802.11 ioctls */ + int route; /* routing socket for events */ + char ifname[IFNAMSIZ+1]; /* interface name */ + unsigned int ifindex; /* interface index */ + void *ctx; + int prev_roaming; /* roaming state to restore on deinit */ + int prev_privacy; /* privacy state to restore on deinit */ + int prev_wpa; /* wpa state to restore on deinit */ +}; + +static int +set80211var(struct wpa_driver_bsd_data *drv, int op, const void *arg, int arg_len) +{ + struct ieee80211req ireq; + + os_memset(&ireq, 0, sizeof(ireq)); + os_strlcpy(ireq.i_name, drv->ifname, IFNAMSIZ); + ireq.i_type = op; + ireq.i_len = arg_len; + ireq.i_data = (void *) arg; + + if (ioctl(drv->sock, SIOCS80211, &ireq) < 0) { + fprintf(stderr, "ioctl[SIOCS80211, op %u, len %u]: %s\n", + op, arg_len, strerror(errno)); + return -1; + } + return 0; +} + +static int +get80211var(struct wpa_driver_bsd_data *drv, int op, void *arg, int arg_len) +{ + struct ieee80211req ireq; + + os_memset(&ireq, 0, sizeof(ireq)); + os_strlcpy(ireq.i_name, drv->ifname, IFNAMSIZ); + ireq.i_type = op; + ireq.i_len = arg_len; + ireq.i_data = arg; + + if (ioctl(drv->sock, SIOCG80211, &ireq) < 0) { + fprintf(stderr, "ioctl[SIOCG80211, op %u, len %u]: %s\n", + op, arg_len, strerror(errno)); + return -1; + } + return ireq.i_len; +} + +static int +set80211param(struct wpa_driver_bsd_data *drv, int op, int arg) +{ + struct ieee80211req ireq; + + os_memset(&ireq, 0, sizeof(ireq)); + os_strlcpy(ireq.i_name, drv->ifname, IFNAMSIZ); + ireq.i_type = op; + ireq.i_val = arg; + + if (ioctl(drv->sock, SIOCS80211, &ireq) < 0) { + fprintf(stderr, "ioctl[SIOCS80211, op %u, arg 0x%x]: %s\n", + op, arg, strerror(errno)); + return -1; + } + return 0; +} + +static int +get80211param(struct wpa_driver_bsd_data *drv, int op) +{ + struct ieee80211req ireq; + + os_memset(&ireq, 0, sizeof(ireq)); + os_strlcpy(ireq.i_name, drv->ifname, IFNAMSIZ); + ireq.i_type = op; + + if (ioctl(drv->sock, SIOCG80211, &ireq) < 0) { + fprintf(stderr, "ioctl[SIOCG80211, op %u]: %s\n", + op, strerror(errno)); + return -1; + } + return ireq.i_val; +} + +static int +getifflags(struct wpa_driver_bsd_data *drv, int *flags) +{ + struct ifreq ifr; + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name)); + if (ioctl(drv->sock, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) { + perror("SIOCGIFFLAGS"); + return errno; + } + *flags = ifr.ifr_flags & 0xffff; + return 0; +} + +static int +setifflags(struct wpa_driver_bsd_data *drv, int flags) +{ + struct ifreq ifr; + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name)); + ifr.ifr_flags = flags & 0xffff; + if (ioctl(drv->sock, SIOCSIFFLAGS, (caddr_t)&ifr) < 0) { + perror("SIOCSIFFLAGS"); + return errno; + } + return 0; +} + +static int +wpa_driver_bsd_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_bsd_data *drv = priv; + + return get80211var(drv, IEEE80211_IOC_BSSID, + bssid, IEEE80211_ADDR_LEN) < 0 ? -1 : 0; +} + +#if 0 +static int +wpa_driver_bsd_set_bssid(void *priv, const char *bssid) +{ + struct wpa_driver_bsd_data *drv = priv; + + return set80211var(drv, IEEE80211_IOC_BSSID, + bssid, IEEE80211_ADDR_LEN); +} +#endif + +static int +wpa_driver_bsd_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_bsd_data *drv = priv; + + return get80211var(drv, IEEE80211_IOC_SSID, + ssid, IEEE80211_NWID_LEN); +} + +static int +wpa_driver_bsd_set_ssid(void *priv, const u8 *ssid, + size_t ssid_len) +{ + struct wpa_driver_bsd_data *drv = priv; + + return set80211var(drv, IEEE80211_IOC_SSID, ssid, ssid_len); +} + +static int +wpa_driver_bsd_set_wpa_ie(struct wpa_driver_bsd_data *drv, + const u8 *wpa_ie, size_t wpa_ie_len) +{ + return set80211var(drv, IEEE80211_IOC_OPTIE, wpa_ie, wpa_ie_len); +} + +static int +wpa_driver_bsd_set_wpa_internal(void *priv, int wpa, int privacy) +{ + struct wpa_driver_bsd_data *drv = priv; + int ret = 0; + + wpa_printf(MSG_DEBUG, "%s: wpa=%d privacy=%d", + __FUNCTION__, wpa, privacy); + + if (!wpa && wpa_driver_bsd_set_wpa_ie(drv, NULL, 0) < 0) + ret = -1; + if (set80211param(drv, IEEE80211_IOC_PRIVACY, privacy) < 0) + ret = -1; + if (set80211param(drv, IEEE80211_IOC_WPA, wpa) < 0) + ret = -1; + + return ret; +} + +static int +wpa_driver_bsd_set_wpa(void *priv, int enabled) +{ + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + + return wpa_driver_bsd_set_wpa_internal(priv, enabled ? 3 : 0, enabled); +} + +static int +wpa_driver_bsd_del_key(struct wpa_driver_bsd_data *drv, int key_idx, + const unsigned char *addr) +{ + struct ieee80211req_del_key wk; + + os_memset(&wk, 0, sizeof(wk)); + if (addr != NULL && + bcmp(addr, "\xff\xff\xff\xff\xff\xff", IEEE80211_ADDR_LEN) != 0) { + struct ether_addr ea; + + os_memcpy(&ea, addr, IEEE80211_ADDR_LEN); + wpa_printf(MSG_DEBUG, "%s: addr=%s keyidx=%d", + __func__, ether_ntoa(&ea), key_idx); + os_memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN); + wk.idk_keyix = (uint8_t) IEEE80211_KEYIX_NONE; + } else { + wpa_printf(MSG_DEBUG, "%s: keyidx=%d", __func__, key_idx); + wk.idk_keyix = key_idx; + } + return set80211var(drv, IEEE80211_IOC_DELKEY, &wk, sizeof(wk)); +} + +static int +wpa_driver_bsd_set_key(void *priv, wpa_alg alg, + const unsigned char *addr, int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + struct wpa_driver_bsd_data *drv = priv; + struct ieee80211req_key wk; + struct ether_addr ea; + char *alg_name; + u_int8_t cipher; + + if (alg == WPA_ALG_NONE) + return wpa_driver_bsd_del_key(drv, key_idx, addr); + + switch (alg) { + case WPA_ALG_WEP: + alg_name = "WEP"; + cipher = IEEE80211_CIPHER_WEP; + break; + case WPA_ALG_TKIP: + alg_name = "TKIP"; + cipher = IEEE80211_CIPHER_TKIP; + break; + case WPA_ALG_CCMP: + alg_name = "CCMP"; + cipher = IEEE80211_CIPHER_AES_CCM; + break; + default: + wpa_printf(MSG_DEBUG, "%s: unknown/unsupported algorithm %d", + __func__, alg); + return -1; + } + + os_memcpy(&ea, addr, IEEE80211_ADDR_LEN); + wpa_printf(MSG_DEBUG, + "%s: alg=%s addr=%s key_idx=%d set_tx=%d seq_len=%zu key_len=%zu", + __func__, alg_name, ether_ntoa(&ea), key_idx, set_tx, + seq_len, key_len); + + if (seq_len > sizeof(u_int64_t)) { + wpa_printf(MSG_DEBUG, "%s: seq_len %zu too big", + __func__, seq_len); + return -2; + } + if (key_len > sizeof(wk.ik_keydata)) { + wpa_printf(MSG_DEBUG, "%s: key length %zu too big", + __func__, key_len); + return -3; + } + + os_memset(&wk, 0, sizeof(wk)); + wk.ik_type = cipher; + wk.ik_flags = IEEE80211_KEY_RECV; + if (set_tx) + wk.ik_flags |= IEEE80211_KEY_XMIT; + os_memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); + /* + * Deduce whether group/global or unicast key by checking + * the address (yech). Note also that we can only mark global + * keys default; doing this for a unicast key is an error. + */ + if (bcmp(addr, "\xff\xff\xff\xff\xff\xff", IEEE80211_ADDR_LEN) == 0) { + wk.ik_flags |= IEEE80211_KEY_GROUP; + wk.ik_keyix = key_idx; + } else { + wk.ik_keyix = (key_idx == 0 ? IEEE80211_KEYIX_NONE : key_idx); + } + if (wk.ik_keyix != IEEE80211_KEYIX_NONE && set_tx) + wk.ik_flags |= IEEE80211_KEY_DEFAULT; + wk.ik_keylen = key_len; + os_memcpy(&wk.ik_keyrsc, seq, seq_len); + os_memcpy(wk.ik_keydata, key, key_len); + + return set80211var(drv, IEEE80211_IOC_WPAKEY, &wk, sizeof(wk)); +} + +static int +wpa_driver_bsd_set_countermeasures(void *priv, int enabled) +{ + struct wpa_driver_bsd_data *drv = priv; + + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); + return set80211param(drv, IEEE80211_IOC_COUNTERMEASURES, enabled); +} + + +static int +wpa_driver_bsd_set_drop_unencrypted(void *priv, int enabled) +{ + struct wpa_driver_bsd_data *drv = priv; + + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); + return set80211param(drv, IEEE80211_IOC_DROPUNENCRYPTED, enabled); +} + +static int +wpa_driver_bsd_deauthenticate(void *priv, const u8 *addr, int reason_code) +{ + struct wpa_driver_bsd_data *drv = priv; + struct ieee80211req_mlme mlme; + + wpa_printf(MSG_DEBUG, "%s", __func__); + os_memset(&mlme, 0, sizeof(mlme)); + mlme.im_op = IEEE80211_MLME_DEAUTH; + mlme.im_reason = reason_code; + os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + return set80211var(drv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme)); +} + +static int +wpa_driver_bsd_disassociate(void *priv, const u8 *addr, int reason_code) +{ + struct wpa_driver_bsd_data *drv = priv; + struct ieee80211req_mlme mlme; + + wpa_printf(MSG_DEBUG, "%s", __func__); + os_memset(&mlme, 0, sizeof(mlme)); + mlme.im_op = IEEE80211_MLME_DISASSOC; + mlme.im_reason = reason_code; + os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + return set80211var(drv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme)); +} + +static int +wpa_driver_bsd_associate(void *priv, struct wpa_driver_associate_params *params) +{ + struct wpa_driver_bsd_data *drv = priv; + struct ieee80211req_mlme mlme; + int privacy; + + wpa_printf(MSG_DEBUG, + "%s: ssid '%.*s' wpa ie len %u pairwise %u group %u key mgmt %u" + , __func__ + , params->ssid_len, params->ssid + , params->wpa_ie_len + , params->pairwise_suite + , params->group_suite + , params->key_mgmt_suite + ); + + /* XXX error handling is wrong but unclear what to do... */ + if (wpa_driver_bsd_set_wpa_ie(drv, params->wpa_ie, params->wpa_ie_len) < 0) + return -1; + + privacy = !(params->pairwise_suite == CIPHER_NONE && + params->group_suite == CIPHER_NONE && + params->key_mgmt_suite == KEY_MGMT_NONE && + params->wpa_ie_len == 0); + wpa_printf(MSG_DEBUG, "%s: set PRIVACY %u", __func__, privacy); + + if (set80211param(drv, IEEE80211_IOC_PRIVACY, privacy) < 0) + return -1; + + if (params->wpa_ie_len && + set80211param(drv, IEEE80211_IOC_WPA, + params->wpa_ie[0] == WLAN_EID_RSN ? 2 : 1) < 0) + return -1; + + os_memset(&mlme, 0, sizeof(mlme)); + mlme.im_op = IEEE80211_MLME_ASSOC; + if (params->ssid != NULL) + os_memcpy(mlme.im_ssid, params->ssid, params->ssid_len); + mlme.im_ssid_len = params->ssid_len; + if (params->bssid != NULL) + os_memcpy(mlme.im_macaddr, params->bssid, IEEE80211_ADDR_LEN); + if (set80211var(drv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme)) < 0) + return -1; + return 0; +} + +static int +wpa_driver_bsd_set_auth_alg(void *priv, int auth_alg) +{ + struct wpa_driver_bsd_data *drv = priv; + int authmode; + + if ((auth_alg & AUTH_ALG_OPEN_SYSTEM) && + (auth_alg & AUTH_ALG_SHARED_KEY)) + authmode = IEEE80211_AUTH_AUTO; + else if (auth_alg & AUTH_ALG_SHARED_KEY) + authmode = IEEE80211_AUTH_SHARED; + else + authmode = IEEE80211_AUTH_OPEN; + + return set80211param(drv, IEEE80211_IOC_AUTHMODE, authmode); +} + +static int +wpa_driver_bsd_scan(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_bsd_data *drv = priv; + int flags; + + /* NB: interface must be marked UP to do a scan */ + if (getifflags(drv, &flags) != 0 || setifflags(drv, flags | IFF_UP) != 0) + return -1; + + /* set desired ssid before scan */ + if (wpa_driver_bsd_set_ssid(drv, ssid, ssid_len) < 0) + return -1; + + /* NB: net80211 delivers a scan complete event so no need to poll */ + return set80211param(drv, IEEE80211_IOC_SCAN_REQ, 0); +} + +#include <net/route.h> +#if __FreeBSD__ +#include <net80211/ieee80211_freebsd.h> +#endif +#if __NetBSD__ +#include <net80211/ieee80211_netbsd.h> +#endif + +static void +wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx) +{ + struct wpa_driver_bsd_data *drv = sock_ctx; + char buf[2048]; + struct if_announcemsghdr *ifan; + struct if_msghdr *ifm; + struct rt_msghdr *rtm; + union wpa_event_data event; + struct ieee80211_michael_event *mic; + int n; + + n = read(sock, buf, sizeof(buf)); + if (n < 0) { + if (errno != EINTR && errno != EAGAIN) + perror("read(PF_ROUTE)"); + return; + } + + rtm = (struct rt_msghdr *) buf; + if (rtm->rtm_version != RTM_VERSION) { + wpa_printf(MSG_DEBUG, "Routing message version %d not " + "understood\n", rtm->rtm_version); + return; + } + os_memset(&event, 0, sizeof(event)); + switch (rtm->rtm_type) { + case RTM_IFANNOUNCE: + ifan = (struct if_announcemsghdr *) rtm; + if (ifan->ifan_index != drv->ifindex) + break; + strlcpy(event.interface_status.ifname, drv->ifname, + sizeof(event.interface_status.ifname)); + switch (ifan->ifan_what) { + case IFAN_DEPARTURE: + event.interface_status.ievent = EVENT_INTERFACE_REMOVED; + default: + return; + } + wpa_printf(MSG_DEBUG, "RTM_IFANNOUNCE: Interface '%s' %s", + event.interface_status.ifname, + ifan->ifan_what == IFAN_DEPARTURE ? + "removed" : "added"); + wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event); + break; + case RTM_IEEE80211: + ifan = (struct if_announcemsghdr *) rtm; + if (ifan->ifan_index != drv->ifindex) + break; + switch (ifan->ifan_what) { + case RTM_IEEE80211_ASSOC: + case RTM_IEEE80211_REASSOC: + wpa_supplicant_event(ctx, EVENT_ASSOC, NULL); + break; + case RTM_IEEE80211_DISASSOC: + wpa_supplicant_event(ctx, EVENT_DISASSOC, NULL); + break; + case RTM_IEEE80211_SCAN: + wpa_supplicant_event(ctx, EVENT_SCAN_RESULTS, NULL); + break; + case RTM_IEEE80211_REPLAY: + /* ignore */ + break; + case RTM_IEEE80211_MICHAEL: + mic = (struct ieee80211_michael_event *) &ifan[1]; + wpa_printf(MSG_DEBUG, + "Michael MIC failure wireless event: " + "keyix=%u src_addr=" MACSTR, mic->iev_keyix, + MAC2STR(mic->iev_src)); + + os_memset(&event, 0, sizeof(event)); + event.michael_mic_failure.unicast = + !IEEE80211_IS_MULTICAST(mic->iev_dst); + wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, + &event); + break; + } + break; + case RTM_IFINFO: + ifm = (struct if_msghdr *) rtm; + if (ifm->ifm_index != drv->ifindex) + break; + if ((rtm->rtm_flags & RTF_UP) == 0) { + strlcpy(event.interface_status.ifname, drv->ifname, + sizeof(event.interface_status.ifname)); + event.interface_status.ievent = EVENT_INTERFACE_REMOVED; + wpa_printf(MSG_DEBUG, "RTM_IFINFO: Interface '%s' DOWN", + event.interface_status.ifname); + wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event); + } + break; + } +} + +/* Compare function for sorting scan results. Return >0 if @b is consider + * better. */ +static int +wpa_scan_result_compar(const void *a, const void *b) +{ + const struct wpa_scan_result *wa = a; + const struct wpa_scan_result *wb = b; + + /* WPA/WPA2 support preferred */ + if ((wb->wpa_ie_len || wb->rsn_ie_len) && + !(wa->wpa_ie_len || wa->rsn_ie_len)) + return 1; + if (!(wb->wpa_ie_len || wb->rsn_ie_len) && + (wa->wpa_ie_len || wa->rsn_ie_len)) + return -1; + + /* privacy support preferred */ + if ((wa->caps & IEEE80211_CAPINFO_PRIVACY) && + (wb->caps & IEEE80211_CAPINFO_PRIVACY) == 0) + return 1; + if ((wa->caps & IEEE80211_CAPINFO_PRIVACY) == 0 && + (wb->caps & IEEE80211_CAPINFO_PRIVACY)) + return -1; + + /* best/max rate preferred if signal level close enough XXX */ + if (wa->maxrate != wb->maxrate && abs(wb->level - wa->level) < 5) + return wb->maxrate - wa->maxrate; + + /* use freq for channel preference */ + + /* all things being equal, use signal level */ + return wb->level - wa->level; +} + +static int +getmaxrate(uint8_t rates[15], uint8_t nrates) +{ + int i, maxrate = -1; + + for (i = 0; i < nrates; i++) { + int rate = rates[i] & IEEE80211_RATE_VAL; + if (rate > maxrate) + rate = maxrate; + } + return maxrate; +} + +/* unalligned little endian access */ +#define LE_READ_4(p) \ + ((u_int32_t) \ + ((((const u_int8_t *)(p))[0] ) | \ + (((const u_int8_t *)(p))[1] << 8) | \ + (((const u_int8_t *)(p))[2] << 16) | \ + (((const u_int8_t *)(p))[3] << 24))) + +static int __inline +iswpaoui(const u_int8_t *frm) +{ + return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI); +} + +static int +wpa_driver_bsd_get_scan_results(void *priv, + struct wpa_scan_result *results, + size_t max_size) +{ +#define min(a,b) ((a)>(b)?(b):(a)) + struct wpa_driver_bsd_data *drv = priv; + uint8_t buf[24*1024]; + uint8_t *cp, *vp; + struct ieee80211req_scan_result *sr; + struct wpa_scan_result *wsr; + int len, ielen; + + os_memset(results, 0, max_size * sizeof(struct wpa_scan_result)); + + len = get80211var(drv, IEEE80211_IOC_SCAN_RESULTS, buf, sizeof(buf)); + if (len < 0) + return -1; + cp = buf; + wsr = results; + while (len >= sizeof(struct ieee80211req_scan_result)) { + sr = (struct ieee80211req_scan_result *) cp; + os_memcpy(wsr->bssid, sr->isr_bssid, IEEE80211_ADDR_LEN); + wsr->ssid_len = sr->isr_ssid_len; + wsr->freq = sr->isr_freq; + wsr->noise = sr->isr_noise; + wsr->qual = sr->isr_rssi; + wsr->level = 0; /* XXX? */ + wsr->caps = sr->isr_capinfo; + wsr->maxrate = getmaxrate(sr->isr_rates, sr->isr_nrates); + vp = (u_int8_t *)(sr+1); + os_memcpy(wsr->ssid, vp, sr->isr_ssid_len); + if (sr->isr_ie_len > 0) { + vp += sr->isr_ssid_len; + ielen = sr->isr_ie_len; + while (ielen > 0) { + switch (vp[0]) { + case IEEE80211_ELEMID_VENDOR: + if (!iswpaoui(vp)) + break; + wsr->wpa_ie_len = + min(2+vp[1], SSID_MAX_WPA_IE_LEN); + os_memcpy(wsr->wpa_ie, vp, + wsr->wpa_ie_len); + break; + case IEEE80211_ELEMID_RSN: + wsr->rsn_ie_len = + min(2+vp[1], SSID_MAX_WPA_IE_LEN); + os_memcpy(wsr->rsn_ie, vp, + wsr->rsn_ie_len); + break; + } + ielen -= 2+vp[1]; + vp += 2+vp[1]; + } + } + + cp += sr->isr_len, len -= sr->isr_len; + wsr++; + } + qsort(results, wsr - results, sizeof(struct wpa_scan_result), + wpa_scan_result_compar); + + wpa_printf(MSG_DEBUG, "Received %d bytes of scan results (%d BSSes)", + len, wsr - results); + + return wsr - results; +#undef min +} + +static void * +wpa_driver_bsd_init(void *ctx, const char *ifname) +{ +#define GETPARAM(drv, param, v) \ + (((v) = get80211param(drv, param)) != -1) + struct wpa_driver_bsd_data *drv; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + /* + * NB: We require the interface name be mappable to an index. + * This implies we do not support having wpa_supplicant + * wait for an interface to appear. This seems ok; that + * doesn't belong here; it's really the job of devd. + */ + drv->ifindex = if_nametoindex(ifname); + if (drv->ifindex == 0) { + wpa_printf(MSG_DEBUG, "%s: interface %s does not exist", + __func__, ifname); + goto fail1; + } + drv->sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->sock < 0) + goto fail1; + drv->route = socket(PF_ROUTE, SOCK_RAW, 0); + if (drv->route < 0) + goto fail; + eloop_register_read_sock(drv->route, + wpa_driver_bsd_event_receive, ctx, drv); + + drv->ctx = ctx; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + + if (!GETPARAM(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming)) { + wpa_printf(MSG_DEBUG, "%s: failed to get roaming state: %s", + __func__, strerror(errno)); + goto fail; + } + if (!GETPARAM(drv, IEEE80211_IOC_PRIVACY, drv->prev_privacy)) { + wpa_printf(MSG_DEBUG, "%s: failed to get privacy state: %s", + __func__, strerror(errno)); + goto fail; + } + if (!GETPARAM(drv, IEEE80211_IOC_WPA, drv->prev_wpa)) { + wpa_printf(MSG_DEBUG, "%s: failed to get wpa state: %s", + __func__, strerror(errno)); + goto fail; + } + if (set80211param(drv, IEEE80211_IOC_ROAMING, IEEE80211_ROAMING_MANUAL) < 0) { + wpa_printf(MSG_DEBUG, "%s: failed to set wpa_supplicant-based " + "roaming: %s", __func__, strerror(errno)); + goto fail; + } + + if (set80211param(drv, IEEE80211_IOC_WPA, 1+2) < 0) { + wpa_printf(MSG_DEBUG, "%s: failed to enable WPA support %s", + __func__, strerror(errno)); + goto fail; + } + + return drv; +fail: + close(drv->sock); +fail1: + os_free(drv); + return NULL; +#undef GETPARAM +} + +static void +wpa_driver_bsd_deinit(void *priv) +{ + struct wpa_driver_bsd_data *drv = priv; + int flags; + + eloop_unregister_read_sock(drv->route); + + /* NB: mark interface down */ + if (getifflags(drv, &flags) == 0) + (void) setifflags(drv, flags &~ IFF_UP); + + wpa_driver_bsd_set_wpa_internal(drv, drv->prev_wpa, drv->prev_privacy); + if (set80211param(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming) < 0) + wpa_printf(MSG_DEBUG, "%s: failed to restore roaming state", + __func__); + + (void) close(drv->route); /* ioctl socket */ + (void) close(drv->sock); /* event socket */ + os_free(drv); +} + + +const struct wpa_driver_ops wpa_driver_bsd_ops = { + .name = "bsd", + .desc = "BSD 802.11 support (Atheros, etc.)", + .init = wpa_driver_bsd_init, + .deinit = wpa_driver_bsd_deinit, + .get_bssid = wpa_driver_bsd_get_bssid, + .get_ssid = wpa_driver_bsd_get_ssid, + .set_wpa = wpa_driver_bsd_set_wpa, + .set_key = wpa_driver_bsd_set_key, + .set_countermeasures = wpa_driver_bsd_set_countermeasures, + .set_drop_unencrypted = wpa_driver_bsd_set_drop_unencrypted, + .scan = wpa_driver_bsd_scan, + .get_scan_results = wpa_driver_bsd_get_scan_results, + .deauthenticate = wpa_driver_bsd_deauthenticate, + .disassociate = wpa_driver_bsd_disassociate, + .associate = wpa_driver_bsd_associate, + .set_auth_alg = wpa_driver_bsd_set_auth_alg, +}; diff --git a/src/drivers/driver_hostap.c b/src/drivers/driver_hostap.c new file mode 100644 index 0000000..84ef3bd --- /dev/null +++ b/src/drivers/driver_hostap.c @@ -0,0 +1,513 @@ +/* + * WPA Supplicant - driver interaction with Linux Host AP driver + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <sys/ioctl.h> + +#include "wireless_copy.h" +#include "common.h" +#include "driver.h" +#include "driver_wext.h" +#include "eloop.h" +#include "driver_hostap.h" + + +struct wpa_driver_hostap_data { + void *wext; /* private data for driver_wext */ + void *ctx; + char ifname[IFNAMSIZ + 1]; + int sock; + int current_mode; /* infra/adhoc */ +}; + + +static int hostapd_ioctl(struct wpa_driver_hostap_data *drv, + struct prism2_hostapd_param *param, + int len, int show_err) +{ + struct iwreq iwr; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) param; + iwr.u.data.length = len; + + if (ioctl(drv->sock, PRISM2_IOCTL_HOSTAPD, &iwr) < 0) { + int ret = errno; + if (show_err) + perror("ioctl[PRISM2_IOCTL_HOSTAPD]"); + return ret; + } + + return 0; +} + + +static int wpa_driver_hostap_set_wpa_ie(struct wpa_driver_hostap_data *drv, + const u8 *wpa_ie, size_t wpa_ie_len) +{ + struct prism2_hostapd_param *param; + int res; + size_t blen = PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN + wpa_ie_len; + if (blen < sizeof(*param)) + blen = sizeof(*param); + + param = os_zalloc(blen); + if (param == NULL) + return -1; + + param->cmd = PRISM2_HOSTAPD_SET_GENERIC_ELEMENT; + param->u.generic_elem.len = wpa_ie_len; + os_memcpy(param->u.generic_elem.data, wpa_ie, wpa_ie_len); + res = hostapd_ioctl(drv, param, blen, 1); + + os_free(param); + + return res; +} + + +static int prism2param(struct wpa_driver_hostap_data *drv, int param, + int value) +{ + struct iwreq iwr; + int *i, ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + i = (int *) iwr.u.name; + *i++ = param; + *i++ = value; + + if (ioctl(drv->sock, PRISM2_IOCTL_PRISM2_PARAM, &iwr) < 0) { + perror("ioctl[PRISM2_IOCTL_PRISM2_PARAM]"); + ret = -1; + } + return ret; +} + + +static int wpa_driver_hostap_set_wpa(void *priv, int enabled) +{ + struct wpa_driver_hostap_data *drv = priv; + int ret = 0; + + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + + if (!enabled && wpa_driver_hostap_set_wpa_ie(drv, NULL, 0) < 0) + ret = -1; + if (prism2param(drv, PRISM2_PARAM_HOST_ROAMING, enabled ? 2 : 0) < 0) + ret = -1; + if (prism2param(drv, PRISM2_PARAM_WPA, enabled) < 0) + ret = -1; + + return ret; +} + + +static void show_set_key_error(struct prism2_hostapd_param *param) +{ + switch (param->u.crypt.err) { + case HOSTAP_CRYPT_ERR_UNKNOWN_ALG: + wpa_printf(MSG_INFO, "Unknown algorithm '%s'.", + param->u.crypt.alg); + wpa_printf(MSG_INFO, "You may need to load kernel module to " + "register that algorithm."); + wpa_printf(MSG_INFO, "E.g., 'modprobe hostap_crypt_wep' for " + "WEP."); + break; + case HOSTAP_CRYPT_ERR_UNKNOWN_ADDR: + wpa_printf(MSG_INFO, "Unknown address " MACSTR ".", + MAC2STR(param->sta_addr)); + break; + case HOSTAP_CRYPT_ERR_CRYPT_INIT_FAILED: + wpa_printf(MSG_INFO, "Crypt algorithm initialization failed."); + break; + case HOSTAP_CRYPT_ERR_KEY_SET_FAILED: + wpa_printf(MSG_INFO, "Key setting failed."); + break; + case HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED: + wpa_printf(MSG_INFO, "TX key index setting failed."); + break; + case HOSTAP_CRYPT_ERR_CARD_CONF_FAILED: + wpa_printf(MSG_INFO, "Card configuration failed."); + break; + } +} + + +static int wpa_driver_hostap_set_key(void *priv, wpa_alg alg, + const u8 *addr, int key_idx, + int set_tx, const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + struct wpa_driver_hostap_data *drv = priv; + struct prism2_hostapd_param *param; + u8 *buf; + size_t blen; + int ret = 0; + char *alg_name; + + switch (alg) { + case WPA_ALG_NONE: + alg_name = "none"; + break; + case WPA_ALG_WEP: + alg_name = "WEP"; + break; + case WPA_ALG_TKIP: + alg_name = "TKIP"; + break; + case WPA_ALG_CCMP: + alg_name = "CCMP"; + break; + default: + return -1; + } + + wpa_printf(MSG_DEBUG, "%s: alg=%s key_idx=%d set_tx=%d seq_len=%lu " + "key_len=%lu", __FUNCTION__, alg_name, key_idx, set_tx, + (unsigned long) seq_len, (unsigned long) key_len); + + if (seq_len > 8) + return -2; + + blen = sizeof(*param) + key_len; + buf = os_zalloc(blen); + if (buf == NULL) + return -1; + + param = (struct prism2_hostapd_param *) buf; + param->cmd = PRISM2_SET_ENCRYPTION; + /* TODO: In theory, STA in client mode can use five keys; four default + * keys for receiving (with keyidx 0..3) and one individual key for + * both transmitting and receiving (keyidx 0) _unicast_ packets. Now, + * keyidx 0 is reserved for this unicast use and default keys can only + * use keyidx 1..3 (i.e., default key with keyidx 0 is not supported). + * This should be fine for more or less all cases, but for completeness + * sake, the driver could be enhanced to support the missing key. */ +#if 0 + if (addr == NULL) + os_memset(param->sta_addr, 0xff, ETH_ALEN); + else + os_memcpy(param->sta_addr, addr, ETH_ALEN); +#else + os_memset(param->sta_addr, 0xff, ETH_ALEN); +#endif + os_strlcpy((char *) param->u.crypt.alg, alg_name, + HOSTAP_CRYPT_ALG_NAME_LEN); + param->u.crypt.flags = set_tx ? HOSTAP_CRYPT_FLAG_SET_TX_KEY : 0; + param->u.crypt.idx = key_idx; + os_memcpy(param->u.crypt.seq, seq, seq_len); + param->u.crypt.key_len = key_len; + os_memcpy((u8 *) (param + 1), key, key_len); + + if (hostapd_ioctl(drv, param, blen, 1)) { + wpa_printf(MSG_WARNING, "Failed to set encryption."); + show_set_key_error(param); + ret = -1; + } + os_free(buf); + + return ret; +} + + +static int wpa_driver_hostap_set_countermeasures(void *priv, int enabled) +{ + struct wpa_driver_hostap_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + return prism2param(drv, PRISM2_PARAM_TKIP_COUNTERMEASURES, enabled); +} + + +static int wpa_driver_hostap_set_drop_unencrypted(void *priv, int enabled) +{ + struct wpa_driver_hostap_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + return prism2param(drv, PRISM2_PARAM_DROP_UNENCRYPTED, enabled); +} + + +static int wpa_driver_hostap_reset(struct wpa_driver_hostap_data *drv, + int type) +{ + struct iwreq iwr; + int *i, ret = 0; + + wpa_printf(MSG_DEBUG, "%s: type=%d", __FUNCTION__, type); + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + i = (int *) iwr.u.name; + *i++ = type; + + if (ioctl(drv->sock, PRISM2_IOCTL_RESET, &iwr) < 0) { + perror("ioctl[PRISM2_IOCTL_RESET]"); + ret = -1; + } + return ret; +} + + +static int wpa_driver_hostap_mlme(struct wpa_driver_hostap_data *drv, + const u8 *addr, int cmd, int reason_code) +{ + struct prism2_hostapd_param param; + int ret; + + /* There does not seem to be a better way of deauthenticating or + * disassociating with Prism2/2.5/3 than sending the management frame + * and then resetting the Port0 to make sure both the AP and the STA + * end up in disconnected state. */ + os_memset(¶m, 0, sizeof(param)); + param.cmd = PRISM2_HOSTAPD_MLME; + os_memcpy(param.sta_addr, addr, ETH_ALEN); + param.u.mlme.cmd = cmd; + param.u.mlme.reason_code = reason_code; + ret = hostapd_ioctl(drv, ¶m, sizeof(param), 1); + if (ret == 0) { + os_sleep(0, 100000); + ret = wpa_driver_hostap_reset(drv, 2); + } + return ret; +} + + +static int wpa_driver_hostap_deauthenticate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_hostap_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + return wpa_driver_hostap_mlme(drv, addr, MLME_STA_DEAUTH, + reason_code); +} + + +static int wpa_driver_hostap_disassociate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_hostap_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + return wpa_driver_hostap_mlme(drv, addr, MLME_STA_DISASSOC, + reason_code); +} + + +static int +wpa_driver_hostap_associate(void *priv, + struct wpa_driver_associate_params *params) +{ + struct wpa_driver_hostap_data *drv = priv; + int ret = 0; + int allow_unencrypted_eapol; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + if (params->mode != drv->current_mode) { + /* At the moment, Host AP driver requires host_roaming=2 for + * infrastructure mode and host_roaming=0 for adhoc. */ + if (prism2param(drv, PRISM2_PARAM_HOST_ROAMING, + params->mode == IEEE80211_MODE_IBSS ? 0 : 2) < + 0) { + wpa_printf(MSG_DEBUG, "%s: failed to set host_roaming", + __func__); + } + drv->current_mode = params->mode; + } + + if (prism2param(drv, PRISM2_PARAM_PRIVACY_INVOKED, + params->key_mgmt_suite != KEY_MGMT_NONE) < 0) + ret = -1; + if (wpa_driver_hostap_set_wpa_ie(drv, params->wpa_ie, + params->wpa_ie_len) < 0) + ret = -1; + if (wpa_driver_wext_set_mode(drv->wext, params->mode) < 0) + ret = -1; + if (params->freq && + wpa_driver_wext_set_freq(drv->wext, params->freq) < 0) + ret = -1; + if (wpa_driver_wext_set_ssid(drv->wext, params->ssid, params->ssid_len) + < 0) + ret = -1; + if (wpa_driver_wext_set_bssid(drv->wext, params->bssid) < 0) + ret = -1; + + /* Allow unencrypted EAPOL messages even if pairwise keys are set when + * not using WPA. IEEE 802.1X specifies that these frames are not + * encrypted, but WPA encrypts them when pairwise keys are in use. */ + if (params->key_mgmt_suite == KEY_MGMT_802_1X || + params->key_mgmt_suite == KEY_MGMT_PSK) + allow_unencrypted_eapol = 0; + else + allow_unencrypted_eapol = 1; + + if (prism2param(drv, PRISM2_PARAM_IEEE_802_1X, + allow_unencrypted_eapol) < 0) { + wpa_printf(MSG_DEBUG, "hostap: Failed to configure " + "ieee_802_1x param"); + /* Ignore this error.. driver_hostap.c can also be used with + * other drivers that do not support this prism2_param. */ + } + + return ret; +} + + +static int wpa_driver_hostap_scan(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_hostap_data *drv = priv; + struct prism2_hostapd_param param; + int ret; + + if (ssid == NULL) { + /* Use standard Linux Wireless Extensions ioctl if possible + * because some drivers using hostap code in wpa_supplicant + * might not support Host AP specific scan request (with SSID + * info). */ + return wpa_driver_wext_scan(drv->wext, ssid, ssid_len); + } + + if (ssid_len > 32) + ssid_len = 32; + + os_memset(¶m, 0, sizeof(param)); + param.cmd = PRISM2_HOSTAPD_SCAN_REQ; + param.u.scan_req.ssid_len = ssid_len; + os_memcpy(param.u.scan_req.ssid, ssid, ssid_len); + ret = hostapd_ioctl(drv, ¶m, sizeof(param), 1); + + /* Not all drivers generate "scan completed" wireless event, so try to + * read results after a timeout. */ + eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv->wext, + drv->ctx); + eloop_register_timeout(3, 0, wpa_driver_wext_scan_timeout, drv->wext, + drv->ctx); + + return ret; +} + + +static int wpa_driver_hostap_set_auth_alg(void *priv, int auth_alg) +{ + struct wpa_driver_hostap_data *drv = priv; + int algs = 0; + + if (auth_alg & AUTH_ALG_OPEN_SYSTEM) + algs |= 1; + if (auth_alg & AUTH_ALG_SHARED_KEY) + algs |= 2; + if (auth_alg & AUTH_ALG_LEAP) + algs |= 4; + if (algs == 0) + algs = 1; /* at least one algorithm should be set */ + + return prism2param(drv, PRISM2_PARAM_AP_AUTH_ALGS, algs); +} + + +static int wpa_driver_hostap_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_hostap_data *drv = priv; + return wpa_driver_wext_get_bssid(drv->wext, bssid); +} + + +static int wpa_driver_hostap_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_hostap_data *drv = priv; + return wpa_driver_wext_get_ssid(drv->wext, ssid); +} + + +static struct wpa_scan_results * wpa_driver_hostap_get_scan_results(void *priv) +{ + struct wpa_driver_hostap_data *drv = priv; + return wpa_driver_wext_get_scan_results(drv->wext); +} + + +static int wpa_driver_hostap_set_operstate(void *priv, int state) +{ + struct wpa_driver_hostap_data *drv = priv; + return wpa_driver_wext_set_operstate(drv->wext, state); +} + + +static void * wpa_driver_hostap_init(void *ctx, const char *ifname) +{ + struct wpa_driver_hostap_data *drv; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->wext = wpa_driver_wext_init(ctx, ifname); + if (drv->wext == NULL) { + os_free(drv); + return NULL; + } + + drv->ctx = ctx; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + drv->sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->sock < 0) { + perror("socket"); + wpa_driver_wext_deinit(drv->wext); + os_free(drv); + return NULL; + } + + if (os_strncmp(ifname, "wlan", 4) == 0) { + /* + * Host AP driver may use both wlan# and wifi# interface in + * wireless events. + */ + char ifname2[IFNAMSIZ + 1]; + os_strlcpy(ifname2, ifname, sizeof(ifname2)); + os_memcpy(ifname2, "wifi", 4); + wpa_driver_wext_alternative_ifindex(drv->wext, ifname2); + } + + return drv; +} + + +static void wpa_driver_hostap_deinit(void *priv) +{ + struct wpa_driver_hostap_data *drv = priv; + wpa_driver_wext_deinit(drv->wext); + close(drv->sock); + os_free(drv); +} + + +const struct wpa_driver_ops wpa_driver_hostap_ops = { + .name = "hostap", + .desc = "Host AP driver (Intersil Prism2/2.5/3)", + .get_bssid = wpa_driver_hostap_get_bssid, + .get_ssid = wpa_driver_hostap_get_ssid, + .set_wpa = wpa_driver_hostap_set_wpa, + .set_key = wpa_driver_hostap_set_key, + .set_countermeasures = wpa_driver_hostap_set_countermeasures, + .set_drop_unencrypted = wpa_driver_hostap_set_drop_unencrypted, + .scan = wpa_driver_hostap_scan, + .get_scan_results2 = wpa_driver_hostap_get_scan_results, + .deauthenticate = wpa_driver_hostap_deauthenticate, + .disassociate = wpa_driver_hostap_disassociate, + .associate = wpa_driver_hostap_associate, + .set_auth_alg = wpa_driver_hostap_set_auth_alg, + .init = wpa_driver_hostap_init, + .deinit = wpa_driver_hostap_deinit, + .set_operstate = wpa_driver_hostap_set_operstate, +}; diff --git a/src/drivers/driver_hostap.h b/src/drivers/driver_hostap.h new file mode 100644 index 0000000..a2508ed --- /dev/null +++ b/src/drivers/driver_hostap.h @@ -0,0 +1,153 @@ +/* + * WPA Supplicant - driver interaction with Linux Host AP driver + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef HOSTAP_DRIVER_H +#define HOSTAP_DRIVER_H + +#define PRISM2_IOCTL_PRISM2_PARAM (SIOCIWFIRSTPRIV + 0) +#define PRISM2_IOCTL_RESET (SIOCIWFIRSTPRIV + 6) +#define PRISM2_IOCTL_HOSTAPD (SIOCDEVPRIVATE + 14) + +/* PRISM2_IOCTL_PRISM2_PARAM ioctl() subtypes: */ +enum { + /* PRISM2_PARAM_PTYPE = 1, */ /* REMOVED 2003-10-22 */ + PRISM2_PARAM_TXRATECTRL = 2, + PRISM2_PARAM_BEACON_INT = 3, + PRISM2_PARAM_PSEUDO_IBSS = 4, + PRISM2_PARAM_ALC = 5, + /* PRISM2_PARAM_TXPOWER = 6, */ /* REMOVED 2003-10-22 */ + PRISM2_PARAM_DUMP = 7, + PRISM2_PARAM_OTHER_AP_POLICY = 8, + PRISM2_PARAM_AP_MAX_INACTIVITY = 9, + PRISM2_PARAM_AP_BRIDGE_PACKETS = 10, + PRISM2_PARAM_DTIM_PERIOD = 11, + PRISM2_PARAM_AP_NULLFUNC_ACK = 12, + PRISM2_PARAM_MAX_WDS = 13, + PRISM2_PARAM_AP_AUTOM_AP_WDS = 14, + PRISM2_PARAM_AP_AUTH_ALGS = 15, + PRISM2_PARAM_MONITOR_ALLOW_FCSERR = 16, + PRISM2_PARAM_HOST_ENCRYPT = 17, + PRISM2_PARAM_HOST_DECRYPT = 18, + PRISM2_PARAM_BUS_MASTER_THRESHOLD_RX = 19, + PRISM2_PARAM_BUS_MASTER_THRESHOLD_TX = 20, + PRISM2_PARAM_HOST_ROAMING = 21, + PRISM2_PARAM_BCRX_STA_KEY = 22, + PRISM2_PARAM_IEEE_802_1X = 23, + PRISM2_PARAM_ANTSEL_TX = 24, + PRISM2_PARAM_ANTSEL_RX = 25, + PRISM2_PARAM_MONITOR_TYPE = 26, + PRISM2_PARAM_WDS_TYPE = 27, + PRISM2_PARAM_HOSTSCAN = 28, + PRISM2_PARAM_AP_SCAN = 29, + PRISM2_PARAM_ENH_SEC = 30, + PRISM2_PARAM_IO_DEBUG = 31, + PRISM2_PARAM_BASIC_RATES = 32, + PRISM2_PARAM_OPER_RATES = 33, + PRISM2_PARAM_HOSTAPD = 34, + PRISM2_PARAM_HOSTAPD_STA = 35, + PRISM2_PARAM_WPA = 36, + PRISM2_PARAM_PRIVACY_INVOKED = 37, + PRISM2_PARAM_TKIP_COUNTERMEASURES = 38, + PRISM2_PARAM_DROP_UNENCRYPTED = 39, + PRISM2_PARAM_SCAN_CHANNEL_MASK = 40, +}; + +/* PRISM2_IOCTL_HOSTAPD ioctl() cmd: */ +enum { + PRISM2_HOSTAPD_FLUSH = 1, + PRISM2_HOSTAPD_ADD_STA = 2, + PRISM2_HOSTAPD_REMOVE_STA = 3, + PRISM2_HOSTAPD_GET_INFO_STA = 4, + /* REMOVED: PRISM2_HOSTAPD_RESET_TXEXC_STA = 5, */ + PRISM2_SET_ENCRYPTION = 6, + PRISM2_GET_ENCRYPTION = 7, + PRISM2_HOSTAPD_SET_FLAGS_STA = 8, + PRISM2_HOSTAPD_GET_RID = 9, + PRISM2_HOSTAPD_SET_RID = 10, + PRISM2_HOSTAPD_SET_ASSOC_AP_ADDR = 11, + PRISM2_HOSTAPD_SET_GENERIC_ELEMENT = 12, + PRISM2_HOSTAPD_MLME = 13, + PRISM2_HOSTAPD_SCAN_REQ = 14, + PRISM2_HOSTAPD_STA_CLEAR_STATS = 15, +}; + +#define PRISM2_HOSTAPD_MAX_BUF_SIZE 1024 +#define PRISM2_HOSTAPD_RID_HDR_LEN \ +((size_t) (&((struct prism2_hostapd_param *) 0)->u.rid.data)) +#define PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN \ +((size_t) (&((struct prism2_hostapd_param *) 0)->u.generic_elem.data)) + +/* Maximum length for algorithm names (-1 for nul termination) used in ioctl() + */ +#define HOSTAP_CRYPT_ALG_NAME_LEN 16 + + +struct prism2_hostapd_param { + u32 cmd; + u8 sta_addr[ETH_ALEN]; + union { + struct { + u16 aid; + u16 capability; + u8 tx_supp_rates; + } add_sta; + struct { + u32 inactive_sec; + } get_info_sta; + struct { + u8 alg[HOSTAP_CRYPT_ALG_NAME_LEN]; + u32 flags; + u32 err; + u8 idx; + u8 seq[8]; /* sequence counter (set: RX, get: TX) */ + u16 key_len; + u8 key[0]; + } crypt; + struct { + u32 flags_and; + u32 flags_or; + } set_flags_sta; + struct { + u16 rid; + u16 len; + u8 data[0]; + } rid; + struct { + u8 len; + u8 data[0]; + } generic_elem; + struct { +#define MLME_STA_DEAUTH 0 +#define MLME_STA_DISASSOC 1 + u16 cmd; + u16 reason_code; + } mlme; + struct { + u8 ssid_len; + u8 ssid[32]; + } scan_req; + } u; +}; + +#define HOSTAP_CRYPT_FLAG_SET_TX_KEY 0x01 +#define HOSTAP_CRYPT_FLAG_PERMANENT 0x02 + +#define HOSTAP_CRYPT_ERR_UNKNOWN_ALG 2 +#define HOSTAP_CRYPT_ERR_UNKNOWN_ADDR 3 +#define HOSTAP_CRYPT_ERR_CRYPT_INIT_FAILED 4 +#define HOSTAP_CRYPT_ERR_KEY_SET_FAILED 5 +#define HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED 6 +#define HOSTAP_CRYPT_ERR_CARD_CONF_FAILED 7 + +#endif /* HOSTAP_DRIVER_H */ diff --git a/src/drivers/driver_iphone.m b/src/drivers/driver_iphone.m new file mode 100644 index 0000000..8e64ffd --- /dev/null +++ b/src/drivers/driver_iphone.m @@ -0,0 +1,466 @@ +/* + * WPA Supplicant - iPhone/iPod touch Apple80211 driver interface + * Copyright (c) 2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#define Boolean __DummyBoolean +#include <CoreFoundation/CoreFoundation.h> +#undef Boolean + +#include "common.h" +#include "driver.h" +#include "eloop.h" +#include "ieee802_11_defs.h" + +#include "MobileApple80211.h" + +struct wpa_driver_iphone_data { + void *ctx; + Apple80211Ref wireless_ctx; + CFArrayRef scan_results; + int ctrl_power; +}; + + +static const void * cfdict_get_key_str(CFDictionaryRef dict, const char *key) +{ + const void *res; + CFStringRef str = CFStringCreateWithCString(kCFAllocatorDefault, key, + kCFStringEncodingMacRoman); + if (str == NULL) + return NULL; + + res = CFDictionaryGetValue(dict, str); + CFRelease(str); + return res; +} + + +static int wpa_driver_iphone_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_iphone_data *drv = priv; + CFDataRef data; + int err, len; + + err = Apple80211CopyValue(drv->wireless_ctx, APPLE80211_VALUE_SSID, 0, + &data); + if (err != 0) { + wpa_printf(MSG_DEBUG, "iPhone: Apple80211CopyValue(SSID) " + "failed: %d", err); + return -1; + } + + len = CFDataGetLength(data); + if (len > 32) { + CFRelease(data); + return -1; + } + os_memcpy(ssid, CFDataGetBytePtr(data), len); + CFRelease(data); + + return len; +} + + +static int wpa_driver_iphone_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_iphone_data *drv = priv; + CFStringRef data; + int err; + int a1, a2, a3, a4, a5, a6; + + err = Apple80211CopyValue(drv->wireless_ctx, APPLE80211_VALUE_BSSID, 0, + &data); + if (err != 0) { + wpa_printf(MSG_DEBUG, "iPhone: Apple80211CopyValue(BSSID) " + "failed: %d", err); + return -1; + } + + sscanf(CFStringGetCStringPtr(data, kCFStringEncodingMacRoman), + "%x:%x:%x:%x:%x:%x", &a1, &a2, &a3, &a4, &a5, &a6); + bssid[0] = a1; + bssid[1] = a2; + bssid[2] = a3; + bssid[3] = a4; + bssid[4] = a5; + bssid[5] = a6; + + CFRelease(data); + + return 0; +} + + +static void wpa_driver_iphone_scan_timeout(void *eloop_ctx, void *timeout_ctx) +{ + wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); +} + + +static int wpa_driver_iphone_scan(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_iphone_data *drv = priv; + int err; + + if (drv->scan_results) { + CFRelease(drv->scan_results); + drv->scan_results = NULL; + } + + err = Apple80211Scan(drv->wireless_ctx, &drv->scan_results, NULL); + if (err) { + wpa_printf(MSG_DEBUG, "iPhone: Apple80211Scan failed: %d", + err); + return -1; + } + + eloop_register_timeout(0, 0, wpa_driver_iphone_scan_timeout, drv, + drv->ctx); + return 0; +} + + +static int wpa_driver_iphone_get_scan_results(void *priv, + struct wpa_scan_result *results, + size_t max_size) +{ + struct wpa_driver_iphone_data *drv = priv; + size_t i, num; + + if (drv->scan_results == NULL) + return 0; + + num = CFArrayGetCount(drv->scan_results); + if (num > max_size) + num = max_size; + os_memset(results, 0, num * sizeof(struct wpa_scan_result)); + + for (i = 0; i < num; i++) { + struct wpa_scan_result *res = &results[i]; + CFDictionaryRef dict = + CFArrayGetValueAtIndex(drv->scan_results, i); + CFDataRef data; + CFStringRef str; + CFNumberRef num; + int val; + + data = cfdict_get_key_str(dict, "SSID"); + if (data) { + res->ssid_len = CFDataGetLength(data); + if (res->ssid_len > 32) + res->ssid_len = 32; + os_memcpy(res->ssid, CFDataGetBytePtr(data), + res->ssid_len); + } + + str = cfdict_get_key_str(dict, "BSSID"); + if (str) { + int a1, a2, a3, a4, a5, a6; + sscanf(CFStringGetCStringPtr( + str, kCFStringEncodingMacRoman), + "%x:%x:%x:%x:%x:%x", + &a1, &a2, &a3, &a4, &a5, &a6); + res->bssid[0] = a1; + res->bssid[1] = a2; + res->bssid[2] = a3; + res->bssid[3] = a4; + res->bssid[4] = a5; + res->bssid[5] = a6; + } + + num = cfdict_get_key_str(dict, "CAPABILITIES"); + if (num) { + if (CFNumberGetValue(num, kCFNumberSInt32Type, &val)) + res->caps = val; + } + + num = cfdict_get_key_str(dict, "CHANNEL"); + if (num) { + if (CFNumberGetValue(num, kCFNumberSInt32Type, &val)) + res->freq = 2407 + val * 5; + } + + num = cfdict_get_key_str(dict, "RSSI"); + if (num) { + if (CFNumberGetValue(num, kCFNumberSInt32Type, &val)) + res->level = val; + } + + num = cfdict_get_key_str(dict, "NOISE"); + if (num) { + if (CFNumberGetValue(num, kCFNumberSInt32Type, &val)) + res->noise = val; + } + + data = cfdict_get_key_str(dict, "IE"); + if (data) { + u8 *ptr = (u8 *) CFDataGetBytePtr(data); + int len = CFDataGetLength(data); + u8 *pos = ptr, *end = ptr + len; + + while (pos + 2 < end) { + if (pos + 2 + pos[1] > end) + break; + if (pos[0] == WLAN_EID_RSN && + pos[1] <= SSID_MAX_WPA_IE_LEN) { + os_memcpy(res->rsn_ie, pos, + 2 + pos[1]); + res->rsn_ie_len = 2 + pos[1]; + } + if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && + pos[1] > 4 && pos[2] == 0x00 && + pos[3] == 0x50 && pos[4] == 0xf2 && + pos[5] == 0x01) { + os_memcpy(res->wpa_ie, pos, + 2 + pos[1]); + res->wpa_ie_len = 2 + pos[1]; + } + + pos = pos + 2 + pos[1]; + } + } + } + + return num; +} + + +static void wpa_driver_iphone_assoc_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_driver_iphone_data *drv = eloop_ctx; + u8 bssid[ETH_ALEN]; + + if (wpa_driver_iphone_get_bssid(drv, bssid) != 0) { + eloop_register_timeout(1, 0, wpa_driver_iphone_assoc_timeout, + drv, drv->ctx); + return; + } + + wpa_supplicant_event(timeout_ctx, EVENT_ASSOC, NULL); +} + + +static int wpa_driver_iphone_associate( + void *priv, struct wpa_driver_associate_params *params) +{ + struct wpa_driver_iphone_data *drv = priv; + int i, num, err; + size_t ssid_len; + CFDictionaryRef bss = NULL; + + /* + * TODO: Consider generating parameters instead of just using an entry + * from scan results in order to support ap_scan=2. + */ + + if (drv->scan_results == NULL) { + wpa_printf(MSG_DEBUG, "iPhone: No scan results - cannot " + "associate"); + return -1; + } + + num = CFArrayGetCount(drv->scan_results); + + for (i = 0; i < num; i++) { + CFDictionaryRef dict = + CFArrayGetValueAtIndex(drv->scan_results, i); + CFDataRef data; + + data = cfdict_get_key_str(dict, "SSID"); + if (data == NULL) + continue; + + ssid_len = CFDataGetLength(data); + if (ssid_len != params->ssid_len || + os_memcmp(CFDataGetBytePtr(data), params->ssid, ssid_len) + != 0) + continue; + + bss = dict; + break; + } + + if (bss == NULL) { + wpa_printf(MSG_DEBUG, "iPhone: Could not find SSID from scan " + "results - cannot associate"); + return -1; + } + + wpa_printf(MSG_DEBUG, "iPhone: Trying to associate with a BSS found " + "from scan results"); + + err = Apple80211Associate(drv->wireless_ctx, bss, NULL); + if (err) { + wpa_printf(MSG_DEBUG, "iPhone: Apple80211Associate() failed: " + "%d", err); + return -1; + } + + /* + * Driver is actually already associated; report association from an + * eloop callback. + */ + eloop_cancel_timeout(wpa_driver_iphone_assoc_timeout, drv, drv->ctx); + eloop_register_timeout(0, 0, wpa_driver_iphone_assoc_timeout, drv, + drv->ctx); + + return 0; +} + + +static int wpa_driver_iphone_set_key(void *priv, 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) +{ + /* + * TODO: Need to either support configuring PMK for 4-way handshake or + * PTK for TKIP/CCMP. + */ + return -1; +} + + +static int wpa_driver_iphone_get_capa(void *priv, struct wpa_driver_capa *capa) +{ + os_memset(capa, 0, sizeof(*capa)); + + capa->key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | + WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK; + capa->enc = WPA_DRIVER_CAPA_ENC_WEP40 | WPA_DRIVER_CAPA_ENC_WEP104 | + WPA_DRIVER_CAPA_ENC_TKIP | WPA_DRIVER_CAPA_ENC_CCMP; + capa->auth = WPA_DRIVER_AUTH_OPEN | WPA_DRIVER_AUTH_SHARED | + WPA_DRIVER_AUTH_LEAP; + capa->flags = WPA_DRIVER_FLAGS_4WAY_HANDSHAKE; + + return 0; +} + + +static void * wpa_driver_iphone_init(void *ctx, const char *ifname) +{ + struct wpa_driver_iphone_data *drv; + int err; + char power; + CFStringRef name; + CFDictionaryRef dict; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->ctx = ctx; + err = Apple80211Open(&drv->wireless_ctx); + if (err) { + wpa_printf(MSG_ERROR, "iPhone: Apple80211Open failed: %d", + err); + os_free(drv); + return NULL; + } + + name = CFStringCreateWithCString(kCFAllocatorDefault, ifname, + kCFStringEncodingISOLatin1); + if (name == NULL) { + wpa_printf(MSG_ERROR, "iPhone: ifname -> CFString failed"); + Apple80211Close(drv->wireless_ctx); + os_free(drv); + return NULL; + } + + err = Apple80211BindToInterface(drv->wireless_ctx, name); + CFRelease(name); + + if (err) { + wpa_printf(MSG_ERROR, "iPhone: Apple80211BindToInterface " + "failed: %d", err); + Apple80211Close(drv->wireless_ctx); + os_free(drv); + return NULL; + } + + err = Apple80211GetPower(drv->wireless_ctx, &power); + if (err) + wpa_printf(MSG_DEBUG, "iPhone: Apple80211GetPower failed: %d", + err); + + wpa_printf(MSG_DEBUG, "iPhone: Power=%d", power); + + if (!power) { + drv->ctrl_power = 1; + err = Apple80211SetPower(drv->wireless_ctx, 1); + if (err) { + wpa_printf(MSG_DEBUG, "iPhone: Apple80211SetPower " + "failed: %d", err); + Apple80211Close(drv->wireless_ctx); + os_free(drv); + return NULL; + } + } + + err = Apple80211GetInfoCopy(drv->wireless_ctx, &dict); + if (err == 0) { + CFShow(dict); + CFRelease(dict); + } else { + printf("Apple80211GetInfoCopy: %d\n", err); + } + + return drv; +} + + +static void wpa_driver_iphone_deinit(void *priv) +{ + struct wpa_driver_iphone_data *drv = priv; + int err; + + eloop_cancel_timeout(wpa_driver_iphone_scan_timeout, drv, drv->ctx); + eloop_cancel_timeout(wpa_driver_iphone_assoc_timeout, drv, drv->ctx); + + if (drv->ctrl_power) { + wpa_printf(MSG_DEBUG, "iPhone: Power down the interface"); + err = Apple80211SetPower(drv->wireless_ctx, 0); + if (err) { + wpa_printf(MSG_DEBUG, "iPhone: Apple80211SetPower(0) " + "failed: %d", err); + } + } + + err = Apple80211Close(drv->wireless_ctx); + if (err) { + wpa_printf(MSG_DEBUG, "iPhone: Apple80211Close failed: %d", + err); + } + + if (drv->scan_results) + CFRelease(drv->scan_results); + + os_free(drv); +} + + +const struct wpa_driver_ops wpa_driver_iphone_ops = { + .name = "iphone", + .desc = "iPhone/iPod touch Apple80211 driver", + .get_ssid = wpa_driver_iphone_get_ssid, + .get_bssid = wpa_driver_iphone_get_bssid, + .init = wpa_driver_iphone_init, + .deinit = wpa_driver_iphone_deinit, + .scan = wpa_driver_iphone_scan, + .get_scan_results = wpa_driver_iphone_get_scan_results, + .associate = wpa_driver_iphone_associate, + .set_key = wpa_driver_iphone_set_key, + .get_capa = wpa_driver_iphone_get_capa, +}; diff --git a/src/drivers/driver_ipw.c b/src/drivers/driver_ipw.c new file mode 100644 index 0000000..3c19ccc --- /dev/null +++ b/src/drivers/driver_ipw.c @@ -0,0 +1,463 @@ +/* + * WPA Supplicant - driver interaction with Linux ipw2100/2200 drivers + * Copyright (c) 2005 Zhu Yi <yi.zhu@intel.com> + * Copyright (c) 2004 Lubomir Gelo <lgelo@cnc.sk> + * Copyright (c) 2003-2004, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * Please note that ipw2100/2200 drivers change to use generic Linux wireless + * extensions if the kernel includes support for WE-18 or newer (Linux 2.6.13 + * or newer). driver_wext.c should be used in those cases. + */ + +#include "includes.h" +#include <sys/ioctl.h> + +#include "wireless_copy.h" +#include "common.h" +#include "driver.h" +#include "driver_wext.h" + +struct wpa_driver_ipw_data { + void *wext; /* private data for driver_wext */ + void *ctx; + char ifname[IFNAMSIZ + 1]; + int sock; +}; + +/* following definitions must be kept in sync with ipw2100.c and ipw2200.c */ + +#define IPW_IOCTL_WPA_SUPPLICANT SIOCIWFIRSTPRIV+30 + +#define IPW_CMD_SET_WPA_PARAM 1 +#define IPW_CMD_SET_WPA_IE 2 +#define IPW_CMD_SET_ENCRYPTION 3 +#define IPW_CMD_MLME 4 + +#define IPW_PARAM_WPA_ENABLED 1 +#define IPW_PARAM_TKIP_COUNTERMEASURES 2 +#define IPW_PARAM_DROP_UNENCRYPTED 3 +#define IPW_PARAM_PRIVACY_INVOKED 4 +#define IPW_PARAM_AUTH_ALGS 5 +#define IPW_PARAM_IEEE_802_1X 6 + +#define IPW_MLME_STA_DEAUTH 1 +#define IPW_MLME_STA_DISASSOC 2 + +#define IPW_CRYPT_ERR_UNKNOWN_ALG 2 +#define IPW_CRYPT_ERR_UNKNOWN_ADDR 3 +#define IPW_CRYPT_ERR_CRYPT_INIT_FAILED 4 +#define IPW_CRYPT_ERR_KEY_SET_FAILED 5 +#define IPW_CRYPT_ERR_TX_KEY_SET_FAILED 6 +#define IPW_CRYPT_ERR_CARD_CONF_FAILED 7 + +#define IPW_CRYPT_ALG_NAME_LEN 16 + +struct ipw_param { + u32 cmd; + u8 sta_addr[ETH_ALEN]; + union { + struct { + u8 name; + u32 value; + } wpa_param; + struct { + u32 len; + u8 reserved[32]; + u8 data[0]; + } wpa_ie; + struct{ + u32 command; + u32 reason_code; + } mlme; + struct { + u8 alg[IPW_CRYPT_ALG_NAME_LEN]; + u8 set_tx; + u32 err; + u8 idx; + u8 seq[8]; + u16 key_len; + u8 key[0]; + } crypt; + + } u; +}; + +/* end of ipw2100.c and ipw2200.c code */ + +static int ipw_ioctl(struct wpa_driver_ipw_data *drv, + struct ipw_param *param, int len, int show_err) +{ + struct iwreq iwr; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) param; + iwr.u.data.length = len; + + if (ioctl(drv->sock, IPW_IOCTL_WPA_SUPPLICANT, &iwr) < 0) { + int ret = errno; + if (show_err) + perror("ioctl[IPW_IOCTL_WPA_SUPPLICANT]"); + return ret; + } + + return 0; +} + + +static void ipw_show_set_key_error(struct ipw_param *param) +{ + switch (param->u.crypt.err) { + case IPW_CRYPT_ERR_UNKNOWN_ALG: + wpa_printf(MSG_INFO, "Unknown algorithm '%s'.", + param->u.crypt.alg); + wpa_printf(MSG_INFO, "You may need to load kernel module to " + "register that algorithm."); + wpa_printf(MSG_INFO, "E.g., 'modprobe ieee80211_crypt_wep' for" + " WEP."); + break; + case IPW_CRYPT_ERR_UNKNOWN_ADDR: + wpa_printf(MSG_INFO, "Unknown address " MACSTR ".", + MAC2STR(param->sta_addr)); + break; + case IPW_CRYPT_ERR_CRYPT_INIT_FAILED: + wpa_printf(MSG_INFO, "Crypt algorithm initialization failed."); + break; + case IPW_CRYPT_ERR_KEY_SET_FAILED: + wpa_printf(MSG_INFO, "Key setting failed."); + break; + case IPW_CRYPT_ERR_TX_KEY_SET_FAILED: + wpa_printf(MSG_INFO, "TX key index setting failed."); + break; + case IPW_CRYPT_ERR_CARD_CONF_FAILED: + wpa_printf(MSG_INFO, "Card configuration failed."); + break; + } +} + + +static int ipw_set_wpa_ie(struct wpa_driver_ipw_data *drv, + const u8 *wpa_ie, size_t wpa_ie_len) +{ + struct ipw_param *param; + int ret; + size_t blen = sizeof(*param) + wpa_ie_len; + + param = os_zalloc(blen); + if (param == NULL) + return -1; + + param->cmd = IPW_CMD_SET_WPA_IE; + param->u.wpa_ie.len = wpa_ie_len; + os_memcpy(param->u.wpa_ie.data, wpa_ie, wpa_ie_len); + + ret = ipw_ioctl(drv, param, blen, 1); + + os_free(param); + return ret; +} + + +static int ipw_set_wpa_param(struct wpa_driver_ipw_data *drv, u8 name, + u32 value) +{ + struct ipw_param param; + + os_memset(¶m, 0, sizeof(param)); + param.cmd = IPW_CMD_SET_WPA_PARAM; + param.u.wpa_param.name = name; + param.u.wpa_param.value = value; + + return ipw_ioctl(drv, ¶m, sizeof(param), 1); +} + + +static int ipw_mlme(struct wpa_driver_ipw_data *drv, const u8 *addr, + int cmd, int reason) +{ + struct ipw_param param; + + os_memset(¶m, 0, sizeof(param)); + os_memcpy(param.sta_addr, addr, ETH_ALEN); + param.cmd = IPW_CMD_MLME; + param.u.mlme.command = cmd; + param.u.mlme.reason_code = reason; + + return ipw_ioctl(drv, ¶m, sizeof(param), 1); +} + + +static int wpa_driver_ipw_set_wpa(void *priv, int enabled) +{ + struct wpa_driver_ipw_data *drv = priv; + int ret = 0; + + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + + if (!enabled && ipw_set_wpa_ie(drv, NULL, 0) < 0) + ret = -1; + + if (ipw_set_wpa_param(drv, IPW_PARAM_WPA_ENABLED, enabled) < 0) + ret = -1; + + return ret; +} + + +static int wpa_driver_ipw_set_key(void *priv, wpa_alg alg, + const u8 *addr, int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + struct wpa_driver_ipw_data *drv = priv; + struct ipw_param *param; + u8 *buf; + size_t blen; + int ret = 0; + char *alg_name; + + switch (alg) { + case WPA_ALG_NONE: + alg_name = "none"; + break; + case WPA_ALG_WEP: + alg_name = "WEP"; + break; + case WPA_ALG_TKIP: + alg_name = "TKIP"; + break; + case WPA_ALG_CCMP: + alg_name = "CCMP"; + break; + default: + return -1; + } + + wpa_printf(MSG_DEBUG, "%s: alg=%s key_idx=%d set_tx=%d seq_len=%lu " + "key_len=%lu", __FUNCTION__, alg_name, key_idx, set_tx, + (unsigned long) seq_len, (unsigned long) key_len); + + if (seq_len > 8) + return -2; + + blen = sizeof(*param) + key_len; + buf = os_zalloc(blen); + if (buf == NULL) + return -1; + + param = (struct ipw_param *) buf; + param->cmd = IPW_CMD_SET_ENCRYPTION; + os_memset(param->sta_addr, 0xff, ETH_ALEN); + os_strlcpy((char *) param->u.crypt.alg, alg_name, + IPW_CRYPT_ALG_NAME_LEN); + param->u.crypt.set_tx = set_tx ? 1 : 0; + param->u.crypt.idx = key_idx; + os_memcpy(param->u.crypt.seq, seq, seq_len); + param->u.crypt.key_len = key_len; + os_memcpy((u8 *) (param + 1), key, key_len); + + if (ipw_ioctl(drv, param, blen, 1)) { + wpa_printf(MSG_WARNING, "Failed to set encryption."); + ipw_show_set_key_error(param); + ret = -1; + } + os_free(buf); + + return ret; +} + + +static int wpa_driver_ipw_set_countermeasures(void *priv, int enabled) +{ + struct wpa_driver_ipw_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + return ipw_set_wpa_param(drv, IPW_PARAM_TKIP_COUNTERMEASURES, + enabled); + +} + + +static int wpa_driver_ipw_set_drop_unencrypted(void *priv, int enabled) +{ + struct wpa_driver_ipw_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + return ipw_set_wpa_param(drv, IPW_PARAM_DROP_UNENCRYPTED, + enabled); +} + + +static int wpa_driver_ipw_deauthenticate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_ipw_data *drv = priv; + return ipw_mlme(drv, addr, IPW_MLME_STA_DEAUTH, reason_code); +} + + +static int wpa_driver_ipw_disassociate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_ipw_data *drv = priv; + return ipw_mlme(drv, addr, IPW_MLME_STA_DISASSOC, reason_code); +} + + +static int +wpa_driver_ipw_associate(void *priv, struct wpa_driver_associate_params *params) +{ + struct wpa_driver_ipw_data *drv = priv; + int ret = 0; + int unencrypted_eapol; + + if (ipw_set_wpa_ie(drv, params->wpa_ie, params->wpa_ie_len) < 0) + ret = -1; + if (wpa_driver_wext_set_ssid(drv->wext, params->ssid, + params->ssid_len) < 0) + ret = -1; + if (wpa_driver_wext_set_bssid(drv->wext, params->bssid) < 0) + ret = -1; + + if (params->key_mgmt_suite == KEY_MGMT_802_1X || + params->key_mgmt_suite == KEY_MGMT_PSK) + unencrypted_eapol = 0; + else + unencrypted_eapol = 1; + + if (ipw_set_wpa_param(drv, IPW_PARAM_IEEE_802_1X, + unencrypted_eapol) < 0) { + wpa_printf(MSG_DEBUG, "ipw: Failed to configure " + "ieee_802_1x param"); + } + + return ret; +} + + +static int wpa_driver_ipw_set_auth_alg(void *priv, int auth_alg) +{ + struct wpa_driver_ipw_data *drv = priv; + int algs = 0; + + if (auth_alg & AUTH_ALG_OPEN_SYSTEM) + algs |= 1; + if (auth_alg & AUTH_ALG_SHARED_KEY) + algs |= 2; + if (auth_alg & AUTH_ALG_LEAP) + algs |= 4; + if (algs == 0) + algs = 1; /* at least one algorithm should be set */ + + wpa_printf(MSG_DEBUG, "%s: auth_alg=0x%x", __FUNCTION__, algs); + return ipw_set_wpa_param(drv, IPW_PARAM_AUTH_ALGS, algs); +} + + +static int wpa_driver_ipw_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_ipw_data *drv = priv; + return wpa_driver_wext_get_bssid(drv->wext, bssid); +} + + +static int wpa_driver_ipw_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_ipw_data *drv = priv; + return wpa_driver_wext_get_ssid(drv->wext, ssid); +} + + +static int wpa_driver_ipw_scan(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_ipw_data *drv = priv; + return wpa_driver_wext_scan(drv->wext, ssid, ssid_len); +} + + +static struct wpa_scan_results * wpa_driver_ipw_get_scan_results(void *priv) +{ + struct wpa_driver_ipw_data *drv = priv; + return wpa_driver_wext_get_scan_results(drv->wext); +} + + +static int wpa_driver_ipw_set_operstate(void *priv, int state) +{ + struct wpa_driver_ipw_data *drv = priv; + return wpa_driver_wext_set_operstate(drv->wext, state); +} + + +static void * wpa_driver_ipw_init(void *ctx, const char *ifname) +{ + struct wpa_driver_ipw_data *drv; + int ver; + + wpa_printf(MSG_DEBUG, "%s is called", __FUNCTION__); + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->wext = wpa_driver_wext_init(ctx, ifname); + if (drv->wext == NULL) { + os_free(drv); + return NULL; + } + + ver = wpa_driver_wext_get_version(drv->wext); + if (ver >= 18) { + wpa_printf(MSG_WARNING, "Linux wireless extensions version %d " + "detected.", ver); + wpa_printf(MSG_WARNING, "ipw2x00 driver uses driver_wext " + "(-Dwext) instead of driver_ipw."); + } + + drv->ctx = ctx; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + drv->sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->sock < 0) { + wpa_driver_wext_deinit(drv->wext); + os_free(drv); + return NULL; + } + + return drv; +} + + +static void wpa_driver_ipw_deinit(void *priv) +{ + struct wpa_driver_ipw_data *drv = priv; + wpa_driver_wext_deinit(drv->wext); + close(drv->sock); + os_free(drv); +} + + +const struct wpa_driver_ops wpa_driver_ipw_ops = { + .name = "ipw", + .desc = "Intel ipw2100/2200 driver (old; use wext with Linux 2.6.13 " + "or newer)", + .get_bssid = wpa_driver_ipw_get_bssid, + .get_ssid = wpa_driver_ipw_get_ssid, + .set_wpa = wpa_driver_ipw_set_wpa, + .set_key = wpa_driver_ipw_set_key, + .set_countermeasures = wpa_driver_ipw_set_countermeasures, + .set_drop_unencrypted = wpa_driver_ipw_set_drop_unencrypted, + .scan = wpa_driver_ipw_scan, + .get_scan_results2 = wpa_driver_ipw_get_scan_results, + .deauthenticate = wpa_driver_ipw_deauthenticate, + .disassociate = wpa_driver_ipw_disassociate, + .associate = wpa_driver_ipw_associate, + .set_auth_alg = wpa_driver_ipw_set_auth_alg, + .init = wpa_driver_ipw_init, + .deinit = wpa_driver_ipw_deinit, + .set_operstate = wpa_driver_ipw_set_operstate, +}; diff --git a/src/drivers/driver_madwifi.c b/src/drivers/driver_madwifi.c new file mode 100644 index 0000000..7521037 --- /dev/null +++ b/src/drivers/driver_madwifi.c @@ -0,0 +1,601 @@ +/* + * WPA Supplicant - driver interaction with MADWIFI 802.11 driver + * Copyright (c) 2004, Sam Leffler <sam@errno.com> + * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * Please note that madwifi supports WPA configuration via Linux wireless + * extensions and if the kernel includes support for this, driver_wext.c should + * be used instead of this driver wrapper. + */ + +#include "includes.h" +#include <sys/ioctl.h> + +#include "common.h" +#include "driver.h" +#include "driver_wext.h" +#include "eloop.h" +#include "ieee802_11_defs.h" +#include "wireless_copy.h" + +/* + * Avoid conflicts with wpa_supplicant definitions by undefining a definition. + */ +#undef WME_OUI_TYPE + +#include <include/compat.h> +#include <net80211/ieee80211.h> +#ifdef WME_NUM_AC +/* Assume this is built against BSD branch of madwifi driver. */ +#define MADWIFI_BSD +#include <net80211/_ieee80211.h> +#endif /* WME_NUM_AC */ +#include <net80211/ieee80211_crypto.h> +#include <net80211/ieee80211_ioctl.h> + + +#ifdef IEEE80211_IOCTL_SETWMMPARAMS +/* Assume this is built against madwifi-ng */ +#define MADWIFI_NG +#endif /* IEEE80211_IOCTL_SETWMMPARAMS */ + +struct wpa_driver_madwifi_data { + void *wext; /* private data for driver_wext */ + void *ctx; + char ifname[IFNAMSIZ + 1]; + int sock; +}; + +static int +set80211priv(struct wpa_driver_madwifi_data *drv, int op, void *data, int len, + int show_err) +{ + struct iwreq iwr; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + if (len < IFNAMSIZ && + op != IEEE80211_IOCTL_SET_APPIEBUF) { + /* + * Argument data fits inline; put it there. + */ + os_memcpy(iwr.u.name, data, len); + } else { + /* + * Argument data too big for inline transfer; setup a + * parameter block instead; the kernel will transfer + * the data for the driver. + */ + iwr.u.data.pointer = data; + iwr.u.data.length = len; + } + + if (ioctl(drv->sock, op, &iwr) < 0) { + if (show_err) { +#ifdef MADWIFI_NG + int first = IEEE80211_IOCTL_SETPARAM; + int last = IEEE80211_IOCTL_KICKMAC; + static const char *opnames[] = { + "ioctl[IEEE80211_IOCTL_SETPARAM]", + "ioctl[IEEE80211_IOCTL_GETPARAM]", + "ioctl[IEEE80211_IOCTL_SETMODE]", + "ioctl[IEEE80211_IOCTL_GETMODE]", + "ioctl[IEEE80211_IOCTL_SETWMMPARAMS]", + "ioctl[IEEE80211_IOCTL_GETWMMPARAMS]", + "ioctl[IEEE80211_IOCTL_SETCHANLIST]", + "ioctl[IEEE80211_IOCTL_GETCHANLIST]", + "ioctl[IEEE80211_IOCTL_CHANSWITCH]", + NULL, + "ioctl[IEEE80211_IOCTL_SET_APPIEBUF]", + "ioctl[IEEE80211_IOCTL_GETSCANRESULTS]", + NULL, + "ioctl[IEEE80211_IOCTL_GETCHANINFO]", + "ioctl[IEEE80211_IOCTL_SETOPTIE]", + "ioctl[IEEE80211_IOCTL_GETOPTIE]", + "ioctl[IEEE80211_IOCTL_SETMLME]", + NULL, + "ioctl[IEEE80211_IOCTL_SETKEY]", + NULL, + "ioctl[IEEE80211_IOCTL_DELKEY]", + NULL, + "ioctl[IEEE80211_IOCTL_ADDMAC]", + NULL, + "ioctl[IEEE80211_IOCTL_DELMAC]", + NULL, + "ioctl[IEEE80211_IOCTL_WDSMAC]", + NULL, + "ioctl[IEEE80211_IOCTL_WDSDELMAC]", + NULL, + "ioctl[IEEE80211_IOCTL_KICKMAC]", + }; +#else /* MADWIFI_NG */ + int first = IEEE80211_IOCTL_SETPARAM; + int last = IEEE80211_IOCTL_CHANLIST; + static const char *opnames[] = { + "ioctl[IEEE80211_IOCTL_SETPARAM]", + "ioctl[IEEE80211_IOCTL_GETPARAM]", + "ioctl[IEEE80211_IOCTL_SETKEY]", + "ioctl[IEEE80211_IOCTL_GETKEY]", + "ioctl[IEEE80211_IOCTL_DELKEY]", + NULL, + "ioctl[IEEE80211_IOCTL_SETMLME]", + NULL, + "ioctl[IEEE80211_IOCTL_SETOPTIE]", + "ioctl[IEEE80211_IOCTL_GETOPTIE]", + "ioctl[IEEE80211_IOCTL_ADDMAC]", + NULL, + "ioctl[IEEE80211_IOCTL_DELMAC]", + NULL, + "ioctl[IEEE80211_IOCTL_CHANLIST]", + }; +#endif /* MADWIFI_NG */ + int idx = op - first; + if (first <= op && op <= last && + idx < (int) (sizeof(opnames) / sizeof(opnames[0])) + && opnames[idx]) + perror(opnames[idx]); + else + perror("ioctl[unknown???]"); + } + return -1; + } + return 0; +} + +static int +set80211param(struct wpa_driver_madwifi_data *drv, int op, int arg, + int show_err) +{ + struct iwreq iwr; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.mode = op; + os_memcpy(iwr.u.name+sizeof(u32), &arg, sizeof(arg)); + + if (ioctl(drv->sock, IEEE80211_IOCTL_SETPARAM, &iwr) < 0) { + if (show_err) + perror("ioctl[IEEE80211_IOCTL_SETPARAM]"); + return -1; + } + return 0; +} + +static int +wpa_driver_madwifi_set_wpa_ie(struct wpa_driver_madwifi_data *drv, + const u8 *wpa_ie, size_t wpa_ie_len) +{ + struct iwreq iwr; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + /* NB: SETOPTIE is not fixed-size so must not be inlined */ + iwr.u.data.pointer = (void *) wpa_ie; + iwr.u.data.length = wpa_ie_len; + + if (ioctl(drv->sock, IEEE80211_IOCTL_SETOPTIE, &iwr) < 0) { + perror("ioctl[IEEE80211_IOCTL_SETOPTIE]"); + return -1; + } + return 0; +} + +static int +wpa_driver_madwifi_del_key(struct wpa_driver_madwifi_data *drv, int key_idx, + const u8 *addr) +{ + struct ieee80211req_del_key wk; + + wpa_printf(MSG_DEBUG, "%s: keyidx=%d", __FUNCTION__, key_idx); + os_memset(&wk, 0, sizeof(wk)); + wk.idk_keyix = key_idx; + if (addr != NULL) + os_memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN); + + return set80211priv(drv, IEEE80211_IOCTL_DELKEY, &wk, sizeof(wk), 1); +} + +static int +wpa_driver_madwifi_set_key(void *priv, wpa_alg alg, + const u8 *addr, int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + struct wpa_driver_madwifi_data *drv = priv; + struct ieee80211req_key wk; + char *alg_name; + u_int8_t cipher; + + if (alg == WPA_ALG_NONE) + return wpa_driver_madwifi_del_key(drv, key_idx, addr); + + switch (alg) { + case WPA_ALG_WEP: + if (addr == NULL || os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", + ETH_ALEN) == 0) { + /* + * madwifi did not seem to like static WEP key + * configuration with IEEE80211_IOCTL_SETKEY, so use + * Linux wireless extensions ioctl for this. + */ + return wpa_driver_wext_set_key(drv->wext, alg, addr, + key_idx, set_tx, + seq, seq_len, + key, key_len); + } + alg_name = "WEP"; + cipher = IEEE80211_CIPHER_WEP; + break; + case WPA_ALG_TKIP: + alg_name = "TKIP"; + cipher = IEEE80211_CIPHER_TKIP; + break; + case WPA_ALG_CCMP: + alg_name = "CCMP"; + cipher = IEEE80211_CIPHER_AES_CCM; + break; + default: + wpa_printf(MSG_DEBUG, "%s: unknown/unsupported algorithm %d", + __FUNCTION__, alg); + return -1; + } + + wpa_printf(MSG_DEBUG, "%s: alg=%s key_idx=%d set_tx=%d seq_len=%lu " + "key_len=%lu", __FUNCTION__, alg_name, key_idx, set_tx, + (unsigned long) seq_len, (unsigned long) key_len); + + if (seq_len > sizeof(u_int64_t)) { + wpa_printf(MSG_DEBUG, "%s: seq_len %lu too big", + __FUNCTION__, (unsigned long) seq_len); + return -2; + } + if (key_len > sizeof(wk.ik_keydata)) { + wpa_printf(MSG_DEBUG, "%s: key length %lu too big", + __FUNCTION__, (unsigned long) key_len); + return -3; + } + + os_memset(&wk, 0, sizeof(wk)); + wk.ik_type = cipher; + wk.ik_flags = IEEE80211_KEY_RECV; + if (addr == NULL || + os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0) + wk.ik_flags |= IEEE80211_KEY_GROUP; + if (set_tx) { + wk.ik_flags |= IEEE80211_KEY_XMIT | IEEE80211_KEY_DEFAULT; + os_memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); + } else + os_memset(wk.ik_macaddr, 0, IEEE80211_ADDR_LEN); + wk.ik_keyix = key_idx; + wk.ik_keylen = key_len; +#ifdef WORDS_BIGENDIAN +#define WPA_KEY_RSC_LEN 8 + { + size_t i; + u8 tmp[WPA_KEY_RSC_LEN]; + os_memset(tmp, 0, sizeof(tmp)); + for (i = 0; i < seq_len; i++) + tmp[WPA_KEY_RSC_LEN - i - 1] = seq[i]; + os_memcpy(&wk.ik_keyrsc, tmp, WPA_KEY_RSC_LEN); + } +#else /* WORDS_BIGENDIAN */ + os_memcpy(&wk.ik_keyrsc, seq, seq_len); +#endif /* WORDS_BIGENDIAN */ + os_memcpy(wk.ik_keydata, key, key_len); + + return set80211priv(drv, IEEE80211_IOCTL_SETKEY, &wk, sizeof(wk), 1); +} + +static int +wpa_driver_madwifi_set_countermeasures(void *priv, int enabled) +{ + struct wpa_driver_madwifi_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + return set80211param(drv, IEEE80211_PARAM_COUNTERMEASURES, enabled, 1); +} + + +static int +wpa_driver_madwifi_set_drop_unencrypted(void *priv, int enabled) +{ + struct wpa_driver_madwifi_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + return set80211param(drv, IEEE80211_PARAM_DROPUNENCRYPTED, enabled, 1); +} + +static int +wpa_driver_madwifi_deauthenticate(void *priv, const u8 *addr, int reason_code) +{ + struct wpa_driver_madwifi_data *drv = priv; + struct ieee80211req_mlme mlme; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + mlme.im_op = IEEE80211_MLME_DEAUTH; + mlme.im_reason = reason_code; + os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + return set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme), 1); +} + +static int +wpa_driver_madwifi_disassociate(void *priv, const u8 *addr, int reason_code) +{ + struct wpa_driver_madwifi_data *drv = priv; + struct ieee80211req_mlme mlme; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + mlme.im_op = IEEE80211_MLME_DISASSOC; + mlme.im_reason = reason_code; + os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + return set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme), 1); +} + +static int +wpa_driver_madwifi_associate(void *priv, + struct wpa_driver_associate_params *params) +{ + struct wpa_driver_madwifi_data *drv = priv; + struct ieee80211req_mlme mlme; + int ret = 0, privacy = 1; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + /* + * NB: Don't need to set the freq or cipher-related state as + * this is implied by the bssid which is used to locate + * the scanned node state which holds it. The ssid is + * needed to disambiguate an AP that broadcasts multiple + * ssid's but uses the same bssid. + */ + /* XXX error handling is wrong but unclear what to do... */ + if (wpa_driver_madwifi_set_wpa_ie(drv, params->wpa_ie, + params->wpa_ie_len) < 0) + ret = -1; + + if (params->pairwise_suite == CIPHER_NONE && + params->group_suite == CIPHER_NONE && + params->key_mgmt_suite == KEY_MGMT_NONE && + params->wpa_ie_len == 0) + privacy = 0; + + if (set80211param(drv, IEEE80211_PARAM_PRIVACY, privacy, 1) < 0) + ret = -1; + + if (params->wpa_ie_len && + set80211param(drv, IEEE80211_PARAM_WPA, + params->wpa_ie[0] == WLAN_EID_RSN ? 2 : 1, 1) < 0) + ret = -1; + + if (params->bssid == NULL) { + /* ap_scan=2 mode - driver takes care of AP selection and + * roaming */ + /* FIX: this does not seem to work; would probably need to + * change something in the driver */ + if (set80211param(drv, IEEE80211_PARAM_ROAMING, 0, 1) < 0) + ret = -1; + + if (wpa_driver_wext_set_ssid(drv->wext, params->ssid, + params->ssid_len) < 0) + ret = -1; + } else { + if (set80211param(drv, IEEE80211_PARAM_ROAMING, 2, 1) < 0) + ret = -1; + if (wpa_driver_wext_set_ssid(drv->wext, params->ssid, + params->ssid_len) < 0) + ret = -1; + os_memset(&mlme, 0, sizeof(mlme)); + mlme.im_op = IEEE80211_MLME_ASSOC; + os_memcpy(mlme.im_macaddr, params->bssid, IEEE80211_ADDR_LEN); + if (set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, + sizeof(mlme), 1) < 0) { + wpa_printf(MSG_DEBUG, "%s: SETMLME[ASSOC] failed", + __func__); + ret = -1; + } + } + + return ret; +} + +static int +wpa_driver_madwifi_set_auth_alg(void *priv, int auth_alg) +{ + struct wpa_driver_madwifi_data *drv = priv; + int authmode; + + if ((auth_alg & AUTH_ALG_OPEN_SYSTEM) && + (auth_alg & AUTH_ALG_SHARED_KEY)) + authmode = IEEE80211_AUTH_AUTO; + else if (auth_alg & AUTH_ALG_SHARED_KEY) + authmode = IEEE80211_AUTH_SHARED; + else + authmode = IEEE80211_AUTH_OPEN; + + return set80211param(drv, IEEE80211_PARAM_AUTHMODE, authmode, 1); +} + +static int +wpa_driver_madwifi_scan(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_madwifi_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + + /* set desired ssid before scan */ + /* FIX: scan should not break the current association, so using + * set_ssid may not be the best way of doing this.. */ + if (wpa_driver_wext_set_ssid(drv->wext, ssid, ssid_len) < 0) + ret = -1; + + if (ioctl(drv->sock, SIOCSIWSCAN, &iwr) < 0) { + perror("ioctl[SIOCSIWSCAN]"); + ret = -1; + } + + /* + * madwifi delivers a scan complete event so no need to poll, but + * register a backup timeout anyway to make sure that we recover even + * if the driver does not send this event for any reason. This timeout + * will only be used if the event is not delivered (event handler will + * cancel the timeout). + */ + eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv->wext, + drv->ctx); + eloop_register_timeout(30, 0, wpa_driver_wext_scan_timeout, drv->wext, + drv->ctx); + + return ret; +} + +static int wpa_driver_madwifi_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_madwifi_data *drv = priv; + return wpa_driver_wext_get_bssid(drv->wext, bssid); +} + + +static int wpa_driver_madwifi_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_madwifi_data *drv = priv; + return wpa_driver_wext_get_ssid(drv->wext, ssid); +} + + +static struct wpa_scan_results * +wpa_driver_madwifi_get_scan_results(void *priv) +{ + struct wpa_driver_madwifi_data *drv = priv; + return wpa_driver_wext_get_scan_results(drv->wext); +} + + +static int wpa_driver_madwifi_set_operstate(void *priv, int state) +{ + struct wpa_driver_madwifi_data *drv = priv; + return wpa_driver_wext_set_operstate(drv->wext, state); +} + + +static int wpa_driver_madwifi_set_probe_req_ie(void *priv, const u8 *ies, + size_t ies_len) +{ + struct ieee80211req_getset_appiebuf *probe_req_ie; + int ret; + + probe_req_ie = os_malloc(sizeof(*probe_req_ie) + ies_len); + if (probe_req_ie == NULL) + return -1; + + probe_req_ie->app_frmtype = IEEE80211_APPIE_FRAME_PROBE_REQ; + probe_req_ie->app_buflen = ies_len; + os_memcpy(probe_req_ie->app_buf, ies, ies_len); + + ret = set80211priv(priv, IEEE80211_IOCTL_SET_APPIEBUF, probe_req_ie, + sizeof(struct ieee80211req_getset_appiebuf) + + ies_len, 1); + + os_free(probe_req_ie); + + return ret; +} + + +static void * wpa_driver_madwifi_init(void *ctx, const char *ifname) +{ + struct wpa_driver_madwifi_data *drv; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->wext = wpa_driver_wext_init(ctx, ifname); + if (drv->wext == NULL) + goto fail; + + drv->ctx = ctx; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + drv->sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->sock < 0) + goto fail2; + + if (set80211param(drv, IEEE80211_PARAM_ROAMING, 2, 1) < 0) { + wpa_printf(MSG_DEBUG, "%s: failed to set wpa_supplicant-based " + "roaming", __FUNCTION__); + goto fail3; + } + + if (set80211param(drv, IEEE80211_PARAM_WPA, 3, 1) < 0) { + wpa_printf(MSG_DEBUG, "%s: failed to enable WPA support", + __FUNCTION__); + goto fail3; + } + + return drv; + +fail3: + close(drv->sock); +fail2: + wpa_driver_wext_deinit(drv->wext); +fail: + os_free(drv); + return NULL; +} + + +static void wpa_driver_madwifi_deinit(void *priv) +{ + struct wpa_driver_madwifi_data *drv = priv; + + if (wpa_driver_madwifi_set_wpa_ie(drv, NULL, 0) < 0) { + wpa_printf(MSG_DEBUG, "%s: failed to clear WPA IE", + __FUNCTION__); + } + if (set80211param(drv, IEEE80211_PARAM_ROAMING, 0, 1) < 0) { + wpa_printf(MSG_DEBUG, "%s: failed to enable driver-based " + "roaming", __FUNCTION__); + } + if (set80211param(drv, IEEE80211_PARAM_PRIVACY, 0, 1) < 0) { + wpa_printf(MSG_DEBUG, "%s: failed to disable forced Privacy " + "flag", __FUNCTION__); + } + if (set80211param(drv, IEEE80211_PARAM_WPA, 0, 1) < 0) { + wpa_printf(MSG_DEBUG, "%s: failed to disable WPA", + __FUNCTION__); + } + + wpa_driver_wext_deinit(drv->wext); + + close(drv->sock); + os_free(drv); +} + + +const struct wpa_driver_ops wpa_driver_madwifi_ops = { + .name = "madwifi", + .desc = "MADWIFI 802.11 support (Atheros, etc.)", + .get_bssid = wpa_driver_madwifi_get_bssid, + .get_ssid = wpa_driver_madwifi_get_ssid, + .set_key = wpa_driver_madwifi_set_key, + .init = wpa_driver_madwifi_init, + .deinit = wpa_driver_madwifi_deinit, + .set_countermeasures = wpa_driver_madwifi_set_countermeasures, + .set_drop_unencrypted = wpa_driver_madwifi_set_drop_unencrypted, + .scan = wpa_driver_madwifi_scan, + .get_scan_results2 = wpa_driver_madwifi_get_scan_results, + .deauthenticate = wpa_driver_madwifi_deauthenticate, + .disassociate = wpa_driver_madwifi_disassociate, + .associate = wpa_driver_madwifi_associate, + .set_auth_alg = wpa_driver_madwifi_set_auth_alg, + .set_operstate = wpa_driver_madwifi_set_operstate, + .set_probe_req_ie = wpa_driver_madwifi_set_probe_req_ie, +}; diff --git a/src/drivers/driver_ndis.c b/src/drivers/driver_ndis.c index b22109b..a90e277 100644 --- a/src/drivers/driver_ndis.c +++ b/src/drivers/driver_ndis.c @@ -56,6 +56,10 @@ static int wpa_driver_ndis_adapter_open(struct wpa_driver_ndis_data *drv); static void wpa_driver_ndis_adapter_close(struct wpa_driver_ndis_data *drv); +static const u8 pae_group_addr[ETH_ALEN] = +{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }; + + /* FIX: to be removed once this can be compiled with the complete NDIS * header files */ #ifndef OID_802_11_BSSID @@ -610,12 +614,7 @@ static int wpa_driver_ndis_get_bssid(void *priv, u8 *bssid) * Report PAE group address as the "BSSID" for wired * connection. */ - bssid[0] = 0x01; - bssid[1] = 0x80; - bssid[2] = 0xc2; - bssid[3] = 0x00; - bssid[4] = 0x00; - bssid[5] = 0x03; + os_memcpy(bssid, pae_group_addr, ETH_ALEN); return 0; } @@ -2704,6 +2703,19 @@ static void wpa_driver_ndis_adapter_close(struct wpa_driver_ndis_data *drv) } +static int ndis_add_multicast(struct wpa_driver_ndis_data *drv) +{ + if (ndis_set_oid(drv, OID_802_3_MULTICAST_LIST, + (const char *) pae_group_addr, ETH_ALEN) < 0) { + wpa_printf(MSG_DEBUG, "NDIS: Failed to add PAE group address " + "to the multicast list"); + return -1; + } + + return 0; +} + + static void * wpa_driver_ndis_init(void *ctx, const char *ifname) { struct wpa_driver_ndis_data *drv; @@ -2799,6 +2811,7 @@ static void * wpa_driver_ndis_init(void *ctx, const char *ifname) "any wireless capabilities - assume it is " "a wired interface"); drv->wired = 1; + ndis_add_multicast(drv); } } diff --git a/src/drivers/driver_ndis_.c b/src/drivers/driver_ndis_.c new file mode 100644 index 0000000..4bee9aa --- /dev/null +++ b/src/drivers/driver_ndis_.c @@ -0,0 +1,105 @@ +/* + * WPA Supplicant - Windows/NDIS driver interface - event processing + * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "driver.h" +#include "eloop.h" + +/* Keep this event processing in a separate file and without WinPcap headers to + * avoid conflicts with some of the header files. */ +struct _ADAPTER; +typedef struct _ADAPTER * LPADAPTER; +#include "driver_ndis.h" + + +void wpa_driver_ndis_event_connect(struct wpa_driver_ndis_data *drv); +void wpa_driver_ndis_event_disconnect(struct wpa_driver_ndis_data *drv); +void wpa_driver_ndis_event_media_specific(struct wpa_driver_ndis_data *drv, + const u8 *data, size_t data_len); +void wpa_driver_ndis_event_adapter_arrival(struct wpa_driver_ndis_data *drv); +void wpa_driver_ndis_event_adapter_removal(struct wpa_driver_ndis_data *drv); + + +enum event_types { EVENT_CONNECT, EVENT_DISCONNECT, + EVENT_MEDIA_SPECIFIC, EVENT_ADAPTER_ARRIVAL, + EVENT_ADAPTER_REMOVAL }; + +/* Event data: + * enum event_types (as int, i.e., 4 octets) + * data length (2 octets (big endian), optional) + * data (variable len, optional) + */ + + +static void wpa_driver_ndis_event_process(struct wpa_driver_ndis_data *drv, + u8 *buf, size_t len) +{ + u8 *pos, *data = NULL; + enum event_types type; + size_t data_len = 0; + + wpa_hexdump(MSG_MSGDUMP, "NDIS: received event data", buf, len); + if (len < sizeof(int)) + return; + type = *((int *) buf); + pos = buf + sizeof(int); + wpa_printf(MSG_DEBUG, "NDIS: event - type %d", type); + + if (buf + len - pos > 2) { + data_len = (int) *pos++ << 8; + data_len += *pos++; + if (data_len > (size_t) (buf + len - pos)) { + wpa_printf(MSG_DEBUG, "NDIS: event data overflow"); + return; + } + data = pos; + wpa_hexdump(MSG_MSGDUMP, "NDIS: event data", data, data_len); + } + + switch (type) { + case EVENT_CONNECT: + wpa_driver_ndis_event_connect(drv); + break; + case EVENT_DISCONNECT: + wpa_driver_ndis_event_disconnect(drv); + break; + case EVENT_MEDIA_SPECIFIC: + wpa_driver_ndis_event_media_specific(drv, data, data_len); + break; + case EVENT_ADAPTER_ARRIVAL: + wpa_driver_ndis_event_adapter_arrival(drv); + break; + case EVENT_ADAPTER_REMOVAL: + wpa_driver_ndis_event_adapter_removal(drv); + break; + } +} + + +void wpa_driver_ndis_event_pipe_cb(void *eloop_data, void *user_data) +{ + struct wpa_driver_ndis_data *drv = eloop_data; + u8 buf[512]; + DWORD len; + + ResetEvent(drv->event_avail); + if (ReadFile(drv->events_pipe, buf, sizeof(buf), &len, NULL)) + wpa_driver_ndis_event_process(drv, buf, len); + else { + wpa_printf(MSG_DEBUG, "%s: ReadFile() failed: %d", __func__, + (int) GetLastError()); + } +} diff --git a/src/drivers/driver_ndiswrapper.c b/src/drivers/driver_ndiswrapper.c new file mode 100644 index 0000000..b5c534a --- /dev/null +++ b/src/drivers/driver_ndiswrapper.c @@ -0,0 +1,370 @@ +/* + * WPA Supplicant - driver interaction with Linux ndiswrapper + * Copyright (c) 2004-2006, Giridhar Pemmasani <giri@lmc.cs.sunysb.edu> + * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * Please note that ndiswrapper supports WPA configuration via Linux wireless + * extensions and if the kernel includes support for this, driver_wext.c should + * be used instead of this driver wrapper. + */ + +#include "includes.h" +#include <sys/ioctl.h> + +#include "wireless_copy.h" +#include "common.h" +#include "driver.h" +#include "driver_wext.h" + +struct wpa_driver_ndiswrapper_data { + void *wext; /* private data for driver_wext */ + void *ctx; + char ifname[IFNAMSIZ + 1]; + int sock; +}; + + +struct wpa_key +{ + wpa_alg alg; + const u8 *addr; + int key_index; + int set_tx; + const u8 *seq; + size_t seq_len; + const u8 *key; + size_t key_len; +}; + +struct wpa_assoc_info +{ + const u8 *bssid; + const u8 *ssid; + size_t ssid_len; + int freq; + const u8 *wpa_ie; + size_t wpa_ie_len; + wpa_cipher pairwise_suite; + wpa_cipher group_suite; + wpa_key_mgmt key_mgmt_suite; + int auth_alg; + int mode; +}; + +#define PRIV_RESET SIOCIWFIRSTPRIV+0 +#define WPA_SET_WPA SIOCIWFIRSTPRIV+1 +#define WPA_SET_KEY SIOCIWFIRSTPRIV+2 +#define WPA_ASSOCIATE SIOCIWFIRSTPRIV+3 +#define WPA_DISASSOCIATE SIOCIWFIRSTPRIV+4 +#define WPA_DROP_UNENCRYPTED SIOCIWFIRSTPRIV+5 +#define WPA_SET_COUNTERMEASURES SIOCIWFIRSTPRIV+6 +#define WPA_DEAUTHENTICATE SIOCIWFIRSTPRIV+7 +#define WPA_SET_AUTH_ALG SIOCIWFIRSTPRIV+8 +#define WPA_INIT SIOCIWFIRSTPRIV+9 +#define WPA_DEINIT SIOCIWFIRSTPRIV+10 +#define WPA_GET_CAPA SIOCIWFIRSTPRIV+11 + +static int get_socket(void) +{ + static const int families[] = { + AF_INET, AF_IPX, AF_AX25, AF_APPLETALK + }; + unsigned int i; + int sock; + + for (i = 0; i < sizeof(families) / sizeof(int); ++i) { + sock = socket(families[i], SOCK_DGRAM, 0); + if (sock >= 0) + return sock; + } + + return -1; +} + +static int iw_set_ext(struct wpa_driver_ndiswrapper_data *drv, int request, + struct iwreq *pwrq) +{ + os_strlcpy(pwrq->ifr_name, drv->ifname, IFNAMSIZ); + return ioctl(drv->sock, request, pwrq); +} + +static int wpa_ndiswrapper_set_wpa(void *priv, int enabled) +{ + struct wpa_driver_ndiswrapper_data *drv = priv; + struct iwreq priv_req; + int ret = 0; + + os_memset(&priv_req, 0, sizeof(priv_req)); + + priv_req.u.data.flags = enabled; + if (iw_set_ext(drv, WPA_SET_WPA, &priv_req) < 0) + ret = -1; + return ret; +} + +static int wpa_ndiswrapper_set_key(void *priv, wpa_alg alg, const u8 *addr, + int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + struct wpa_driver_ndiswrapper_data *drv = priv; + struct wpa_key wpa_key; + int ret = 0; + struct iwreq priv_req; + + os_memset(&priv_req, 0, sizeof(priv_req)); + + wpa_key.alg = alg; + wpa_key.addr = addr; + wpa_key.key_index = key_idx; + wpa_key.set_tx = set_tx; + wpa_key.seq = seq; + wpa_key.seq_len = seq_len; + wpa_key.key = key; + wpa_key.key_len = key_len; + + priv_req.u.data.pointer = (void *)&wpa_key; + priv_req.u.data.length = sizeof(wpa_key); + + if (iw_set_ext(drv, WPA_SET_KEY, &priv_req) < 0) + ret = -1; + + if (alg == WPA_ALG_NONE) { + /* + * ndiswrapper did not seem to be clearing keys properly in + * some cases with WPA_SET_KEY. For example, roaming from WPA + * enabled AP to plaintext one seemed to fail since the driver + * did not associate. Try to make sure the keys are cleared so + * that plaintext APs can be used in all cases. + */ + wpa_driver_wext_set_key(drv->wext, alg, addr, key_idx, set_tx, + seq, seq_len, key, key_len); + } + + return ret; +} + +static int wpa_ndiswrapper_set_countermeasures(void *priv, int enabled) +{ + struct wpa_driver_ndiswrapper_data *drv = priv; + int ret = 0; + struct iwreq priv_req; + + os_memset(&priv_req, 0, sizeof(priv_req)); + + priv_req.u.param.value = enabled; + if (iw_set_ext(drv, WPA_SET_COUNTERMEASURES, &priv_req) < 0) + ret = -1; + + return ret; +} + +static int wpa_ndiswrapper_set_drop_unencrypted(void *priv, + int enabled) +{ + struct wpa_driver_ndiswrapper_data *drv = priv; + int ret = 0; + struct iwreq priv_req; + + os_memset(&priv_req, 0, sizeof(priv_req)); + + priv_req.u.param.value = enabled; + if (iw_set_ext(drv, WPA_DROP_UNENCRYPTED, &priv_req) < 0) + ret = -1; + return ret; +} + +static int wpa_ndiswrapper_deauthenticate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_ndiswrapper_data *drv = priv; + int ret = 0; + struct iwreq priv_req; + + os_memset(&priv_req, 0, sizeof(priv_req)); + + priv_req.u.param.value = reason_code; + os_memcpy(&priv_req.u.ap_addr.sa_data, addr, ETH_ALEN); + if (iw_set_ext(drv, WPA_DEAUTHENTICATE, &priv_req) < 0) + ret = -1; + return ret; +} + +static int wpa_ndiswrapper_disassociate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_ndiswrapper_data *drv = priv; + int ret = 0; + struct iwreq priv_req; + + os_memset(&priv_req, 0, sizeof(priv_req)); + + os_memcpy(&priv_req.u.ap_addr.sa_data, addr, ETH_ALEN); + if (iw_set_ext(drv, WPA_DISASSOCIATE, &priv_req) < 0) + ret = -1; + return ret; +} + +static int +wpa_ndiswrapper_associate(void *priv, + struct wpa_driver_associate_params *params) +{ + struct wpa_driver_ndiswrapper_data *drv = priv; + int ret = 0; + struct wpa_assoc_info wpa_assoc_info; + struct iwreq priv_req; + + os_memset(&priv_req, 0, sizeof(priv_req)); + os_memset(&wpa_assoc_info, 0, sizeof(wpa_assoc_info)); + + wpa_assoc_info.bssid = params->bssid; + wpa_assoc_info.ssid = params->ssid; + wpa_assoc_info.ssid_len = params->ssid_len; + wpa_assoc_info.freq = params->freq; + wpa_assoc_info.wpa_ie = params->wpa_ie; + wpa_assoc_info.wpa_ie_len = params->wpa_ie_len; + wpa_assoc_info.pairwise_suite = params->pairwise_suite; + wpa_assoc_info.group_suite = params->group_suite; + wpa_assoc_info.key_mgmt_suite = params->key_mgmt_suite; + wpa_assoc_info.auth_alg = params->auth_alg; + wpa_assoc_info.mode = params->mode; + + priv_req.u.data.pointer = (void *)&wpa_assoc_info; + priv_req.u.data.length = sizeof(wpa_assoc_info); + + if (iw_set_ext(drv, WPA_ASSOCIATE, &priv_req) < 0) + ret = -1; + return ret; +} + +static int wpa_ndiswrapper_set_auth_alg(void *priv, int auth_alg) +{ + struct wpa_driver_ndiswrapper_data *drv = priv; + int ret = 0; + struct iwreq priv_req; + + os_memset(&priv_req, 0, sizeof(priv_req)); + + priv_req.u.param.value = auth_alg; + if (iw_set_ext(drv, WPA_SET_AUTH_ALG, &priv_req) < 0) + ret = -1; + return ret; +} + +static int wpa_ndiswrapper_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_ndiswrapper_data *drv = priv; + return wpa_driver_wext_get_bssid(drv->wext, bssid); +} + + +static int wpa_ndiswrapper_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_ndiswrapper_data *drv = priv; + return wpa_driver_wext_get_ssid(drv->wext, ssid); +} + + +static int wpa_ndiswrapper_scan(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_ndiswrapper_data *drv = priv; + return wpa_driver_wext_scan(drv->wext, ssid, ssid_len); +} + + +static struct wpa_scan_results * wpa_ndiswrapper_get_scan_results(void *priv) +{ + struct wpa_driver_ndiswrapper_data *drv = priv; + return wpa_driver_wext_get_scan_results(drv->wext); +} + + +static int wpa_ndiswrapper_get_capa(void *priv, struct wpa_driver_capa *capa) +{ + struct wpa_driver_ndiswrapper_data *drv = priv; + int ret = 0; + struct iwreq priv_req; + + os_memset(&priv_req, 0, sizeof(priv_req)); + + priv_req.u.data.pointer = (void *) capa; + priv_req.u.data.length = sizeof(*capa); + if (iw_set_ext(drv, WPA_GET_CAPA, &priv_req) < 0) + ret = -1; + return ret; + +} + + +static int wpa_ndiswrapper_set_operstate(void *priv, int state) +{ + struct wpa_driver_ndiswrapper_data *drv = priv; + return wpa_driver_wext_set_operstate(drv->wext, state); +} + + +static void * wpa_ndiswrapper_init(void *ctx, const char *ifname) +{ + struct wpa_driver_ndiswrapper_data *drv; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->wext = wpa_driver_wext_init(ctx, ifname); + if (drv->wext == NULL) { + os_free(drv); + return NULL; + } + + drv->ctx = ctx; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + drv->sock = get_socket(); + if (drv->sock < 0) { + wpa_driver_wext_deinit(drv->wext); + os_free(drv); + return NULL; + } + + return drv; +} + + +static void wpa_ndiswrapper_deinit(void *priv) +{ + struct wpa_driver_ndiswrapper_data *drv = priv; + wpa_driver_wext_deinit(drv->wext); + close(drv->sock); + os_free(drv); +} + + +const struct wpa_driver_ops wpa_driver_ndiswrapper_ops = { + .name = "ndiswrapper", + .desc = "Linux ndiswrapper (deprecated; use wext)", + .set_wpa = wpa_ndiswrapper_set_wpa, + .set_key = wpa_ndiswrapper_set_key, + .set_countermeasures = wpa_ndiswrapper_set_countermeasures, + .set_drop_unencrypted = wpa_ndiswrapper_set_drop_unencrypted, + .deauthenticate = wpa_ndiswrapper_deauthenticate, + .disassociate = wpa_ndiswrapper_disassociate, + .associate = wpa_ndiswrapper_associate, + .set_auth_alg = wpa_ndiswrapper_set_auth_alg, + + .get_bssid = wpa_ndiswrapper_get_bssid, + .get_ssid = wpa_ndiswrapper_get_ssid, + .scan = wpa_ndiswrapper_scan, + .get_scan_results2 = wpa_ndiswrapper_get_scan_results, + .init = wpa_ndiswrapper_init, + .deinit = wpa_ndiswrapper_deinit, + .get_capa = wpa_ndiswrapper_get_capa, + .set_operstate = wpa_ndiswrapper_set_operstate, +}; diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c new file mode 100644 index 0000000..9ab6d17 --- /dev/null +++ b/src/drivers/driver_nl80211.c @@ -0,0 +1,2766 @@ +/* + * WPA Supplicant - driver interaction with Linux nl80211/cfg80211 + * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <sys/ioctl.h> +#include <net/if_arp.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/family.h> +#include <netlink/genl/ctrl.h> +#include "nl80211_copy.h" +#ifdef CONFIG_CLIENT_MLME +#include <netpacket/packet.h> +#include <linux/if_ether.h> +#include "radiotap.h" +#include "radiotap_iter.h" +#endif /* CONFIG_CLIENT_MLME */ + +#include "wireless_copy.h" +#include "common.h" +#include "driver.h" +#include "eloop.h" +#include "ieee802_11_defs.h" + +#ifndef IFF_LOWER_UP +#define IFF_LOWER_UP 0x10000 /* driver signals L1 up */ +#endif +#ifndef IFF_DORMANT +#define IFF_DORMANT 0x20000 /* driver signals dormant */ +#endif + +#ifndef IF_OPER_DORMANT +#define IF_OPER_DORMANT 5 +#endif +#ifndef IF_OPER_UP +#define IF_OPER_UP 6 +#endif + + +struct wpa_driver_nl80211_data { + void *ctx; + int wext_event_sock; + int ioctl_sock; + char ifname[IFNAMSIZ + 1]; + int ifindex; + int if_removed; + u8 *assoc_req_ies; + size_t assoc_req_ies_len; + u8 *assoc_resp_ies; + size_t assoc_resp_ies_len; + struct wpa_driver_capa capa; + int has_capability; + int we_version_compiled; + + /* for set_auth_alg fallback */ + int use_crypt; + int auth_alg_fallback; + + int operstate; + + char mlmedev[IFNAMSIZ + 1]; + + int scan_complete_events; + + struct nl_handle *nl_handle; + struct nl_cache *nl_cache; + struct nl_cb *nl_cb; + struct genl_family *nl80211; + +#ifdef CONFIG_CLIENT_MLME + int monitor_sock; /* socket for monitor */ + int monitor_ifidx; +#endif /* CONFIG_CLIENT_MLME */ +}; + + +static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, + void *timeout_ctx); +static int wpa_driver_nl80211_set_mode(void *priv, int mode); +static int wpa_driver_nl80211_flush_pmkid(void *priv); +static int wpa_driver_nl80211_get_range(void *priv); +static void +wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv); + + +/* nl80211 code */ +static int ack_handler(struct nl_msg *msg, void *arg) +{ + int *err = arg; + *err = 0; + return NL_STOP; +} + +static int finish_handler(struct nl_msg *msg, void *arg) +{ + int *ret = arg; + *ret = 0; + return NL_SKIP; +} + +static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, + void *arg) +{ + int *ret = arg; + *ret = err->error; + return NL_SKIP; +} + +static int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv, + struct nl_msg *msg, + int (*valid_handler)(struct nl_msg *, void *), + void *valid_data) +{ + struct nl_cb *cb; + int err = -ENOMEM; + + cb = nl_cb_clone(drv->nl_cb); + if (!cb) + goto out; + + err = nl_send_auto_complete(drv->nl_handle, msg); + if (err < 0) + goto out; + + err = 1; + + nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err); + nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err); + nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err); + + if (valid_handler) + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, + valid_handler, valid_data); + + while (err > 0) + nl_recvmsgs(drv->nl_handle, cb); + out: + nl_cb_put(cb); + nlmsg_free(msg); + return err; +} + + +struct family_data { + const char *group; + int id; +}; + + +static int family_handler(struct nl_msg *msg, void *arg) +{ + struct family_data *res = arg; + struct nlattr *tb[CTRL_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *mcgrp; + int i; + + nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + if (!tb[CTRL_ATTR_MCAST_GROUPS]) + return NL_SKIP; + + nla_for_each_nested(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], i) { + struct nlattr *tb2[CTRL_ATTR_MCAST_GRP_MAX + 1]; + nla_parse(tb2, CTRL_ATTR_MCAST_GRP_MAX, nla_data(mcgrp), + nla_len(mcgrp), NULL); + if (!tb2[CTRL_ATTR_MCAST_GRP_NAME] || + !tb2[CTRL_ATTR_MCAST_GRP_ID] || + os_strncmp(nla_data(tb2[CTRL_ATTR_MCAST_GRP_NAME]), + res->group, + nla_len(tb2[CTRL_ATTR_MCAST_GRP_NAME])) != 0) + continue; + res->id = nla_get_u32(tb2[CTRL_ATTR_MCAST_GRP_ID]); + break; + }; + + return NL_SKIP; +} + + +static int nl_get_multicast_id(struct wpa_driver_nl80211_data *drv, + const char *family, const char *group) +{ + struct nl_msg *msg; + int ret = -1; + struct family_data res = { group, -ENOENT }; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + genlmsg_put(msg, 0, 0, genl_ctrl_resolve(drv->nl_handle, "nlctrl"), + 0, 0, CTRL_CMD_GETFAMILY, 0); + NLA_PUT_STRING(msg, CTRL_ATTR_FAMILY_NAME, family); + + ret = send_and_recv_msgs(drv, msg, family_handler, &res); + msg = NULL; + if (ret == 0) + ret = res.id; + +nla_put_failure: + nlmsg_free(msg); + return ret; +} + + +static int wpa_driver_nl80211_send_oper_ifla( + struct wpa_driver_nl80211_data *drv, + int linkmode, int operstate) +{ + struct { + struct nlmsghdr hdr; + struct ifinfomsg ifinfo; + char opts[16]; + } req; + struct rtattr *rta; + static int nl_seq; + ssize_t ret; + + os_memset(&req, 0, sizeof(req)); + + req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + req.hdr.nlmsg_type = RTM_SETLINK; + req.hdr.nlmsg_flags = NLM_F_REQUEST; + req.hdr.nlmsg_seq = ++nl_seq; + req.hdr.nlmsg_pid = 0; + + req.ifinfo.ifi_family = AF_UNSPEC; + req.ifinfo.ifi_type = 0; + req.ifinfo.ifi_index = drv->ifindex; + req.ifinfo.ifi_flags = 0; + req.ifinfo.ifi_change = 0; + + if (linkmode != -1) { + rta = aliasing_hide_typecast( + ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)), + struct rtattr); + rta->rta_type = IFLA_LINKMODE; + rta->rta_len = RTA_LENGTH(sizeof(char)); + *((char *) RTA_DATA(rta)) = linkmode; + req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + + RTA_LENGTH(sizeof(char)); + } + if (operstate != -1) { + rta = (struct rtattr *) + ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)); + rta->rta_type = IFLA_OPERSTATE; + rta->rta_len = RTA_LENGTH(sizeof(char)); + *((char *) RTA_DATA(rta)) = operstate; + req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + + RTA_LENGTH(sizeof(char)); + } + + wpa_printf(MSG_DEBUG, "WEXT: Operstate: linkmode=%d, operstate=%d", + linkmode, operstate); + + ret = send(drv->wext_event_sock, &req, req.hdr.nlmsg_len, 0); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "WEXT: Sending operstate IFLA failed: " + "%s (assume operstate is not supported)", + strerror(errno)); + } + + return ret < 0 ? -1 : 0; +} + + +static int wpa_driver_nl80211_set_auth_param( + struct wpa_driver_nl80211_data *drv, int idx, u32 value) +{ + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.param.flags = idx & IW_AUTH_INDEX; + iwr.u.param.value = value; + + if (ioctl(drv->ioctl_sock, SIOCSIWAUTH, &iwr) < 0) { + if (errno != EOPNOTSUPP) { + wpa_printf(MSG_DEBUG, "WEXT: SIOCSIWAUTH(param %d " + "value 0x%x) failed: %s)", + idx, value, strerror(errno)); + } + ret = errno == EOPNOTSUPP ? -2 : -1; + } + + return ret; +} + + +static int wpa_driver_nl80211_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_nl80211_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + + if (ioctl(drv->ioctl_sock, SIOCGIWAP, &iwr) < 0) { + perror("ioctl[SIOCGIWAP]"); + ret = -1; + } + os_memcpy(bssid, iwr.u.ap_addr.sa_data, ETH_ALEN); + + return ret; +} + + +static int wpa_driver_nl80211_set_bssid(void *priv, const u8 *bssid) +{ + struct wpa_driver_nl80211_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.ap_addr.sa_family = ARPHRD_ETHER; + if (bssid) + os_memcpy(iwr.u.ap_addr.sa_data, bssid, ETH_ALEN); + else + os_memset(iwr.u.ap_addr.sa_data, 0, ETH_ALEN); + + if (ioctl(drv->ioctl_sock, SIOCSIWAP, &iwr) < 0) { + perror("ioctl[SIOCSIWAP]"); + ret = -1; + } + + return ret; +} + + +static int wpa_driver_nl80211_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_nl80211_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.essid.pointer = (caddr_t) ssid; + iwr.u.essid.length = 32; + + if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) { + perror("ioctl[SIOCGIWESSID]"); + ret = -1; + } else { + ret = iwr.u.essid.length; + if (ret > 32) + ret = 32; + /* Some drivers include nul termination in the SSID, so let's + * remove it here before further processing. WE-21 changes this + * to explicitly require the length _not_ to include nul + * termination. */ + if (ret > 0 && ssid[ret - 1] == '\0' && + drv->we_version_compiled < 21) + ret--; + } + + return ret; +} + + +static int wpa_driver_nl80211_set_ssid(void *priv, const u8 *ssid, + size_t ssid_len) +{ + struct wpa_driver_nl80211_data *drv = priv; + struct iwreq iwr; + int ret = 0; + char buf[33]; + + if (ssid_len > 32) + return -1; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + /* flags: 1 = ESSID is active, 0 = not (promiscuous) */ + iwr.u.essid.flags = (ssid_len != 0); + os_memset(buf, 0, sizeof(buf)); + os_memcpy(buf, ssid, ssid_len); + iwr.u.essid.pointer = (caddr_t) buf; + if (drv->we_version_compiled < 21) { + /* For historic reasons, set SSID length to include one extra + * character, C string nul termination, even though SSID is + * really an octet string that should not be presented as a C + * string. Some Linux drivers decrement the length by one and + * can thus end up missing the last octet of the SSID if the + * length is not incremented here. WE-21 changes this to + * explicitly require the length _not_ to include nul + * termination. */ + if (ssid_len) + ssid_len++; + } + iwr.u.essid.length = ssid_len; + + if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) { + perror("ioctl[SIOCSIWESSID]"); + ret = -1; + } + + return ret; +} + + +static int wpa_driver_nl80211_set_freq(void *priv, int freq) +{ + struct wpa_driver_nl80211_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.freq.m = freq * 100000; + iwr.u.freq.e = 1; + + if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) { + perror("ioctl[SIOCSIWFREQ]"); + ret = -1; + } + + return ret; +} + + +static void +wpa_driver_nl80211_event_wireless_custom(void *ctx, char *custom) +{ + union wpa_event_data data; + + wpa_printf(MSG_MSGDUMP, "WEXT: Custom wireless event: '%s'", + custom); + + os_memset(&data, 0, sizeof(data)); + /* Host AP driver */ + if (os_strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) { + data.michael_mic_failure.unicast = + os_strstr(custom, " unicast ") != NULL; + /* TODO: parse parameters(?) */ + wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); + } else if (os_strncmp(custom, "ASSOCINFO(ReqIEs=", 17) == 0) { + char *spos; + int bytes; + + spos = custom + 17; + + bytes = strspn(spos, "0123456789abcdefABCDEF"); + if (!bytes || (bytes & 1)) + return; + bytes /= 2; + + data.assoc_info.req_ies = os_malloc(bytes); + if (data.assoc_info.req_ies == NULL) + return; + + data.assoc_info.req_ies_len = bytes; + hexstr2bin(spos, data.assoc_info.req_ies, bytes); + + spos += bytes * 2; + + data.assoc_info.resp_ies = NULL; + data.assoc_info.resp_ies_len = 0; + + if (os_strncmp(spos, " RespIEs=", 9) == 0) { + spos += 9; + + bytes = strspn(spos, "0123456789abcdefABCDEF"); + if (!bytes || (bytes & 1)) + goto done; + bytes /= 2; + + data.assoc_info.resp_ies = os_malloc(bytes); + if (data.assoc_info.resp_ies == NULL) + goto done; + + data.assoc_info.resp_ies_len = bytes; + hexstr2bin(spos, data.assoc_info.resp_ies, bytes); + } + + wpa_supplicant_event(ctx, EVENT_ASSOCINFO, &data); + + done: + os_free(data.assoc_info.resp_ies); + os_free(data.assoc_info.req_ies); +#ifdef CONFIG_PEERKEY + } else if (os_strncmp(custom, "STKSTART.request=", 17) == 0) { + if (hwaddr_aton(custom + 17, data.stkstart.peer)) { + wpa_printf(MSG_DEBUG, "WEXT: unrecognized " + "STKSTART.request '%s'", custom + 17); + return; + } + wpa_supplicant_event(ctx, EVENT_STKSTART, &data); +#endif /* CONFIG_PEERKEY */ + } +} + + +static int wpa_driver_nl80211_event_wireless_michaelmicfailure( + void *ctx, const char *ev, size_t len) +{ + const struct iw_michaelmicfailure *mic; + union wpa_event_data data; + + if (len < sizeof(*mic)) + return -1; + + mic = (const struct iw_michaelmicfailure *) ev; + + wpa_printf(MSG_DEBUG, "Michael MIC failure wireless event: " + "flags=0x%x src_addr=" MACSTR, mic->flags, + MAC2STR(mic->src_addr.sa_data)); + + os_memset(&data, 0, sizeof(data)); + data.michael_mic_failure.unicast = !(mic->flags & IW_MICFAILURE_GROUP); + wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); + + return 0; +} + + +static int wpa_driver_nl80211_event_wireless_pmkidcand( + struct wpa_driver_nl80211_data *drv, const char *ev, size_t len) +{ + const struct iw_pmkid_cand *cand; + union wpa_event_data data; + const u8 *addr; + + if (len < sizeof(*cand)) + return -1; + + cand = (const struct iw_pmkid_cand *) ev; + addr = (const u8 *) cand->bssid.sa_data; + + wpa_printf(MSG_DEBUG, "PMKID candidate wireless event: " + "flags=0x%x index=%d bssid=" MACSTR, cand->flags, + cand->index, MAC2STR(addr)); + + os_memset(&data, 0, sizeof(data)); + os_memcpy(data.pmkid_candidate.bssid, addr, ETH_ALEN); + data.pmkid_candidate.index = cand->index; + data.pmkid_candidate.preauth = cand->flags & IW_PMKID_CAND_PREAUTH; + wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE, &data); + + return 0; +} + + +static int wpa_driver_nl80211_event_wireless_assocreqie( + struct wpa_driver_nl80211_data *drv, const char *ev, int len) +{ + if (len < 0) + return -1; + + wpa_hexdump(MSG_DEBUG, "AssocReq IE wireless event", (const u8 *) ev, + len); + os_free(drv->assoc_req_ies); + drv->assoc_req_ies = os_malloc(len); + if (drv->assoc_req_ies == NULL) { + drv->assoc_req_ies_len = 0; + return -1; + } + os_memcpy(drv->assoc_req_ies, ev, len); + drv->assoc_req_ies_len = len; + + return 0; +} + + +static int wpa_driver_nl80211_event_wireless_assocrespie( + struct wpa_driver_nl80211_data *drv, const char *ev, int len) +{ + if (len < 0) + return -1; + + wpa_hexdump(MSG_DEBUG, "AssocResp IE wireless event", (const u8 *) ev, + len); + os_free(drv->assoc_resp_ies); + drv->assoc_resp_ies = os_malloc(len); + if (drv->assoc_resp_ies == NULL) { + drv->assoc_resp_ies_len = 0; + return -1; + } + os_memcpy(drv->assoc_resp_ies, ev, len); + drv->assoc_resp_ies_len = len; + + return 0; +} + + +static void wpa_driver_nl80211_event_assoc_ies(struct wpa_driver_nl80211_data *drv) +{ + union wpa_event_data data; + + if (drv->assoc_req_ies == NULL && drv->assoc_resp_ies == NULL) + return; + + os_memset(&data, 0, sizeof(data)); + if (drv->assoc_req_ies) { + data.assoc_info.req_ies = drv->assoc_req_ies; + drv->assoc_req_ies = NULL; + data.assoc_info.req_ies_len = drv->assoc_req_ies_len; + } + if (drv->assoc_resp_ies) { + data.assoc_info.resp_ies = drv->assoc_resp_ies; + drv->assoc_resp_ies = NULL; + data.assoc_info.resp_ies_len = drv->assoc_resp_ies_len; + } + + wpa_supplicant_event(drv->ctx, EVENT_ASSOCINFO, &data); + + os_free(data.assoc_info.req_ies); + os_free(data.assoc_info.resp_ies); +} + + +static void wpa_driver_nl80211_event_wireless(struct wpa_driver_nl80211_data *drv, + void *ctx, char *data, int len) +{ + struct iw_event iwe_buf, *iwe = &iwe_buf; + char *pos, *end, *custom, *buf; + + pos = data; + end = data + len; + + while (pos + IW_EV_LCP_LEN <= end) { + /* Event data may be unaligned, so make a local, aligned copy + * before processing. */ + os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); + wpa_printf(MSG_DEBUG, "Wireless event: cmd=0x%x len=%d", + iwe->cmd, iwe->len); + if (iwe->len <= IW_EV_LCP_LEN) + return; + + custom = pos + IW_EV_POINT_LEN; + if (drv->we_version_compiled > 18 && + (iwe->cmd == IWEVMICHAELMICFAILURE || + iwe->cmd == IWEVCUSTOM || + iwe->cmd == IWEVASSOCREQIE || + iwe->cmd == IWEVASSOCRESPIE || + iwe->cmd == IWEVPMKIDCAND)) { + /* WE-19 removed the pointer from struct iw_point */ + char *dpos = (char *) &iwe_buf.u.data.length; + int dlen = dpos - (char *) &iwe_buf; + os_memcpy(dpos, pos + IW_EV_LCP_LEN, + sizeof(struct iw_event) - dlen); + } else { + os_memcpy(&iwe_buf, pos, sizeof(struct iw_event)); + custom += IW_EV_POINT_OFF; + } + + switch (iwe->cmd) { + case SIOCGIWAP: + wpa_printf(MSG_DEBUG, "Wireless event: new AP: " + MACSTR, + MAC2STR((u8 *) iwe->u.ap_addr.sa_data)); + if (is_zero_ether_addr( + (const u8 *) iwe->u.ap_addr.sa_data) || + os_memcmp(iwe->u.ap_addr.sa_data, + "\x44\x44\x44\x44\x44\x44", ETH_ALEN) == + 0) { + os_free(drv->assoc_req_ies); + drv->assoc_req_ies = NULL; + os_free(drv->assoc_resp_ies); + drv->assoc_resp_ies = NULL; + wpa_supplicant_event(ctx, EVENT_DISASSOC, + NULL); + + } else { + wpa_driver_nl80211_event_assoc_ies(drv); + wpa_supplicant_event(ctx, EVENT_ASSOC, NULL); + } + break; + case IWEVMICHAELMICFAILURE: + wpa_driver_nl80211_event_wireless_michaelmicfailure( + ctx, custom, iwe->u.data.length); + break; + case IWEVCUSTOM: + if (custom + iwe->u.data.length > end) + return; + buf = os_malloc(iwe->u.data.length + 1); + if (buf == NULL) + return; + os_memcpy(buf, custom, iwe->u.data.length); + buf[iwe->u.data.length] = '\0'; + wpa_driver_nl80211_event_wireless_custom(ctx, buf); + os_free(buf); + break; + case IWEVASSOCREQIE: + wpa_driver_nl80211_event_wireless_assocreqie( + drv, custom, iwe->u.data.length); + break; + case IWEVASSOCRESPIE: + wpa_driver_nl80211_event_wireless_assocrespie( + drv, custom, iwe->u.data.length); + break; + case IWEVPMKIDCAND: + wpa_driver_nl80211_event_wireless_pmkidcand( + drv, custom, iwe->u.data.length); + break; + } + + pos += iwe->len; + } +} + + +static void wpa_driver_nl80211_event_link(struct wpa_driver_nl80211_data *drv, + void *ctx, char *buf, size_t len, + int del) +{ + union wpa_event_data event; + + os_memset(&event, 0, sizeof(event)); + if (len > sizeof(event.interface_status.ifname)) + len = sizeof(event.interface_status.ifname) - 1; + os_memcpy(event.interface_status.ifname, buf, len); + event.interface_status.ievent = del ? EVENT_INTERFACE_REMOVED : + EVENT_INTERFACE_ADDED; + + wpa_printf(MSG_DEBUG, "RTM_%sLINK, IFLA_IFNAME: Interface '%s' %s", + del ? "DEL" : "NEW", + event.interface_status.ifname, + del ? "removed" : "added"); + + if (os_strcmp(drv->ifname, event.interface_status.ifname) == 0) { + if (del) + drv->if_removed = 1; + else + drv->if_removed = 0; + } + + wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event); +} + + +static int wpa_driver_nl80211_own_ifname(struct wpa_driver_nl80211_data *drv, + struct nlmsghdr *h) +{ + struct ifinfomsg *ifi; + int attrlen, _nlmsg_len, rta_len; + struct rtattr *attr; + + ifi = NLMSG_DATA(h); + + _nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); + + attrlen = h->nlmsg_len - _nlmsg_len; + if (attrlen < 0) + return 0; + + attr = (struct rtattr *) (((char *) ifi) + _nlmsg_len); + + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + if (attr->rta_type == IFLA_IFNAME) { + if (os_strcmp(((char *) attr) + rta_len, drv->ifname) + == 0) + return 1; + else + break; + } + attr = RTA_NEXT(attr, attrlen); + } + + return 0; +} + + +static int wpa_driver_nl80211_own_ifindex(struct wpa_driver_nl80211_data *drv, + int ifindex, struct nlmsghdr *h) +{ + if (drv->ifindex == ifindex) + return 1; + + if (drv->if_removed && wpa_driver_nl80211_own_ifname(drv, h)) { + drv->ifindex = if_nametoindex(drv->ifname); + wpa_printf(MSG_DEBUG, "nl80211: Update ifindex for a removed " + "interface"); + wpa_driver_nl80211_finish_drv_init(drv); + return 1; + } + + return 0; +} + + +static void wpa_driver_nl80211_event_rtm_newlink(struct wpa_driver_nl80211_data *drv, + void *ctx, struct nlmsghdr *h, + size_t len) +{ + struct ifinfomsg *ifi; + int attrlen, _nlmsg_len, rta_len; + struct rtattr * attr; + + if (len < sizeof(*ifi)) + return; + + ifi = NLMSG_DATA(h); + + if (!wpa_driver_nl80211_own_ifindex(drv, ifi->ifi_index, h)) { + wpa_printf(MSG_DEBUG, "Ignore event for foreign ifindex %d", + ifi->ifi_index); + return; + } + + wpa_printf(MSG_DEBUG, "RTM_NEWLINK: operstate=%d ifi_flags=0x%x " + "(%s%s%s%s)", + drv->operstate, ifi->ifi_flags, + (ifi->ifi_flags & IFF_UP) ? "[UP]" : "", + (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "", + (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "", + (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : ""); + /* + * Some drivers send the association event before the operup event--in + * this case, lifting operstate in wpa_driver_nl80211_set_operstate() + * fails. This will hit us when wpa_supplicant does not need to do + * IEEE 802.1X authentication + */ + if (drv->operstate == 1 && + (ifi->ifi_flags & (IFF_LOWER_UP | IFF_DORMANT)) == IFF_LOWER_UP && + !(ifi->ifi_flags & IFF_RUNNING)) + wpa_driver_nl80211_send_oper_ifla(drv, -1, IF_OPER_UP); + + _nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); + + attrlen = h->nlmsg_len - _nlmsg_len; + if (attrlen < 0) + return; + + attr = (struct rtattr *) (((char *) ifi) + _nlmsg_len); + + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + if (attr->rta_type == IFLA_WIRELESS) { + wpa_driver_nl80211_event_wireless( + drv, ctx, ((char *) attr) + rta_len, + attr->rta_len - rta_len); + } else if (attr->rta_type == IFLA_IFNAME) { + wpa_driver_nl80211_event_link( + drv, ctx, + ((char *) attr) + rta_len, + attr->rta_len - rta_len, 0); + } + attr = RTA_NEXT(attr, attrlen); + } +} + + +static void wpa_driver_nl80211_event_rtm_dellink(struct wpa_driver_nl80211_data *drv, + void *ctx, struct nlmsghdr *h, + size_t len) +{ + struct ifinfomsg *ifi; + int attrlen, _nlmsg_len, rta_len; + struct rtattr * attr; + + if (len < sizeof(*ifi)) + return; + + ifi = NLMSG_DATA(h); + + _nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); + + attrlen = h->nlmsg_len - _nlmsg_len; + if (attrlen < 0) + return; + + attr = (struct rtattr *) (((char *) ifi) + _nlmsg_len); + + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + if (attr->rta_type == IFLA_IFNAME) { + wpa_driver_nl80211_event_link( + drv, ctx, + ((char *) attr) + rta_len, + attr->rta_len - rta_len, 1); + } + attr = RTA_NEXT(attr, attrlen); + } +} + + +static void wpa_driver_nl80211_event_receive_wext(int sock, void *eloop_ctx, + void *sock_ctx) +{ + char buf[8192]; + int left; + struct sockaddr_nl from; + socklen_t fromlen; + struct nlmsghdr *h; + int max_events = 10; + +try_again: + fromlen = sizeof(from); + left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, + (struct sockaddr *) &from, &fromlen); + if (left < 0) { + if (errno != EINTR && errno != EAGAIN) + perror("recvfrom(netlink)"); + return; + } + + h = (struct nlmsghdr *) buf; + while (left >= (int) sizeof(*h)) { + int len, plen; + + len = h->nlmsg_len; + plen = len - sizeof(*h); + if (len > left || plen < 0) { + wpa_printf(MSG_DEBUG, "Malformed netlink message: " + "len=%d left=%d plen=%d", + len, left, plen); + break; + } + + switch (h->nlmsg_type) { + case RTM_NEWLINK: + wpa_driver_nl80211_event_rtm_newlink(eloop_ctx, sock_ctx, + h, plen); + break; + case RTM_DELLINK: + wpa_driver_nl80211_event_rtm_dellink(eloop_ctx, sock_ctx, + h, plen); + break; + } + + len = NLMSG_ALIGN(len); + left -= len; + h = (struct nlmsghdr *) ((char *) h + len); + } + + if (left > 0) { + wpa_printf(MSG_DEBUG, "%d extra bytes in the end of netlink " + "message", left); + } + + if (--max_events > 0) { + /* + * Try to receive all events in one eloop call in order to + * limit race condition on cases where AssocInfo event, Assoc + * event, and EAPOL frames are received more or less at the + * same time. We want to process the event messages first + * before starting EAPOL processing. + */ + goto try_again; + } +} + + +static int no_seq_check(struct nl_msg *msg, void *arg) +{ + return NL_OK; +} + + +static int process_event(struct nl_msg *msg, void *arg) +{ + struct wpa_driver_nl80211_data *drv = arg; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (tb[NL80211_ATTR_IFINDEX]) { + int ifindex = nla_get_u32(tb[NL80211_ATTR_IFINDEX]); + if (ifindex != drv->ifindex) { + wpa_printf(MSG_DEBUG, "nl80211: Ignored event (cmd=%d)" + " for foreign interface (ifindex %d)", + gnlh->cmd, ifindex); + return NL_SKIP; + } + } + + switch (gnlh->cmd) { + case NL80211_CMD_TRIGGER_SCAN: + wpa_printf(MSG_DEBUG, "nl80211: Scan trigger"); + break; + case NL80211_CMD_NEW_SCAN_RESULTS: + wpa_printf(MSG_DEBUG, "nl80211: New scan results available"); + drv->scan_complete_events = 1; + eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, + drv->ctx); + wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, NULL); + break; + case NL80211_CMD_SCAN_ABORTED: + wpa_printf(MSG_DEBUG, "nl80211: Scan aborted"); + /* + * Need to indicate that scan results are available in order + * not to make wpa_supplicant stop its scanning. + */ + eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, + drv->ctx); + wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, NULL); + break; + default: + wpa_printf(MSG_DEBUG, "nl0211: Ignored unknown event (cmd=%d)", + gnlh->cmd); + break; + } + + return NL_SKIP; +} + + +static void wpa_driver_nl80211_event_receive(int sock, void *eloop_ctx, + void *sock_ctx) +{ + struct nl_cb *cb; + struct wpa_driver_nl80211_data *drv = eloop_ctx; + + wpa_printf(MSG_DEBUG, "nl80211: Event message available"); + + cb = nl_cb_clone(drv->nl_cb); + if (!cb) + return; + nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL); + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, process_event, drv); + nl_recvmsgs(drv->nl_handle, cb); + nl_cb_put(cb); +} + + +static int wpa_driver_nl80211_get_ifflags_ifname(struct wpa_driver_nl80211_data *drv, + const char *ifname, int *flags) +{ + struct ifreq ifr; + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + if (ioctl(drv->ioctl_sock, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) { + perror("ioctl[SIOCGIFFLAGS]"); + return -1; + } + *flags = ifr.ifr_flags & 0xffff; + return 0; +} + + +/** + * wpa_driver_nl80211_get_ifflags - Get interface flags (SIOCGIFFLAGS) + * @drv: driver_nl80211 private data + * @flags: Pointer to returned flags value + * Returns: 0 on success, -1 on failure + */ +static int wpa_driver_nl80211_get_ifflags(struct wpa_driver_nl80211_data *drv, + int *flags) +{ + return wpa_driver_nl80211_get_ifflags_ifname(drv, drv->ifname, flags); +} + + +static int wpa_driver_nl80211_set_ifflags_ifname( + struct wpa_driver_nl80211_data *drv, + const char *ifname, int flags) +{ + struct ifreq ifr; + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + ifr.ifr_flags = flags & 0xffff; + if (ioctl(drv->ioctl_sock, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { + perror("SIOCSIFFLAGS"); + return -1; + } + return 0; +} + + +/** + * wpa_driver_nl80211_set_ifflags - Set interface flags (SIOCSIFFLAGS) + * @drv: driver_nl80211 private data + * @flags: New value for flags + * Returns: 0 on success, -1 on failure + */ +static int wpa_driver_nl80211_set_ifflags(struct wpa_driver_nl80211_data *drv, + int flags) +{ + return wpa_driver_nl80211_set_ifflags_ifname(drv, drv->ifname, flags); +} + + +/** + * wpa_driver_nl80211_set_country - ask nl80211 to set the regulatory domain + * @priv: driver_nl80211 private data + * @alpha2_arg: country to which to switch to + * Returns: 0 on success, -1 on failure + * + * This asks nl80211 to set the regulatory domain for given + * country ISO / IEC alpha2. + */ +static int wpa_driver_nl80211_set_country(void *priv, const char *alpha2_arg) +{ + struct wpa_driver_nl80211_data *drv = priv; + char alpha2[3]; + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + goto nla_put_failure; + + alpha2[0] = alpha2_arg[0]; + alpha2[1] = alpha2_arg[1]; + alpha2[2] = '\0'; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_REQ_SET_REG, 0); + + NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, alpha2); + if (send_and_recv_msgs(drv, msg, NULL, NULL)) + return -EINVAL; + return 0; +nla_put_failure: + return -EINVAL; +} + + +static int wpa_driver_nl80211_set_probe_req_ie(void *priv, const u8 *ies, + size_t ies_len) +{ + struct wpa_driver_nl80211_data *drv = priv; + struct nl_msg *msg; + int ret = -1; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, + NL80211_CMD_SET_MGMT_EXTRA_IE, 0); + + NLA_PUT_U8(msg, NL80211_ATTR_MGMT_SUBTYPE, 4 /* ProbeReq */); + if (ies) + NLA_PUT(msg, NL80211_ATTR_IE, ies_len, ies); + + ret = 0; + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + return ret; + +nla_put_failure: + return -ENOBUFS; +} + + +#ifdef CONFIG_CLIENT_MLME + +static int nl80211_set_vif(struct wpa_driver_nl80211_data *drv, + int drop_unencrypted, int userspace_mlme) +{ +#ifdef NL80211_CMD_SET_VIF + struct nl_msg *msg; + int ret = -1; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, + NL80211_CMD_SET_VIF, 0); + + if (drop_unencrypted >= 0) + NLA_PUT_U8(msg, NL80211_ATTR_VIF_DROP_UNENCRYPTED, + drop_unencrypted); + if (userspace_mlme >= 0) + NLA_PUT_U8(msg, NL80211_ATTR_VIF_USERSPACE_MLME, + userspace_mlme); + + ret = 0; + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + return ret; + +nla_put_failure: + return -ENOBUFS; +#else /* NL80211_CMD_SET_VIF */ + return -1; +#endif /* NL80211_CMD_SET_VIF */ +} + + +static int wpa_driver_nl80211_set_userspace_mlme( + struct wpa_driver_nl80211_data *drv, int enabled) +{ + return nl80211_set_vif(drv, -1, enabled); +} + + +static void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv, + int ifidx) +{ + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + goto nla_put_failure; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_DEL_INTERFACE, 0); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifidx); + if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0) + return; +nla_put_failure: + wpa_printf(MSG_ERROR, "nl80211: Failed to remove interface."); +} + + +static int nl80211_create_iface(struct wpa_driver_nl80211_data *drv, + const char *ifname, enum nl80211_iftype iftype) +{ + struct nl_msg *msg, *flags = NULL; + int ifidx, err; + int ret = -ENOBUFS; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_NEW_INTERFACE, 0); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(drv->ifname)); + NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, ifname); + NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, iftype); + + if (iftype == NL80211_IFTYPE_MONITOR) { + flags = nlmsg_alloc(); + if (!flags) + goto nla_put_failure; + + NLA_PUT_FLAG(flags, NL80211_MNTR_FLAG_COOK_FRAMES); + + err = nla_put_nested(msg, NL80211_ATTR_MNTR_FLAGS, flags); + + nlmsg_free(flags); + + if (err) + goto nla_put_failure; + } + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret) { + nla_put_failure: + wpa_printf(MSG_ERROR, "nl80211: Failed to create interface %d", + ret); + return ret; + } + + ifidx = if_nametoindex(ifname); + if (ifidx <= 0) + return -1; + + return ifidx; +} + + +static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct wpa_driver_nl80211_data *drv = eloop_ctx; + int len; + unsigned char buf[3000]; + struct ieee80211_radiotap_iterator iter; + int ret; + int injected = 0, failed = 0, rxflags = 0; + struct ieee80211_rx_status rx_status; + + len = recv(sock, buf, sizeof(buf), 0); + if (len < 0) { + perror("recv"); + return; + } + + if (ieee80211_radiotap_iterator_init(&iter, (void *) buf, len)) { + wpa_printf(MSG_DEBUG, "nl80211: received invalid radiotap " + "frame"); + return; + } + + os_memset(&rx_status, 0, sizeof(rx_status)); + + while (1) { + ret = ieee80211_radiotap_iterator_next(&iter); + if (ret == -ENOENT) + break; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: received invalid " + "radiotap frame (%d)", ret); + return; + } + switch (iter.this_arg_index) { + case IEEE80211_RADIOTAP_FLAGS: + if (*iter.this_arg & IEEE80211_RADIOTAP_F_FCS) + len -= 4; + break; + case IEEE80211_RADIOTAP_RX_FLAGS: + rxflags = 1; + break; + case IEEE80211_RADIOTAP_TX_FLAGS: + injected = 1; + failed = le_to_host16((*(u16 *) iter.this_arg)) & + IEEE80211_RADIOTAP_F_TX_FAIL; + break; + case IEEE80211_RADIOTAP_DATA_RETRIES: + break; + case IEEE80211_RADIOTAP_CHANNEL: + /* TODO convert from freq/flags to channel number + * rx_status.channel = XXX; + */ + break; + case IEEE80211_RADIOTAP_RATE: + break; + case IEEE80211_RADIOTAP_DB_ANTSIGNAL: + rx_status.ssi = *iter.this_arg; + break; + } + } + + if (rxflags && injected) + return; + + if (!injected) { + wpa_supplicant_sta_rx(drv->ctx, buf + iter.max_length, + len - iter.max_length, &rx_status); + } else if (failed) { + /* TX failure callback */ + } else { + /* TX success (ACK) callback */ + } +} + + +static int wpa_driver_nl80211_create_monitor_interface( + struct wpa_driver_nl80211_data *drv) +{ + char buf[IFNAMSIZ]; + struct sockaddr_ll ll; + int optval, flags; + socklen_t optlen; + + os_snprintf(buf, IFNAMSIZ, "mon.%s", drv->ifname); + buf[IFNAMSIZ - 1] = '\0'; + + drv->monitor_ifidx = + nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR); + + if (drv->monitor_ifidx < 0) + return -1; + + if (wpa_driver_nl80211_get_ifflags_ifname(drv, buf, &flags) != 0 || + wpa_driver_nl80211_set_ifflags_ifname(drv, buf, flags | IFF_UP) != + 0) { + wpa_printf(MSG_ERROR, "nl80211: Could not set interface '%s' " + "UP", buf); + goto error; + } + + os_memset(&ll, 0, sizeof(ll)); + ll.sll_family = AF_PACKET; + ll.sll_ifindex = drv->monitor_ifidx; + drv->monitor_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + if (drv->monitor_sock < 0) { + perror("socket[PF_PACKET,SOCK_RAW]"); + goto error; + } + + if (bind(drv->monitor_sock, (struct sockaddr *) &ll, + sizeof(ll)) < 0) { + perror("monitor socket bind"); + goto error; + } + + optlen = sizeof(optval); + optval = 20; + if (setsockopt + (drv->monitor_sock, SOL_SOCKET, SO_PRIORITY, &optval, optlen)) { + perror("Failed to set socket priority"); + goto error; + } + + if (eloop_register_read_sock(drv->monitor_sock, handle_monitor_read, + drv, NULL)) { + wpa_printf(MSG_ERROR, "nl80211: Could not register monitor " + "read socket"); + goto error; + } + + return 0; + + error: + nl80211_remove_iface(drv, drv->monitor_ifidx); + return -1; +} + +#endif /* CONFIG_CLIENT_MLME */ + + +/** + * wpa_driver_nl80211_init - Initialize nl80211 driver interface + * @ctx: context to be used when calling wpa_supplicant functions, + * e.g., wpa_supplicant_event() + * @ifname: interface name, e.g., wlan0 + * Returns: Pointer to private data, %NULL on failure + */ +static void * wpa_driver_nl80211_init(void *ctx, const char *ifname) +{ + int s, ret; + struct sockaddr_nl local; + struct wpa_driver_nl80211_data *drv; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->ctx = ctx; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + + drv->nl_cb = nl_cb_alloc(NL_CB_DEFAULT); + if (drv->nl_cb == NULL) { + wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink " + "callbacks"); + goto err1; + } + + drv->nl_handle = nl_handle_alloc_cb(drv->nl_cb); + if (drv->nl_handle == NULL) { + wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink " + "callbacks"); + goto err2; + } + + if (genl_connect(drv->nl_handle)) { + wpa_printf(MSG_ERROR, "nl80211: Failed to connect to generic " + "netlink"); + goto err3; + } + + drv->nl_cache = genl_ctrl_alloc_cache(drv->nl_handle); + if (drv->nl_cache == NULL) { + wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic " + "netlink cache"); + goto err3; + } + + drv->nl80211 = genl_ctrl_search_by_name(drv->nl_cache, "nl80211"); + if (drv->nl80211 == NULL) { + wpa_printf(MSG_ERROR, "nl80211: 'nl80211' generic netlink not " + "found"); + goto err4; + } + + ret = nl_get_multicast_id(drv, "nl80211", "scan"); + if (ret >= 0) + ret = nl_socket_add_membership(drv->nl_handle, ret); + if (ret < 0) { + wpa_printf(MSG_ERROR, "nl80211: Could not add multicast " + "membership for scan events: %d (%s)", + ret, strerror(-ret)); + goto err4; + } + eloop_register_read_sock(nl_socket_get_fd(drv->nl_handle), + wpa_driver_nl80211_event_receive, drv, ctx); + + drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->ioctl_sock < 0) { + perror("socket(PF_INET,SOCK_DGRAM)"); + goto err5; + } + + s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (s < 0) { + perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)"); + goto err6; + } + + os_memset(&local, 0, sizeof(local)); + local.nl_family = AF_NETLINK; + local.nl_groups = RTMGRP_LINK; + if (bind(s, (struct sockaddr *) &local, sizeof(local)) < 0) { + perror("bind(netlink)"); + close(s); + goto err6; + } + + eloop_register_read_sock(s, wpa_driver_nl80211_event_receive_wext, drv, + ctx); + drv->wext_event_sock = s; + + wpa_driver_nl80211_finish_drv_init(drv); + + return drv; + +err6: + close(drv->ioctl_sock); +err5: + genl_family_put(drv->nl80211); +err4: + nl_cache_free(drv->nl_cache); +err3: + nl_handle_destroy(drv->nl_handle); +err2: + nl_cb_put(drv->nl_cb); +err1: + os_free(drv); + return NULL; +} + + +static void +wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv) +{ + int flags; + + drv->ifindex = if_nametoindex(drv->ifname); + + if (wpa_driver_nl80211_set_mode(drv, 0) < 0) { + printf("Could not configure driver to use managed mode\n"); + } + + if (wpa_driver_nl80211_get_ifflags(drv, &flags) != 0) + printf("Could not get interface '%s' flags\n", drv->ifname); + else if (!(flags & IFF_UP)) { + if (wpa_driver_nl80211_set_ifflags(drv, flags | IFF_UP) != 0) { + printf("Could not set interface '%s' UP\n", + drv->ifname); + } + } + + /* + * Make sure that the driver does not have any obsolete PMKID entries. + */ + wpa_driver_nl80211_flush_pmkid(drv); + + wpa_driver_nl80211_get_range(drv); + + wpa_driver_nl80211_send_oper_ifla(drv, 1, IF_OPER_DORMANT); +} + + +/** + * wpa_driver_nl80211_deinit - Deinitialize nl80211 driver interface + * @priv: Pointer to private nl80211 data from wpa_driver_nl80211_init() + * + * Shut down driver interface and processing of driver events. Free + * private data buffer if one was allocated in wpa_driver_nl80211_init(). + */ +static void wpa_driver_nl80211_deinit(void *priv) +{ + struct wpa_driver_nl80211_data *drv = priv; + int flags; + +#ifdef CONFIG_CLIENT_MLME + if (drv->monitor_sock >= 0) { + eloop_unregister_read_sock(drv->monitor_sock); + close(drv->monitor_sock); + } + if (drv->monitor_ifidx > 0) + nl80211_remove_iface(drv, drv->monitor_ifidx); + if (drv->capa.flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) + wpa_driver_nl80211_set_userspace_mlme(drv, 0); +#endif /* CONFIG_CLIENT_MLME */ + + eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); + + /* + * Clear possibly configured driver parameters in order to make it + * easier to use the driver after wpa_supplicant has been terminated. + */ + (void) wpa_driver_nl80211_set_bssid(drv, + (u8 *) "\x00\x00\x00\x00\x00\x00"); + + wpa_driver_nl80211_send_oper_ifla(priv, 0, IF_OPER_UP); + + eloop_unregister_read_sock(drv->wext_event_sock); + + if (wpa_driver_nl80211_get_ifflags(drv, &flags) == 0) + (void) wpa_driver_nl80211_set_ifflags(drv, flags & ~IFF_UP); + + close(drv->wext_event_sock); + close(drv->ioctl_sock); + os_free(drv->assoc_req_ies); + os_free(drv->assoc_resp_ies); + + eloop_unregister_read_sock(nl_socket_get_fd(drv->nl_handle)); + genl_family_put(drv->nl80211); + nl_cache_free(drv->nl_cache); + nl_handle_destroy(drv->nl_handle); + nl_cb_put(drv->nl_cb); + + os_free(drv); +} + + +/** + * wpa_driver_nl80211_scan_timeout - Scan timeout to report scan completion + * @eloop_ctx: Unused + * @timeout_ctx: ctx argument given to wpa_driver_nl80211_init() + * + * This function can be used as registered timeout when starting a scan to + * generate a scan completed event if the driver does not report this. + */ +static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx) +{ + wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); + wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); +} + + +/** + * wpa_driver_nl80211_scan - Request the driver to initiate scan + * @priv: Pointer to private wext data from wpa_driver_nl80211_init() + * @ssid: Specific SSID to scan for (ProbeReq) or %NULL to scan for + * all SSIDs (either active scan with broadcast SSID or passive + * scan + * @ssid_len: Length of the SSID + * Returns: 0 on success, -1 on failure + */ +static int wpa_driver_nl80211_scan(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_nl80211_data *drv = priv; + int ret = 0, timeout; + struct nl_msg *msg, *ssids; + + msg = nlmsg_alloc(); + ssids = nlmsg_alloc(); + if (!msg || !ssids) { + nlmsg_free(msg); + nlmsg_free(ssids); + return -1; + } + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, + NL80211_CMD_TRIGGER_SCAN, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + + if (ssid && ssid_len) { + /* Request an active scan for a specific SSID */ + NLA_PUT(ssids, 1, ssid_len, ssid); + } else { + /* Request an active scan for wildcard SSID */ + NLA_PUT(ssids, 1, 0, ""); + } + nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Scan trigger failed: ret=%d " + "(%s)", ret, strerror(-ret)); + goto nla_put_failure; + } + + /* Not all drivers generate "scan completed" wireless event, so try to + * read results after a timeout. */ + timeout = 10; + if (drv->scan_complete_events) { + /* + * The driver seems to deliver SIOCGIWSCAN events to notify + * when scan is complete, so use longer timeout to avoid race + * conditions with scanning and following association request. + */ + timeout = 30; + } + wpa_printf(MSG_DEBUG, "Scan requested (ret=%d) - scan timeout %d " + "seconds", ret, timeout); + eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); + eloop_register_timeout(timeout, 0, wpa_driver_nl80211_scan_timeout, + drv, drv->ctx); + +nla_put_failure: + nlmsg_free(ssids); + nlmsg_free(msg); + return ret; +} + + +static int bss_info_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *bss[NL80211_BSS_MAX + 1]; + static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = { + [NL80211_BSS_BSSID] = { .type = NLA_UNSPEC }, + [NL80211_BSS_FREQUENCY] = { .type = NLA_U32 }, + [NL80211_BSS_TSF] = { .type = NLA_U64 }, + [NL80211_BSS_BEACON_INTERVAL] = { .type = NLA_U16 }, + [NL80211_BSS_CAPABILITY] = { .type = NLA_U16 }, + [NL80211_BSS_INFORMATION_ELEMENTS] = { .type = NLA_UNSPEC }, + [NL80211_BSS_SIGNAL_MBM] = { .type = NLA_U32 }, + [NL80211_BSS_SIGNAL_UNSPEC] = { .type = NLA_U8 }, + }; + struct wpa_scan_results *res = arg; + struct wpa_scan_res **tmp; + struct wpa_scan_res *r; + const u8 *ie; + size_t ie_len; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + if (!tb[NL80211_ATTR_BSS]) + return NL_SKIP; + if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS], + bss_policy)) + return NL_SKIP; + if (bss[NL80211_BSS_INFORMATION_ELEMENTS]) { + ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]); + ie_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]); + } else { + ie = NULL; + ie_len = 0; + } + + r = os_zalloc(sizeof(*r) + ie_len); + if (r == NULL) + return NL_SKIP; + if (bss[NL80211_BSS_BSSID]) + os_memcpy(r->bssid, nla_data(bss[NL80211_BSS_BSSID]), + ETH_ALEN); + if (bss[NL80211_BSS_FREQUENCY]) + r->freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]); + if (bss[NL80211_BSS_BEACON_INTERVAL]) + r->beacon_int = nla_get_u16(bss[NL80211_BSS_BEACON_INTERVAL]); + if (bss[NL80211_BSS_CAPABILITY]) + r->caps = nla_get_u16(bss[NL80211_BSS_CAPABILITY]); + if (bss[NL80211_BSS_SIGNAL_UNSPEC]) + r->qual = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]); + if (bss[NL80211_BSS_SIGNAL_MBM]) + r->level = nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM]); + if (bss[NL80211_BSS_TSF]) + r->tsf = nla_get_u64(bss[NL80211_BSS_TSF]); + r->ie_len = ie_len; + if (ie) + os_memcpy(r + 1, ie, ie_len); + + tmp = os_realloc(res->res, + (res->num + 1) * sizeof(struct wpa_scan_res *)); + if (tmp == NULL) { + os_free(r); + return NL_SKIP; + } + tmp[res->num++] = r; + res->res = tmp; + + return NL_SKIP; +} + + +/** + * wpa_driver_nl80211_get_scan_results - Fetch the latest scan results + * @priv: Pointer to private wext data from wpa_driver_nl80211_init() + * Returns: Scan results on success, -1 on failure + */ +static struct wpa_scan_results * +wpa_driver_nl80211_get_scan_results(void *priv) +{ + struct wpa_driver_nl80211_data *drv = priv; + struct nl_msg *msg; + struct wpa_scan_results *res; + int ret; + + res = os_zalloc(sizeof(*res)); + if (res == NULL) + return 0; + msg = nlmsg_alloc(); + if (!msg) + goto nla_put_failure; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, NLM_F_DUMP, + NL80211_CMD_GET_SCAN, 0); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + + ret = send_and_recv_msgs(drv, msg, bss_info_handler, res); + msg = NULL; + if (ret == 0) { + wpa_printf(MSG_DEBUG, "Received scan results (%lu BSSes)", + (unsigned long) res->num); + return res; + } + wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d " + "(%s)", ret, strerror(-ret)); +nla_put_failure: + nlmsg_free(msg); + wpa_scan_results_free(res); + return NULL; +} + + +static int wpa_driver_nl80211_get_range(void *priv) +{ + struct wpa_driver_nl80211_data *drv = priv; + struct iw_range *range; + struct iwreq iwr; + int minlen; + size_t buflen; + + /* + * Use larger buffer than struct iw_range in order to allow the + * structure to grow in the future. + */ + buflen = sizeof(struct iw_range) + 500; + range = os_zalloc(buflen); + if (range == NULL) + return -1; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) range; + iwr.u.data.length = buflen; + + minlen = ((char *) &range->enc_capa) - (char *) range + + sizeof(range->enc_capa); + + if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) { + perror("ioctl[SIOCGIWRANGE]"); + os_free(range); + return -1; + } else if (iwr.u.data.length >= minlen && + range->we_version_compiled >= 18) { + wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d " + "WE(source)=%d enc_capa=0x%x", + range->we_version_compiled, + range->we_version_source, + range->enc_capa); + drv->has_capability = 1; + drv->we_version_compiled = range->we_version_compiled; + if (range->enc_capa & IW_ENC_CAPA_WPA) { + drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA | + WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK; + } + if (range->enc_capa & IW_ENC_CAPA_WPA2) { + drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK; + } + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40 | + WPA_DRIVER_CAPA_ENC_WEP104; + if (range->enc_capa & IW_ENC_CAPA_CIPHER_TKIP) + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP; + if (range->enc_capa & IW_ENC_CAPA_CIPHER_CCMP) + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP; + drv->capa.auth = WPA_DRIVER_AUTH_OPEN | + WPA_DRIVER_AUTH_SHARED | + WPA_DRIVER_AUTH_LEAP; + wpa_printf(MSG_DEBUG, " capabilities: key_mgmt 0x%x enc 0x%x", + drv->capa.key_mgmt, drv->capa.enc); + } else { + wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: too old (short) data - " + "assuming WPA is not supported"); + } + + os_free(range); + return 0; +} + + +static int wpa_driver_nl80211_set_wpa(void *priv, int enabled) +{ + struct wpa_driver_nl80211_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + return wpa_driver_nl80211_set_auth_param(drv, IW_AUTH_WPA_ENABLED, + enabled); +} + + +static int wpa_driver_nl80211_set_key(void *priv, wpa_alg alg, + const u8 *addr, int key_idx, + int set_tx, const u8 *seq, + size_t seq_len, + const u8 *key, size_t key_len) +{ + struct wpa_driver_nl80211_data *drv = priv; + int err; + struct nl_msg *msg; + + wpa_printf(MSG_DEBUG, "%s: alg=%d addr=%p key_idx=%d set_tx=%d " + "seq_len=%lu key_len=%lu", + __func__, alg, addr, key_idx, set_tx, + (unsigned long) seq_len, (unsigned long) key_len); + + msg = nlmsg_alloc(); + if (msg == NULL) + return -1; + + if (alg == WPA_ALG_NONE) { + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, + NL80211_CMD_DEL_KEY, 0); + } else { + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, + NL80211_CMD_NEW_KEY, 0); + NLA_PUT(msg, NL80211_ATTR_KEY_DATA, key_len, key); + switch (alg) { + case WPA_ALG_WEP: + if (key_len == 5) + NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, + 0x000FAC01); + else + NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, + 0x000FAC05); + break; + case WPA_ALG_TKIP: + NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, 0x000FAC02); + break; + case WPA_ALG_CCMP: + NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, 0x000FAC04); + break; +#ifdef CONFIG_IEEE80211W + case WPA_ALG_IGTK: + NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, 0x000FAC06); + break; +#endif /* CONFIG_IEEE80211W */ + default: + nlmsg_free(msg); + return -1; + } + } + + if (addr && os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) != 0) + { + wpa_printf(MSG_DEBUG, " addr=" MACSTR, MAC2STR(addr)); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + } + NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + + err = send_and_recv_msgs(drv, msg, NULL, NULL); + if (err) { + wpa_printf(MSG_DEBUG, "nl80211: set_key failed; err=%d", err); + return -1; + } + + if (set_tx && alg != WPA_ALG_NONE) { + msg = nlmsg_alloc(); + if (msg == NULL) + return -1; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_SET_KEY, 0); + NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT_FLAG(msg, NL80211_ATTR_KEY_DEFAULT); + + err = send_and_recv_msgs(drv, msg, NULL, NULL); + if (err) { + wpa_printf(MSG_DEBUG, "nl80211: set default key " + "failed; err=%d", err); + return -1; + } + } + + return 0; + +nla_put_failure: + return -ENOBUFS; +} + + +static int wpa_driver_nl80211_set_countermeasures(void *priv, + int enabled) +{ + struct wpa_driver_nl80211_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + return wpa_driver_nl80211_set_auth_param(drv, + IW_AUTH_TKIP_COUNTERMEASURES, + enabled); +} + + +static int wpa_driver_nl80211_set_drop_unencrypted(void *priv, + int enabled) +{ + struct wpa_driver_nl80211_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + drv->use_crypt = enabled; + return wpa_driver_nl80211_set_auth_param(drv, IW_AUTH_DROP_UNENCRYPTED, + enabled); +} + + +static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv, + const u8 *addr, int cmd, int reason_code) +{ + struct iwreq iwr; + struct iw_mlme mlme; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + os_memset(&mlme, 0, sizeof(mlme)); + mlme.cmd = cmd; + mlme.reason_code = reason_code; + mlme.addr.sa_family = ARPHRD_ETHER; + os_memcpy(mlme.addr.sa_data, addr, ETH_ALEN); + iwr.u.data.pointer = (caddr_t) &mlme; + iwr.u.data.length = sizeof(mlme); + + if (ioctl(drv->ioctl_sock, SIOCSIWMLME, &iwr) < 0) { + perror("ioctl[SIOCSIWMLME]"); + ret = -1; + } + + return ret; +} + + +static int wpa_driver_nl80211_deauthenticate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_nl80211_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + return wpa_driver_nl80211_mlme(drv, addr, IW_MLME_DEAUTH, reason_code); +} + + +static int wpa_driver_nl80211_disassociate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_nl80211_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + return wpa_driver_nl80211_mlme(drv, addr, IW_MLME_DISASSOC, + reason_code); +} + + +static int wpa_driver_nl80211_set_gen_ie(void *priv, const u8 *ie, + size_t ie_len) +{ + struct wpa_driver_nl80211_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) ie; + iwr.u.data.length = ie_len; + + if (ioctl(drv->ioctl_sock, SIOCSIWGENIE, &iwr) < 0) { + perror("ioctl[SIOCSIWGENIE]"); + ret = -1; + } + + return ret; +} + + +static int wpa_driver_nl80211_cipher2wext(int cipher) +{ + switch (cipher) { + case CIPHER_NONE: + return IW_AUTH_CIPHER_NONE; + case CIPHER_WEP40: + return IW_AUTH_CIPHER_WEP40; + case CIPHER_TKIP: + return IW_AUTH_CIPHER_TKIP; + case CIPHER_CCMP: + return IW_AUTH_CIPHER_CCMP; + case CIPHER_WEP104: + return IW_AUTH_CIPHER_WEP104; + default: + return 0; + } +} + + +static int wpa_driver_nl80211_keymgmt2wext(int keymgmt) +{ + switch (keymgmt) { + case KEY_MGMT_802_1X: + case KEY_MGMT_802_1X_NO_WPA: + return IW_AUTH_KEY_MGMT_802_1X; + case KEY_MGMT_PSK: + return IW_AUTH_KEY_MGMT_PSK; + default: + return 0; + } +} + + +static int +wpa_driver_nl80211_auth_alg_fallback(struct wpa_driver_nl80211_data *drv, + struct wpa_driver_associate_params *params) +{ + struct iwreq iwr; + int ret = 0; + + wpa_printf(MSG_DEBUG, "WEXT: Driver did not support " + "SIOCSIWAUTH for AUTH_ALG, trying SIOCSIWENCODE"); + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + /* Just changing mode, not actual keys */ + iwr.u.encoding.flags = 0; + iwr.u.encoding.pointer = (caddr_t) NULL; + iwr.u.encoding.length = 0; + + /* + * Note: IW_ENCODE_{OPEN,RESTRICTED} can be interpreted to mean two + * different things. Here they are used to indicate Open System vs. + * Shared Key authentication algorithm. However, some drivers may use + * them to select between open/restricted WEP encrypted (open = allow + * both unencrypted and encrypted frames; restricted = only allow + * encrypted frames). + */ + + if (!drv->use_crypt) { + iwr.u.encoding.flags |= IW_ENCODE_DISABLED; + } else { + if (params->auth_alg & AUTH_ALG_OPEN_SYSTEM) + iwr.u.encoding.flags |= IW_ENCODE_OPEN; + if (params->auth_alg & AUTH_ALG_SHARED_KEY) + iwr.u.encoding.flags |= IW_ENCODE_RESTRICTED; + } + + if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) { + perror("ioctl[SIOCSIWENCODE]"); + ret = -1; + } + + return ret; +} + + +static int wpa_driver_nl80211_associate( + void *priv, struct wpa_driver_associate_params *params) +{ + struct wpa_driver_nl80211_data *drv = priv; + int ret = 0; + int allow_unencrypted_eapol; + int value; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + /* + * If the driver did not support SIOCSIWAUTH, fallback to + * SIOCSIWENCODE here. + */ + if (drv->auth_alg_fallback && + wpa_driver_nl80211_auth_alg_fallback(drv, params) < 0) + ret = -1; + + if (!params->bssid && + wpa_driver_nl80211_set_bssid(drv, NULL) < 0) + ret = -1; + + /* TODO: should consider getting wpa version and cipher/key_mgmt suites + * from configuration, not from here, where only the selected suite is + * available */ + if (wpa_driver_nl80211_set_gen_ie(drv, params->wpa_ie, params->wpa_ie_len) + < 0) + ret = -1; + if (params->wpa_ie == NULL || params->wpa_ie_len == 0) + value = IW_AUTH_WPA_VERSION_DISABLED; + else if (params->wpa_ie[0] == WLAN_EID_RSN) + value = IW_AUTH_WPA_VERSION_WPA2; + else + value = IW_AUTH_WPA_VERSION_WPA; + if (wpa_driver_nl80211_set_auth_param(drv, + IW_AUTH_WPA_VERSION, value) < 0) + ret = -1; + value = wpa_driver_nl80211_cipher2wext(params->pairwise_suite); + if (wpa_driver_nl80211_set_auth_param(drv, + IW_AUTH_CIPHER_PAIRWISE, value) < 0) + ret = -1; + value = wpa_driver_nl80211_cipher2wext(params->group_suite); + if (wpa_driver_nl80211_set_auth_param(drv, + IW_AUTH_CIPHER_GROUP, value) < 0) + ret = -1; + value = wpa_driver_nl80211_keymgmt2wext(params->key_mgmt_suite); + if (wpa_driver_nl80211_set_auth_param(drv, + IW_AUTH_KEY_MGMT, value) < 0) + ret = -1; + value = params->key_mgmt_suite != KEY_MGMT_NONE || + params->pairwise_suite != CIPHER_NONE || + params->group_suite != CIPHER_NONE || + params->wpa_ie_len; + if (wpa_driver_nl80211_set_auth_param(drv, + IW_AUTH_PRIVACY_INVOKED, value) < 0) + ret = -1; + + /* Allow unencrypted EAPOL messages even if pairwise keys are set when + * not using WPA. IEEE 802.1X specifies that these frames are not + * encrypted, but WPA encrypts them when pairwise keys are in use. */ + if (params->key_mgmt_suite == KEY_MGMT_802_1X || + params->key_mgmt_suite == KEY_MGMT_PSK) + allow_unencrypted_eapol = 0; + else + allow_unencrypted_eapol = 1; + + if (wpa_driver_nl80211_set_auth_param(drv, + IW_AUTH_RX_UNENCRYPTED_EAPOL, + allow_unencrypted_eapol) < 0) + ret = -1; + if (params->freq && wpa_driver_nl80211_set_freq(drv, params->freq) < 0) + ret = -1; + if (wpa_driver_nl80211_set_ssid(drv, params->ssid, params->ssid_len) < 0) + ret = -1; + if (params->bssid && + wpa_driver_nl80211_set_bssid(drv, params->bssid) < 0) + ret = -1; + + return ret; +} + + +static int wpa_driver_nl80211_set_auth_alg(void *priv, int auth_alg) +{ + struct wpa_driver_nl80211_data *drv = priv; + int algs = 0, res; + + if (auth_alg & AUTH_ALG_OPEN_SYSTEM) + algs |= IW_AUTH_ALG_OPEN_SYSTEM; + if (auth_alg & AUTH_ALG_SHARED_KEY) + algs |= IW_AUTH_ALG_SHARED_KEY; + if (auth_alg & AUTH_ALG_LEAP) + algs |= IW_AUTH_ALG_LEAP; + if (algs == 0) { + /* at least one algorithm should be set */ + algs = IW_AUTH_ALG_OPEN_SYSTEM; + } + + res = wpa_driver_nl80211_set_auth_param(drv, IW_AUTH_80211_AUTH_ALG, + algs); + drv->auth_alg_fallback = res == -2; + return res; +} + + +/** + * wpa_driver_nl80211_set_mode - Set wireless mode (infra/adhoc), SIOCSIWMODE + * @priv: Pointer to private wext data from wpa_driver_nl80211_init() + * @mode: 0 = infra/BSS (associate with an AP), 1 = adhoc/IBSS + * Returns: 0 on success, -1 on failure + */ +static int wpa_driver_nl80211_set_mode(void *priv, int mode) +{ + struct wpa_driver_nl80211_data *drv = priv; + int ret = -1, flags; + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_SET_INTERFACE, 0); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, + mode ? NL80211_IFTYPE_ADHOC : NL80211_IFTYPE_STATION); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (!ret) + return 0; + else + goto try_again; + +nla_put_failure: + wpa_printf(MSG_ERROR, "nl80211: Failed to set interface mode: %d (%s)", + ret, strerror(-ret)); + return -1; + +try_again: + /* mac80211 doesn't allow mode changes while the device is up, so + * take the device down, try to set the mode again, and bring the + * device back up. + */ + if (wpa_driver_nl80211_get_ifflags(drv, &flags) == 0) { + (void) wpa_driver_nl80211_set_ifflags(drv, flags & ~IFF_UP); + + /* Try to set the mode again while the interface is down */ + msg = nlmsg_alloc(); + if (!msg) + return -1; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_SET_INTERFACE, 0); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, + mode ? NL80211_IFTYPE_ADHOC : + NL80211_IFTYPE_STATION); + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret) { + wpa_printf(MSG_ERROR, "Failed to set interface %s " + "mode(try_again): %d (%s)", + drv->ifname, ret, strerror(-ret)); + } + + /* Ignore return value of get_ifflags to ensure that the device + * is always up like it was before this function was called. + */ + (void) wpa_driver_nl80211_get_ifflags(drv, &flags); + (void) wpa_driver_nl80211_set_ifflags(drv, flags | IFF_UP); + } + + return ret; +} + + +static int wpa_driver_nl80211_pmksa(struct wpa_driver_nl80211_data *drv, + u32 cmd, const u8 *bssid, const u8 *pmkid) +{ + struct iwreq iwr; + struct iw_pmksa pmksa; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + os_memset(&pmksa, 0, sizeof(pmksa)); + pmksa.cmd = cmd; + pmksa.bssid.sa_family = ARPHRD_ETHER; + if (bssid) + os_memcpy(pmksa.bssid.sa_data, bssid, ETH_ALEN); + if (pmkid) + os_memcpy(pmksa.pmkid, pmkid, IW_PMKID_LEN); + iwr.u.data.pointer = (caddr_t) &pmksa; + iwr.u.data.length = sizeof(pmksa); + + if (ioctl(drv->ioctl_sock, SIOCSIWPMKSA, &iwr) < 0) { + if (errno != EOPNOTSUPP) + perror("ioctl[SIOCSIWPMKSA]"); + ret = -1; + } + + return ret; +} + + +static int wpa_driver_nl80211_add_pmkid(void *priv, const u8 *bssid, + const u8 *pmkid) +{ + struct wpa_driver_nl80211_data *drv = priv; + return wpa_driver_nl80211_pmksa(drv, IW_PMKSA_ADD, bssid, pmkid); +} + + +static int wpa_driver_nl80211_remove_pmkid(void *priv, const u8 *bssid, + const u8 *pmkid) +{ + struct wpa_driver_nl80211_data *drv = priv; + return wpa_driver_nl80211_pmksa(drv, IW_PMKSA_REMOVE, bssid, pmkid); +} + + +static int wpa_driver_nl80211_flush_pmkid(void *priv) +{ + struct wpa_driver_nl80211_data *drv = priv; + return wpa_driver_nl80211_pmksa(drv, IW_PMKSA_FLUSH, NULL, NULL); +} + + +static int wpa_driver_nl80211_get_capa(void *priv, + struct wpa_driver_capa *capa) +{ + struct wpa_driver_nl80211_data *drv = priv; + if (!drv->has_capability) + return -1; + os_memcpy(capa, &drv->capa, sizeof(*capa)); + return 0; +} + + +static int wpa_driver_nl80211_set_operstate(void *priv, int state) +{ + struct wpa_driver_nl80211_data *drv = priv; + + wpa_printf(MSG_DEBUG, "%s: operstate %d->%d (%s)", + __func__, drv->operstate, state, state ? "UP" : "DORMANT"); + drv->operstate = state; + return wpa_driver_nl80211_send_oper_ifla( + drv, -1, state ? IF_OPER_UP : IF_OPER_DORMANT); +} + + +#ifdef CONFIG_CLIENT_MLME +static int wpa_driver_nl80211_open_mlme(struct wpa_driver_nl80211_data *drv) +{ + if (wpa_driver_nl80211_set_userspace_mlme(drv, 1) < 0) { + wpa_printf(MSG_ERROR, "nl80211: Failed to enable userspace " + "MLME"); + return -1; + } + if (wpa_driver_nl80211_create_monitor_interface(drv)) { + wpa_printf(MSG_ERROR, "nl80211: Failed to create monitor " + "interface"); + return -1; + } + return 0; +} +#endif /* CONFIG_CLIENT_MLME */ + + +static int wpa_driver_nl80211_set_param(void *priv, const char *param) +{ +#ifdef CONFIG_CLIENT_MLME + struct wpa_driver_nl80211_data *drv = priv; + + if (param == NULL) + return 0; + + wpa_printf(MSG_DEBUG, "%s: param='%s'", __func__, param); + + if (os_strstr(param, "use_mlme=1")) { + wpa_printf(MSG_DEBUG, "nl80211: Using user space MLME"); + drv->capa.flags |= WPA_DRIVER_FLAGS_USER_SPACE_MLME; + + if (wpa_driver_nl80211_open_mlme(drv)) + return -1; + } +#endif /* CONFIG_CLIENT_MLME */ + + return 0; +} + + +#ifdef CONFIG_CLIENT_MLME + +struct phy_info_arg { + u16 *num_modes; + struct wpa_hw_modes *modes; +}; + + +static int phy_info_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct phy_info_arg *phy_info = arg; + + struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1]; + + struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1]; + static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] + = { + [NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 }, + [NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_PASSIVE_SCAN] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_NO_IBSS] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG }, + }; + + struct nlattr *tb_rate[NL80211_BITRATE_ATTR_MAX + 1]; + static struct nla_policy rate_policy[NL80211_BITRATE_ATTR_MAX + 1] = { + [NL80211_BITRATE_ATTR_RATE] = { .type = NLA_U32 }, + [NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE] = + { .type = NLA_FLAG }, + }; + + struct nlattr *nl_band; + struct nlattr *nl_freq; + struct nlattr *nl_rate; + int rem_band, rem_freq, rem_rate; + struct wpa_hw_modes *mode; + int idx, mode_is_set; + + nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (!tb_msg[NL80211_ATTR_WIPHY_BANDS]) + return NL_SKIP; + + nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], + rem_band) { + mode = os_realloc(phy_info->modes, + (*phy_info->num_modes + 1) * sizeof(*mode)); + if (!mode) + return NL_SKIP; + phy_info->modes = mode; + + mode_is_set = 0; + + mode = &phy_info->modes[*(phy_info->num_modes)]; + os_memset(mode, 0, sizeof(*mode)); + *(phy_info->num_modes) += 1; + + nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band), + nla_len(nl_band), NULL); + + nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], + rem_freq) { + nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, + nla_data(nl_freq), nla_len(nl_freq), + freq_policy); + if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ]) + continue; + mode->num_channels++; + } + + mode->channels = os_zalloc(mode->num_channels * + sizeof(struct wpa_channel_data)); + if (!mode->channels) + return NL_SKIP; + + idx = 0; + + nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], + rem_freq) { + nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, + nla_data(nl_freq), nla_len(nl_freq), + freq_policy); + if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ]) + continue; + + mode->channels[idx].freq = nla_get_u32( + tb_freq[NL80211_FREQUENCY_ATTR_FREQ]); + mode->channels[idx].flag |= WPA_CHAN_W_SCAN | + WPA_CHAN_W_ACTIVE_SCAN | + WPA_CHAN_W_IBSS; + + if (!mode_is_set) { + /* crude heuristic */ + if (mode->channels[idx].freq < 4000) + mode->mode = WPA_MODE_IEEE80211B; + else + mode->mode = WPA_MODE_IEEE80211A; + mode_is_set = 1; + } + + /* crude heuristic */ + if (mode->channels[idx].freq < 4000) { + if (mode->channels[idx].freq == 2484) + mode->channels[idx].chan = 14; + else + mode->channels[idx].chan = + (mode->channels[idx].freq - + 2407) / 5; + } else + mode->channels[idx].chan = + mode->channels[idx].freq / 5 - 1000; + + if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED]) + mode->channels[idx].flag &= ~WPA_CHAN_W_SCAN; + if (tb_freq[NL80211_FREQUENCY_ATTR_PASSIVE_SCAN]) + mode->channels[idx].flag &= + ~WPA_CHAN_W_ACTIVE_SCAN; + if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IBSS]) + mode->channels[idx].flag &= ~WPA_CHAN_W_IBSS; + idx++; + } + + nla_for_each_nested(nl_rate, tb_band[NL80211_BAND_ATTR_RATES], + rem_rate) { + nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, + nla_data(nl_rate), nla_len(nl_rate), + rate_policy); + if (!tb_rate[NL80211_BITRATE_ATTR_RATE]) + continue; + mode->num_rates++; + } + + mode->rates = os_zalloc(mode->num_rates * + sizeof(struct wpa_rate_data)); + if (!mode->rates) + return NL_SKIP; + + idx = 0; + + nla_for_each_nested(nl_rate, tb_band[NL80211_BAND_ATTR_RATES], + rem_rate) { + nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, + nla_data(nl_rate), nla_len(nl_rate), + rate_policy); + if (!tb_rate[NL80211_BITRATE_ATTR_RATE]) + continue; + mode->rates[idx].rate = nla_get_u32( + tb_rate[NL80211_BITRATE_ATTR_RATE]); + + /* crude heuristic */ + if (mode->mode == WPA_MODE_IEEE80211B && + mode->rates[idx].rate > 200) + mode->mode = WPA_MODE_IEEE80211G; + + if (tb_rate[NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE]) + mode->rates[idx].flags |= WPA_RATE_PREAMBLE2; + + idx++; + } + } + + return NL_SKIP; +} + + +static struct wpa_hw_modes * +wpa_driver_nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) +{ + struct wpa_driver_nl80211_data *drv = priv; + struct nl_msg *msg; + struct phy_info_arg result = { + .num_modes = num_modes, + .modes = NULL, + }; + + *num_modes = 0; + *flags = 0; + + msg = nlmsg_alloc(); + if (!msg) + return NULL; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_GET_WIPHY, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + + if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) + return result.modes; +nla_put_failure: + return NULL; +} + + +static int wpa_driver_nl80211_set_channel(void *priv, wpa_hw_mode phymode, + int chan, int freq) +{ + return wpa_driver_nl80211_set_freq(priv, freq); +} + + +static int wpa_driver_nl80211_send_mlme(void *priv, const u8 *data, + size_t data_len) +{ + struct wpa_driver_nl80211_data *drv = priv; + __u8 rtap_hdr[] = { + 0x00, 0x00, /* radiotap version */ + 0x0e, 0x00, /* radiotap length */ + 0x02, 0xc0, 0x00, 0x00, /* bmap: flags, tx and rx flags */ + 0x0c, /* F_WEP | F_FRAG (encrypt/fragment if required) */ + 0x00, /* padding */ + 0x00, 0x00, /* RX and TX flags to indicate that */ + 0x00, 0x00, /* this is the injected frame directly */ + }; + struct iovec iov[2] = { + { + .iov_base = &rtap_hdr, + .iov_len = sizeof(rtap_hdr), + }, + { + .iov_base = (void *) data, + .iov_len = data_len, + } + }; + struct msghdr msg = { + .msg_name = NULL, + .msg_namelen = 0, + .msg_iov = iov, + .msg_iovlen = 2, + .msg_control = NULL, + .msg_controllen = 0, + .msg_flags = 0, + }; + + if (sendmsg(drv->monitor_sock, &msg, 0) < 0) { + perror("send[MLME]"); + return -1; + } + + return 0; +} + + +static int wpa_driver_nl80211_mlme_add_sta(void *priv, const u8 *addr, + const u8 *supp_rates, + size_t supp_rates_len) +{ + struct wpa_driver_nl80211_data *drv = priv; + struct nl_msg *msg; + int ret = -1; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_NEW_STATION, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + /* TODO: Get proper Association ID and listen interval */ + NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, 1); + NLA_PUT(msg, NL80211_ATTR_STA_SUPPORTED_RATES, supp_rates_len, + supp_rates); + NLA_PUT_U16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL, 1); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + /* ignore EEXIST, this happens if a STA associates while associated */ + if (ret == -EEXIST || ret >= 0) + ret = 0; + +nla_put_failure: + return ret; +} + + +static int wpa_driver_nl80211_mlme_remove_sta(void *priv, const u8 *addr) +{ + struct wpa_driver_nl80211_data *drv = priv; + struct nl_msg *msg; + int ret = -1; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_DEL_STATION, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + + ret = 0; + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + return ret; + +nla_put_failure: + return -ENOBUFS; +} + +#endif /* CONFIG_CLIENT_MLME */ + + +const struct wpa_driver_ops wpa_driver_nl80211_ops = { + .name = "nl80211", + .desc = "Linux nl80211/cfg80211", + .get_bssid = wpa_driver_nl80211_get_bssid, + .get_ssid = wpa_driver_nl80211_get_ssid, + .set_wpa = wpa_driver_nl80211_set_wpa, + .set_key = wpa_driver_nl80211_set_key, + .set_countermeasures = wpa_driver_nl80211_set_countermeasures, + .set_drop_unencrypted = wpa_driver_nl80211_set_drop_unencrypted, + .scan = wpa_driver_nl80211_scan, + .get_scan_results2 = wpa_driver_nl80211_get_scan_results, + .deauthenticate = wpa_driver_nl80211_deauthenticate, + .disassociate = wpa_driver_nl80211_disassociate, + .set_mode = wpa_driver_nl80211_set_mode, + .associate = wpa_driver_nl80211_associate, + .set_auth_alg = wpa_driver_nl80211_set_auth_alg, + .init = wpa_driver_nl80211_init, + .deinit = wpa_driver_nl80211_deinit, + .set_param = wpa_driver_nl80211_set_param, + .add_pmkid = wpa_driver_nl80211_add_pmkid, + .remove_pmkid = wpa_driver_nl80211_remove_pmkid, + .flush_pmkid = wpa_driver_nl80211_flush_pmkid, + .get_capa = wpa_driver_nl80211_get_capa, + .set_operstate = wpa_driver_nl80211_set_operstate, + .set_country = wpa_driver_nl80211_set_country, + .set_probe_req_ie = wpa_driver_nl80211_set_probe_req_ie, +#ifdef CONFIG_CLIENT_MLME + .get_hw_feature_data = wpa_driver_nl80211_get_hw_feature_data, + .set_channel = wpa_driver_nl80211_set_channel, + .set_ssid = wpa_driver_nl80211_set_ssid, + .set_bssid = wpa_driver_nl80211_set_bssid, + .send_mlme = wpa_driver_nl80211_send_mlme, + .mlme_add_sta = wpa_driver_nl80211_mlme_add_sta, + .mlme_remove_sta = wpa_driver_nl80211_mlme_remove_sta, +#endif /* CONFIG_CLIENT_MLME */ +}; diff --git a/src/drivers/driver_osx.m b/src/drivers/driver_osx.m new file mode 100644 index 0000000..93d7df0 --- /dev/null +++ b/src/drivers/driver_osx.m @@ -0,0 +1,432 @@ +/* + * WPA Supplicant - Mac OS X Apple80211 driver interface + * Copyright (c) 2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#define Boolean __DummyBoolean +#include <CoreFoundation/CoreFoundation.h> +#undef Boolean + +#include "common.h" +#include "driver.h" +#include "eloop.h" + +#include "Apple80211.h" + +struct wpa_driver_osx_data { + void *ctx; + WirelessRef wireless_ctx; + CFArrayRef scan_results; +}; + + +#ifndef CONFIG_NO_STDOUT_DEBUG +extern int wpa_debug_level; + +static void dump_dict_cb(const void *key, const void *value, void *context) +{ + if (MSG_DEBUG < wpa_debug_level) + return; + + wpa_printf(MSG_DEBUG, "Key:"); + CFShow(key); + wpa_printf(MSG_DEBUG, "Value:"); + CFShow(value); +} +#endif /* CONFIG_NO_STDOUT_DEBUG */ + + +static void wpa_driver_osx_dump_dict(CFDictionaryRef dict, const char *title) +{ +#ifndef CONFIG_NO_STDOUT_DEBUG + wpa_printf(MSG_DEBUG, "OSX: Dump dictionary %s - %u entries", + title, (unsigned int) CFDictionaryGetCount(dict)); + CFDictionaryApplyFunction(dict, dump_dict_cb, NULL); +#endif /* CONFIG_NO_STDOUT_DEBUG */ +} + + +static int wpa_driver_osx_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_osx_data *drv = priv; + WirelessError err; + WirelessInfo info; + int len; + + err = WirelessGetInfo(drv->wireless_ctx, &info); + if (err) { + wpa_printf(MSG_DEBUG, "OSX: WirelessGetInfo failed: %d", + (int) err); + return -1; + } + if (!info.power) { + wpa_printf(MSG_DEBUG, "OSX: Wireless device power off"); + return -1; + } + + for (len = 0; len < 32; len++) + if (info.ssid[len] == 0) + break; + + os_memcpy(ssid, info.ssid, len); + return len; +} + + +static int wpa_driver_osx_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_osx_data *drv = priv; + WirelessError err; + WirelessInfo info; + + err = WirelessGetInfo(drv->wireless_ctx, &info); + if (err) { + wpa_printf(MSG_DEBUG, "OSX: WirelessGetInfo failed: %d", + (int) err); + return -1; + } + if (!info.power) { + wpa_printf(MSG_DEBUG, "OSX: Wireless device power off"); + return -1; + } + + os_memcpy(bssid, info.bssID, ETH_ALEN); + return 0; +} + + +static void wpa_driver_osx_scan_timeout(void *eloop_ctx, void *timeout_ctx) +{ + wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); +} + + +static int wpa_driver_osx_scan(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_osx_data *drv = priv; + WirelessError err; + + if (drv->scan_results) { + CFRelease(drv->scan_results); + drv->scan_results = NULL; + } + + if (ssid) { + CFStringRef data; + data = CFStringCreateWithBytes(kCFAllocatorDefault, + ssid, ssid_len, + kCFStringEncodingISOLatin1, + FALSE); + if (data == NULL) { + wpa_printf(MSG_DEBUG, "CFStringCreateWithBytes " + "failed"); + return -1; + } + + err = WirelessDirectedScan(drv->wireless_ctx, + &drv->scan_results, 0, data); + CFRelease(data); + if (err) { + wpa_printf(MSG_DEBUG, "OSX: WirelessDirectedScan " + "failed: 0x%08x", (unsigned int) err); + return -1; + } + } else { + err = WirelessScan(drv->wireless_ctx, &drv->scan_results, 0); + if (err) { + wpa_printf(MSG_DEBUG, "OSX: WirelessScan failed: " + "0x%08x", (unsigned int) err); + return -1; + } + } + + eloop_register_timeout(0, 0, wpa_driver_osx_scan_timeout, drv, + drv->ctx); + return 0; +} + + +static int wpa_driver_osx_get_scan_results(void *priv, + struct wpa_scan_result *results, + size_t max_size) +{ + struct wpa_driver_osx_data *drv = priv; + size_t i, num; + + if (drv->scan_results == NULL) + return 0; + + num = CFArrayGetCount(drv->scan_results); + if (num > max_size) + num = max_size; + os_memset(results, 0, num * sizeof(struct wpa_scan_result)); + + for (i = 0; i < num; i++) { + struct wpa_scan_result *res = &results[i]; + WirelessNetworkInfo *info; + info = (WirelessNetworkInfo *) + CFDataGetBytePtr(CFArrayGetValueAtIndex( + drv->scan_results, i)); + + os_memcpy(res->bssid, info->bssid, ETH_ALEN); + if (info->ssid_len > 32) { + wpa_printf(MSG_DEBUG, "OSX: Invalid SSID length %d in " + "scan results", (int) info->ssid_len); + continue; + } + os_memcpy(res->ssid, info->ssid, info->ssid_len); + res->ssid_len = info->ssid_len; + res->caps = info->capability; + res->freq = 2407 + info->channel * 5; + res->level = info->signal; + res->noise = info->noise; + } + + return num; +} + + +static void wpa_driver_osx_assoc_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_driver_osx_data *drv = eloop_ctx; + u8 bssid[ETH_ALEN]; + CFDictionaryRef ai; + + if (wpa_driver_osx_get_bssid(drv, bssid) != 0) { + eloop_register_timeout(1, 0, wpa_driver_osx_assoc_timeout, + drv, drv->ctx); + return; + } + + ai = WirelessGetAssociationInfo(drv->wireless_ctx); + if (ai) { + wpa_driver_osx_dump_dict(ai, "WirelessGetAssociationInfo"); + CFRelease(ai); + } else { + wpa_printf(MSG_DEBUG, "OSX: Failed to get association info"); + } + + wpa_supplicant_event(timeout_ctx, EVENT_ASSOC, NULL); +} + + +static int wpa_driver_osx_associate(void *priv, + struct wpa_driver_associate_params *params) +{ + struct wpa_driver_osx_data *drv = priv; + WirelessError err; + CFDataRef ssid; + CFStringRef key; + int assoc_type; + + ssid = CFDataCreate(kCFAllocatorDefault, params->ssid, + params->ssid_len); + if (ssid == NULL) + return -1; + + /* TODO: support for WEP */ + if (params->key_mgmt_suite == KEY_MGMT_PSK) { + if (params->passphrase == NULL) + return -1; + key = CFStringCreateWithCString(kCFAllocatorDefault, + params->passphrase, + kCFStringEncodingISOLatin1); + if (key == NULL) { + CFRelease(ssid); + return -1; + } + } else + key = NULL; + + if (params->key_mgmt_suite == KEY_MGMT_NONE) + assoc_type = 0; + else + assoc_type = 4; + + wpa_printf(MSG_DEBUG, "OSX: WirelessAssociate(type=%d key=%p)", + assoc_type, key); + err = WirelessAssociate(drv->wireless_ctx, assoc_type, ssid, key); + CFRelease(ssid); + if (key) + CFRelease(key); + if (err) { + wpa_printf(MSG_DEBUG, "OSX: WirelessAssociate failed: 0x%08x", + (unsigned int) err); + return -1; + } + + /* + * Driver is actually already associated; report association from an + * eloop callback. + */ + eloop_cancel_timeout(wpa_driver_osx_assoc_timeout, drv, drv->ctx); + eloop_register_timeout(0, 0, wpa_driver_osx_assoc_timeout, drv, + drv->ctx); + + return 0; +} + + +static int wpa_driver_osx_set_key(void *priv, wpa_alg alg, const u8 *addr, + int key_idx, int set_tx, const u8 *seq, + size_t seq_len, const u8 *key, + size_t key_len) +{ + struct wpa_driver_osx_data *drv = priv; + WirelessError err; + + if (alg == WPA_ALG_WEP) { + err = WirelessSetKey(drv->wireless_ctx, 1, key_idx, key_len, + key); + if (err != 0) { + wpa_printf(MSG_DEBUG, "OSX: WirelessSetKey failed: " + "0x%08x", (unsigned int) err); + return -1; + } + + return 0; + } + + if (alg == WPA_ALG_PMK) { + err = WirelessSetWPAKey(drv->wireless_ctx, 1, key_len, key); + if (err != 0) { + wpa_printf(MSG_DEBUG, "OSX: WirelessSetWPAKey failed: " + "0x%08x", (unsigned int) err); + return -1; + } + return 0; + } + + wpa_printf(MSG_DEBUG, "OSX: Unsupported set_key alg %d", alg); + return -1; +} + + +static int wpa_driver_osx_get_capa(void *priv, struct wpa_driver_capa *capa) +{ + os_memset(capa, 0, sizeof(*capa)); + + capa->key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | + WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK; + capa->enc = WPA_DRIVER_CAPA_ENC_WEP40 | WPA_DRIVER_CAPA_ENC_WEP104 | + WPA_DRIVER_CAPA_ENC_TKIP | WPA_DRIVER_CAPA_ENC_CCMP; + capa->auth = WPA_DRIVER_AUTH_OPEN | WPA_DRIVER_AUTH_SHARED | + WPA_DRIVER_AUTH_LEAP; + capa->flags = WPA_DRIVER_FLAGS_4WAY_HANDSHAKE; + + return 0; +} + + +static void * wpa_driver_osx_init(void *ctx, const char *ifname) +{ + struct wpa_driver_osx_data *drv; + WirelessError err; + u8 enabled, power; + + if (!WirelessIsAvailable()) { + wpa_printf(MSG_ERROR, "OSX: No wireless interface available"); + return NULL; + } + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->ctx = ctx; + err = WirelessAttach(&drv->wireless_ctx, 0); + if (err) { + wpa_printf(MSG_ERROR, "OSX: WirelessAttach failed: %d", + (int) err); + os_free(drv); + return NULL; + } + + err = WirelessGetEnabled(drv->wireless_ctx, &enabled); + if (err) + wpa_printf(MSG_DEBUG, "OSX: WirelessGetEnabled failed: 0x%08x", + (unsigned int) err); + err = WirelessGetPower(drv->wireless_ctx, &power); + if (err) + wpa_printf(MSG_DEBUG, "OSX: WirelessGetPower failed: 0x%08x", + (unsigned int) err); + + wpa_printf(MSG_DEBUG, "OSX: Enabled=%d Power=%d", enabled, power); + + if (!enabled) { + err = WirelessSetEnabled(drv->wireless_ctx, 1); + if (err) { + wpa_printf(MSG_DEBUG, "OSX: WirelessSetEnabled failed:" + " 0x%08x", (unsigned int) err); + WirelessDetach(drv->wireless_ctx); + os_free(drv); + return NULL; + } + } + + if (!power) { + err = WirelessSetPower(drv->wireless_ctx, 1); + if (err) { + wpa_printf(MSG_DEBUG, "OSX: WirelessSetPower failed: " + "0x%08x", (unsigned int) err); + WirelessDetach(drv->wireless_ctx); + os_free(drv); + return NULL; + } + } + + return drv; +} + + +static void wpa_driver_osx_deinit(void *priv) +{ + struct wpa_driver_osx_data *drv = priv; + WirelessError err; + + eloop_cancel_timeout(wpa_driver_osx_scan_timeout, drv, drv->ctx); + eloop_cancel_timeout(wpa_driver_osx_assoc_timeout, drv, drv->ctx); + + err = WirelessSetPower(drv->wireless_ctx, 0); + if (err) { + wpa_printf(MSG_DEBUG, "OSX: WirelessSetPower(0) failed: " + "0x%08x", (unsigned int) err); + } + + err = WirelessDetach(drv->wireless_ctx); + if (err) { + wpa_printf(MSG_DEBUG, "OSX: WirelessDetach failed: 0x%08x", + (unsigned int) err); + } + + if (drv->scan_results) + CFRelease(drv->scan_results); + + os_free(drv); +} + + +const struct wpa_driver_ops wpa_driver_osx_ops = { + .name = "osx", + .desc = "Mac OS X Apple80211 driver", + .get_ssid = wpa_driver_osx_get_ssid, + .get_bssid = wpa_driver_osx_get_bssid, + .init = wpa_driver_osx_init, + .deinit = wpa_driver_osx_deinit, + .scan = wpa_driver_osx_scan, + .get_scan_results = wpa_driver_osx_get_scan_results, + .associate = wpa_driver_osx_associate, + .set_key = wpa_driver_osx_set_key, + .get_capa = wpa_driver_osx_get_capa, +}; diff --git a/src/drivers/driver_prism54.c b/src/drivers/driver_prism54.c new file mode 100644 index 0000000..e64e762 --- /dev/null +++ b/src/drivers/driver_prism54.c @@ -0,0 +1,381 @@ +/* + * WPA Supplicant - driver interaction with Linux Prism54.org driver + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004, Luis R. Rodriguez <mcgrof@ruslug.rutgers.edu> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <sys/ioctl.h> + +#include "wireless_copy.h" +#include "common.h" +#include "driver.h" +#include "driver_wext.h" +#include "driver_hostap.h" + +struct wpa_driver_prism54_data { + void *wext; /* private data for driver_wext */ + void *ctx; + char ifname[IFNAMSIZ + 1]; + int sock; +}; + +#define PRISM54_SET_WPA SIOCIWFIRSTPRIV+12 +#define PRISM54_HOSTAPD SIOCIWFIRSTPRIV+25 +#define PRISM54_DROP_UNENCRYPTED SIOCIWFIRSTPRIV+26 + +static void show_set_key_error(struct prism2_hostapd_param *); + +static int hostapd_ioctl_prism54(struct wpa_driver_prism54_data *drv, + struct prism2_hostapd_param *param, + int len, int show_err) +{ + struct iwreq iwr; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) param; + iwr.u.data.length = len; + + if (ioctl(drv->sock, PRISM54_HOSTAPD, &iwr) < 0) { + int ret = errno; + if (show_err) + perror("ioctl[PRISM54_HOSTAPD]"); + return ret; + } + + return 0; +} + + +static int wpa_driver_prism54_set_wpa_ie(struct wpa_driver_prism54_data *drv, + const u8 *wpa_ie, + size_t wpa_ie_len) +{ + struct prism2_hostapd_param *param; + int res; + size_t blen = PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN + wpa_ie_len; + if (blen < sizeof(*param)) + blen = sizeof(*param); + + param = os_zalloc(blen); + if (param == NULL) + return -1; + + param->cmd = PRISM2_HOSTAPD_SET_GENERIC_ELEMENT; + param->u.generic_elem.len = wpa_ie_len; + os_memcpy(param->u.generic_elem.data, wpa_ie, wpa_ie_len); + res = hostapd_ioctl_prism54(drv, param, blen, 1); + + os_free(param); + + return res; +} + + +/* This is called at wpa_supplicant daemon init time */ +static int wpa_driver_prism54_set_wpa(void *priv, int enabled) +{ + struct wpa_driver_prism54_data *drv = priv; + struct prism2_hostapd_param *param; + int res; + size_t blen = PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN; + if (blen < sizeof(*param)) + blen = sizeof(*param); + + param = os_zalloc(blen); + if (param == NULL) + return -1; + + param->cmd = PRISM54_SET_WPA; + param->u.generic_elem.len = 0; + res = hostapd_ioctl_prism54(drv, param, blen, 1); + + os_free(param); + + return res; +} + + +static int wpa_driver_prism54_set_key(void *priv, wpa_alg alg, + const u8 *addr, int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + struct wpa_driver_prism54_data *drv = priv; + struct prism2_hostapd_param *param; + u8 *buf; + size_t blen; + int ret = 0; + char *alg_name; + + switch (alg) { + case WPA_ALG_NONE: + alg_name = "none"; + return -1; + break; + case WPA_ALG_WEP: + alg_name = "WEP"; + return -1; + break; + case WPA_ALG_TKIP: + alg_name = "TKIP"; + break; + case WPA_ALG_CCMP: + alg_name = "CCMP"; + return -1; + break; + default: + return -1; + } + + wpa_printf(MSG_DEBUG, "%s: alg=%s key_idx=%d set_tx=%d seq_len=%lu " + "key_len=%lu", __FUNCTION__, alg_name, key_idx, set_tx, + (unsigned long) seq_len, (unsigned long) key_len); + + if (seq_len > 8) + return -2; + + blen = sizeof(*param) + key_len; + buf = os_zalloc(blen); + if (buf == NULL) + return -1; + + param = (struct prism2_hostapd_param *) buf; + param->cmd = PRISM2_SET_ENCRYPTION; + /* TODO: In theory, STA in client mode can use five keys; four default + * keys for receiving (with keyidx 0..3) and one individual key for + * both transmitting and receiving (keyidx 0) _unicast_ packets. Now, + * keyidx 0 is reserved for this unicast use and default keys can only + * use keyidx 1..3 (i.e., default key with keyidx 0 is not supported). + * This should be fine for more or less all cases, but for completeness + * sake, the driver could be enhanced to support the missing key. */ +#if 0 + if (addr == NULL) + os_memset(param->sta_addr, 0xff, ETH_ALEN); + else + os_memcpy(param->sta_addr, addr, ETH_ALEN); +#else + os_memset(param->sta_addr, 0xff, ETH_ALEN); +#endif + os_strlcpy((char *) param->u.crypt.alg, alg_name, + HOSTAP_CRYPT_ALG_NAME_LEN); + param->u.crypt.flags = set_tx ? HOSTAP_CRYPT_FLAG_SET_TX_KEY : 0; + param->u.crypt.idx = key_idx; + os_memcpy(param->u.crypt.seq, seq, seq_len); + param->u.crypt.key_len = key_len; + os_memcpy((u8 *) (param + 1), key, key_len); + + if (hostapd_ioctl_prism54(drv, param, blen, 1)) { + wpa_printf(MSG_WARNING, "Failed to set encryption."); + show_set_key_error(param); + ret = -1; + } + os_free(buf); + + return ret; +} + + +static int wpa_driver_prism54_set_countermeasures(void *priv, + int enabled) +{ + /* FIX */ + printf("wpa_driver_prism54_set_countermeasures - not yet " + "implemented\n"); + return 0; +} + + +static int wpa_driver_prism54_set_drop_unencrypted(void *priv, + int enabled) +{ + struct wpa_driver_prism54_data *drv = priv; + struct prism2_hostapd_param *param; + int res; + size_t blen = PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN; + if (blen < sizeof(*param)) + blen = sizeof(*param); + + param = os_zalloc(blen); + if (param == NULL) + return -1; + + param->cmd = PRISM54_DROP_UNENCRYPTED; + param->u.generic_elem.len = 0; + res = hostapd_ioctl_prism54(drv, param, blen, 1); + + os_free(param); + + return res; +} + + +static int wpa_driver_prism54_deauthenticate(void *priv, const u8 *addr, + int reason_code) +{ + /* FIX */ + printf("wpa_driver_prism54_deauthenticate - not yet implemented\n"); + return 0; +} + + +static int wpa_driver_prism54_disassociate(void *priv, const u8 *addr, + int reason_code) +{ + /* FIX */ + printf("wpa_driver_prism54_disassociate - not yet implemented\n"); + return 0; +} + + +static int +wpa_driver_prism54_associate(void *priv, + struct wpa_driver_associate_params *params) +{ + struct wpa_driver_prism54_data *drv = priv; + int ret = 0; + + if (wpa_driver_prism54_set_wpa_ie(drv, params->wpa_ie, + params->wpa_ie_len) < 0) + ret = -1; + if (wpa_driver_wext_set_freq(drv->wext, params->freq) < 0) + ret = -1; + if (wpa_driver_wext_set_ssid(drv->wext, params->ssid, + params->ssid_len) < 0) + ret = -1; + if (wpa_driver_wext_set_bssid(drv->wext, params->bssid) < 0) + ret = -1; + + return ret; +} + +static void show_set_key_error(struct prism2_hostapd_param *param) +{ + switch (param->u.crypt.err) { + case HOSTAP_CRYPT_ERR_UNKNOWN_ALG: + wpa_printf(MSG_INFO, "Unknown algorithm '%s'.", + param->u.crypt.alg); + wpa_printf(MSG_INFO, "You may need to load kernel module to " + "register that algorithm."); + wpa_printf(MSG_INFO, "E.g., 'modprobe hostap_crypt_wep' for " + "WEP."); + break; + case HOSTAP_CRYPT_ERR_UNKNOWN_ADDR: + wpa_printf(MSG_INFO, "Unknown address " MACSTR ".", + MAC2STR(param->sta_addr)); + break; + case HOSTAP_CRYPT_ERR_CRYPT_INIT_FAILED: + wpa_printf(MSG_INFO, "Crypt algorithm initialization failed."); + break; + case HOSTAP_CRYPT_ERR_KEY_SET_FAILED: + wpa_printf(MSG_INFO, "Key setting failed."); + break; + case HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED: + wpa_printf(MSG_INFO, "TX key index setting failed."); + break; + case HOSTAP_CRYPT_ERR_CARD_CONF_FAILED: + wpa_printf(MSG_INFO, "Card configuration failed."); + break; + } +} + + +static int wpa_driver_prism54_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_prism54_data *drv = priv; + return wpa_driver_wext_get_bssid(drv->wext, bssid); +} + + +static int wpa_driver_prism54_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_prism54_data *drv = priv; + return wpa_driver_wext_get_ssid(drv->wext, ssid); +} + + +static int wpa_driver_prism54_scan(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_prism54_data *drv = priv; + return wpa_driver_wext_scan(drv->wext, ssid, ssid_len); +} + + +static struct wpa_scan_results * +wpa_driver_prism54_get_scan_results(void *priv) +{ + struct wpa_driver_prism54_data *drv = priv; + return wpa_driver_wext_get_scan_results(drv->wext); +} + + +static int wpa_driver_prism54_set_operstate(void *priv, int state) +{ + struct wpa_driver_prism54_data *drv = priv; + return wpa_driver_wext_set_operstate(drv->wext, state); +} + + +static void * wpa_driver_prism54_init(void *ctx, const char *ifname) +{ + struct wpa_driver_prism54_data *drv; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->wext = wpa_driver_wext_init(ctx, ifname); + if (drv->wext == NULL) { + os_free(drv); + return NULL; + } + + drv->ctx = ctx; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + drv->sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->sock < 0) { + wpa_driver_wext_deinit(drv->wext); + os_free(drv); + return NULL; + } + + return drv; +} + + +static void wpa_driver_prism54_deinit(void *priv) +{ + struct wpa_driver_prism54_data *drv = priv; + wpa_driver_wext_deinit(drv->wext); + close(drv->sock); + os_free(drv); +} + + +const struct wpa_driver_ops wpa_driver_prism54_ops = { + .name = "prism54", + .desc = "Prism54.org driver (Intersil Prism GT/Duette/Indigo)", + .get_bssid = wpa_driver_prism54_get_bssid, + .get_ssid = wpa_driver_prism54_get_ssid, + .set_wpa = wpa_driver_prism54_set_wpa, + .set_key = wpa_driver_prism54_set_key, + .set_countermeasures = wpa_driver_prism54_set_countermeasures, + .set_drop_unencrypted = wpa_driver_prism54_set_drop_unencrypted, + .scan = wpa_driver_prism54_scan, + .get_scan_results2 = wpa_driver_prism54_get_scan_results, + .deauthenticate = wpa_driver_prism54_deauthenticate, + .disassociate = wpa_driver_prism54_disassociate, + .associate = wpa_driver_prism54_associate, + .init = wpa_driver_prism54_init, + .deinit = wpa_driver_prism54_deinit, + .set_operstate = wpa_driver_prism54_set_operstate, +}; diff --git a/src/drivers/driver_privsep.c b/src/drivers/driver_privsep.c new file mode 100644 index 0000000..fdf299d --- /dev/null +++ b/src/drivers/driver_privsep.c @@ -0,0 +1,820 @@ +/* + * WPA Supplicant - privilege separated driver interface + * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <sys/un.h> + +#include "common.h" +#include "driver.h" +#include "eloop.h" +#include "privsep_commands.h" + + +struct wpa_driver_privsep_data { + void *ctx; + u8 own_addr[ETH_ALEN]; + int priv_socket; + char *own_socket_path; + int cmd_socket; + char *own_cmd_path; + struct sockaddr_un priv_addr; + char ifname[16]; +}; + + +static int wpa_priv_reg_cmd(struct wpa_driver_privsep_data *drv, int cmd) +{ + int res; + + res = sendto(drv->priv_socket, &cmd, sizeof(cmd), 0, + (struct sockaddr *) &drv->priv_addr, + sizeof(drv->priv_addr)); + if (res < 0) + perror("sendto"); + return res < 0 ? -1 : 0; +} + + +static int wpa_priv_cmd(struct wpa_driver_privsep_data *drv, int cmd, + const void *data, size_t data_len, + void *reply, size_t *reply_len) +{ + struct msghdr msg; + struct iovec io[2]; + + io[0].iov_base = &cmd; + io[0].iov_len = sizeof(cmd); + io[1].iov_base = (u8 *) data; + io[1].iov_len = data_len; + + os_memset(&msg, 0, sizeof(msg)); + msg.msg_iov = io; + msg.msg_iovlen = data ? 2 : 1; + msg.msg_name = &drv->priv_addr; + msg.msg_namelen = sizeof(drv->priv_addr); + + if (sendmsg(drv->cmd_socket, &msg, 0) < 0) { + perror("sendmsg(cmd_socket)"); + return -1; + } + + if (reply) { + fd_set rfds; + struct timeval tv; + int res; + + FD_ZERO(&rfds); + FD_SET(drv->cmd_socket, &rfds); + tv.tv_sec = 5; + tv.tv_usec = 0; + res = select(drv->cmd_socket + 1, &rfds, NULL, NULL, &tv); + if (res < 0 && errno != EINTR) { + perror("select"); + return -1; + } + + if (FD_ISSET(drv->cmd_socket, &rfds)) { + res = recv(drv->cmd_socket, reply, *reply_len, 0); + if (res < 0) { + perror("recv"); + return -1; + } + *reply_len = res; + } else { + wpa_printf(MSG_DEBUG, "PRIVSEP: Timeout while waiting " + "for reply (cmd=%d)", cmd); + return -1; + } + } + + return 0; +} + + +static int wpa_driver_privsep_set_wpa(void *priv, int enabled) +{ + struct wpa_driver_privsep_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); + return wpa_priv_cmd(drv, PRIVSEP_CMD_SET_WPA, &enabled, + sizeof(enabled), NULL, NULL); +} + + +static int wpa_driver_privsep_scan(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_privsep_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s: priv=%p", __func__, priv); + return wpa_priv_cmd(drv, PRIVSEP_CMD_SCAN, ssid, ssid_len, + NULL, NULL); +} + + +static struct wpa_scan_results * +wpa_driver_privsep_get_scan_results2(void *priv) +{ + struct wpa_driver_privsep_data *drv = priv; + int res, num; + u8 *buf, *pos, *end; + size_t reply_len = 60000; + struct wpa_scan_results *results; + struct wpa_scan_res *r; + + buf = os_malloc(reply_len); + if (buf == NULL) + return NULL; + res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_SCAN_RESULTS, + NULL, 0, buf, &reply_len); + if (res < 0) { + os_free(buf); + return NULL; + } + + wpa_printf(MSG_DEBUG, "privsep: Received %lu bytes of scan results", + (unsigned long) reply_len); + if (reply_len < sizeof(int)) { + wpa_printf(MSG_DEBUG, "privsep: Invalid scan result len %lu", + (unsigned long) reply_len); + os_free(buf); + return NULL; + } + + pos = buf; + end = buf + reply_len; + os_memcpy(&num, pos, sizeof(int)); + if (num < 0 || num > 1000) { + os_free(buf); + return NULL; + } + pos += sizeof(int); + + results = os_zalloc(sizeof(*results)); + if (results == NULL) { + os_free(buf); + return NULL; + } + + results->res = os_zalloc(num * sizeof(struct wpa_scan_res *)); + if (results->res == NULL) { + os_free(results); + os_free(buf); + return NULL; + } + + while (results->num < (size_t) num && pos + sizeof(int) < end) { + int len; + os_memcpy(&len, pos, sizeof(int)); + pos += sizeof(int); + if (len < 0 || len > 10000 || pos + len > end) + break; + + r = os_malloc(len); + if (r == NULL) + break; + os_memcpy(r, pos, len); + pos += len; + if (sizeof(*r) + r->ie_len > (size_t) len) { + os_free(r); + break; + } + + results->res[results->num++] = r; + } + + os_free(buf); + return results; +} + + +static int wpa_driver_privsep_set_key(void *priv, wpa_alg alg, const u8 *addr, + int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + struct wpa_driver_privsep_data *drv = priv; + struct privsep_cmd_set_key cmd; + + wpa_printf(MSG_DEBUG, "%s: priv=%p alg=%d key_idx=%d set_tx=%d", + __func__, priv, alg, key_idx, set_tx); + + os_memset(&cmd, 0, sizeof(cmd)); + cmd.alg = alg; + if (addr) + os_memcpy(cmd.addr, addr, ETH_ALEN); + else + os_memset(cmd.addr, 0xff, ETH_ALEN); + cmd.key_idx = key_idx; + cmd.set_tx = set_tx; + if (seq && seq_len > 0 && seq_len < sizeof(cmd.seq)) { + os_memcpy(cmd.seq, seq, seq_len); + cmd.seq_len = seq_len; + } + if (key && key_len > 0 && key_len < sizeof(cmd.key)) { + os_memcpy(cmd.key, key, key_len); + cmd.key_len = key_len; + } + + return wpa_priv_cmd(drv, PRIVSEP_CMD_SET_KEY, &cmd, sizeof(cmd), + NULL, NULL); +} + + +static int wpa_driver_privsep_associate( + void *priv, struct wpa_driver_associate_params *params) +{ + struct wpa_driver_privsep_data *drv = priv; + struct privsep_cmd_associate *data; + int res; + size_t buflen; + + wpa_printf(MSG_DEBUG, "%s: priv=%p freq=%d pairwise_suite=%d " + "group_suite=%d key_mgmt_suite=%d auth_alg=%d mode=%d", + __func__, priv, params->freq, params->pairwise_suite, + params->group_suite, params->key_mgmt_suite, + params->auth_alg, params->mode); + + buflen = sizeof(*data) + params->wpa_ie_len; + data = os_zalloc(buflen); + if (data == NULL) + return -1; + + if (params->bssid) + os_memcpy(data->bssid, params->bssid, ETH_ALEN); + os_memcpy(data->ssid, params->ssid, params->ssid_len); + data->ssid_len = params->ssid_len; + data->freq = params->freq; + data->pairwise_suite = params->pairwise_suite; + data->group_suite = params->group_suite; + data->key_mgmt_suite = params->key_mgmt_suite; + data->auth_alg = params->auth_alg; + data->mode = params->mode; + data->wpa_ie_len = params->wpa_ie_len; + if (params->wpa_ie) + os_memcpy(data + 1, params->wpa_ie, params->wpa_ie_len); + /* TODO: add support for other assoc parameters */ + + res = wpa_priv_cmd(drv, PRIVSEP_CMD_ASSOCIATE, data, buflen, + NULL, NULL); + os_free(data); + + return res; +} + + +static int wpa_driver_privsep_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_privsep_data *drv = priv; + int res; + size_t len = ETH_ALEN; + + res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_BSSID, NULL, 0, bssid, &len); + if (res < 0 || len != ETH_ALEN) + return -1; + return 0; +} + + +static int wpa_driver_privsep_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_privsep_data *drv = priv; + int res, ssid_len; + u8 reply[sizeof(int) + 32]; + size_t len = sizeof(reply); + + res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_SSID, NULL, 0, reply, &len); + if (res < 0 || len < sizeof(int)) + return -1; + os_memcpy(&ssid_len, reply, sizeof(int)); + if (ssid_len < 0 || ssid_len > 32 || sizeof(int) + ssid_len > len) { + wpa_printf(MSG_DEBUG, "privsep: Invalid get SSID reply"); + return -1; + } + os_memcpy(ssid, &reply[sizeof(int)], ssid_len); + return ssid_len; +} + + +static int wpa_driver_privsep_deauthenticate(void *priv, const u8 *addr, + int reason_code) +{ + //struct wpa_driver_privsep_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s addr=" MACSTR " reason_code=%d", + __func__, MAC2STR(addr), reason_code); + wpa_printf(MSG_DEBUG, "%s - TODO", __func__); + return 0; +} + + +static int wpa_driver_privsep_disassociate(void *priv, const u8 *addr, + int reason_code) +{ + //struct wpa_driver_privsep_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s addr=" MACSTR " reason_code=%d", + __func__, MAC2STR(addr), reason_code); + wpa_printf(MSG_DEBUG, "%s - TODO", __func__); + return 0; +} + + +static void wpa_driver_privsep_event_assoc(void *ctx, wpa_event_type event, + u8 *buf, size_t len) +{ + union wpa_event_data data; + int inc_data = 0; + u8 *pos, *end; + int ie_len; + + os_memset(&data, 0, sizeof(data)); + + pos = buf; + end = buf + len; + + if (end - pos < (int) sizeof(int)) + return; + os_memcpy(&ie_len, pos, sizeof(int)); + pos += sizeof(int); + if (ie_len < 0 || ie_len > end - pos) + return; + if (ie_len) { + data.assoc_info.req_ies = pos; + data.assoc_info.req_ies_len = ie_len; + pos += ie_len; + inc_data = 1; + } + + wpa_supplicant_event(ctx, event, inc_data ? &data : NULL); +} + + +static void wpa_driver_privsep_event_interface_status(void *ctx, u8 *buf, + size_t len) +{ + union wpa_event_data data; + int ievent; + + if (len < sizeof(int) || + len - sizeof(int) > sizeof(data.interface_status.ifname)) + return; + + os_memcpy(&ievent, buf, sizeof(int)); + + os_memset(&data, 0, sizeof(data)); + data.interface_status.ievent = ievent; + os_memcpy(data.interface_status.ifname, buf + sizeof(int), + len - sizeof(int)); + wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &data); +} + + +static void wpa_driver_privsep_event_michael_mic_failure( + void *ctx, u8 *buf, size_t len) +{ + union wpa_event_data data; + + if (len != sizeof(int)) + return; + + os_memset(&data, 0, sizeof(data)); + os_memcpy(&data.michael_mic_failure.unicast, buf, sizeof(int)); + wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); +} + + +static void wpa_driver_privsep_event_pmkid_candidate(void *ctx, u8 *buf, + size_t len) +{ + union wpa_event_data data; + + if (len != sizeof(struct pmkid_candidate)) + return; + + os_memset(&data, 0, sizeof(data)); + os_memcpy(&data.pmkid_candidate, buf, len); + wpa_supplicant_event(ctx, EVENT_PMKID_CANDIDATE, &data); +} + + +static void wpa_driver_privsep_event_stkstart(void *ctx, u8 *buf, size_t len) +{ + union wpa_event_data data; + + if (len != ETH_ALEN) + return; + + os_memset(&data, 0, sizeof(data)); + os_memcpy(data.stkstart.peer, buf, ETH_ALEN); + wpa_supplicant_event(ctx, EVENT_STKSTART, &data); +} + + +static void wpa_driver_privsep_event_ft_response(void *ctx, u8 *buf, + size_t len) +{ + union wpa_event_data data; + + if (len < sizeof(int) + ETH_ALEN) + return; + + os_memset(&data, 0, sizeof(data)); + os_memcpy(&data.ft_ies.ft_action, buf, sizeof(int)); + os_memcpy(data.ft_ies.target_ap, buf + sizeof(int), ETH_ALEN); + data.ft_ies.ies = buf + sizeof(int) + ETH_ALEN; + data.ft_ies.ies_len = len - sizeof(int) - ETH_ALEN; + wpa_supplicant_event(ctx, EVENT_FT_RESPONSE, &data); +} + + +static void wpa_driver_privsep_event_rx_eapol(void *ctx, u8 *buf, size_t len) +{ + if (len < ETH_ALEN) + return; + + wpa_supplicant_rx_eapol(ctx, buf, buf + ETH_ALEN, len - ETH_ALEN); +} + + +static void wpa_driver_privsep_event_sta_rx(void *ctx, u8 *buf, size_t len) +{ +#ifdef CONFIG_CLIENT_MLME + struct ieee80211_rx_status *rx_status; + + if (len < sizeof(*rx_status)) + return; + rx_status = (struct ieee80211_rx_status *) buf; + buf += sizeof(*rx_status); + len -= sizeof(*rx_status); + + wpa_supplicant_sta_rx(ctx, buf, len, rx_status); +#endif /* CONFIG_CLIENT_MLME */ +} + + +static void wpa_driver_privsep_receive(int sock, void *eloop_ctx, + void *sock_ctx) +{ + struct wpa_driver_privsep_data *drv = eloop_ctx; + u8 *buf, *event_buf; + size_t event_len; + int res, event; + enum privsep_event e; + struct sockaddr_un from; + socklen_t fromlen = sizeof(from); + const size_t buflen = 2000; + + buf = os_malloc(buflen); + if (buf == NULL) + return; + res = recvfrom(sock, buf, buflen, 0, + (struct sockaddr *) &from, &fromlen); + if (res < 0) { + perror("recvfrom(priv_socket)"); + os_free(buf); + return; + } + + wpa_printf(MSG_DEBUG, "privsep_driver: received %u bytes", res); + + if (res < (int) sizeof(int)) { + wpa_printf(MSG_DEBUG, "Too short event message (len=%d)", res); + return; + } + + os_memcpy(&event, buf, sizeof(int)); + event_buf = &buf[sizeof(int)]; + event_len = res - sizeof(int); + wpa_printf(MSG_DEBUG, "privsep: Event %d received (len=%lu)", + event, (unsigned long) event_len); + + e = event; + switch (e) { + case PRIVSEP_EVENT_SCAN_RESULTS: + wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, NULL); + break; + case PRIVSEP_EVENT_ASSOC: + wpa_driver_privsep_event_assoc(drv->ctx, EVENT_ASSOC, + event_buf, event_len); + break; + case PRIVSEP_EVENT_DISASSOC: + wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); + break; + case PRIVSEP_EVENT_ASSOCINFO: + wpa_driver_privsep_event_assoc(drv->ctx, EVENT_ASSOCINFO, + event_buf, event_len); + break; + case PRIVSEP_EVENT_MICHAEL_MIC_FAILURE: + wpa_driver_privsep_event_michael_mic_failure( + drv->ctx, event_buf, event_len); + break; + case PRIVSEP_EVENT_INTERFACE_STATUS: + wpa_driver_privsep_event_interface_status(drv->ctx, event_buf, + event_len); + break; + case PRIVSEP_EVENT_PMKID_CANDIDATE: + wpa_driver_privsep_event_pmkid_candidate(drv->ctx, event_buf, + event_len); + break; + case PRIVSEP_EVENT_STKSTART: + wpa_driver_privsep_event_stkstart(drv->ctx, event_buf, + event_len); + break; + case PRIVSEP_EVENT_FT_RESPONSE: + wpa_driver_privsep_event_ft_response(drv->ctx, event_buf, + event_len); + break; + case PRIVSEP_EVENT_RX_EAPOL: + wpa_driver_privsep_event_rx_eapol(drv->ctx, event_buf, + event_len); + break; + case PRIVSEP_EVENT_STA_RX: + wpa_driver_privsep_event_sta_rx(drv->ctx, event_buf, + event_len); + break; + } + + os_free(buf); +} + + +static void * wpa_driver_privsep_init(void *ctx, const char *ifname) +{ + struct wpa_driver_privsep_data *drv; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->ctx = ctx; + drv->priv_socket = -1; + drv->cmd_socket = -1; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + + return drv; +} + + +static void wpa_driver_privsep_deinit(void *priv) +{ + struct wpa_driver_privsep_data *drv = priv; + + if (drv->priv_socket >= 0) { + wpa_priv_reg_cmd(drv, PRIVSEP_CMD_UNREGISTER); + eloop_unregister_read_sock(drv->priv_socket); + close(drv->priv_socket); + } + + if (drv->own_socket_path) { + unlink(drv->own_socket_path); + os_free(drv->own_socket_path); + } + + if (drv->cmd_socket >= 0) { + eloop_unregister_read_sock(drv->cmd_socket); + close(drv->cmd_socket); + } + + if (drv->own_cmd_path) { + unlink(drv->own_cmd_path); + os_free(drv->own_cmd_path); + } + + os_free(drv); +} + + +static int wpa_driver_privsep_set_param(void *priv, const char *param) +{ + struct wpa_driver_privsep_data *drv = priv; + const char *pos; + char *own_dir, *priv_dir; + static unsigned int counter = 0; + size_t len; + struct sockaddr_un addr; + + wpa_printf(MSG_DEBUG, "%s: param='%s'", __func__, param); + if (param == NULL) + pos = NULL; + else + pos = os_strstr(param, "own_dir="); + if (pos) { + char *end; + own_dir = os_strdup(pos + 8); + if (own_dir == NULL) + return -1; + end = os_strchr(own_dir, ' '); + if (end) + *end = '\0'; + } else { + own_dir = os_strdup("/tmp"); + if (own_dir == NULL) + return -1; + } + + if (param == NULL) + pos = NULL; + else + pos = os_strstr(param, "priv_dir="); + if (pos) { + char *end; + priv_dir = os_strdup(pos + 9); + if (priv_dir == NULL) { + os_free(own_dir); + return -1; + } + end = os_strchr(priv_dir, ' '); + if (end) + *end = '\0'; + } else { + priv_dir = os_strdup("/var/run/wpa_priv"); + if (priv_dir == NULL) { + os_free(own_dir); + return -1; + } + } + + len = os_strlen(own_dir) + 50; + drv->own_socket_path = os_malloc(len); + if (drv->own_socket_path == NULL) { + os_free(priv_dir); + os_free(own_dir); + return -1; + } + os_snprintf(drv->own_socket_path, len, "%s/wpa_privsep-%d-%d", + own_dir, getpid(), counter++); + + len = os_strlen(own_dir) + 50; + drv->own_cmd_path = os_malloc(len); + if (drv->own_cmd_path == NULL) { + os_free(drv->own_socket_path); + drv->own_socket_path = NULL; + os_free(priv_dir); + os_free(own_dir); + return -1; + } + os_snprintf(drv->own_cmd_path, len, "%s/wpa_privsep-%d-%d", + own_dir, getpid(), counter++); + + os_free(own_dir); + + drv->priv_addr.sun_family = AF_UNIX; + os_snprintf(drv->priv_addr.sun_path, sizeof(drv->priv_addr.sun_path), + "%s/%s", priv_dir, drv->ifname); + os_free(priv_dir); + + drv->priv_socket = socket(PF_UNIX, SOCK_DGRAM, 0); + if (drv->priv_socket < 0) { + perror("socket(PF_UNIX)"); + os_free(drv->own_socket_path); + drv->own_socket_path = NULL; + return -1; + } + + os_memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + os_strlcpy(addr.sun_path, drv->own_socket_path, sizeof(addr.sun_path)); + if (bind(drv->priv_socket, (struct sockaddr *) &addr, sizeof(addr)) < + 0) { + perror("bind(PF_UNIX)"); + close(drv->priv_socket); + drv->priv_socket = -1; + unlink(drv->own_socket_path); + os_free(drv->own_socket_path); + drv->own_socket_path = NULL; + return -1; + } + + eloop_register_read_sock(drv->priv_socket, wpa_driver_privsep_receive, + drv, NULL); + + drv->cmd_socket = socket(PF_UNIX, SOCK_DGRAM, 0); + if (drv->cmd_socket < 0) { + perror("socket(PF_UNIX)"); + os_free(drv->own_cmd_path); + drv->own_cmd_path = NULL; + return -1; + } + + os_memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + os_strlcpy(addr.sun_path, drv->own_cmd_path, sizeof(addr.sun_path)); + if (bind(drv->cmd_socket, (struct sockaddr *) &addr, sizeof(addr)) < 0) + { + perror("bind(PF_UNIX)"); + close(drv->cmd_socket); + drv->cmd_socket = -1; + unlink(drv->own_cmd_path); + os_free(drv->own_cmd_path); + drv->own_cmd_path = NULL; + return -1; + } + + if (wpa_priv_reg_cmd(drv, PRIVSEP_CMD_REGISTER) < 0) { + wpa_printf(MSG_ERROR, "Failed to register with wpa_priv"); + return -1; + } + + return 0; +} + + +static int wpa_driver_privsep_get_capa(void *priv, + struct wpa_driver_capa *capa) +{ + struct wpa_driver_privsep_data *drv = priv; + int res; + size_t len = sizeof(*capa); + + res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_CAPA, NULL, 0, capa, &len); + if (res < 0 || len != sizeof(*capa)) + return -1; + return 0; +} + + +static const u8 * wpa_driver_privsep_get_mac_addr(void *priv) +{ + struct wpa_driver_privsep_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s", __func__); + return drv->own_addr; +} + + +static int wpa_driver_privsep_set_mode(void *priv, int mode) +{ + struct wpa_driver_privsep_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s mode=%d", __func__, mode); + return wpa_priv_cmd(drv, PRIVSEP_CMD_SET_MODE, &mode, sizeof(mode), + NULL, NULL); +} + + +static int wpa_driver_privsep_set_country(void *priv, const char *alpha2) +{ + struct wpa_driver_privsep_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s country='%s'", __func__, alpha2); + return wpa_priv_cmd(drv, PRIVSEP_CMD_SET_COUNTRY, alpha2, + os_strlen(alpha2), NULL, NULL); +} + + +struct wpa_driver_ops wpa_driver_privsep_ops = { + "privsep", + "wpa_supplicant privilege separated driver", + wpa_driver_privsep_get_bssid, + wpa_driver_privsep_get_ssid, + wpa_driver_privsep_set_wpa, + wpa_driver_privsep_set_key, + wpa_driver_privsep_init, + wpa_driver_privsep_deinit, + wpa_driver_privsep_set_param, + NULL /* set_countermeasures */, + NULL /* set_drop_unencrypted */, + wpa_driver_privsep_scan, + NULL /* get_scan_results */, + wpa_driver_privsep_deauthenticate, + wpa_driver_privsep_disassociate, + wpa_driver_privsep_associate, + NULL /* set_auth_alg */, + NULL /* add_pmkid */, + NULL /* remove_pmkid */, + NULL /* flush_pmkid */, + wpa_driver_privsep_get_capa, + NULL /* poll */, + NULL /* get_ifname */, + wpa_driver_privsep_get_mac_addr, + NULL /* send_eapol */, + NULL /* set_operstate */, + NULL /* mlme_setprotection */, + NULL /* get_hw_feature_data */, + NULL /* set_channel */, + NULL /* set_ssid */, + NULL /* set_bssid */, + NULL /* send_mlme */, + NULL /* mlme_add_sta */, + NULL /* mlme_remove_sta */, + NULL /* update_ft_ies */, + NULL /* send_ft_action */, + wpa_driver_privsep_get_scan_results2, + NULL /* set_probe_req_ie */, + wpa_driver_privsep_set_mode, + wpa_driver_privsep_set_country, + NULL /* global_init */, + NULL /* global_deinit */, + NULL /* init2 */, + NULL /* get_interfaces */ +}; + + +struct wpa_driver_ops *wpa_supplicant_drivers[] = +{ + &wpa_driver_privsep_ops, + NULL +}; diff --git a/src/drivers/driver_ps3.c b/src/drivers/driver_ps3.c new file mode 100644 index 0000000..fde3425 --- /dev/null +++ b/src/drivers/driver_ps3.c @@ -0,0 +1,186 @@ +/* + * WPA Supplicant - PS3 Linux wireless extension driver interface + * Copyright 2007, 2008 Sony Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <sys/ioctl.h> +#include "wireless_copy.h" +#include "common.h" +#include "wpa_common.h" +#include "driver.h" +#include "eloop.h" +#include "driver_wext.h" +#include "ieee802_11_defs.h" + +static int wpa_driver_ps3_set_wpa_key(struct wpa_driver_wext_data *drv, + struct wpa_driver_associate_params *params) +{ + int ret, i; + struct iwreq iwr; + char *buf, *str; + + if (!params->psk && !params->passphrase) { + wpa_printf(MSG_INFO, "%s:no PSK error", __func__); + return -EINVAL; + } + + os_memset(&iwr, 0, sizeof(iwr)); + if (params->psk) { + /* includes null */ + iwr.u.data.length = PMK_LEN * 2 + 1; + buf = os_malloc(iwr.u.data.length); + if (!buf) + return -ENOMEM; + str = buf; + for (i = 0; i < PMK_LEN; i++) { + str += snprintf(str, iwr.u.data.length - (str - buf), + "%02x", params->psk[i]); + } + } else if (params->passphrase) { + /* including quotations and null */ + iwr.u.data.length = strlen(params->passphrase) + 3; + buf = os_malloc(iwr.u.data.length); + if (!buf) + return -ENOMEM; + buf[0] = '"'; + os_memcpy(buf + 1, params->passphrase, iwr.u.data.length - 3); + buf[iwr.u.data.length - 2] = '"'; + buf[iwr.u.data.length - 1] = '\0'; + } else + return -EINVAL; + iwr.u.data.pointer = (caddr_t) buf; + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + ret = ioctl(drv->ioctl_sock, SIOCIWFIRSTPRIV, &iwr); + os_free(buf); + + return ret; +} + +static int wpa_driver_ps3_set_wep_keys(struct wpa_driver_wext_data *drv, + struct wpa_driver_associate_params *params) +{ + int ret, i; + struct iwreq iwr; + + for (i = 0; i < 4; i++) { + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.encoding.flags = i + 1; + if (params->wep_key_len[i]) { + iwr.u.encoding.pointer = (caddr_t) params->wep_key[i]; + iwr.u.encoding.length = params->wep_key_len[i]; + } else + iwr.u.encoding.flags = IW_ENCODE_NOKEY | + IW_ENCODE_DISABLED; + + if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) { + perror("ioctl[SIOCSIWENCODE]"); + ret = -1; + } + } + return ret; +} + +static int wpa_driver_ps3_associate(void *priv, + struct wpa_driver_associate_params *params) +{ + struct wpa_driver_wext_data *drv = priv; + int ret, value; + + wpa_printf(MSG_DEBUG, "%s: <-", __func__); + + /* clear BSSID */ + if (!params->bssid && + wpa_driver_wext_set_bssid(drv, NULL) < 0) + ret = -1; + + if (wpa_driver_wext_set_mode(drv, params->mode) < 0) + ret = -1; + + if (params->wpa_ie == NULL || params->wpa_ie_len == 0) + value = IW_AUTH_WPA_VERSION_DISABLED; + else if (params->wpa_ie[0] == WLAN_EID_RSN) + value = IW_AUTH_WPA_VERSION_WPA2; + else + value = IW_AUTH_WPA_VERSION_WPA; + if (wpa_driver_wext_set_auth_param(drv, + IW_AUTH_WPA_VERSION, value) < 0) + ret = -1; + value = wpa_driver_wext_cipher2wext(params->pairwise_suite); + if (wpa_driver_wext_set_auth_param(drv, + IW_AUTH_CIPHER_PAIRWISE, value) < 0) + ret = -1; + value = wpa_driver_wext_cipher2wext(params->group_suite); + if (wpa_driver_wext_set_auth_param(drv, + IW_AUTH_CIPHER_GROUP, value) < 0) + ret = -1; + value = wpa_driver_wext_keymgmt2wext(params->key_mgmt_suite); + if (wpa_driver_wext_set_auth_param(drv, IW_AUTH_KEY_MGMT, value) < 0) + ret = -1; + + /* set selected BSSID */ + if (params->bssid && + wpa_driver_wext_set_bssid(drv, params->bssid) < 0) + ret = -1; + + switch (params->group_suite) { + case CIPHER_NONE: + ret = 0; + break; + case CIPHER_WEP40: + case CIPHER_WEP104: + ret = wpa_driver_ps3_set_wep_keys(drv, params); + break; + case CIPHER_TKIP: + case CIPHER_CCMP: + ret = wpa_driver_ps3_set_wpa_key(drv, params); + break; + } + + /* start to associate */ + ret = wpa_driver_wext_set_ssid(drv, params->ssid, params->ssid_len); + + wpa_printf(MSG_DEBUG, "%s: ->", __func__); + + return ret; +} + +static int wpa_driver_ps3_get_capa(void *priv, struct wpa_driver_capa *capa) +{ + int ret; + wpa_printf(MSG_DEBUG, "%s:<-", __func__); + + ret = wpa_driver_wext_get_capa(priv, capa); + if (ret) { + wpa_printf(MSG_INFO, "%s: base wext returns error %d", + __func__, ret); + return ret; + } + /* PS3 hypervisor does association and 4way handshake by itself */ + capa->flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE; + wpa_printf(MSG_DEBUG, "%s:->", __func__); + return 0; +} + +const struct wpa_driver_ops wpa_driver_ps3_ops = { + .name = "ps3", + .desc = "PLAYSTATION3 Linux wireless extension driver", + .get_bssid = wpa_driver_wext_get_bssid, + .get_ssid = wpa_driver_wext_get_ssid, + .scan = wpa_driver_wext_scan, + .get_scan_results2 = wpa_driver_wext_get_scan_results, + .associate = wpa_driver_ps3_associate, /* PS3 */ + .init = wpa_driver_wext_init, + .deinit = wpa_driver_wext_deinit, + .get_capa = wpa_driver_ps3_get_capa, /* PS3 */ +}; diff --git a/src/drivers/driver_ralink.c b/src/drivers/driver_ralink.c new file mode 100644 index 0000000..e9313cb --- /dev/null +++ b/src/drivers/driver_ralink.c @@ -0,0 +1,1505 @@ +/* + * WPA Supplicant - driver interaction with Ralink Wireless Client + * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi> + * Copyright (c) 2007, Snowpin Lee <snowpin_lee@ralinktech.com.tw> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + */ + +#include "includes.h" +#include <sys/ioctl.h> + +#include "wireless_copy.h" +#include "common.h" +#include "driver.h" +#include "l2_packet/l2_packet.h" +#include "eloop.h" +#include "ieee802_11_defs.h" +#include "priv_netlink.h" +#include "driver_ralink.h" + +static void wpa_driver_ralink_scan_timeout(void *eloop_ctx, void *timeout_ctx); + +#define MAX_SSID_LEN 32 + +struct wpa_driver_ralink_data { + void *ctx; + int ioctl_sock; + int event_sock; + char ifname[IFNAMSIZ + 1]; + u8 *assoc_req_ies; + size_t assoc_req_ies_len; + u8 *assoc_resp_ies; + size_t assoc_resp_ies_len; + int no_of_pmkid; + struct ndis_pmkid_entry *pmkid; + int we_version_compiled; + int ap_scan; + int scanning_done; + u8 g_driver_down; +}; + +static int ralink_set_oid(struct wpa_driver_ralink_data *drv, + unsigned short oid, char *data, int len) +{ + char *buf; + struct iwreq iwr; + + buf = os_zalloc(len); + if (buf == NULL) + return -1; + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.flags = oid; + iwr.u.data.flags |= OID_GET_SET_TOGGLE; + + if (data) + os_memcpy(buf, data, len); + + iwr.u.data.pointer = (caddr_t) buf; + iwr.u.data.length = len; + + if (ioctl(drv->ioctl_sock, RT_PRIV_IOCTL, &iwr) < 0) { + wpa_printf(MSG_DEBUG, "%s: oid=0x%x len (%d) failed", + __func__, oid, len); + os_free(buf); + return -1; + } + os_free(buf); + return 0; +} + +static int +ralink_get_new_driver_flag(struct wpa_driver_ralink_data *drv) +{ + struct iwreq iwr; + UCHAR enabled = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = (UCHAR*) &enabled; + iwr.u.data.flags = RT_OID_NEW_DRIVER; + + if (ioctl(drv->ioctl_sock, RT_PRIV_IOCTL, &iwr) < 0) { + wpa_printf(MSG_DEBUG, "%s: failed", __func__); + return 0; + } + + return (enabled == 1) ? 1 : 0; +} + +static int wpa_driver_ralink_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_ralink_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + if (drv->g_driver_down == 1) + return -1; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + + if (ioctl(drv->ioctl_sock, SIOCGIWAP, &iwr) < 0) { + perror("ioctl[SIOCGIWAP]"); + ret = -1; + } + os_memcpy(bssid, iwr.u.ap_addr.sa_data, ETH_ALEN); + + return ret; +} + +static int wpa_driver_ralink_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_ralink_data *drv = priv; +#if 0 + struct wpa_supplicant *wpa_s = drv->ctx; + struct wpa_ssid *entry; +#endif + int ssid_len; + u8 bssid[ETH_ALEN]; + u8 ssid_str[MAX_SSID_LEN]; + struct iwreq iwr; +#if 0 + int result = 0; +#endif + int ret = 0; +#if 0 + BOOLEAN ieee8021x_mode = FALSE; + BOOLEAN ieee8021x_required_key = FALSE; +#endif + + if (drv->g_driver_down == 1) + return -1; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.essid.pointer = (caddr_t) ssid; + iwr.u.essid.length = 32; + + if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) { + perror("ioctl[SIOCGIWESSID]"); + ret = -1; + } else + ret = iwr.u.essid.length; + + if (ret <= 0) + return ret; + + ssid_len = ret; + os_memset(ssid_str, 0, MAX_SSID_LEN); + os_memcpy(ssid_str, ssid, ssid_len); + + if (drv->ap_scan == 0) { + /* Read BSSID form driver */ + if (wpa_driver_ralink_get_bssid(priv, bssid) < 0) { + wpa_printf(MSG_WARNING, "Could not read BSSID from " + "driver."); + return ret; + } + +#if 0 + entry = wpa_s->conf->ssid; + while (entry) { + if (!entry->disabled && ssid_len == entry->ssid_len && + os_memcmp(ssid_str, entry->ssid, ssid_len) == 0 && + (!entry->bssid_set || + os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0)) { + /* match the config of driver */ + result = 1; + break; + } + entry = entry->next; + } + + if (result) { + wpa_printf(MSG_DEBUG, "Ready to set 802.1x mode and " + "ieee_required_keys parameters to driver"); + + /* set 802.1x mode and ieee_required_keys parameter */ + if (entry->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) { + if ((entry->eapol_flags & (EAPOL_FLAG_REQUIRE_KEY_UNICAST | EAPOL_FLAG_REQUIRE_KEY_BROADCAST))) + ieee8021x_required_key = TRUE; + ieee8021x_mode = TRUE; + } + + if (ralink_set_oid(drv, OID_802_11_SET_IEEE8021X, (char *) &ieee8021x_mode, sizeof(BOOLEAN)) < 0) + { + wpa_printf(MSG_DEBUG, "RALINK: Failed to set OID_802_11_SET_IEEE8021X(%d)", (int) ieee8021x_mode); + } + else + { + wpa_printf(MSG_DEBUG, "ieee8021x_mode is %s", ieee8021x_mode ? "TRUE" : "FALSE"); + } + + if (ralink_set_oid(drv, OID_802_11_SET_IEEE8021X_REQUIRE_KEY, (char *) &ieee8021x_required_key, sizeof(BOOLEAN)) < 0) + { + wpa_printf(MSG_DEBUG, "ERROR: Failed to set OID_802_11_SET_IEEE8021X_REQUIRE_KEY(%d)", (int) ieee8021x_required_key); + } + else + { + wpa_printf(MSG_DEBUG, "ieee8021x_required_key is %s and eapol_flag(%d)", ieee8021x_required_key ? "TRUE" : "FALSE", + entry->eapol_flags); + } + } +#endif + } + + return ret; +} + +static int wpa_driver_ralink_set_ssid(struct wpa_driver_ralink_data *drv, + const u8 *ssid, size_t ssid_len) +{ + NDIS_802_11_SSID *buf; + int ret = 0; + struct iwreq iwr; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + buf = os_zalloc(sizeof(NDIS_802_11_SSID)); + if (buf == NULL) + return -1; + os_memset(buf, 0, sizeof(buf)); + buf->SsidLength = ssid_len; + os_memcpy(buf->Ssid, ssid, ssid_len); + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + + iwr.u.data.flags = OID_802_11_SSID; + iwr.u.data.flags |= OID_GET_SET_TOGGLE; + iwr.u.data.pointer = (caddr_t) buf; + iwr.u.data.length = sizeof(NDIS_802_11_SSID); + + if (ioctl(drv->ioctl_sock, RT_PRIV_IOCTL, &iwr) < 0) { + perror("ioctl[RT_PRIV_IOCTL] -- OID_802_11_SSID"); + ret = -1; + } + os_free(buf); + return ret; +} + +static void wpa_driver_ralink_event_pmkid(struct wpa_driver_ralink_data *drv, + const u8 *data, size_t data_len) +{ + NDIS_802_11_PMKID_CANDIDATE_LIST *pmkid; + size_t i; + union wpa_event_data event; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + if (data_len < 8) { + wpa_printf(MSG_DEBUG, "RALINK: Too short PMKID Candidate List " + "Event (len=%lu)", (unsigned long) data_len); + return; + } + pmkid = (NDIS_802_11_PMKID_CANDIDATE_LIST *) data; + wpa_printf(MSG_DEBUG, "RALINK: PMKID Candidate List Event - Version %d" + " NumCandidates %d", + (int) pmkid->Version, (int) pmkid->NumCandidates); + + if (pmkid->Version != 1) { + wpa_printf(MSG_DEBUG, "RALINK: Unsupported PMKID Candidate " + "List Version %d", (int) pmkid->Version); + return; + } + + if (data_len < 8 + pmkid->NumCandidates * sizeof(PMKID_CANDIDATE)) { + wpa_printf(MSG_DEBUG, "RALINK: PMKID Candidate List " + "underflow"); + + return; + } + + + + os_memset(&event, 0, sizeof(event)); + for (i = 0; i < pmkid->NumCandidates; i++) { + PMKID_CANDIDATE *p = &pmkid->CandidateList[i]; + wpa_printf(MSG_DEBUG, "RALINK: %lu: " MACSTR " Flags 0x%x", + (unsigned long) i, MAC2STR(p->BSSID), + (int) p->Flags); + os_memcpy(event.pmkid_candidate.bssid, p->BSSID, ETH_ALEN); + event.pmkid_candidate.index = i; + event.pmkid_candidate.preauth = + p->Flags & NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED; + wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE, + &event); + } +} + +static int wpa_driver_ralink_set_pmkid(struct wpa_driver_ralink_data *drv) +{ + int len, count, i, ret; + struct ndis_pmkid_entry *entry; + NDIS_802_11_PMKID *p; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + count = 0; + entry = drv->pmkid; + while (entry) { + count++; + if (count >= drv->no_of_pmkid) + break; + entry = entry->next; + } + len = 8 + count * sizeof(BSSID_INFO); + p = os_zalloc(len); + if (p == NULL) + return -1; + p->Length = len; + p->BSSIDInfoCount = count; + entry = drv->pmkid; + for (i = 0; i < count; i++) { + os_memcpy(&p->BSSIDInfo[i].BSSID, entry->bssid, ETH_ALEN); + os_memcpy(&p->BSSIDInfo[i].PMKID, entry->pmkid, 16); + entry = entry->next; + } + wpa_hexdump(MSG_MSGDUMP, "NDIS: OID_802_11_PMKID", + (const u8 *) p, len); + ret = ralink_set_oid(drv, OID_802_11_PMKID, (char *) p, len); + os_free(p); + return ret; +} + +static int wpa_driver_ralink_add_pmkid(void *priv, const u8 *bssid, + const u8 *pmkid) +{ + struct wpa_driver_ralink_data *drv = priv; + struct ndis_pmkid_entry *entry, *prev; + + if (drv->g_driver_down == 1) + return -1; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + if (drv->no_of_pmkid == 0) + return 0; + + prev = NULL; + entry = drv->pmkid; + while (entry) { + if (os_memcmp(entry->bssid, bssid, ETH_ALEN) == 0) + break; + prev = entry; + entry = entry->next; + } + + if (entry) { + /* Replace existing entry for this BSSID and move it into the + * beginning of the list. */ + os_memcpy(entry->pmkid, pmkid, 16); + if (prev) { + prev->next = entry->next; + entry->next = drv->pmkid; + drv->pmkid = entry; + } + } else { + entry = os_malloc(sizeof(*entry)); + if (entry) { + os_memcpy(entry->bssid, bssid, ETH_ALEN); + os_memcpy(entry->pmkid, pmkid, 16); + entry->next = drv->pmkid; + drv->pmkid = entry; + } + } + + return wpa_driver_ralink_set_pmkid(drv); +} + + +static int wpa_driver_ralink_remove_pmkid(void *priv, const u8 *bssid, + const u8 *pmkid) +{ + struct wpa_driver_ralink_data *drv = priv; + struct ndis_pmkid_entry *entry, *prev; + + if (drv->g_driver_down == 1) + return -1; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + if (drv->no_of_pmkid == 0) + return 0; + + entry = drv->pmkid; + prev = NULL; + drv->pmkid = NULL; + while (entry) { + if (os_memcmp(entry->bssid, bssid, ETH_ALEN) == 0 && + os_memcmp(entry->pmkid, pmkid, 16) == 0) { + if (prev) + prev->next = entry->next; + else + drv->pmkid = entry->next; + os_free(entry); + break; + } + prev = entry; + entry = entry->next; + } + return wpa_driver_ralink_set_pmkid(drv); +} + + +static int wpa_driver_ralink_flush_pmkid(void *priv) +{ + struct wpa_driver_ralink_data *drv = priv; + NDIS_802_11_PMKID p; + struct ndis_pmkid_entry *pmkid, *prev; + + if (drv->g_driver_down == 1) + return -1; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + if (drv->no_of_pmkid == 0) + return 0; + + pmkid = drv->pmkid; + drv->pmkid = NULL; + while (pmkid) { + prev = pmkid; + pmkid = pmkid->next; + os_free(prev); + } + + os_memset(&p, 0, sizeof(p)); + p.Length = 8; + p.BSSIDInfoCount = 0; + wpa_hexdump(MSG_MSGDUMP, "NDIS: OID_802_11_PMKID (flush)", + (const u8 *) &p, 8); + return ralink_set_oid(drv, OID_802_11_PMKID, (char *) &p, 8); +} + +static void +wpa_driver_ralink_event_wireless_custom(struct wpa_driver_ralink_data *drv, + void *ctx, char *custom) +{ + union wpa_event_data data; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom); + + os_memset(&data, 0, sizeof(data)); + /* Host AP driver */ + if (os_strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) { + /* receive a MICFAILURE report */ + data.michael_mic_failure.unicast = + os_strstr(custom, " unicast") != NULL; + /* TODO: parse parameters(?) */ + wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); + } else if (os_strncmp(custom, "ASSOCINFO_ReqIEs=", 17) == 0) { + /* receive assoc. req. IEs */ + char *spos; + int bytes; + + spos = custom + 17; + /*get IE's length */ + /* + * bytes = strlen(spos); ==> bug, bytes may less than original + * size by using this way to get size. snowpin 20070312 + * if (!bytes) + * return; + */ + bytes = drv->assoc_req_ies_len; + + data.assoc_info.req_ies = os_malloc(bytes); + if (data.assoc_info.req_ies == NULL) + return; + + data.assoc_info.req_ies_len = bytes; + os_memcpy(data.assoc_info.req_ies, spos, bytes); + + /* skip the '\0' byte */ + spos += bytes + 1; + + data.assoc_info.resp_ies = NULL; + data.assoc_info.resp_ies_len = 0; + + if (os_strncmp(spos, " RespIEs=", 9) == 0) { + /* receive assoc. resp. IEs */ + spos += 9; + /* get IE's length */ + bytes = os_strlen(spos); + if (!bytes) + goto done; + + + data.assoc_info.resp_ies = os_malloc(bytes); + if (data.assoc_info.resp_ies == NULL) + goto done; + + data.assoc_info.resp_ies_len = bytes; + os_memcpy(data.assoc_info.resp_ies, spos, bytes); + } + + wpa_supplicant_event(ctx, EVENT_ASSOCINFO, &data); + + /* free allocated memory */ + done: + os_free(data.assoc_info.resp_ies); + os_free(data.assoc_info.req_ies); + } +} + +static void +wpa_driver_ralink_event_wireless(struct wpa_driver_ralink_data *drv, + void *ctx, char *data, int len) +{ + struct iw_event iwe_buf, *iwe = &iwe_buf; + char *pos, *end, *custom, *buf, *assoc_info_buf, *info_pos; +#if 0 + BOOLEAN ieee8021x_required_key = FALSE; +#endif + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + assoc_info_buf = info_pos = NULL; + pos = data; + end = data + len; + + while (pos + IW_EV_LCP_LEN <= end) { + /* Event data may be unaligned, so make a local, aligned copy + * before processing. */ + os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); + wpa_printf(MSG_DEBUG, "Wireless event: cmd=0x%x len=%d", + iwe->cmd, iwe->len); + if (iwe->len <= IW_EV_LCP_LEN) + return; + + custom = pos + IW_EV_POINT_LEN; + + if (drv->we_version_compiled > 18 && iwe->cmd == IWEVCUSTOM) { + /* WE-19 removed the pointer from struct iw_point */ + char *dpos = (char *) &iwe_buf.u.data.length; + int dlen = dpos - (char *) &iwe_buf; + os_memcpy(dpos, pos + IW_EV_LCP_LEN, + sizeof(struct iw_event) - dlen); + } else { + os_memcpy(&iwe_buf, pos, sizeof(struct iw_event)); + custom += IW_EV_POINT_OFF; + } + + switch (iwe->cmd) { + case IWEVCUSTOM: + if (custom + iwe->u.data.length > end) + return; + buf = os_malloc(iwe->u.data.length + 1); + if (buf == NULL) + return; + os_memcpy(buf, custom, iwe->u.data.length); + buf[iwe->u.data.length] = '\0'; + + if (drv->ap_scan == 1) { + if ((iwe->u.data.flags == RT_ASSOC_EVENT_FLAG) + || (iwe->u.data.flags == + RT_REQIE_EVENT_FLAG) || + (iwe->u.data.flags == RT_RESPIE_EVENT_FLAG) + || (iwe->u.data.flags == + RT_ASSOCINFO_EVENT_FLAG)) { + if (drv->scanning_done == 0) { + os_free(buf); + return; + } + } + } + + if (iwe->u.data.flags == RT_ASSOC_EVENT_FLAG) { + wpa_printf(MSG_DEBUG, "Custom wireless event: " + "receive ASSOCIATED_EVENT !!!"); + /* determine whether the dynamic-WEP is used or + * not */ +#if 0 + if (wpa_s && wpa_s->current_ssid && + wpa_s->current_ssid->key_mgmt == + WPA_KEY_MGMT_IEEE8021X_NO_WPA) { + if ((wpa_s->current_ssid->eapol_flags & + (EAPOL_FLAG_REQUIRE_KEY_UNICAST | EAPOL_FLAG_REQUIRE_KEY_BROADCAST))) { + //wpa_printf(MSG_DEBUG, "The current ssid - (%s), eapol_flag = %d.\n", + // wpa_ssid_txt(wpa_s->current_ssid->ssid, wpa_s->current_ssid->ssid_len),wpa_s->current_ssid->eapol_flags); + ieee8021x_required_key = TRUE; + } + + if (ralink_set_oid(drv, OID_802_11_SET_IEEE8021X_REQUIRE_KEY, (char *) &ieee8021x_required_key, sizeof(BOOLEAN)) < 0) + { + wpa_printf(MSG_DEBUG, "ERROR: Failed to set OID_802_11_SET_IEEE8021X_REQUIRE_KEY(%d)", + (int) ieee8021x_required_key); + } + + wpa_printf(MSG_DEBUG, "ieee8021x_required_key is %s and eapol_flag(%d).\n", ieee8021x_required_key ? "TRUE" : "FALSE", + wpa_s->current_ssid->eapol_flags); + } +#endif + + wpa_supplicant_event(ctx, EVENT_ASSOC, NULL); + } else if (iwe->u.data.flags == RT_REQIE_EVENT_FLAG) { + wpa_printf(MSG_DEBUG, "Custom wireless event: " + "receive ReqIEs !!!"); + drv->assoc_req_ies = + os_malloc(iwe->u.data.length); + if (drv->assoc_req_ies == NULL) { + os_free(buf); + return; + } + + drv->assoc_req_ies_len = iwe->u.data.length; + os_memcpy(drv->assoc_req_ies, custom, + iwe->u.data.length); + } else if (iwe->u.data.flags == RT_RESPIE_EVENT_FLAG) { + wpa_printf(MSG_DEBUG, "Custom wireless event: " + "receive RespIEs !!!"); + drv->assoc_resp_ies = + os_malloc(iwe->u.data.length); + if (drv->assoc_resp_ies == NULL) { + os_free(drv->assoc_req_ies); + drv->assoc_req_ies = NULL; + os_free(buf); + return; + } + + drv->assoc_resp_ies_len = iwe->u.data.length; + os_memcpy(drv->assoc_resp_ies, custom, + iwe->u.data.length); + } else if (iwe->u.data.flags == + RT_ASSOCINFO_EVENT_FLAG) { + wpa_printf(MSG_DEBUG, "Custom wireless event: " + "receive ASSOCINFO_EVENT !!!"); + + assoc_info_buf = + os_zalloc(drv->assoc_req_ies_len + + drv->assoc_resp_ies_len + 1); + + if (assoc_info_buf == NULL) { + os_free(drv->assoc_req_ies); + drv->assoc_req_ies = NULL; + os_free(drv->assoc_resp_ies); + drv->assoc_resp_ies = NULL; + os_free(buf); + return; + } + + if (drv->assoc_req_ies) { + os_memcpy(assoc_info_buf, + drv->assoc_req_ies, + drv->assoc_req_ies_len); + } + info_pos = assoc_info_buf + + drv->assoc_req_ies_len; + if (drv->assoc_resp_ies) { + os_memcpy(info_pos, + drv->assoc_resp_ies, + drv->assoc_resp_ies_len); + } + assoc_info_buf[drv->assoc_req_ies_len + + drv->assoc_resp_ies_len] = '\0'; + wpa_driver_ralink_event_wireless_custom( + drv, ctx, assoc_info_buf); + os_free(drv->assoc_req_ies); + drv->assoc_req_ies = NULL; + os_free(drv->assoc_resp_ies); + drv->assoc_resp_ies = NULL; + os_free(assoc_info_buf); + } else if (iwe->u.data.flags == RT_DISASSOC_EVENT_FLAG) + { + wpa_printf(MSG_DEBUG, "Custom wireless event: " + "receive DISASSOCIATED_EVENT !!!"); + wpa_supplicant_event(ctx, EVENT_DISASSOC, + NULL); + } else if (iwe->u.data.flags == RT_PMKIDCAND_FLAG) { + wpa_printf(MSG_DEBUG, "Custom wireless event: " + "receive PMKIDCAND_EVENT !!!"); + wpa_driver_ralink_event_pmkid( + drv, (const u8 *) custom, + iwe->u.data.length); + } else if (iwe->u.data.flags == RT_INTERFACE_DOWN) { + drv->g_driver_down = 1; + eloop_terminate(); + } else if (iwe->u.data.flags == RT_REPORT_AP_INFO) { + if (drv->ap_scan != 1) { + typedef struct PACKED { + UCHAR bssid[MAC_ADDR_LEN]; + UCHAR ssid[MAX_LEN_OF_SSID]; + INT ssid_len; + UCHAR wpa_ie[40]; + INT wpa_ie_len; + UCHAR rsn_ie[40]; + INT rsn_ie_len; + INT freq; + USHORT caps; + } *PAPINFO; + + wpa_printf(MSG_DEBUG, "Custom wireless" + " event: receive " + "RT_REPORT_AP_INFO !!!"); + //printf("iwe->u.data.length = %d\n", iwe->u.data.length); + //wpa_hexdump(MSG_DEBUG, "AP_Info: ", buf, iwe->u.data.length); +#if 0 + wpa_s->num_scan_results = 1; + if (wpa_s->scan_results) + os_free(wpa_s->scan_results); + wpa_s->scan_results = os_malloc(sizeof(struct wpa_scan_result) + 1); + if (wpa_s->scan_results) { + PAPINFO pApInfo = (PAPINFO)buf; + os_memcpy(wpa_s->scan_results[0].bssid, pApInfo->bssid, ETH_ALEN); + os_memcpy(wpa_s->scan_results[0].ssid, pApInfo->ssid, pApInfo->ssid_len); + wpa_s->scan_results[0].ssid_len = pApInfo->ssid_len; + if (pApInfo->wpa_ie_len > 0) { + os_memcpy(wpa_s->scan_results[0].wpa_ie, pApInfo->wpa_ie, pApInfo->wpa_ie_len); + wpa_s->scan_results[0].wpa_ie_len = pApInfo->wpa_ie_len; + } else if (pApInfo->rsn_ie_len > 0) { + os_memcpy(wpa_s->scan_results[0].rsn_ie, pApInfo->rsn_ie, pApInfo->rsn_ie_len); + wpa_s->scan_results[0].rsn_ie_len = pApInfo->rsn_ie_len; + } + wpa_s->scan_results[0].caps = pApInfo->caps; + wpa_s->scan_results[0].freq = pApInfo->freq; + } else { + wpa_printf("wpa_s->scan_" + "results fail to " + "os_malloc!!\n"); + } +#endif + } + } else { + wpa_driver_ralink_event_wireless_custom( + drv, ctx, buf); + } + os_free(buf); + break; + } + + pos += iwe->len; + } +} + +static void +wpa_driver_ralink_event_rtm_newlink(struct wpa_driver_ralink_data *drv, + void *ctx, struct nlmsghdr *h, int len) +{ + struct ifinfomsg *ifi; + int attrlen, nlmsg_len, rta_len; + struct rtattr * attr; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + if (len < (int) sizeof(*ifi)) + return; + + ifi = NLMSG_DATA(h); + wpa_hexdump(MSG_DEBUG, "ifi: ", (u8 *) ifi, sizeof(struct ifinfomsg)); + + nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); + + attrlen = h->nlmsg_len - nlmsg_len; + wpa_printf(MSG_DEBUG, "attrlen=%d", attrlen); + if (attrlen < 0) + return; + + attr = (struct rtattr *) (((char *) ifi) + nlmsg_len); + wpa_hexdump(MSG_DEBUG, "attr1: ", (u8 *) attr, sizeof(struct rtattr)); + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + wpa_hexdump(MSG_DEBUG, "attr2: ", (u8 *)attr,rta_len); + while (RTA_OK(attr, attrlen)) { + wpa_printf(MSG_DEBUG, "rta_type=%02x\n", attr->rta_type); + if (attr->rta_type == IFLA_WIRELESS) { + wpa_driver_ralink_event_wireless( + drv, ctx, + ((char *) attr) + rta_len, + attr->rta_len - rta_len); + } + attr = RTA_NEXT(attr, attrlen); + wpa_hexdump(MSG_DEBUG, "attr3: ", + (u8 *) attr, sizeof(struct rtattr)); + } +} + +static void wpa_driver_ralink_event_receive(int sock, void *ctx, + void *sock_ctx) +{ + char buf[8192]; + int left; + struct sockaddr_nl from; + socklen_t fromlen; + struct nlmsghdr *h; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + fromlen = sizeof(from); + left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, + (struct sockaddr *) &from, &fromlen); + + if (left < 0) { + if (errno != EINTR && errno != EAGAIN) + perror("recvfrom(netlink)"); + return; + } + + h = (struct nlmsghdr *) buf; + wpa_hexdump(MSG_DEBUG, "h: ", (u8 *)h, h->nlmsg_len); + + while (left >= (int) sizeof(*h)) { + int len, plen; + + len = h->nlmsg_len; + plen = len - sizeof(*h); + if (len > left || plen < 0) { + wpa_printf(MSG_DEBUG, "Malformed netlink message: " + "len=%d left=%d plen=%d", len, left, plen); + break; + } + + switch (h->nlmsg_type) { + case RTM_NEWLINK: + wpa_driver_ralink_event_rtm_newlink(ctx, sock_ctx, h, + plen); + break; + } + + len = NLMSG_ALIGN(len); + left -= len; + h = (struct nlmsghdr *) ((char *) h + len); + } + + if (left > 0) { + wpa_printf(MSG_DEBUG, "%d extra bytes in the end of netlink " + "message", left); + } + +} + +static int +ralink_get_we_version_compiled(struct wpa_driver_ralink_data *drv) +{ + struct iwreq iwr; + UINT we_version_compiled = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) &we_version_compiled; + iwr.u.data.flags = RT_OID_WE_VERSION_COMPILED; + + if (ioctl(drv->ioctl_sock, RT_PRIV_IOCTL, &iwr) < 0) { + wpa_printf(MSG_DEBUG, "%s: failed", __func__); + return -1; + } + + drv->we_version_compiled = we_version_compiled; + + return 0; +} + +static int +ralink_set_iface_flags(void *priv, int dev_up) +{ + struct wpa_driver_ralink_data *drv = priv; + struct ifreq ifr; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + if (drv->ioctl_sock < 0) + return -1; + + os_memset(&ifr, 0, sizeof(ifr)); + os_snprintf(ifr.ifr_name, IFNAMSIZ, "%s", drv->ifname); + + if (ioctl(drv->ioctl_sock, SIOCGIFFLAGS, &ifr) != 0) { + perror("ioctl[SIOCGIFFLAGS]"); + return -1; + } + + if (dev_up) + ifr.ifr_flags |= IFF_UP; + else + ifr.ifr_flags &= ~IFF_UP; + + if (ioctl(drv->ioctl_sock, SIOCSIFFLAGS, &ifr) != 0) { + perror("ioctl[SIOCSIFFLAGS]"); + return -1; + } + + return 0; +} + +static void * wpa_driver_ralink_init(void *ctx, const char *ifname) +{ + int s; + struct wpa_driver_ralink_data *drv; + struct ifreq ifr; + struct sockaddr_nl local; + UCHAR enable_wpa_supplicant = 0; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + /* open socket to kernel */ + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + perror("socket"); + return NULL; + } + /* do it */ + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + + if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) { + perror(ifr.ifr_name); + return NULL; + } + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + + drv->scanning_done = 1; + drv->ap_scan = 1; /* for now - let's assume ap_scan=1 is used */ + drv->ctx = ctx; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + drv->ioctl_sock = s; + drv->g_driver_down = 0; + + s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (s < 0) { + perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)"); + close(drv->ioctl_sock); + os_free(drv); + return NULL; + } + + os_memset(&local, 0, sizeof(local)); + local.nl_family = AF_NETLINK; + local.nl_groups = RTMGRP_LINK; + + if (bind(s, (struct sockaddr *) &local, sizeof(local)) < 0) { + perror("bind(netlink)"); + close(s); + close(drv->ioctl_sock); + os_free(drv); + return NULL; + } + + eloop_register_read_sock(s, wpa_driver_ralink_event_receive, drv, ctx); + drv->event_sock = s; + drv->no_of_pmkid = 4; /* Number of PMKID saved supported */ + + ralink_set_iface_flags(drv, 1); /* mark up during setup */ + ralink_get_we_version_compiled(drv); + wpa_driver_ralink_flush_pmkid(drv); + + if (drv->ap_scan == 1) + enable_wpa_supplicant = 1; + else + enable_wpa_supplicant = 2; + /* trigger driver support wpa_supplicant */ + if (ralink_set_oid(drv, RT_OID_WPA_SUPPLICANT_SUPPORT, + (PCHAR) &enable_wpa_supplicant, sizeof(UCHAR)) < 0) + { + wpa_printf(MSG_DEBUG, "RALINK: Failed to set " + "RT_OID_WPA_SUPPLICANT_SUPPORT(%d)", + (int) enable_wpa_supplicant); + wpa_printf(MSG_ERROR, "RALINK: Driver does not support " + "wpa_supplicant"); + close(s); + close(drv->ioctl_sock); + os_free(drv); + return NULL; + } + + if (drv->ap_scan == 1) + drv->scanning_done = 0; + + return drv; +} + +static void wpa_driver_ralink_deinit(void *priv) +{ + struct wpa_driver_ralink_data *drv = priv; + UCHAR enable_wpa_supplicant; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + enable_wpa_supplicant = 0; + + if (drv->g_driver_down == 0) { + /* trigger driver disable wpa_supplicant support */ + if (ralink_set_oid(drv, RT_OID_WPA_SUPPLICANT_SUPPORT, + (char *) &enable_wpa_supplicant, + sizeof(BOOLEAN)) < 0) { + wpa_printf(MSG_DEBUG, "RALINK: Failed to set " + "RT_OID_WPA_SUPPLICANT_SUPPORT(%d)", + (int) enable_wpa_supplicant); + } + + wpa_driver_ralink_flush_pmkid(drv); + + sleep(1); + ralink_set_iface_flags(drv, 0); + } + + eloop_cancel_timeout(wpa_driver_ralink_scan_timeout, drv, drv->ctx); + eloop_unregister_read_sock(drv->event_sock); + close(drv->event_sock); + close(drv->ioctl_sock); + os_free(drv); +} + +static void wpa_driver_ralink_scan_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_driver_ralink_data *drv = eloop_ctx; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); + wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); + + drv->scanning_done = 1; + +} + +static int wpa_driver_ralink_scan(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_ralink_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + if (drv->g_driver_down == 1) + return -1; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + if (ssid_len > IW_ESSID_MAX_SIZE) { + wpa_printf(MSG_DEBUG, "%s: too long SSID (%lu)", + __FUNCTION__, (unsigned long) ssid_len); + return -1; + } + + /* wpa_driver_ralink_set_ssid(drv, ssid, ssid_len); */ + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + + if (ioctl(drv->ioctl_sock, SIOCSIWSCAN, &iwr) < 0) { + perror("ioctl[SIOCSIWSCAN]"); + ret = -1; + } + + /* Not all drivers generate "scan completed" wireless event, so try to + * read results after a timeout. */ + eloop_cancel_timeout(wpa_driver_ralink_scan_timeout, drv, drv->ctx); + eloop_register_timeout(4, 0, wpa_driver_ralink_scan_timeout, drv, + drv->ctx); + + drv->scanning_done = 0; + + return ret; +} + +static int +wpa_driver_ralink_get_scan_results(void *priv, + struct wpa_scan_result *results, + size_t max_size) +{ + struct wpa_driver_ralink_data *drv = priv; + UCHAR *buf = NULL; + NDIS_802_11_BSSID_LIST_EX *wsr; + NDIS_WLAN_BSSID_EX *wbi; + struct iwreq iwr; + int rv = 0; + size_t ap_num; + u8 *pos, *end; + + if (drv->g_driver_down == 1) + return -1; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + if (drv->we_version_compiled >= 17) { + buf = os_zalloc(8192); + iwr.u.data.length = 8192; + } else { + buf = os_zalloc(4096); + iwr.u.data.length = 4096; + } + if (buf == NULL) + return -1; + + wsr = (NDIS_802_11_BSSID_LIST_EX *) buf; + + wsr->NumberOfItems = 0; + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = (void *) buf; + iwr.u.data.flags = OID_802_11_BSSID_LIST; + + if ((rv = ioctl(drv->ioctl_sock, RT_PRIV_IOCTL, &iwr)) < 0) { + wpa_printf(MSG_DEBUG, "ioctl fail: rv = %d", rv); + os_free(buf); + return -1; + } + + os_memset(results, 0, max_size * sizeof(struct wpa_scan_result)); + + for (ap_num = 0, wbi = wsr->Bssid; ap_num < wsr->NumberOfItems; + ++ap_num) { + os_memcpy(results[ap_num].bssid, &wbi->MacAddress, ETH_ALEN); + os_memcpy(results[ap_num].ssid, wbi->Ssid.Ssid, + wbi->Ssid.SsidLength); + results[ap_num].ssid_len = wbi->Ssid.SsidLength; + results[ap_num].freq = (wbi->Configuration.DSConfig / 1000); + + /* get ie's */ + wpa_hexdump(MSG_DEBUG, "RALINK: AP IEs", + (u8 *) wbi + sizeof(*wbi) - 1, wbi->IELength); + + pos = (u8 *) wbi + sizeof(*wbi) - 1; + end = (u8 *) wbi + sizeof(*wbi) + wbi->IELength; + + if (wbi->IELength < sizeof(NDIS_802_11_FIXED_IEs)) + break; + + pos += sizeof(NDIS_802_11_FIXED_IEs) - 2; + os_memcpy(&results[ap_num].caps, pos, 2); + pos += 2; + + while (pos + 1 < end && pos + 2 + pos[1] <= end) { + u8 ielen = 2 + pos[1]; + + if (ielen > SSID_MAX_WPA_IE_LEN) { + pos += ielen; + continue; + } + + if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && + pos[1] >= 4 && + os_memcmp(pos + 2, "\x00\x50\xf2\x01", 4) == 0) { + os_memcpy(results[ap_num].wpa_ie, pos, ielen); + results[ap_num].wpa_ie_len = ielen; + } else if (pos[0] == WLAN_EID_RSN) { + os_memcpy(results[ap_num].rsn_ie, pos, ielen); + results[ap_num].rsn_ie_len = ielen; + } + pos += ielen; + } + + wbi = (NDIS_WLAN_BSSID_EX *) ((u8 *) wbi + wbi->Length); + } + + os_free(buf); + return ap_num; +} + +static int ralink_set_auth_mode(struct wpa_driver_ralink_data *drv, + NDIS_802_11_AUTHENTICATION_MODE mode) +{ + NDIS_802_11_AUTHENTICATION_MODE auth_mode = mode; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + if (ralink_set_oid(drv, OID_802_11_AUTHENTICATION_MODE, + (char *) &auth_mode, sizeof(auth_mode)) < 0) { + wpa_printf(MSG_DEBUG, "RALINK: Failed to set " + "OID_802_11_AUTHENTICATION_MODE (%d)", + (int) auth_mode); + return -1; + } + return 0; +} + +static int wpa_driver_ralink_remove_key(struct wpa_driver_ralink_data *drv, + int key_idx, const u8 *addr, + const u8 *bssid, int pairwise) +{ + NDIS_802_11_REMOVE_KEY rkey; + NDIS_802_11_KEY_INDEX _index; + int res, res2; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + os_memset(&rkey, 0, sizeof(rkey)); + + rkey.Length = sizeof(rkey); + rkey.KeyIndex = key_idx; + + if (pairwise) + rkey.KeyIndex |= 1 << 30; + + os_memcpy(rkey.BSSID, bssid, ETH_ALEN); + + res = ralink_set_oid(drv, OID_802_11_REMOVE_KEY, (char *) &rkey, + sizeof(rkey)); + + /* AlbertY@20060210 removed it */ + if (0 /* !pairwise */) { + res2 = ralink_set_oid(drv, OID_802_11_REMOVE_WEP, + (char *) &_index, sizeof(_index)); + } else + res2 = 0; + + if (res < 0 && res2 < 0) + return res; + return 0; +} + +static int wpa_driver_ralink_add_wep(struct wpa_driver_ralink_data *drv, + int pairwise, int key_idx, int set_tx, + const u8 *key, size_t key_len) +{ + NDIS_802_11_WEP *wep; + size_t len; + int res; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + len = 12 + key_len; + wep = os_zalloc(len); + if (wep == NULL) + return -1; + + wep->Length = len; + wep->KeyIndex = key_idx; + + if (set_tx) + wep->KeyIndex |= 0x80000000; + + wep->KeyLength = key_len; + os_memcpy(wep->KeyMaterial, key, key_len); + + wpa_hexdump_key(MSG_MSGDUMP, "RALINK: OID_802_11_ADD_WEP", + (const u8 *) wep, len); + res = ralink_set_oid(drv, OID_802_11_ADD_WEP, (char *) wep, len); + + os_free(wep); + + return res; +} + +static int wpa_driver_ralink_set_key(void *priv, wpa_alg alg, const u8 *addr, + int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + struct wpa_driver_ralink_data *drv = priv; + size_t len, i; + NDIS_802_11_KEY *nkey; + int res, pairwise; + u8 bssid[ETH_ALEN]; + + if (drv->g_driver_down == 1) + return -1; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + if (addr == NULL || os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", + ETH_ALEN) == 0) { + /* Group Key */ + pairwise = 0; + wpa_driver_ralink_get_bssid(drv, bssid); + } else { + /* Pairwise Key */ + pairwise = 1; + os_memcpy(bssid, addr, ETH_ALEN); + } + + if (alg == WPA_ALG_NONE || key_len == 0) { + return wpa_driver_ralink_remove_key(drv, key_idx, addr, bssid, + pairwise); + } + + if (alg == WPA_ALG_WEP) { + return wpa_driver_ralink_add_wep(drv, pairwise, key_idx, + set_tx, key, key_len); + } + + len = 12 + 6 + 6 + 8 + key_len; + + nkey = os_zalloc(len); + if (nkey == NULL) + return -1; + + nkey->Length = len; + nkey->KeyIndex = key_idx; + + if (set_tx) + nkey->KeyIndex |= 1 << 31; + + if (pairwise) + nkey->KeyIndex |= 1 << 30; + + if (seq && seq_len) + nkey->KeyIndex |= 1 << 29; + + nkey->KeyLength = key_len; + os_memcpy(nkey->BSSID, bssid, ETH_ALEN); + + if (seq && seq_len) { + for (i = 0; i < seq_len; i++) + nkey->KeyRSC |= seq[i] << (i * 8); + } + if (alg == WPA_ALG_TKIP && key_len == 32) { + os_memcpy(nkey->KeyMaterial, key, 16); + os_memcpy(nkey->KeyMaterial + 16, key + 24, 8); + os_memcpy(nkey->KeyMaterial + 24, key + 16, 8); + } else { + os_memcpy(nkey->KeyMaterial, key, key_len); + } + + wpa_printf(MSG_DEBUG, "%s: alg=%d key_idx=%d set_tx=%d seq_len=%lu " + "key_len=%lu", __FUNCTION__, alg, key_idx, set_tx, + (unsigned long) seq_len, (unsigned long) key_len); + + wpa_hexdump_key(MSG_MSGDUMP, "RALINK: OID_802_11_ADD_KEY", + (const u8 *) nkey, len); + res = ralink_set_oid(drv, OID_802_11_ADD_KEY, (char *) nkey, len); + os_free(nkey); + + return res; +} + +static int wpa_driver_ralink_disassociate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_ralink_data *drv = priv; + + if (drv->g_driver_down == 1) + return -1; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + if (ralink_set_oid(drv, OID_802_11_DISASSOCIATE, " ", 4) < 0) { + wpa_printf(MSG_DEBUG, "RALINK: Failed to set " + "OID_802_11_DISASSOCIATE"); + } + + return 0; +} + +static int wpa_driver_ralink_deauthenticate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_ralink_data *drv = priv; + + wpa_printf(MSG_DEBUG, "g_driver_down = %d", drv->g_driver_down); + + if (drv->g_driver_down == 1) + return -1; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + if (ralink_get_new_driver_flag(drv) == 0) { + return wpa_driver_ralink_disassociate(priv, addr, reason_code); + } else { + MLME_DEAUTH_REQ_STRUCT mlme; + os_memset(&mlme, 0, sizeof(MLME_DEAUTH_REQ_STRUCT)); + mlme.Reason = reason_code; + os_memcpy(mlme.Addr, addr, MAC_ADDR_LEN); + return ralink_set_oid(drv, OID_802_11_DEAUTHENTICATION, + (char *) &mlme, + sizeof(MLME_DEAUTH_REQ_STRUCT)); + } +} + +static int +wpa_driver_ralink_associate(void *priv, + struct wpa_driver_associate_params *params) +{ + struct wpa_driver_ralink_data *drv = priv; + + NDIS_802_11_NETWORK_INFRASTRUCTURE mode; + NDIS_802_11_AUTHENTICATION_MODE auth_mode; + NDIS_802_11_WEP_STATUS encr; + BOOLEAN ieee8021xMode; + + if (drv->g_driver_down == 1) + return -1; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + if (params->mode == IEEE80211_MODE_IBSS) + mode = Ndis802_11IBSS; + else + mode = Ndis802_11Infrastructure; + + if (ralink_set_oid(drv, OID_802_11_INFRASTRUCTURE_MODE, + (char *) &mode, sizeof(mode)) < 0) { + wpa_printf(MSG_DEBUG, "RALINK: Failed to set " + "OID_802_11_INFRASTRUCTURE_MODE (%d)", + (int) mode); + /* Try to continue anyway */ + } + + if (params->wpa_ie == NULL || params->wpa_ie_len == 0) { + if (params->auth_alg & AUTH_ALG_SHARED_KEY) { + if (params->auth_alg & AUTH_ALG_OPEN_SYSTEM) + auth_mode = Ndis802_11AuthModeAutoSwitch; + else + auth_mode = Ndis802_11AuthModeShared; + } else + auth_mode = Ndis802_11AuthModeOpen; + } else if (params->wpa_ie[0] == WLAN_EID_RSN) { + if (params->key_mgmt_suite == KEY_MGMT_PSK) + auth_mode = Ndis802_11AuthModeWPA2PSK; + else + auth_mode = Ndis802_11AuthModeWPA2; + } else { + if (params->key_mgmt_suite == KEY_MGMT_WPA_NONE) + auth_mode = Ndis802_11AuthModeWPANone; + else if (params->key_mgmt_suite == KEY_MGMT_PSK) + auth_mode = Ndis802_11AuthModeWPAPSK; + else + auth_mode = Ndis802_11AuthModeWPA; + } + + switch (params->pairwise_suite) { + case CIPHER_CCMP: + encr = Ndis802_11Encryption3Enabled; + break; + case CIPHER_TKIP: + encr = Ndis802_11Encryption2Enabled; + break; + case CIPHER_WEP40: + case CIPHER_WEP104: + encr = Ndis802_11Encryption1Enabled; + break; + case CIPHER_NONE: + if (params->group_suite == CIPHER_CCMP) + encr = Ndis802_11Encryption3Enabled; + else if (params->group_suite == CIPHER_TKIP) + encr = Ndis802_11Encryption2Enabled; + else + encr = Ndis802_11EncryptionDisabled; + break; + default: + encr = Ndis802_11EncryptionDisabled; + break; + } + + ralink_set_auth_mode(drv, auth_mode); + + /* notify driver that IEEE8021x mode is enabled */ + if (params->key_mgmt_suite == KEY_MGMT_802_1X_NO_WPA) + ieee8021xMode = TRUE; + else + ieee8021xMode = FALSE; + + if (ralink_set_oid(drv, OID_802_11_SET_IEEE8021X, + (char *) &ieee8021xMode, sizeof(BOOLEAN)) < 0) { + wpa_printf(MSG_DEBUG, "RALINK: Failed to set " + "OID_802_11_SET_IEEE8021X(%d)", + (int) ieee8021xMode); + } + + if (ralink_set_oid(drv, OID_802_11_WEP_STATUS, + (char *) &encr, sizeof(encr)) < 0) { + wpa_printf(MSG_DEBUG, "RALINK: Failed to set " + "OID_802_11_WEP_STATUS(%d)", + (int) encr); + } + + if ((ieee8021xMode == FALSE) && + (encr == Ndis802_11Encryption1Enabled)) { + /* static WEP */ + int enabled = 0; + if (ralink_set_oid(drv, OID_802_11_DROP_UNENCRYPTED, + (char *) &enabled, sizeof(enabled)) < 0) { + wpa_printf(MSG_DEBUG, "RALINK: Failed to set " + "OID_802_11_DROP_UNENCRYPTED(%d)", + (int) encr); + } + } + + return wpa_driver_ralink_set_ssid(drv, params->ssid, params->ssid_len); +} + +static int +wpa_driver_ralink_set_countermeasures(void *priv, int enabled) +{ + struct wpa_driver_ralink_data *drv = priv; + if (drv->g_driver_down == 1) + return -1; + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + return ralink_set_oid(drv, OID_SET_COUNTERMEASURES, (char *) &enabled, + sizeof(int)); +} + +const struct wpa_driver_ops wpa_driver_ralink_ops = { + .name = "ralink", + .desc = "Ralink Wireless Client driver", + .get_bssid = wpa_driver_ralink_get_bssid, + .get_ssid = wpa_driver_ralink_get_ssid, + .set_key = wpa_driver_ralink_set_key, + .init = wpa_driver_ralink_init, + .deinit = wpa_driver_ralink_deinit, + .set_countermeasures = wpa_driver_ralink_set_countermeasures, + .scan = wpa_driver_ralink_scan, + .get_scan_results = wpa_driver_ralink_get_scan_results, + .deauthenticate = wpa_driver_ralink_deauthenticate, + .disassociate = wpa_driver_ralink_disassociate, + .associate = wpa_driver_ralink_associate, + .add_pmkid = wpa_driver_ralink_add_pmkid, + .remove_pmkid = wpa_driver_ralink_remove_pmkid, + .flush_pmkid = wpa_driver_ralink_flush_pmkid, +}; diff --git a/src/drivers/driver_ralink.h b/src/drivers/driver_ralink.h new file mode 100644 index 0000000..ddf44de --- /dev/null +++ b/src/drivers/driver_ralink.h @@ -0,0 +1,382 @@ +/* + * WPA Supplicant - driver_ralink exported functions + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * Copyright (c) 2007, Snowpin Lee <snowpin_lee@ralinktech.com.tw> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +// Ralink defined OIDs +#if WIRELESS_EXT <= 11 +#ifndef SIOCDEVPRIVATE +#define SIOCDEVPRIVATE 0x8BE0 +#endif +#define SIOCIWFIRSTPRIV SIOCDEVPRIVATE +#endif + +#define RT_PRIV_IOCTL (SIOCIWFIRSTPRIV + 0x0E) +#define RTPRIV_IOCTL_SET (SIOCIWFIRSTPRIV + 0x02) + +// IEEE 802.11 OIDs & Ralink defined OIDs ****** + +// (RaConfig Set/QueryInform) ==> +#define OID_GET_SET_TOGGLE 0x8000 + +#define OID_802_11_ADD_WEP 0x0112 +#define OID_802_11_REMOVE_WEP 0x0113 +#define OID_802_11_DISASSOCIATE 0x0114 +#define OID_802_11_PRIVACY_FILTER 0x0118 +#define OID_802_11_ASSOCIATION_INFORMATION 0x011E +#define OID_802_11_BSSID_LIST_SCAN 0x0508 +#define OID_802_11_SSID 0x0509 +#define OID_802_11_BSSID 0x050A +#define OID_802_11_WEP_STATUS 0x0510 +#define OID_802_11_AUTHENTICATION_MODE 0x0511 +#define OID_802_11_INFRASTRUCTURE_MODE 0x0512 +#define OID_802_11_TX_POWER_LEVEL 0x0517 +#define OID_802_11_REMOVE_KEY 0x0519 +#define OID_802_11_ADD_KEY 0x0520 +#define OID_802_11_DEAUTHENTICATION 0x0526 +#define OID_802_11_DROP_UNENCRYPTED 0x0527 +#define OID_802_11_BSSID_LIST 0x0609 +#define OID_802_3_CURRENT_ADDRESS 0x060A +#define OID_SET_COUNTERMEASURES 0x0616 +#define OID_802_11_SET_IEEE8021X 0x0617 // For IEEE8021x mode +#define OID_802_11_SET_IEEE8021X_REQUIRE_KEY 0x0618 // For DynamicWEP in IEEE802.1x mode +#define OID_802_11_PMKID 0x0620 +#define RT_OID_WPA_SUPPLICANT_SUPPORT 0x0621 // for trigger driver enable/disable wpa_supplicant support +#define RT_OID_WE_VERSION_COMPILED 0x0622 +#define RT_OID_NEW_DRIVER 0x0623 + +#define PACKED __attribute__ ((packed)) + +//wpa_supplicant event flags +#define RT_ASSOC_EVENT_FLAG 0x0101 +#define RT_DISASSOC_EVENT_FLAG 0x0102 +#define RT_REQIE_EVENT_FLAG 0x0103 +#define RT_RESPIE_EVENT_FLAG 0x0104 +#define RT_ASSOCINFO_EVENT_FLAG 0x0105 +#define RT_PMKIDCAND_FLAG 0x0106 +#define RT_INTERFACE_DOWN 0x0107 +#define RT_REPORT_AP_INFO 0x0108 + +// +// IEEE 802.11 Structures and definitions +// +// new types for Media Specific Indications + +#ifndef ULONG +#define CHAR char +#define INT int +#define SHORT int +#define UINT u32 +#undef ULONG +//#define ULONG u32 +#define ULONG unsigned long /* 32-bit in 32-bit CPU or 64-bit in 64-bit CPU */ +#define USHORT unsigned short +#define UCHAR unsigned char + +#define uint32 u32 +#define uint8 u8 + + +#define BOOLEAN u8 +//#define LARGE_INTEGER s64 +#define VOID void +#define LONG long +#define LONGLONG s64 +#define ULONGLONG u64 +typedef VOID *PVOID; +typedef CHAR *PCHAR; +typedef UCHAR *PUCHAR; +typedef USHORT *PUSHORT; +typedef LONG *PLONG; +typedef ULONG *PULONG; + +typedef union _LARGE_INTEGER { + struct { + ULONG LowPart; + LONG HighPart; + }vv; + struct { + ULONG LowPart; + LONG HighPart; + } u; + s64 QuadPart; +} LARGE_INTEGER; + +#endif + +#define NDIS_802_11_LENGTH_SSID 32 +#define NDIS_802_11_LENGTH_RATES 8 +#define NDIS_802_11_LENGTH_RATES_EX 16 +#define MAX_LEN_OF_SSID 32 +#define MAC_ADDR_LEN 6 + +typedef UCHAR NDIS_802_11_MAC_ADDRESS[6]; + +// mask for authentication/integrity fields +#define NDIS_802_11_AUTH_REQUEST_AUTH_FIELDS 0x0f + +#define NDIS_802_11_AUTH_REQUEST_REAUTH 0x01 +#define NDIS_802_11_AUTH_REQUEST_KEYUPDATE 0x02 +#define NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR 0x06 +#define NDIS_802_11_AUTH_REQUEST_GROUP_ERROR 0x0E + +// Added new types for OFDM 5G and 2.4G +typedef enum _NDIS_802_11_NETWORK_TYPE +{ + Ndis802_11FH, + Ndis802_11DS, + Ndis802_11OFDM5, + Ndis802_11OFDM24, + Ndis802_11Automode, + Ndis802_11NetworkTypeMax // not a real type, defined as an upper bound +} NDIS_802_11_NETWORK_TYPE, *PNDIS_802_11_NETWORK_TYPE; + +// +// Received Signal Strength Indication +// +typedef LONG NDIS_802_11_RSSI; // in dBm + +typedef struct _NDIS_802_11_CONFIGURATION_FH +{ + ULONG Length; // Length of structure + ULONG HopPattern; // As defined by 802.11, MSB set + ULONG HopSet; // to one if non-802.11 + ULONG DwellTime; // units are Kusec +} NDIS_802_11_CONFIGURATION_FH, *PNDIS_802_11_CONFIGURATION_FH; + +typedef struct _NDIS_802_11_CONFIGURATION +{ + ULONG Length; // Length of structure + ULONG BeaconPeriod; // units are Kusec + ULONG ATIMWindow; // units are Kusec + ULONG DSConfig; // Frequency, units are kHz + NDIS_802_11_CONFIGURATION_FH FHConfig; +} NDIS_802_11_CONFIGURATION, *PNDIS_802_11_CONFIGURATION; + +typedef ULONG NDIS_802_11_KEY_INDEX; +typedef ULONGLONG NDIS_802_11_KEY_RSC; + +// Key mapping keys require a BSSID +typedef struct _NDIS_802_11_KEY +{ + UINT Length; // Length of this structure + UINT KeyIndex; + UINT KeyLength; // length of key in bytes + NDIS_802_11_MAC_ADDRESS BSSID; + NDIS_802_11_KEY_RSC KeyRSC; + UCHAR KeyMaterial[1]; // variable length depending on above field +} NDIS_802_11_KEY, *PNDIS_802_11_KEY; + +typedef struct _NDIS_802_11_REMOVE_KEY +{ + UINT Length; // Length of this structure + UINT KeyIndex; + NDIS_802_11_MAC_ADDRESS BSSID; +} NDIS_802_11_REMOVE_KEY, *PNDIS_802_11_REMOVE_KEY; + +typedef struct PACKED _NDIS_802_11_WEP +{ + UINT Length; // Length of this structure + UINT KeyIndex; // 0 is the per-client key, 1-N are the + // global keys + UINT KeyLength; // length of key in bytes + UCHAR KeyMaterial[1];// variable length depending on above field +} NDIS_802_11_WEP, *PNDIS_802_11_WEP; + + +typedef enum _NDIS_802_11_NETWORK_INFRASTRUCTURE +{ + Ndis802_11IBSS, + Ndis802_11Infrastructure, + Ndis802_11AutoUnknown, + Ndis802_11InfrastructureMax // Not a real value, defined as upper bound +} NDIS_802_11_NETWORK_INFRASTRUCTURE, *PNDIS_802_11_NETWORK_INFRASTRUCTURE; + +// PMKID Structures +typedef UCHAR NDIS_802_11_PMKID_VALUE[16]; + +typedef struct _BSSID_INFO +{ + NDIS_802_11_MAC_ADDRESS BSSID; + NDIS_802_11_PMKID_VALUE PMKID; +} BSSID_INFO, *PBSSID_INFO; + +typedef struct _NDIS_802_11_PMKID +{ + ULONG Length; + ULONG BSSIDInfoCount; + BSSID_INFO BSSIDInfo[1]; +} NDIS_802_11_PMKID, *PNDIS_802_11_PMKID; + +//Added new types for PMKID Candidate lists. +typedef struct _PMKID_CANDIDATE { + NDIS_802_11_MAC_ADDRESS BSSID; + ULONG Flags; +} PMKID_CANDIDATE, *PPMKID_CANDIDATE; + +typedef struct _NDIS_802_11_PMKID_CANDIDATE_LIST +{ + ULONG Version; // Version of the structure + ULONG NumCandidates; // No. of pmkid candidates + PMKID_CANDIDATE CandidateList[1]; +} NDIS_802_11_PMKID_CANDIDATE_LIST, *PNDIS_802_11_PMKID_CANDIDATE_LIST; + +//Flags for PMKID Candidate list structure +#define NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED 0x01 + +// Add new authentication modes +typedef enum _NDIS_802_11_AUTHENTICATION_MODE +{ + Ndis802_11AuthModeOpen, + Ndis802_11AuthModeShared, + Ndis802_11AuthModeAutoSwitch, + Ndis802_11AuthModeWPA, + Ndis802_11AuthModeWPAPSK, + Ndis802_11AuthModeWPANone, + Ndis802_11AuthModeWPA2, + Ndis802_11AuthModeWPA2PSK, + Ndis802_11AuthModeMax // Not a real mode, defined as upper bound +} NDIS_802_11_AUTHENTICATION_MODE, *PNDIS_802_11_AUTHENTICATION_MODE; + +typedef UCHAR NDIS_802_11_RATES[NDIS_802_11_LENGTH_RATES]; // Set of 8 data rates +typedef UCHAR NDIS_802_11_RATES_EX[NDIS_802_11_LENGTH_RATES_EX]; // Set of 16 data rates + +typedef struct PACKED _NDIS_802_11_SSID +{ + INT SsidLength; // length of SSID field below, in bytes; + // this can be zero. + UCHAR Ssid[NDIS_802_11_LENGTH_SSID]; // SSID information field +} NDIS_802_11_SSID, *PNDIS_802_11_SSID; + + +typedef struct PACKED _NDIS_WLAN_BSSID +{ + ULONG Length; // Length of this structure + NDIS_802_11_MAC_ADDRESS MacAddress; // BSSID + UCHAR Reserved[2]; + NDIS_802_11_SSID Ssid; // SSID + ULONG Privacy; // WEP encryption requirement + NDIS_802_11_RSSI Rssi; // receive signal + // strength in dBm + NDIS_802_11_NETWORK_TYPE NetworkTypeInUse; + NDIS_802_11_CONFIGURATION Configuration; + NDIS_802_11_NETWORK_INFRASTRUCTURE InfrastructureMode; + NDIS_802_11_RATES SupportedRates; +} NDIS_WLAN_BSSID, *PNDIS_WLAN_BSSID; + +typedef struct PACKED _NDIS_802_11_BSSID_LIST +{ + UINT NumberOfItems; // in list below, at least 1 + NDIS_WLAN_BSSID Bssid[1]; +} NDIS_802_11_BSSID_LIST, *PNDIS_802_11_BSSID_LIST; + +// Added Capabilities, IELength and IEs for each BSSID +typedef struct PACKED _NDIS_WLAN_BSSID_EX +{ + ULONG Length; // Length of this structure + NDIS_802_11_MAC_ADDRESS MacAddress; // BSSID + UCHAR Reserved[2]; + NDIS_802_11_SSID Ssid; // SSID + UINT Privacy; // WEP encryption requirement + NDIS_802_11_RSSI Rssi; // receive signal + // strength in dBm + NDIS_802_11_NETWORK_TYPE NetworkTypeInUse; + NDIS_802_11_CONFIGURATION Configuration; + NDIS_802_11_NETWORK_INFRASTRUCTURE InfrastructureMode; + NDIS_802_11_RATES_EX SupportedRates; + ULONG IELength; + UCHAR IEs[1]; +} NDIS_WLAN_BSSID_EX, *PNDIS_WLAN_BSSID_EX; + +typedef struct PACKED _NDIS_802_11_BSSID_LIST_EX +{ + UINT NumberOfItems; // in list below, at least 1 + NDIS_WLAN_BSSID_EX Bssid[1]; +} NDIS_802_11_BSSID_LIST_EX, *PNDIS_802_11_BSSID_LIST_EX; + +typedef struct PACKED _NDIS_802_11_FIXED_IEs +{ + UCHAR Timestamp[8]; + USHORT BeaconInterval; + USHORT Capabilities; +} NDIS_802_11_FIXED_IEs, *PNDIS_802_11_FIXED_IEs; + +// Added new encryption types +// Also aliased typedef to new name +typedef enum _NDIS_802_11_WEP_STATUS +{ + Ndis802_11WEPEnabled, + Ndis802_11Encryption1Enabled = Ndis802_11WEPEnabled, + Ndis802_11WEPDisabled, + Ndis802_11EncryptionDisabled = Ndis802_11WEPDisabled, + Ndis802_11WEPKeyAbsent, + Ndis802_11Encryption1KeyAbsent = Ndis802_11WEPKeyAbsent, + Ndis802_11WEPNotSupported, + Ndis802_11EncryptionNotSupported = Ndis802_11WEPNotSupported, + Ndis802_11Encryption2Enabled, + Ndis802_11Encryption2KeyAbsent, + Ndis802_11Encryption3Enabled, + Ndis802_11Encryption3KeyAbsent +} NDIS_802_11_WEP_STATUS, *PNDIS_802_11_WEP_STATUS, + NDIS_802_11_ENCRYPTION_STATUS, *PNDIS_802_11_ENCRYPTION_STATUS; + +typedef enum _NDIS_802_11_RELOAD_DEFAULTS +{ + Ndis802_11ReloadWEPKeys +} NDIS_802_11_RELOAD_DEFAULTS, *PNDIS_802_11_RELOAD_DEFAULTS; + +#define NDIS_802_11_AI_REQFI_CAPABILITIES 1 +#define NDIS_802_11_AI_REQFI_LISTENINTERVAL 2 +#define NDIS_802_11_AI_REQFI_CURRENTAPADDRESS 4 + +#define NDIS_802_11_AI_RESFI_CAPABILITIES 1 +#define NDIS_802_11_AI_RESFI_STATUSCODE 2 +#define NDIS_802_11_AI_RESFI_ASSOCIATIONID 4 + +typedef struct _NDIS_802_11_AI_REQFI +{ + USHORT Capabilities; + USHORT ListenInterval; + NDIS_802_11_MAC_ADDRESS CurrentAPAddress; +} NDIS_802_11_AI_REQFI, *PNDIS_802_11_AI_REQFI; + +typedef struct _NDIS_802_11_AI_RESFI +{ + USHORT Capabilities; + USHORT StatusCode; + USHORT AssociationId; +} NDIS_802_11_AI_RESFI, *PNDIS_802_11_AI_RESFI; + +typedef struct _NDIS_802_11_ASSOCIATION_INFORMATION +{ + ULONG Length; + USHORT AvailableRequestFixedIEs; + NDIS_802_11_AI_REQFI RequestFixedIEs; + ULONG RequestIELength; + ULONG OffsetRequestIEs; + USHORT AvailableResponseFixedIEs; + NDIS_802_11_AI_RESFI ResponseFixedIEs; + ULONG ResponseIELength; + ULONG OffsetResponseIEs; +} NDIS_802_11_ASSOCIATION_INFORMATION, *PNDIS_802_11_ASSOCIATION_INFORMATION; + +struct ndis_pmkid_entry { + struct ndis_pmkid_entry *next; + u8 bssid[ETH_ALEN]; + u8 pmkid[16]; +}; + +typedef struct _MLME_DEAUTH_REQ_STRUCT { + UCHAR Addr[MAC_ADDR_LEN]; + USHORT Reason; +} MLME_DEAUTH_REQ_STRUCT, *PMLME_DEAUTH_REQ_STRUCT; diff --git a/src/drivers/driver_roboswitch.c b/src/drivers/driver_roboswitch.c new file mode 100644 index 0000000..bc11a48 --- /dev/null +++ b/src/drivers/driver_roboswitch.c @@ -0,0 +1,476 @@ +/* + * WPA Supplicant - roboswitch driver interface + * Copyright (c) 2008-2009 Jouke Witteveen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <sys/ioctl.h> +#include <linux/if.h> +#include <linux/sockios.h> +#include <linux/if_ether.h> +#include <linux/mii.h> + +#include "common.h" +#include "driver.h" +#include "l2_packet/l2_packet.h" + +#ifndef ETH_P_EAPOL +#define ETH_P_EAPOL 0x888e +#endif + +#define ROBO_PHY_ADDR 0x1e /* RoboSwitch PHY address */ + +/* MII access registers */ +#define ROBO_MII_PAGE 0x10 /* MII page register */ +#define ROBO_MII_ADDR 0x11 /* MII address register */ +#define ROBO_MII_DATA_OFFSET 0x18 /* Start of MII data registers */ + +#define ROBO_MII_PAGE_ENABLE 0x01 /* MII page op code */ +#define ROBO_MII_ADDR_WRITE 0x01 /* MII address write op code */ +#define ROBO_MII_ADDR_READ 0x02 /* MII address read op code */ +#define ROBO_MII_DATA_MAX 4 /* Consecutive MII data registers */ +#define ROBO_MII_RETRY_MAX 10 /* Read attempts before giving up */ + +/* Page numbers */ +#define ROBO_ARLCTRL_PAGE 0x04 /* ARL control page */ +#define ROBO_VLAN_PAGE 0x34 /* VLAN page */ + +/* ARL control page registers */ +#define ROBO_ARLCTRL_CONF 0x00 /* ARL configuration register */ +#define ROBO_ARLCTRL_ADDR_1 0x10 /* Multiport address 1 */ +#define ROBO_ARLCTRL_VEC_1 0x16 /* Multiport vector 1 */ +#define ROBO_ARLCTRL_ADDR_2 0x20 /* Multiport address 2 */ +#define ROBO_ARLCTRL_VEC_2 0x26 /* Multiport vector 2 */ + +/* VLAN page registers */ +#define ROBO_VLAN_ACCESS 0x08 /* VLAN table access register */ +#define ROBO_VLAN_ACCESS_5350 0x06 /* VLAN table access register (5350) */ +#define ROBO_VLAN_READ 0x0c /* VLAN read register */ +#define ROBO_VLAN_MAX 0xff /* Maximum number of VLANs */ + + +static const u8 pae_group_addr[ETH_ALEN] = +{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }; + + +struct wpa_driver_roboswitch_data { + void *ctx; + struct l2_packet_data *l2; + char ifname[IFNAMSIZ + 1]; + u8 own_addr[ETH_ALEN]; + struct ifreq ifr; + int fd, is_5350; + u16 ports; +}; + + +/* Copied from the kernel-only part of mii.h. */ +static inline struct mii_ioctl_data *if_mii(struct ifreq *rq) +{ + return (struct mii_ioctl_data *) &rq->ifr_ifru; +} + + +/* + * RoboSwitch uses 16-bit Big Endian addresses. + * The ordering of the words is reversed in the MII registers. + */ +static void wpa_driver_roboswitch_addr_be16(const u8 addr[ETH_ALEN], u16 *be) +{ + int i; + for (i = 0; i < ETH_ALEN; i += 2) + be[(ETH_ALEN - i) / 2 - 1] = WPA_GET_BE16(addr + i); +} + + +static u16 wpa_driver_roboswitch_mdio_read( + struct wpa_driver_roboswitch_data *drv, u8 reg) +{ + struct mii_ioctl_data *mii = if_mii(&drv->ifr); + + mii->phy_id = ROBO_PHY_ADDR; + mii->reg_num = reg; + + if (ioctl(drv->fd, SIOCGMIIREG, &drv->ifr) < 0) { + perror("ioctl[SIOCGMIIREG]"); + return 0x00; + } + return mii->val_out; +} + + +static void wpa_driver_roboswitch_mdio_write( + struct wpa_driver_roboswitch_data *drv, u8 reg, u16 val) +{ + struct mii_ioctl_data *mii = if_mii(&drv->ifr); + + mii->phy_id = ROBO_PHY_ADDR; + mii->reg_num = reg; + mii->val_in = val; + + if (ioctl(drv->fd, SIOCSMIIREG, &drv->ifr) < 0) { + perror("ioctl[SIOCSMIIREG"); + } +} + + +static int wpa_driver_roboswitch_reg(struct wpa_driver_roboswitch_data *drv, + u8 page, u8 reg, u8 op) +{ + int i; + + /* set page number */ + wpa_driver_roboswitch_mdio_write(drv, ROBO_MII_PAGE, + (page << 8) | ROBO_MII_PAGE_ENABLE); + /* set register address */ + wpa_driver_roboswitch_mdio_write(drv, ROBO_MII_ADDR, (reg << 8) | op); + + /* check if operation completed */ + for (i = 0; i < ROBO_MII_RETRY_MAX; ++i) { + if ((wpa_driver_roboswitch_mdio_read(drv, ROBO_MII_ADDR) & 3) + == 0) + return 0; + } + /* timeout */ + return -1; +} + + +static int wpa_driver_roboswitch_read(struct wpa_driver_roboswitch_data *drv, + u8 page, u8 reg, u16 *val, int len) +{ + int i; + + if (len > ROBO_MII_DATA_MAX || + wpa_driver_roboswitch_reg(drv, page, reg, ROBO_MII_ADDR_READ) < 0) + return -1; + + for (i = 0; i < len; ++i) { + val[i] = wpa_driver_roboswitch_mdio_read( + drv, ROBO_MII_DATA_OFFSET + i); + } + + return 0; +} + + +static int wpa_driver_roboswitch_write(struct wpa_driver_roboswitch_data *drv, + u8 page, u8 reg, u16 *val, int len) +{ + int i; + + if (len > ROBO_MII_DATA_MAX) return -1; + for (i = 0; i < len; ++i) { + wpa_driver_roboswitch_mdio_write(drv, ROBO_MII_DATA_OFFSET + i, + val[i]); + } + return wpa_driver_roboswitch_reg(drv, page, reg, ROBO_MII_ADDR_WRITE); +} + + +static void wpa_driver_roboswitch_receive(void *priv, const u8 *src_addr, + const u8 *buf, size_t len) +{ + struct wpa_driver_roboswitch_data *drv = priv; + + if (len > 14 && WPA_GET_BE16(buf + 12) == ETH_P_EAPOL && + os_memcmp(buf, drv->own_addr, ETH_ALEN) == 0) { + wpa_supplicant_rx_eapol(drv->ctx, src_addr, buf + 14, + len - 14); + } +} + + +static int wpa_driver_roboswitch_get_ssid(void *priv, u8 *ssid) +{ + ssid[0] = 0; + return 0; +} + + +static int wpa_driver_roboswitch_get_bssid(void *priv, u8 *bssid) +{ + /* Report PAE group address as the "BSSID" for wired connection. */ + os_memcpy(bssid, pae_group_addr, ETH_ALEN); + return 0; +} + + +static int wpa_driver_roboswitch_set_param(void *priv, const char *param) +{ + struct wpa_driver_roboswitch_data *drv = priv; + char *sep; + + if (param == NULL || os_strstr(param, "multicast_only=1") == NULL) { + sep = drv->ifname + os_strlen(drv->ifname); + *sep = '.'; + drv->l2 = l2_packet_init(drv->ifname, NULL, ETH_P_ALL, + wpa_driver_roboswitch_receive, drv, + 1); + if (drv->l2 == NULL) { + wpa_printf(MSG_INFO, "%s: Unable to listen on %s", + __func__, drv->ifname); + return -1; + } + *sep = '\0'; + l2_packet_get_own_addr(drv->l2, drv->own_addr); + } else { + wpa_printf(MSG_DEBUG, "%s: Ignoring unicast frames", __func__); + drv->l2 = NULL; + } + return 0; +} + + +static const char * wpa_driver_roboswitch_get_ifname(void *priv) +{ + struct wpa_driver_roboswitch_data *drv = priv; + return drv->ifname; +} + + +static int wpa_driver_roboswitch_join(struct wpa_driver_roboswitch_data *drv, + u16 ports, const u8 *addr) +{ + u16 read1[3], read2[3], addr_be16[3]; + + wpa_driver_roboswitch_addr_be16(addr, addr_be16); + + if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_CONF, read1, 1) < 0) + return -1; + if (!(read1[0] & (1 << 4))) { + /* multiport addresses are not yet enabled */ + read1[0] |= 1 << 4; + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_1, addr_be16, 3); + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_1, &ports, 1); + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_2, addr_be16, 3); + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_2, &ports, 1); + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_CONF, read1, 1); + } else { + /* if both multiport addresses are the same we can add */ + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_1, read1, 3); + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_2, read2, 3); + if (os_memcmp(read1, read2, 6) != 0) + return -1; + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_1, read1, 1); + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_2, read2, 1); + if (read1[0] != read2[0]) + return -1; + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_1, addr_be16, 3); + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_1, &ports, 1); + } + return 0; +} + + +static int wpa_driver_roboswitch_leave(struct wpa_driver_roboswitch_data *drv, + u16 ports, const u8 *addr) +{ + u16 _read, addr_be16[3], addr_read[3], ports_read; + + wpa_driver_roboswitch_addr_be16(addr, addr_be16); + + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_CONF, + &_read, 1); + /* If ARL control is disabled, there is nothing to leave. */ + if (!(_read & (1 << 4))) return -1; + + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_1, addr_read, 3); + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_VEC_1, + &ports_read, 1); + /* check if we occupy multiport address 1 */ + if (os_memcmp(addr_read, addr_be16, 6) == 0 && ports_read == ports) { + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_2, addr_read, 3); + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_2, &ports_read, 1); + /* and multiport address 2 */ + if (os_memcmp(addr_read, addr_be16, 6) == 0 && + ports_read == ports) { + _read &= ~(1 << 4); + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_CONF, &_read, + 1); + } else { + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_1, + addr_read, 3); + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_1, + &ports_read, 1); + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_2, + addr_read, 3); + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_2, + &ports_read, 1); + } + } else { + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_2, addr_read, 3); + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_2, &ports_read, 1); + /* or multiport address 2 */ + if (os_memcmp(addr_read, addr_be16, 6) == 0 && + ports_read == ports) { + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_1, + addr_read, 3); + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_1, + &ports_read, 1); + } else return -1; + } + return 0; +} + + +static void * wpa_driver_roboswitch_init(void *ctx, const char *ifname) +{ + struct wpa_driver_roboswitch_data *drv; + char *sep; + u16 vlan = 0, _read[2]; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) return NULL; + drv->ctx = ctx; + drv->own_addr[0] = '\0'; + + /* copy ifname and take a pointer to the second to last character */ + sep = drv->ifname + + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)) - 2; + /* find the '.' seperating <interface> and <vlan> */ + while (sep > drv->ifname && *sep != '.') sep--; + if (sep <= drv->ifname) { + wpa_printf(MSG_INFO, "%s: No <interface>.<vlan> pair in " + "interface name %s", __func__, drv->ifname); + os_free(drv); + return NULL; + } + *sep = '\0'; + while (*++sep) { + if (*sep < '0' || *sep > '9') { + wpa_printf(MSG_INFO, "%s: Invalid vlan specification " + "in interface name %s", __func__, ifname); + os_free(drv); + return NULL; + } + vlan *= 10; + vlan += *sep - '0'; + if (vlan > ROBO_VLAN_MAX) { + wpa_printf(MSG_INFO, "%s: VLAN out of range in " + "interface name %s", __func__, ifname); + os_free(drv); + return NULL; + } + } + + drv->fd = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->fd < 0) { + wpa_printf(MSG_INFO, "%s: Unable to create socket", __func__); + os_free(drv); + return NULL; + } + + os_memset(&drv->ifr, 0, sizeof(drv->ifr)); + os_strlcpy(drv->ifr.ifr_name, drv->ifname, IFNAMSIZ); + if (ioctl(drv->fd, SIOCGMIIPHY, &drv->ifr) < 0) { + perror("ioctl[SIOCGMIIPHY]"); + os_free(drv); + return NULL; + } + if (if_mii(&drv->ifr)->phy_id != ROBO_PHY_ADDR) { + wpa_printf(MSG_INFO, "%s: Invalid phy address (not a " + "RoboSwitch?)", __func__); + os_free(drv); + return NULL; + } + + /* set and read back to see if the register can be used */ + _read[0] = ROBO_VLAN_MAX; + wpa_driver_roboswitch_write(drv, ROBO_VLAN_PAGE, ROBO_VLAN_ACCESS_5350, + _read, 1); + wpa_driver_roboswitch_read(drv, ROBO_VLAN_PAGE, ROBO_VLAN_ACCESS_5350, + _read + 1, 1); + drv->is_5350 = _read[0] == _read[1]; + + /* set the read bit */ + vlan |= 1 << 13; + wpa_driver_roboswitch_write(drv, ROBO_VLAN_PAGE, + drv->is_5350 ? ROBO_VLAN_ACCESS_5350 + : ROBO_VLAN_ACCESS, + &vlan, 1); + wpa_driver_roboswitch_read(drv, ROBO_VLAN_PAGE, ROBO_VLAN_READ, _read, + drv->is_5350 ? 2 : 1); + if (!(drv->is_5350 ? _read[1] & (1 << 4) : _read[0] & (1 << 14))) { + wpa_printf(MSG_INFO, "%s: Could not get port information for " + "VLAN %d", __func__, vlan & ~(1 << 13)); + os_free(drv); + return NULL; + } + drv->ports = _read[0] & 0x001F; + /* add the MII port */ + drv->ports |= 1 << 8; + if (wpa_driver_roboswitch_join(drv, drv->ports, pae_group_addr) < 0) { + wpa_printf(MSG_INFO, "%s: Unable to join PAE group", __func__); + os_free(drv); + return NULL; + } else { + wpa_printf(MSG_DEBUG, "%s: Added PAE group address to " + "RoboSwitch ARL", __func__); + } + + return drv; +} + + +static void wpa_driver_roboswitch_deinit(void *priv) +{ + struct wpa_driver_roboswitch_data *drv = priv; + + if (drv->l2) { + l2_packet_deinit(drv->l2); + drv->l2 = NULL; + } + if (wpa_driver_roboswitch_leave(drv, drv->ports, pae_group_addr) < 0) { + wpa_printf(MSG_DEBUG, "%s: Unable to leave PAE group", + __func__); + } + + close(drv->fd); + os_free(drv); +} + + +const struct wpa_driver_ops wpa_driver_roboswitch_ops = { + .name = "roboswitch", + .desc = "wpa_supplicant roboswitch driver", + .get_ssid = wpa_driver_roboswitch_get_ssid, + .get_bssid = wpa_driver_roboswitch_get_bssid, + .init = wpa_driver_roboswitch_init, + .deinit = wpa_driver_roboswitch_deinit, + .set_param = wpa_driver_roboswitch_set_param, + .get_ifname = wpa_driver_roboswitch_get_ifname, +}; diff --git a/src/drivers/driver_test.c b/src/drivers/driver_test.c new file mode 100644 index 0000000..2a41cf2 --- /dev/null +++ b/src/drivers/driver_test.c @@ -0,0 +1,1230 @@ +/* + * WPA Supplicant - testing driver interface + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +/* Make dure we get winsock2.h for Windows build to get sockaddr_storage */ +#include "build_config.h" +#ifdef CONFIG_NATIVE_WINDOWS +#include <winsock2.h> +#endif /* CONFIG_NATIVE_WINDOWS */ + +#include "includes.h" + +#ifndef CONFIG_NATIVE_WINDOWS +#include <sys/un.h> +#include <dirent.h> +#include <sys/stat.h> +#define DRIVER_TEST_UNIX +#endif /* CONFIG_NATIVE_WINDOWS */ + +#include "common.h" +#include "driver.h" +#include "l2_packet/l2_packet.h" +#include "eloop.h" +#include "sha1.h" +#include "ieee802_11_defs.h" + + +struct wpa_driver_test_global { + int dummy; +}; + +struct wpa_driver_test_data { + struct wpa_driver_test_global *global; + void *ctx; + u8 own_addr[ETH_ALEN]; + int test_socket; +#ifdef DRIVER_TEST_UNIX + struct sockaddr_un hostapd_addr; +#endif /* DRIVER_TEST_UNIX */ + int hostapd_addr_set; + struct sockaddr_in hostapd_addr_udp; + int hostapd_addr_udp_set; + char *own_socket_path; + char *test_dir; + u8 bssid[ETH_ALEN]; + u8 ssid[32]; + size_t ssid_len; +#define MAX_SCAN_RESULTS 30 + struct wpa_scan_res *scanres[MAX_SCAN_RESULTS]; + size_t num_scanres; + int use_associnfo; + u8 assoc_wpa_ie[80]; + size_t assoc_wpa_ie_len; + int use_mlme; + int associated; + u8 *probe_req_ie; + size_t probe_req_ie_len; +}; + + +static void wpa_driver_test_poll(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_driver_test_data *drv = eloop_ctx; + +#ifdef DRIVER_TEST_UNIX + if (drv->associated && drv->hostapd_addr_set) { + struct stat st; + if (stat(drv->hostapd_addr.sun_path, &st) < 0) { + wpa_printf(MSG_DEBUG, "%s: lost connection to AP: %s", + __func__, strerror(errno)); + drv->associated = 0; + wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); + } + } +#endif /* DRIVER_TEST_UNIX */ + + eloop_register_timeout(1, 0, wpa_driver_test_poll, drv, NULL); +} + + +static int wpa_driver_test_set_wpa(void *priv, int enabled) +{ + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); + return 0; +} + + +static void wpa_driver_test_scan_timeout(void *eloop_ctx, void *timeout_ctx) +{ + wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); + wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); +} + + +#ifdef DRIVER_TEST_UNIX +static void wpa_driver_scan_dir(struct wpa_driver_test_data *drv, + const char *path) +{ + struct dirent *dent; + DIR *dir; + struct sockaddr_un addr; + char cmd[512], *pos, *end; + int ret; + + dir = opendir(path); + if (dir == NULL) + return; + + end = cmd + sizeof(cmd); + pos = cmd; + ret = os_snprintf(pos, end - pos, "SCAN " MACSTR, + MAC2STR(drv->own_addr)); + if (ret >= 0 && ret < end - pos) + pos += ret; + if (drv->probe_req_ie) { + ret = os_snprintf(pos, end - pos, " "); + if (ret >= 0 && ret < end - pos) + pos += ret; + pos += wpa_snprintf_hex(pos, end - pos, drv->probe_req_ie, + drv->probe_req_ie_len); + } + end[-1] = '\0'; + + while ((dent = readdir(dir))) { + if (os_strncmp(dent->d_name, "AP-", 3) != 0) + continue; + wpa_printf(MSG_DEBUG, "%s: SCAN %s", __func__, dent->d_name); + + os_memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + os_snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s", + path, dent->d_name); + + if (sendto(drv->test_socket, cmd, os_strlen(cmd), 0, + (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("sendto(test_socket)"); + } + } + closedir(dir); +} +#endif /* DRIVER_TEST_UNIX */ + + +static int wpa_driver_test_scan(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_test_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s: priv=%p", __func__, priv); + + drv->num_scanres = 0; + +#ifdef DRIVER_TEST_UNIX + if (drv->test_socket >= 0 && drv->test_dir) + wpa_driver_scan_dir(drv, drv->test_dir); + + if (drv->test_socket >= 0 && drv->hostapd_addr_set && + sendto(drv->test_socket, "SCAN", 4, 0, + (struct sockaddr *) &drv->hostapd_addr, + sizeof(drv->hostapd_addr)) < 0) { + perror("sendto(test_socket)"); + } +#endif /* DRIVER_TEST_UNIX */ + + if (drv->test_socket >= 0 && drv->hostapd_addr_udp_set && + sendto(drv->test_socket, "SCAN", 4, 0, + (struct sockaddr *) &drv->hostapd_addr_udp, + sizeof(drv->hostapd_addr_udp)) < 0) { + perror("sendto(test_socket)"); + } + + eloop_cancel_timeout(wpa_driver_test_scan_timeout, drv, drv->ctx); + eloop_register_timeout(1, 0, wpa_driver_test_scan_timeout, drv, + drv->ctx); + return 0; +} + + +static struct wpa_scan_results * wpa_driver_test_get_scan_results2(void *priv) +{ + struct wpa_driver_test_data *drv = priv; + struct wpa_scan_results *res; + size_t i; + + res = os_zalloc(sizeof(*res)); + if (res == NULL) + return NULL; + + res->res = os_zalloc(drv->num_scanres * sizeof(struct wpa_scan_res *)); + if (res->res == NULL) { + os_free(res); + return NULL; + } + + for (i = 0; i < drv->num_scanres; i++) { + struct wpa_scan_res *r; + if (drv->scanres[i] == NULL) + continue; + r = os_malloc(sizeof(*r) + drv->scanres[i]->ie_len); + if (r == NULL) + break; + os_memcpy(r, drv->scanres[i], + sizeof(*r) + drv->scanres[i]->ie_len); + res->res[res->num++] = r; + } + + return res; +} + + +static int wpa_driver_test_set_key(void *priv, 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, "%s: priv=%p alg=%d key_idx=%d set_tx=%d", + __func__, priv, alg, key_idx, set_tx); + if (addr) { + wpa_printf(MSG_DEBUG, " addr=" MACSTR, MAC2STR(addr)); + } + if (seq) { + wpa_hexdump(MSG_DEBUG, " seq", seq, seq_len); + } + if (key) { + wpa_hexdump(MSG_DEBUG, " key", key, key_len); + } + return 0; +} + + +static int wpa_driver_test_associate( + void *priv, struct wpa_driver_associate_params *params) +{ + struct wpa_driver_test_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s: priv=%p freq=%d pairwise_suite=%d " + "group_suite=%d key_mgmt_suite=%d auth_alg=%d mode=%d", + __func__, priv, params->freq, params->pairwise_suite, + params->group_suite, params->key_mgmt_suite, + params->auth_alg, params->mode); + if (params->bssid) { + wpa_printf(MSG_DEBUG, " bssid=" MACSTR, + MAC2STR(params->bssid)); + } + if (params->ssid) { + wpa_hexdump_ascii(MSG_DEBUG, " ssid", + params->ssid, params->ssid_len); + } + if (params->wpa_ie) { + wpa_hexdump(MSG_DEBUG, " wpa_ie", + params->wpa_ie, params->wpa_ie_len); + drv->assoc_wpa_ie_len = params->wpa_ie_len; + if (drv->assoc_wpa_ie_len > sizeof(drv->assoc_wpa_ie)) + drv->assoc_wpa_ie_len = sizeof(drv->assoc_wpa_ie); + os_memcpy(drv->assoc_wpa_ie, params->wpa_ie, + drv->assoc_wpa_ie_len); + } else + drv->assoc_wpa_ie_len = 0; + +#ifdef DRIVER_TEST_UNIX + if (drv->test_dir && params->bssid) { + os_memset(&drv->hostapd_addr, 0, sizeof(drv->hostapd_addr)); + drv->hostapd_addr.sun_family = AF_UNIX; + os_snprintf(drv->hostapd_addr.sun_path, + sizeof(drv->hostapd_addr.sun_path), + "%s/AP-" MACSTR, + drv->test_dir, MAC2STR(params->bssid)); + drv->hostapd_addr_set = 1; + } +#endif /* DRIVER_TEST_UNIX */ + + if (drv->test_socket >= 0 && + (drv->hostapd_addr_set || drv->hostapd_addr_udp_set)) { + char cmd[200], *pos, *end; + int ret; + end = cmd + sizeof(cmd); + pos = cmd; + ret = os_snprintf(pos, end - pos, "ASSOC " MACSTR " ", + MAC2STR(drv->own_addr)); + if (ret >= 0 && ret < end - pos) + pos += ret; + pos += wpa_snprintf_hex(pos, end - pos, params->ssid, + params->ssid_len); + ret = os_snprintf(pos, end - pos, " "); + if (ret >= 0 && ret < end - pos) + pos += ret; + pos += wpa_snprintf_hex(pos, end - pos, params->wpa_ie, + params->wpa_ie_len); + end[-1] = '\0'; +#ifdef DRIVER_TEST_UNIX + if (drv->hostapd_addr_set && + sendto(drv->test_socket, cmd, os_strlen(cmd), 0, + (struct sockaddr *) &drv->hostapd_addr, + sizeof(drv->hostapd_addr)) < 0) { + perror("sendto(test_socket)"); + return -1; + } +#endif /* DRIVER_TEST_UNIX */ + if (drv->hostapd_addr_udp_set && + sendto(drv->test_socket, cmd, os_strlen(cmd), 0, + (struct sockaddr *) &drv->hostapd_addr_udp, + sizeof(drv->hostapd_addr_udp)) < 0) { + perror("sendto(test_socket)"); + return -1; + } + + os_memcpy(drv->ssid, params->ssid, params->ssid_len); + drv->ssid_len = params->ssid_len; + } else { + drv->associated = 1; + wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL); + } + + return 0; +} + + +static int wpa_driver_test_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_test_data *drv = priv; + os_memcpy(bssid, drv->bssid, ETH_ALEN); + return 0; +} + + +static int wpa_driver_test_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_test_data *drv = priv; + os_memcpy(ssid, drv->ssid, 32); + return drv->ssid_len; +} + + +static int wpa_driver_test_send_disassoc(struct wpa_driver_test_data *drv) +{ +#ifdef DRIVER_TEST_UNIX + if (drv->test_socket >= 0 && + sendto(drv->test_socket, "DISASSOC", 8, 0, + (struct sockaddr *) &drv->hostapd_addr, + sizeof(drv->hostapd_addr)) < 0) { + perror("sendto(test_socket)"); + return -1; + } +#endif /* DRIVER_TEST_UNIX */ + if (drv->test_socket >= 0 && drv->hostapd_addr_udp_set && + sendto(drv->test_socket, "DISASSOC", 8, 0, + (struct sockaddr *) &drv->hostapd_addr_udp, + sizeof(drv->hostapd_addr_udp)) < 0) { + perror("sendto(test_socket)"); + return -1; + } + return 0; +} + + +static int wpa_driver_test_deauthenticate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_test_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s addr=" MACSTR " reason_code=%d", + __func__, MAC2STR(addr), reason_code); + os_memset(drv->bssid, 0, ETH_ALEN); + drv->associated = 0; + wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); + return wpa_driver_test_send_disassoc(drv); +} + + +static int wpa_driver_test_disassociate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_test_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s addr=" MACSTR " reason_code=%d", + __func__, MAC2STR(addr), reason_code); + os_memset(drv->bssid, 0, ETH_ALEN); + drv->associated = 0; + wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); + return wpa_driver_test_send_disassoc(drv); +} + + +static void wpa_driver_test_scanresp(struct wpa_driver_test_data *drv, + struct sockaddr *from, + socklen_t fromlen, + const char *data) +{ + struct wpa_scan_res *res; + const char *pos, *pos2; + size_t len; + u8 *ie_pos, *ie_start, *ie_end; +#define MAX_IE_LEN 1000 + + wpa_printf(MSG_DEBUG, "test_driver: SCANRESP %s", data); + if (drv->num_scanres >= MAX_SCAN_RESULTS) { + wpa_printf(MSG_DEBUG, "test_driver: No room for the new scan " + "result"); + return; + } + + /* SCANRESP BSSID SSID IEs */ + + res = os_zalloc(sizeof(*res) + MAX_IE_LEN); + if (res == NULL) + return; + ie_start = ie_pos = (u8 *) (res + 1); + ie_end = ie_pos + MAX_IE_LEN; + + if (hwaddr_aton(data, res->bssid)) { + wpa_printf(MSG_DEBUG, "test_driver: invalid BSSID in scanres"); + os_free(res); + return; + } + + pos = data + 17; + while (*pos == ' ') + pos++; + pos2 = os_strchr(pos, ' '); + if (pos2 == NULL) { + wpa_printf(MSG_DEBUG, "test_driver: invalid SSID termination " + "in scanres"); + os_free(res); + return; + } + len = (pos2 - pos) / 2; + if (len > 32) + len = 32; + /* + * Generate SSID IE from the SSID field since this IE is not included + * in the main IE field. + */ + *ie_pos++ = WLAN_EID_SSID; + *ie_pos++ = len; + if (hexstr2bin(pos, ie_pos, len) < 0) { + wpa_printf(MSG_DEBUG, "test_driver: invalid SSID in scanres"); + os_free(res); + return; + } + ie_pos += len; + + pos = pos2 + 1; + pos2 = os_strchr(pos, ' '); + if (pos2 == NULL) + len = os_strlen(pos) / 2; + else + len = (pos2 - pos) / 2; + if ((int) len > ie_end - ie_pos) + len = ie_end - ie_pos; + if (hexstr2bin(pos, ie_pos, len) < 0) { + wpa_printf(MSG_DEBUG, "test_driver: invalid IEs in scanres"); + os_free(res); + return; + } + ie_pos += len; + res->ie_len = ie_pos - ie_start; + + if (pos2) { + pos = pos2 + 1; + while (*pos == ' ') + pos++; + if (os_strncmp(pos, "PRIVACY", 7) == 0) + res->caps |= IEEE80211_CAP_PRIVACY; + } + + os_free(drv->scanres[drv->num_scanres]); + drv->scanres[drv->num_scanres++] = res; +} + + +static void wpa_driver_test_assocresp(struct wpa_driver_test_data *drv, + struct sockaddr *from, + socklen_t fromlen, + const char *data) +{ + /* ASSOCRESP BSSID <res> */ + if (hwaddr_aton(data, drv->bssid)) { + wpa_printf(MSG_DEBUG, "test_driver: invalid BSSID in " + "assocresp"); + } + if (drv->use_associnfo) { + union wpa_event_data event; + os_memset(&event, 0, sizeof(event)); + event.assoc_info.req_ies = drv->assoc_wpa_ie; + event.assoc_info.req_ies_len = drv->assoc_wpa_ie_len; + wpa_supplicant_event(drv->ctx, EVENT_ASSOCINFO, &event); + } + drv->associated = 1; + wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL); +} + + +static void wpa_driver_test_disassoc(struct wpa_driver_test_data *drv, + struct sockaddr *from, + socklen_t fromlen) +{ + drv->associated = 0; + wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); +} + + +static void wpa_driver_test_eapol(struct wpa_driver_test_data *drv, + struct sockaddr *from, + socklen_t fromlen, + const u8 *data, size_t data_len) +{ + const u8 *src = drv->bssid; + + if (data_len > 14) { + /* Skip Ethernet header */ + src = data + ETH_ALEN; + data += 14; + data_len -= 14; + } + wpa_supplicant_rx_eapol(drv->ctx, src, data, data_len); +} + + +static void wpa_driver_test_mlme(struct wpa_driver_test_data *drv, + struct sockaddr *from, + socklen_t fromlen, + const u8 *data, size_t data_len) +{ +#ifdef CONFIG_CLIENT_MLME + struct ieee80211_rx_status rx_status; + os_memset(&rx_status, 0, sizeof(rx_status)); + wpa_supplicant_sta_rx(drv->ctx, data, data_len, &rx_status); +#endif /* CONFIG_CLIENT_MLME */ +} + + +static void wpa_driver_test_receive_unix(int sock, void *eloop_ctx, + void *sock_ctx) +{ + struct wpa_driver_test_data *drv = eloop_ctx; + char *buf; + int res; + struct sockaddr_storage from; + socklen_t fromlen = sizeof(from); + const size_t buflen = 2000; + + buf = os_malloc(buflen); + if (buf == NULL) + return; + res = recvfrom(sock, buf, buflen - 1, 0, + (struct sockaddr *) &from, &fromlen); + if (res < 0) { + perror("recvfrom(test_socket)"); + os_free(buf); + return; + } + buf[res] = '\0'; + + wpa_printf(MSG_DEBUG, "test_driver: received %u bytes", res); + + if (os_strncmp(buf, "SCANRESP ", 9) == 0) { + wpa_driver_test_scanresp(drv, (struct sockaddr *) &from, + fromlen, buf + 9); + } else if (os_strncmp(buf, "ASSOCRESP ", 10) == 0) { + wpa_driver_test_assocresp(drv, (struct sockaddr *) &from, + fromlen, buf + 10); + } else if (os_strcmp(buf, "DISASSOC") == 0) { + wpa_driver_test_disassoc(drv, (struct sockaddr *) &from, + fromlen); + } else if (os_strcmp(buf, "DEAUTH") == 0) { + wpa_driver_test_disassoc(drv, (struct sockaddr *) &from, + fromlen); + } else if (os_strncmp(buf, "EAPOL ", 6) == 0) { + wpa_driver_test_eapol(drv, (struct sockaddr *) &from, fromlen, + (const u8 *) buf + 6, res - 6); + } else if (os_strncmp(buf, "MLME ", 5) == 0) { + wpa_driver_test_mlme(drv, (struct sockaddr *) &from, fromlen, + (const u8 *) buf + 5, res - 5); + } else { + wpa_hexdump_ascii(MSG_DEBUG, "Unknown test_socket command", + (u8 *) buf, res); + } + os_free(buf); +} + + +static void * wpa_driver_test_init2(void *ctx, const char *ifname, + void *global_priv) +{ + struct wpa_driver_test_data *drv; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->global = global_priv; + drv->ctx = ctx; + drv->test_socket = -1; + + /* Set dummy BSSID and SSID for testing. */ + drv->bssid[0] = 0x02; + drv->bssid[1] = 0x00; + drv->bssid[2] = 0x00; + drv->bssid[3] = 0x00; + drv->bssid[4] = 0x00; + drv->bssid[5] = 0x01; + os_memcpy(drv->ssid, "test", 5); + drv->ssid_len = 4; + + /* Generate a MAC address to help testing with multiple STAs */ + drv->own_addr[0] = 0x02; /* locally administered */ + sha1_prf((const u8 *) ifname, os_strlen(ifname), + "wpa_supplicant test mac addr generation", + NULL, 0, drv->own_addr + 1, ETH_ALEN - 1); + eloop_register_timeout(1, 0, wpa_driver_test_poll, drv, NULL); + + return drv; +} + + +static void wpa_driver_test_close_test_socket(struct wpa_driver_test_data *drv) +{ + if (drv->test_socket >= 0) { + eloop_unregister_read_sock(drv->test_socket); + close(drv->test_socket); + drv->test_socket = -1; + } + + if (drv->own_socket_path) { + unlink(drv->own_socket_path); + os_free(drv->own_socket_path); + drv->own_socket_path = NULL; + } +} + + +static void wpa_driver_test_deinit(void *priv) +{ + struct wpa_driver_test_data *drv = priv; + int i; + wpa_driver_test_close_test_socket(drv); + eloop_cancel_timeout(wpa_driver_test_scan_timeout, drv, drv->ctx); + eloop_cancel_timeout(wpa_driver_test_poll, drv, NULL); + os_free(drv->test_dir); + for (i = 0; i < MAX_SCAN_RESULTS; i++) + os_free(drv->scanres[i]); + os_free(drv->probe_req_ie); + os_free(drv); +} + + +static int wpa_driver_test_attach(struct wpa_driver_test_data *drv, + const char *dir) +{ +#ifdef DRIVER_TEST_UNIX + static unsigned int counter = 0; + struct sockaddr_un addr; + size_t len; + + os_free(drv->own_socket_path); + if (dir) { + len = os_strlen(dir) + 30; + drv->own_socket_path = os_malloc(len); + if (drv->own_socket_path == NULL) + return -1; + os_snprintf(drv->own_socket_path, len, "%s/STA-" MACSTR, + dir, MAC2STR(drv->own_addr)); + } else { + drv->own_socket_path = os_malloc(100); + if (drv->own_socket_path == NULL) + return -1; + os_snprintf(drv->own_socket_path, 100, + "/tmp/wpa_supplicant_test-%d-%d", + getpid(), counter++); + } + + drv->test_socket = socket(PF_UNIX, SOCK_DGRAM, 0); + if (drv->test_socket < 0) { + perror("socket(PF_UNIX)"); + os_free(drv->own_socket_path); + drv->own_socket_path = NULL; + return -1; + } + + os_memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + os_strlcpy(addr.sun_path, drv->own_socket_path, sizeof(addr.sun_path)); + if (bind(drv->test_socket, (struct sockaddr *) &addr, + sizeof(addr)) < 0) { + perror("bind(PF_UNIX)"); + close(drv->test_socket); + unlink(drv->own_socket_path); + os_free(drv->own_socket_path); + drv->own_socket_path = NULL; + return -1; + } + + eloop_register_read_sock(drv->test_socket, + wpa_driver_test_receive_unix, drv, NULL); + + return 0; +#else /* DRIVER_TEST_UNIX */ + return -1; +#endif /* DRIVER_TEST_UNIX */ +} + + +static int wpa_driver_test_attach_udp(struct wpa_driver_test_data *drv, + char *dst) +{ + char *pos; + + pos = os_strchr(dst, ':'); + if (pos == NULL) + return -1; + *pos++ = '\0'; + wpa_printf(MSG_DEBUG, "%s: addr=%s port=%s", __func__, dst, pos); + + drv->test_socket = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->test_socket < 0) { + perror("socket(PF_INET)"); + return -1; + } + + os_memset(&drv->hostapd_addr_udp, 0, sizeof(drv->hostapd_addr_udp)); + drv->hostapd_addr_udp.sin_family = AF_INET; +#if defined(CONFIG_NATIVE_WINDOWS) || defined(CONFIG_ANSI_C_EXTRA) + { + int a[4]; + u8 *pos; + sscanf(dst, "%d.%d.%d.%d", &a[0], &a[1], &a[2], &a[3]); + pos = (u8 *) &drv->hostapd_addr_udp.sin_addr; + *pos++ = a[0]; + *pos++ = a[1]; + *pos++ = a[2]; + *pos++ = a[3]; + } +#else /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */ + inet_aton(dst, &drv->hostapd_addr_udp.sin_addr); +#endif /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */ + drv->hostapd_addr_udp.sin_port = htons(atoi(pos)); + + drv->hostapd_addr_udp_set = 1; + + eloop_register_read_sock(drv->test_socket, + wpa_driver_test_receive_unix, drv, NULL); + + return 0; +} + + +static int wpa_driver_test_set_param(void *priv, const char *param) +{ + struct wpa_driver_test_data *drv = priv; + const char *pos; + + wpa_printf(MSG_DEBUG, "%s: param='%s'", __func__, param); + if (param == NULL) + return 0; + + wpa_driver_test_close_test_socket(drv); + +#ifdef DRIVER_TEST_UNIX + pos = os_strstr(param, "test_socket="); + if (pos) { + const char *pos2; + size_t len; + + pos += 12; + pos2 = os_strchr(pos, ' '); + if (pos2) + len = pos2 - pos; + else + len = os_strlen(pos); + if (len > sizeof(drv->hostapd_addr.sun_path)) + return -1; + os_memset(&drv->hostapd_addr, 0, sizeof(drv->hostapd_addr)); + drv->hostapd_addr.sun_family = AF_UNIX; + os_memcpy(drv->hostapd_addr.sun_path, pos, len); + drv->hostapd_addr_set = 1; + } +#endif /* DRIVER_TEST_UNIX */ + + pos = os_strstr(param, "test_dir="); + if (pos) { + char *end; + os_free(drv->test_dir); + drv->test_dir = os_strdup(pos + 9); + if (drv->test_dir == NULL) + return -1; + end = os_strchr(drv->test_dir, ' '); + if (end) + *end = '\0'; + if (wpa_driver_test_attach(drv, drv->test_dir)) + return -1; + } else { + pos = os_strstr(param, "test_udp="); + if (pos) { + char *dst, *epos; + dst = os_strdup(pos + 9); + if (dst == NULL) + return -1; + epos = os_strchr(dst, ' '); + if (epos) + *epos = '\0'; + if (wpa_driver_test_attach_udp(drv, dst)) + return -1; + os_free(dst); + } else if (wpa_driver_test_attach(drv, NULL)) + return -1; + } + + if (os_strstr(param, "use_associnfo=1")) { + wpa_printf(MSG_DEBUG, "test_driver: Use AssocInfo events"); + drv->use_associnfo = 1; + } + +#ifdef CONFIG_CLIENT_MLME + if (os_strstr(param, "use_mlme=1")) { + wpa_printf(MSG_DEBUG, "test_driver: Use internal MLME"); + drv->use_mlme = 1; + } +#endif /* CONFIG_CLIENT_MLME */ + + return 0; +} + + +static const u8 * wpa_driver_test_get_mac_addr(void *priv) +{ + struct wpa_driver_test_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s", __func__); + return drv->own_addr; +} + + +static int wpa_driver_test_send_eapol(void *priv, const u8 *dest, u16 proto, + const u8 *data, size_t data_len) +{ + struct wpa_driver_test_data *drv = priv; + char *msg; + size_t msg_len; + struct l2_ethhdr eth; + struct sockaddr *addr; + socklen_t alen; +#ifdef DRIVER_TEST_UNIX + struct sockaddr_un addr_un; +#endif /* DRIVER_TEST_UNIX */ + + wpa_hexdump(MSG_MSGDUMP, "test_send_eapol TX frame", data, data_len); + + os_memset(ð, 0, sizeof(eth)); + os_memcpy(eth.h_dest, dest, ETH_ALEN); + os_memcpy(eth.h_source, drv->own_addr, ETH_ALEN); + eth.h_proto = host_to_be16(proto); + + msg_len = 6 + sizeof(eth) + data_len; + msg = os_malloc(msg_len); + if (msg == NULL) + return -1; + os_memcpy(msg, "EAPOL ", 6); + os_memcpy(msg + 6, ð, sizeof(eth)); + os_memcpy(msg + 6 + sizeof(eth), data, data_len); + + if (os_memcmp(dest, drv->bssid, ETH_ALEN) == 0 || + drv->test_dir == NULL) { + if (drv->hostapd_addr_udp_set) { + addr = (struct sockaddr *) &drv->hostapd_addr_udp; + alen = sizeof(drv->hostapd_addr_udp); + } else { +#ifdef DRIVER_TEST_UNIX + addr = (struct sockaddr *) &drv->hostapd_addr; + alen = sizeof(drv->hostapd_addr); +#else /* DRIVER_TEST_UNIX */ + os_free(msg); + return -1; +#endif /* DRIVER_TEST_UNIX */ + } + } else { +#ifdef DRIVER_TEST_UNIX + struct stat st; + os_memset(&addr_un, 0, sizeof(addr_un)); + addr_un.sun_family = AF_UNIX; + os_snprintf(addr_un.sun_path, sizeof(addr_un.sun_path), + "%s/STA-" MACSTR, drv->test_dir, MAC2STR(dest)); + if (stat(addr_un.sun_path, &st) < 0) { + os_snprintf(addr_un.sun_path, sizeof(addr_un.sun_path), + "%s/AP-" MACSTR, + drv->test_dir, MAC2STR(dest)); + } + addr = (struct sockaddr *) &addr_un; + alen = sizeof(addr_un); +#else /* DRIVER_TEST_UNIX */ + os_free(msg); + return -1; +#endif /* DRIVER_TEST_UNIX */ + } + + if (sendto(drv->test_socket, msg, msg_len, 0, addr, alen) < 0) { + perror("sendmsg(test_socket)"); + os_free(msg); + return -1; + } + + os_free(msg); + return 0; +} + + +static int wpa_driver_test_get_capa(void *priv, struct wpa_driver_capa *capa) +{ + struct wpa_driver_test_data *drv = priv; + os_memset(capa, 0, sizeof(*capa)); + capa->key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | + WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK | + WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE | + WPA_DRIVER_CAPA_KEY_MGMT_FT | + WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK; + capa->enc = WPA_DRIVER_CAPA_ENC_WEP40 | + WPA_DRIVER_CAPA_ENC_WEP104 | + WPA_DRIVER_CAPA_ENC_TKIP | + WPA_DRIVER_CAPA_ENC_CCMP; + capa->auth = WPA_DRIVER_AUTH_OPEN | + WPA_DRIVER_AUTH_SHARED | + WPA_DRIVER_AUTH_LEAP; + if (drv->use_mlme) + capa->flags |= WPA_DRIVER_FLAGS_USER_SPACE_MLME; + + return 0; +} + + +static int wpa_driver_test_mlme_setprotection(void *priv, const u8 *addr, + int protect_type, + int key_type) +{ + wpa_printf(MSG_DEBUG, "%s: protect_type=%d key_type=%d", + __func__, protect_type, key_type); + + if (addr) { + wpa_printf(MSG_DEBUG, "%s: addr=" MACSTR, + __func__, MAC2STR(addr)); + } + + return 0; +} + + +#ifdef CONFIG_CLIENT_MLME +static struct wpa_hw_modes * +wpa_driver_test_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) +{ + struct wpa_hw_modes *modes; + + *num_modes = 1; + *flags = 0; + modes = os_zalloc(*num_modes * sizeof(struct wpa_hw_modes)); + if (modes == NULL) + return NULL; + modes[0].mode = WPA_MODE_IEEE80211G; + modes[0].num_channels = 1; + modes[0].num_rates = 1; + modes[0].channels = os_zalloc(sizeof(struct wpa_channel_data)); + modes[0].rates = os_zalloc(sizeof(struct wpa_rate_data)); + if (modes[0].channels == NULL || modes[0].rates == NULL) { + wpa_supplicant_sta_free_hw_features(modes, *num_modes); + return NULL; + } + modes[0].channels[0].chan = 1; + modes[0].channels[0].freq = 2412; + modes[0].channels[0].flag = WPA_CHAN_W_SCAN | WPA_CHAN_W_ACTIVE_SCAN; + modes[0].rates[0].rate = 10; + modes[0].rates[0].flags = WPA_RATE_BASIC | WPA_RATE_SUPPORTED | + WPA_RATE_CCK | WPA_RATE_MANDATORY; + + return modes; +} + + +static int wpa_driver_test_set_channel(void *priv, wpa_hw_mode phymode, + int chan, int freq) +{ + wpa_printf(MSG_DEBUG, "%s: phymode=%d chan=%d freq=%d", + __func__, phymode, chan, freq); + return 0; +} + + +static int wpa_driver_test_send_mlme(void *priv, const u8 *data, + size_t data_len) +{ + struct wpa_driver_test_data *drv = priv; + struct msghdr msg; + struct iovec io[2]; + struct sockaddr_un addr; + const u8 *dest; + struct dirent *dent; + DIR *dir; + + wpa_hexdump(MSG_MSGDUMP, "test_send_mlme", data, data_len); + if (data_len < 10) + return -1; + dest = data + 4; + + io[0].iov_base = "MLME "; + io[0].iov_len = 5; + io[1].iov_base = (u8 *) data; + io[1].iov_len = data_len; + + os_memset(&msg, 0, sizeof(msg)); + msg.msg_iov = io; + msg.msg_iovlen = 2; + if (os_memcmp(dest, drv->bssid, ETH_ALEN) == 0 || + drv->test_dir == NULL) { + if (drv->hostapd_addr_udp_set) { + msg.msg_name = &drv->hostapd_addr_udp; + msg.msg_namelen = sizeof(drv->hostapd_addr_udp); + } else { +#ifdef DRIVER_TEST_UNIX + msg.msg_name = &drv->hostapd_addr; + msg.msg_namelen = sizeof(drv->hostapd_addr); +#endif /* DRIVER_TEST_UNIX */ + } + } else if (os_memcmp(dest, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0) + { + dir = opendir(drv->test_dir); + if (dir == NULL) + return -1; + while ((dent = readdir(dir))) { +#ifdef _DIRENT_HAVE_D_TYPE + /* Skip the file if it is not a socket. + * Also accept DT_UNKNOWN (0) in case + * the C library or underlying file + * system does not support d_type. */ + if (dent->d_type != DT_SOCK && + dent->d_type != DT_UNKNOWN) + continue; +#endif /* _DIRENT_HAVE_D_TYPE */ + if (os_strcmp(dent->d_name, ".") == 0 || + os_strcmp(dent->d_name, "..") == 0) + continue; + wpa_printf(MSG_DEBUG, "%s: Send broadcast MLME to %s", + __func__, dent->d_name); + os_memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + os_snprintf(addr.sun_path, sizeof(addr.sun_path), + "%s/%s", drv->test_dir, dent->d_name); + + msg.msg_name = &addr; + msg.msg_namelen = sizeof(addr); + + if (sendmsg(drv->test_socket, &msg, 0) < 0) + perror("sendmsg(test_socket)"); + } + closedir(dir); + return 0; + } else { + struct stat st; + os_memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + os_snprintf(addr.sun_path, sizeof(addr.sun_path), + "%s/AP-" MACSTR, drv->test_dir, MAC2STR(dest)); + if (stat(addr.sun_path, &st) < 0) { + os_snprintf(addr.sun_path, sizeof(addr.sun_path), + "%s/STA-" MACSTR, + drv->test_dir, MAC2STR(dest)); + } + msg.msg_name = &addr; + msg.msg_namelen = sizeof(addr); + } + + if (sendmsg(drv->test_socket, &msg, 0) < 0) { + perror("sendmsg(test_socket)"); + return -1; + } + + return 0; +} + + +static int wpa_driver_test_mlme_add_sta(void *priv, const u8 *addr, + const u8 *supp_rates, + size_t supp_rates_len) +{ + wpa_printf(MSG_DEBUG, "%s: addr=" MACSTR, __func__, MAC2STR(addr)); + return 0; +} + + +static int wpa_driver_test_mlme_remove_sta(void *priv, const u8 *addr) +{ + wpa_printf(MSG_DEBUG, "%s: addr=" MACSTR, __func__, MAC2STR(addr)); + return 0; +} + + +static int wpa_driver_test_set_ssid(void *priv, const u8 *ssid, + size_t ssid_len) +{ + wpa_printf(MSG_DEBUG, "%s", __func__); + return 0; +} + + +static int wpa_driver_test_set_bssid(void *priv, const u8 *bssid) +{ + wpa_printf(MSG_DEBUG, "%s: bssid=" MACSTR, __func__, MAC2STR(bssid)); + return 0; +} +#endif /* CONFIG_CLIENT_MLME */ + + +static int wpa_driver_test_set_probe_req_ie(void *priv, const u8 *ies, + size_t ies_len) +{ + struct wpa_driver_test_data *drv = priv; + + os_free(drv->probe_req_ie); + if (ies) { + drv->probe_req_ie = os_malloc(ies_len); + if (drv->probe_req_ie == NULL) { + drv->probe_req_ie_len = 0; + return -1; + } + os_memcpy(drv->probe_req_ie, ies, ies_len); + drv->probe_req_ie_len = ies_len; + } else { + drv->probe_req_ie = NULL; + drv->probe_req_ie_len = 0; + } + return 0; +} + + +static void * wpa_driver_test_global_init(void) +{ + struct wpa_driver_test_global *global; + + global = os_zalloc(sizeof(*global)); + return global; +} + + +static void wpa_driver_test_global_deinit(void *priv) +{ + struct wpa_driver_test_global *global = priv; + os_free(global); +} + + +static struct wpa_interface_info * +wpa_driver_test_get_interfaces(void *global_priv) +{ + /* struct wpa_driver_test_global *global = priv; */ + struct wpa_interface_info *iface; + + iface = os_zalloc(sizeof(*iface)); + if (iface == NULL) + return iface; + iface->ifname = os_strdup("sta0"); + iface->desc = os_strdup("test interface 0"); + iface->drv_name = "test"; + iface->next = os_zalloc(sizeof(*iface)); + if (iface->next) { + iface->next->ifname = os_strdup("sta1"); + iface->next->desc = os_strdup("test interface 1"); + iface->next->drv_name = "test"; + } + + return iface; +} + + +const struct wpa_driver_ops wpa_driver_test_ops = { + "test", + "wpa_supplicant test driver", + wpa_driver_test_get_bssid, + wpa_driver_test_get_ssid, + wpa_driver_test_set_wpa, + wpa_driver_test_set_key, + NULL /* init */, + wpa_driver_test_deinit, + wpa_driver_test_set_param, + NULL /* set_countermeasures */, + NULL /* set_drop_unencrypted */, + wpa_driver_test_scan, + NULL /* get_scan_results */, + wpa_driver_test_deauthenticate, + wpa_driver_test_disassociate, + wpa_driver_test_associate, + NULL /* set_auth_alg */, + NULL /* add_pmkid */, + NULL /* remove_pmkid */, + NULL /* flush_pmkid */, + wpa_driver_test_get_capa, + NULL /* poll */, + NULL /* get_ifname */, + wpa_driver_test_get_mac_addr, + wpa_driver_test_send_eapol, + NULL /* set_operstate */, + wpa_driver_test_mlme_setprotection, +#ifdef CONFIG_CLIENT_MLME + wpa_driver_test_get_hw_feature_data, + wpa_driver_test_set_channel, + wpa_driver_test_set_ssid, + wpa_driver_test_set_bssid, + wpa_driver_test_send_mlme, + wpa_driver_test_mlme_add_sta, + wpa_driver_test_mlme_remove_sta, +#else /* CONFIG_CLIENT_MLME */ + NULL /* get_hw_feature_data */, + NULL /* set_channel */, + NULL /* set_ssid */, + NULL /* set_bssid */, + NULL /* send_mlme */, + NULL /* mlme_add_sta */, + NULL /* mlme_remove_sta */, +#endif /* CONFIG_CLIENT_MLME */ + NULL /* update_ft_ies */, + NULL /* send_ft_action */, + wpa_driver_test_get_scan_results2, + wpa_driver_test_set_probe_req_ie, + NULL /* set_mode */, + NULL /* set_country */, + wpa_driver_test_global_init, + wpa_driver_test_global_deinit, + wpa_driver_test_init2, + wpa_driver_test_get_interfaces +}; diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c new file mode 100644 index 0000000..e771d37 --- /dev/null +++ b/src/drivers/driver_wext.c @@ -0,0 +1,2375 @@ +/* + * WPA Supplicant - driver interaction with generic Linux Wireless Extensions + * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * This file implements a driver interface for the Linux Wireless Extensions. + * When used with WE-18 or newer, this interface can be used as-is with number + * of drivers. In addition to this, some of the common functions in this file + * can be used by other driver interface implementations that use generic WE + * ioctls, but require private ioctls for some of the functionality. + */ + +#include "includes.h" +#include <sys/ioctl.h> +#include <net/if_arp.h> + +#include "wireless_copy.h" +#include "common.h" +#include "driver.h" +#include "eloop.h" +#include "priv_netlink.h" +#include "driver_wext.h" +#include "ieee802_11_defs.h" +#include "wpa_common.h" + + +static int wpa_driver_wext_flush_pmkid(void *priv); +static int wpa_driver_wext_get_range(void *priv); +static void wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv); +static void wpa_driver_wext_disconnect(struct wpa_driver_wext_data *drv); + + +static int wpa_driver_wext_send_oper_ifla(struct wpa_driver_wext_data *drv, + int linkmode, int operstate) +{ + struct { + struct nlmsghdr hdr; + struct ifinfomsg ifinfo; + char opts[16]; + } req; + struct rtattr *rta; + static int nl_seq; + ssize_t ret; + + os_memset(&req, 0, sizeof(req)); + + req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + req.hdr.nlmsg_type = RTM_SETLINK; + req.hdr.nlmsg_flags = NLM_F_REQUEST; + req.hdr.nlmsg_seq = ++nl_seq; + req.hdr.nlmsg_pid = 0; + + req.ifinfo.ifi_family = AF_UNSPEC; + req.ifinfo.ifi_type = 0; + req.ifinfo.ifi_index = drv->ifindex; + req.ifinfo.ifi_flags = 0; + req.ifinfo.ifi_change = 0; + + if (linkmode != -1) { + rta = aliasing_hide_typecast( + ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)), + struct rtattr); + rta->rta_type = IFLA_LINKMODE; + rta->rta_len = RTA_LENGTH(sizeof(char)); + *((char *) RTA_DATA(rta)) = linkmode; + req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + + RTA_LENGTH(sizeof(char)); + } + if (operstate != -1) { + rta = (struct rtattr *) + ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)); + rta->rta_type = IFLA_OPERSTATE; + rta->rta_len = RTA_LENGTH(sizeof(char)); + *((char *) RTA_DATA(rta)) = operstate; + req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + + RTA_LENGTH(sizeof(char)); + } + + wpa_printf(MSG_DEBUG, "WEXT: Operstate: linkmode=%d, operstate=%d", + linkmode, operstate); + + ret = send(drv->event_sock, &req, req.hdr.nlmsg_len, 0); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "WEXT: Sending operstate IFLA failed: " + "%s (assume operstate is not supported)", + strerror(errno)); + } + + return ret < 0 ? -1 : 0; +} + + +int wpa_driver_wext_set_auth_param(struct wpa_driver_wext_data *drv, + int idx, u32 value) +{ + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.param.flags = idx & IW_AUTH_INDEX; + iwr.u.param.value = value; + + if (ioctl(drv->ioctl_sock, SIOCSIWAUTH, &iwr) < 0) { + if (errno != EOPNOTSUPP) { + wpa_printf(MSG_DEBUG, "WEXT: SIOCSIWAUTH(param %d " + "value 0x%x) failed: %s)", + idx, value, strerror(errno)); + } + ret = errno == EOPNOTSUPP ? -2 : -1; + } + + return ret; +} + + +/** + * wpa_driver_wext_get_bssid - Get BSSID, SIOCGIWAP + * @priv: Pointer to private wext data from wpa_driver_wext_init() + * @bssid: Buffer for BSSID + * Returns: 0 on success, -1 on failure + */ +int wpa_driver_wext_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_wext_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + + if (ioctl(drv->ioctl_sock, SIOCGIWAP, &iwr) < 0) { + perror("ioctl[SIOCGIWAP]"); + ret = -1; + } + os_memcpy(bssid, iwr.u.ap_addr.sa_data, ETH_ALEN); + + return ret; +} + + +/** + * wpa_driver_wext_set_bssid - Set BSSID, SIOCSIWAP + * @priv: Pointer to private wext data from wpa_driver_wext_init() + * @bssid: BSSID + * Returns: 0 on success, -1 on failure + */ +int wpa_driver_wext_set_bssid(void *priv, const u8 *bssid) +{ + struct wpa_driver_wext_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.ap_addr.sa_family = ARPHRD_ETHER; + if (bssid) + os_memcpy(iwr.u.ap_addr.sa_data, bssid, ETH_ALEN); + else + os_memset(iwr.u.ap_addr.sa_data, 0, ETH_ALEN); + + if (ioctl(drv->ioctl_sock, SIOCSIWAP, &iwr) < 0) { + perror("ioctl[SIOCSIWAP]"); + ret = -1; + } + + return ret; +} + + +/** + * wpa_driver_wext_get_ssid - Get SSID, SIOCGIWESSID + * @priv: Pointer to private wext data from wpa_driver_wext_init() + * @ssid: Buffer for the SSID; must be at least 32 bytes long + * Returns: SSID length on success, -1 on failure + */ +int wpa_driver_wext_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_wext_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.essid.pointer = (caddr_t) ssid; + iwr.u.essid.length = 32; + + if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) { + perror("ioctl[SIOCGIWESSID]"); + ret = -1; + } else { + ret = iwr.u.essid.length; + if (ret > 32) + ret = 32; + /* Some drivers include nul termination in the SSID, so let's + * remove it here before further processing. WE-21 changes this + * to explicitly require the length _not_ to include nul + * termination. */ + if (ret > 0 && ssid[ret - 1] == '\0' && + drv->we_version_compiled < 21) + ret--; + } + + return ret; +} + + +/** + * wpa_driver_wext_set_ssid - Set SSID, SIOCSIWESSID + * @priv: Pointer to private wext data from wpa_driver_wext_init() + * @ssid: SSID + * @ssid_len: Length of SSID (0..32) + * Returns: 0 on success, -1 on failure + */ +int wpa_driver_wext_set_ssid(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_wext_data *drv = priv; + struct iwreq iwr; + int ret = 0; + char buf[33]; + + if (ssid_len > 32) + return -1; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + /* flags: 1 = ESSID is active, 0 = not (promiscuous) */ + iwr.u.essid.flags = (ssid_len != 0); + os_memset(buf, 0, sizeof(buf)); + os_memcpy(buf, ssid, ssid_len); + iwr.u.essid.pointer = (caddr_t) buf; + if (drv->we_version_compiled < 21) { + /* For historic reasons, set SSID length to include one extra + * character, C string nul termination, even though SSID is + * really an octet string that should not be presented as a C + * string. Some Linux drivers decrement the length by one and + * can thus end up missing the last octet of the SSID if the + * length is not incremented here. WE-21 changes this to + * explicitly require the length _not_ to include nul + * termination. */ + if (ssid_len) + ssid_len++; + } + iwr.u.essid.length = ssid_len; + + if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) { + perror("ioctl[SIOCSIWESSID]"); + ret = -1; + } + + return ret; +} + + +/** + * wpa_driver_wext_set_freq - Set frequency/channel, SIOCSIWFREQ + * @priv: Pointer to private wext data from wpa_driver_wext_init() + * @freq: Frequency in MHz + * Returns: 0 on success, -1 on failure + */ +int wpa_driver_wext_set_freq(void *priv, int freq) +{ + struct wpa_driver_wext_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.freq.m = freq * 100000; + iwr.u.freq.e = 1; + + if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) { + perror("ioctl[SIOCSIWFREQ]"); + ret = -1; + } + + return ret; +} + + +static void +wpa_driver_wext_event_wireless_custom(void *ctx, char *custom) +{ + union wpa_event_data data; + + wpa_printf(MSG_MSGDUMP, "WEXT: Custom wireless event: '%s'", + custom); + + os_memset(&data, 0, sizeof(data)); + /* Host AP driver */ + if (os_strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) { + data.michael_mic_failure.unicast = + os_strstr(custom, " unicast ") != NULL; + /* TODO: parse parameters(?) */ + wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); + } else if (os_strncmp(custom, "ASSOCINFO(ReqIEs=", 17) == 0) { + char *spos; + int bytes; + + spos = custom + 17; + + bytes = strspn(spos, "0123456789abcdefABCDEF"); + if (!bytes || (bytes & 1)) + return; + bytes /= 2; + + data.assoc_info.req_ies = os_malloc(bytes); + if (data.assoc_info.req_ies == NULL) + return; + + data.assoc_info.req_ies_len = bytes; + hexstr2bin(spos, data.assoc_info.req_ies, bytes); + + spos += bytes * 2; + + data.assoc_info.resp_ies = NULL; + data.assoc_info.resp_ies_len = 0; + + if (os_strncmp(spos, " RespIEs=", 9) == 0) { + spos += 9; + + bytes = strspn(spos, "0123456789abcdefABCDEF"); + if (!bytes || (bytes & 1)) + goto done; + bytes /= 2; + + data.assoc_info.resp_ies = os_malloc(bytes); + if (data.assoc_info.resp_ies == NULL) + goto done; + + data.assoc_info.resp_ies_len = bytes; + hexstr2bin(spos, data.assoc_info.resp_ies, bytes); + } + + wpa_supplicant_event(ctx, EVENT_ASSOCINFO, &data); + + done: + os_free(data.assoc_info.resp_ies); + os_free(data.assoc_info.req_ies); +#ifdef CONFIG_PEERKEY + } else if (os_strncmp(custom, "STKSTART.request=", 17) == 0) { + if (hwaddr_aton(custom + 17, data.stkstart.peer)) { + wpa_printf(MSG_DEBUG, "WEXT: unrecognized " + "STKSTART.request '%s'", custom + 17); + return; + } + wpa_supplicant_event(ctx, EVENT_STKSTART, &data); +#endif /* CONFIG_PEERKEY */ + } +} + + +static int wpa_driver_wext_event_wireless_michaelmicfailure( + void *ctx, const char *ev, size_t len) +{ + const struct iw_michaelmicfailure *mic; + union wpa_event_data data; + + if (len < sizeof(*mic)) + return -1; + + mic = (const struct iw_michaelmicfailure *) ev; + + wpa_printf(MSG_DEBUG, "Michael MIC failure wireless event: " + "flags=0x%x src_addr=" MACSTR, mic->flags, + MAC2STR(mic->src_addr.sa_data)); + + os_memset(&data, 0, sizeof(data)); + data.michael_mic_failure.unicast = !(mic->flags & IW_MICFAILURE_GROUP); + wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); + + return 0; +} + + +static int wpa_driver_wext_event_wireless_pmkidcand( + struct wpa_driver_wext_data *drv, const char *ev, size_t len) +{ + const struct iw_pmkid_cand *cand; + union wpa_event_data data; + const u8 *addr; + + if (len < sizeof(*cand)) + return -1; + + cand = (const struct iw_pmkid_cand *) ev; + addr = (const u8 *) cand->bssid.sa_data; + + wpa_printf(MSG_DEBUG, "PMKID candidate wireless event: " + "flags=0x%x index=%d bssid=" MACSTR, cand->flags, + cand->index, MAC2STR(addr)); + + os_memset(&data, 0, sizeof(data)); + os_memcpy(data.pmkid_candidate.bssid, addr, ETH_ALEN); + data.pmkid_candidate.index = cand->index; + data.pmkid_candidate.preauth = cand->flags & IW_PMKID_CAND_PREAUTH; + wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE, &data); + + return 0; +} + + +static int wpa_driver_wext_event_wireless_assocreqie( + struct wpa_driver_wext_data *drv, const char *ev, int len) +{ + if (len < 0) + return -1; + + wpa_hexdump(MSG_DEBUG, "AssocReq IE wireless event", (const u8 *) ev, + len); + os_free(drv->assoc_req_ies); + drv->assoc_req_ies = os_malloc(len); + if (drv->assoc_req_ies == NULL) { + drv->assoc_req_ies_len = 0; + return -1; + } + os_memcpy(drv->assoc_req_ies, ev, len); + drv->assoc_req_ies_len = len; + + return 0; +} + + +static int wpa_driver_wext_event_wireless_assocrespie( + struct wpa_driver_wext_data *drv, const char *ev, int len) +{ + if (len < 0) + return -1; + + wpa_hexdump(MSG_DEBUG, "AssocResp IE wireless event", (const u8 *) ev, + len); + os_free(drv->assoc_resp_ies); + drv->assoc_resp_ies = os_malloc(len); + if (drv->assoc_resp_ies == NULL) { + drv->assoc_resp_ies_len = 0; + return -1; + } + os_memcpy(drv->assoc_resp_ies, ev, len); + drv->assoc_resp_ies_len = len; + + return 0; +} + + +static void wpa_driver_wext_event_assoc_ies(struct wpa_driver_wext_data *drv) +{ + union wpa_event_data data; + + if (drv->assoc_req_ies == NULL && drv->assoc_resp_ies == NULL) + return; + + os_memset(&data, 0, sizeof(data)); + if (drv->assoc_req_ies) { + data.assoc_info.req_ies = drv->assoc_req_ies; + drv->assoc_req_ies = NULL; + data.assoc_info.req_ies_len = drv->assoc_req_ies_len; + } + if (drv->assoc_resp_ies) { + data.assoc_info.resp_ies = drv->assoc_resp_ies; + drv->assoc_resp_ies = NULL; + data.assoc_info.resp_ies_len = drv->assoc_resp_ies_len; + } + + wpa_supplicant_event(drv->ctx, EVENT_ASSOCINFO, &data); + + os_free(data.assoc_info.req_ies); + os_free(data.assoc_info.resp_ies); +} + + +static void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv, + void *ctx, char *data, int len) +{ + struct iw_event iwe_buf, *iwe = &iwe_buf; + char *pos, *end, *custom, *buf; + + pos = data; + end = data + len; + + while (pos + IW_EV_LCP_LEN <= end) { + /* Event data may be unaligned, so make a local, aligned copy + * before processing. */ + os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); + wpa_printf(MSG_DEBUG, "Wireless event: cmd=0x%x len=%d", + iwe->cmd, iwe->len); + if (iwe->len <= IW_EV_LCP_LEN) + return; + + custom = pos + IW_EV_POINT_LEN; + if (drv->we_version_compiled > 18 && + (iwe->cmd == IWEVMICHAELMICFAILURE || + iwe->cmd == IWEVCUSTOM || + iwe->cmd == IWEVASSOCREQIE || + iwe->cmd == IWEVASSOCRESPIE || + iwe->cmd == IWEVPMKIDCAND)) { + /* WE-19 removed the pointer from struct iw_point */ + char *dpos = (char *) &iwe_buf.u.data.length; + int dlen = dpos - (char *) &iwe_buf; + os_memcpy(dpos, pos + IW_EV_LCP_LEN, + sizeof(struct iw_event) - dlen); + } else { + os_memcpy(&iwe_buf, pos, sizeof(struct iw_event)); + custom += IW_EV_POINT_OFF; + } + + switch (iwe->cmd) { + case SIOCGIWAP: + wpa_printf(MSG_DEBUG, "Wireless event: new AP: " + MACSTR, + MAC2STR((u8 *) iwe->u.ap_addr.sa_data)); + if (is_zero_ether_addr( + (const u8 *) iwe->u.ap_addr.sa_data) || + os_memcmp(iwe->u.ap_addr.sa_data, + "\x44\x44\x44\x44\x44\x44", ETH_ALEN) == + 0) { + os_free(drv->assoc_req_ies); + drv->assoc_req_ies = NULL; + os_free(drv->assoc_resp_ies); + drv->assoc_resp_ies = NULL; + wpa_supplicant_event(ctx, EVENT_DISASSOC, + NULL); + + } else { + wpa_driver_wext_event_assoc_ies(drv); + wpa_supplicant_event(ctx, EVENT_ASSOC, NULL); + } + break; + case IWEVMICHAELMICFAILURE: + if (custom + iwe->u.data.length > end) { + wpa_printf(MSG_DEBUG, "WEXT: Invalid " + "IWEVMICHAELMICFAILURE length"); + return; + } + wpa_driver_wext_event_wireless_michaelmicfailure( + ctx, custom, iwe->u.data.length); + break; + case IWEVCUSTOM: + if (custom + iwe->u.data.length > end) { + wpa_printf(MSG_DEBUG, "WEXT: Invalid " + "IWEVCUSTOM length"); + return; + } + buf = os_malloc(iwe->u.data.length + 1); + if (buf == NULL) + return; + os_memcpy(buf, custom, iwe->u.data.length); + buf[iwe->u.data.length] = '\0'; + wpa_driver_wext_event_wireless_custom(ctx, buf); + os_free(buf); + break; + case SIOCGIWSCAN: + drv->scan_complete_events = 1; + eloop_cancel_timeout(wpa_driver_wext_scan_timeout, + drv, ctx); + wpa_supplicant_event(ctx, EVENT_SCAN_RESULTS, NULL); + break; + case IWEVASSOCREQIE: + if (custom + iwe->u.data.length > end) { + wpa_printf(MSG_DEBUG, "WEXT: Invalid " + "IWEVASSOCREQIE length"); + return; + } + wpa_driver_wext_event_wireless_assocreqie( + drv, custom, iwe->u.data.length); + break; + case IWEVASSOCRESPIE: + if (custom + iwe->u.data.length > end) { + wpa_printf(MSG_DEBUG, "WEXT: Invalid " + "IWEVASSOCRESPIE length"); + return; + } + wpa_driver_wext_event_wireless_assocrespie( + drv, custom, iwe->u.data.length); + break; + case IWEVPMKIDCAND: + if (custom + iwe->u.data.length > end) { + wpa_printf(MSG_DEBUG, "WEXT: Invalid " + "IWEVPMKIDCAND length"); + return; + } + wpa_driver_wext_event_wireless_pmkidcand( + drv, custom, iwe->u.data.length); + break; + } + + pos += iwe->len; + } +} + + +static void wpa_driver_wext_event_link(struct wpa_driver_wext_data *drv, + void *ctx, char *buf, size_t len, + int del) +{ + union wpa_event_data event; + + os_memset(&event, 0, sizeof(event)); + if (len > sizeof(event.interface_status.ifname)) + len = sizeof(event.interface_status.ifname) - 1; + os_memcpy(event.interface_status.ifname, buf, len); + event.interface_status.ievent = del ? EVENT_INTERFACE_REMOVED : + EVENT_INTERFACE_ADDED; + + wpa_printf(MSG_DEBUG, "RTM_%sLINK, IFLA_IFNAME: Interface '%s' %s", + del ? "DEL" : "NEW", + event.interface_status.ifname, + del ? "removed" : "added"); + + if (os_strcmp(drv->ifname, event.interface_status.ifname) == 0) { + if (del) + drv->if_removed = 1; + else + drv->if_removed = 0; + } + + wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event); +} + + +static int wpa_driver_wext_own_ifname(struct wpa_driver_wext_data *drv, + struct nlmsghdr *h) +{ + struct ifinfomsg *ifi; + int attrlen, nlmsg_len, rta_len; + struct rtattr *attr; + + ifi = NLMSG_DATA(h); + + nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); + + attrlen = h->nlmsg_len - nlmsg_len; + if (attrlen < 0) + return 0; + + attr = (struct rtattr *) (((char *) ifi) + nlmsg_len); + + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + if (attr->rta_type == IFLA_IFNAME) { + if (os_strcmp(((char *) attr) + rta_len, drv->ifname) + == 0) + return 1; + else + break; + } + attr = RTA_NEXT(attr, attrlen); + } + + return 0; +} + + +static int wpa_driver_wext_own_ifindex(struct wpa_driver_wext_data *drv, + int ifindex, struct nlmsghdr *h) +{ + if (drv->ifindex == ifindex || drv->ifindex2 == ifindex) + return 1; + + if (drv->if_removed && wpa_driver_wext_own_ifname(drv, h)) { + drv->ifindex = if_nametoindex(drv->ifname); + wpa_printf(MSG_DEBUG, "WEXT: Update ifindex for a removed " + "interface"); + wpa_driver_wext_finish_drv_init(drv); + return 1; + } + + return 0; +} + + +static void wpa_driver_wext_event_rtm_newlink(struct wpa_driver_wext_data *drv, + void *ctx, struct nlmsghdr *h, + size_t len) +{ + struct ifinfomsg *ifi; + int attrlen, nlmsg_len, rta_len; + struct rtattr * attr; + + if (len < sizeof(*ifi)) + return; + + ifi = NLMSG_DATA(h); + + if (!wpa_driver_wext_own_ifindex(drv, ifi->ifi_index, h)) { + wpa_printf(MSG_DEBUG, "Ignore event for foreign ifindex %d", + ifi->ifi_index); + return; + } + + wpa_printf(MSG_DEBUG, "RTM_NEWLINK: operstate=%d ifi_flags=0x%x " + "(%s%s%s%s)", + drv->operstate, ifi->ifi_flags, + (ifi->ifi_flags & IFF_UP) ? "[UP]" : "", + (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "", + (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "", + (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : ""); + /* + * Some drivers send the association event before the operup event--in + * this case, lifting operstate in wpa_driver_wext_set_operstate() + * fails. This will hit us when wpa_supplicant does not need to do + * IEEE 802.1X authentication + */ + if (drv->operstate == 1 && + (ifi->ifi_flags & (IFF_LOWER_UP | IFF_DORMANT)) == IFF_LOWER_UP && + !(ifi->ifi_flags & IFF_RUNNING)) + wpa_driver_wext_send_oper_ifla(drv, -1, IF_OPER_UP); + + nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); + + attrlen = h->nlmsg_len - nlmsg_len; + if (attrlen < 0) + return; + + attr = (struct rtattr *) (((char *) ifi) + nlmsg_len); + + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + if (attr->rta_type == IFLA_WIRELESS) { + wpa_driver_wext_event_wireless( + drv, ctx, ((char *) attr) + rta_len, + attr->rta_len - rta_len); + } else if (attr->rta_type == IFLA_IFNAME) { + wpa_driver_wext_event_link(drv, ctx, + ((char *) attr) + rta_len, + attr->rta_len - rta_len, 0); + } + attr = RTA_NEXT(attr, attrlen); + } +} + + +static void wpa_driver_wext_event_rtm_dellink(struct wpa_driver_wext_data *drv, + void *ctx, struct nlmsghdr *h, + size_t len) +{ + struct ifinfomsg *ifi; + int attrlen, nlmsg_len, rta_len; + struct rtattr * attr; + + if (len < sizeof(*ifi)) + return; + + ifi = NLMSG_DATA(h); + + nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); + + attrlen = h->nlmsg_len - nlmsg_len; + if (attrlen < 0) + return; + + attr = (struct rtattr *) (((char *) ifi) + nlmsg_len); + + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + if (attr->rta_type == IFLA_IFNAME) { + wpa_driver_wext_event_link(drv, ctx, + ((char *) attr) + rta_len, + attr->rta_len - rta_len, 1); + } + attr = RTA_NEXT(attr, attrlen); + } +} + + +static void wpa_driver_wext_event_receive(int sock, void *eloop_ctx, + void *sock_ctx) +{ + char buf[8192]; + int left; + struct sockaddr_nl from; + socklen_t fromlen; + struct nlmsghdr *h; + int max_events = 10; + +try_again: + fromlen = sizeof(from); + left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, + (struct sockaddr *) &from, &fromlen); + if (left < 0) { + if (errno != EINTR && errno != EAGAIN) + perror("recvfrom(netlink)"); + return; + } + + h = (struct nlmsghdr *) buf; + while (left >= (int) sizeof(*h)) { + int len, plen; + + len = h->nlmsg_len; + plen = len - sizeof(*h); + if (len > left || plen < 0) { + wpa_printf(MSG_DEBUG, "Malformed netlink message: " + "len=%d left=%d plen=%d", + len, left, plen); + break; + } + + switch (h->nlmsg_type) { + case RTM_NEWLINK: + wpa_driver_wext_event_rtm_newlink(eloop_ctx, sock_ctx, + h, plen); + break; + case RTM_DELLINK: + wpa_driver_wext_event_rtm_dellink(eloop_ctx, sock_ctx, + h, plen); + break; + } + + len = NLMSG_ALIGN(len); + left -= len; + h = (struct nlmsghdr *) ((char *) h + len); + } + + if (left > 0) { + wpa_printf(MSG_DEBUG, "%d extra bytes in the end of netlink " + "message", left); + } + + if (--max_events > 0) { + /* + * Try to receive all events in one eloop call in order to + * limit race condition on cases where AssocInfo event, Assoc + * event, and EAPOL frames are received more or less at the + * same time. We want to process the event messages first + * before starting EAPOL processing. + */ + goto try_again; + } +} + + +static int wpa_driver_wext_get_ifflags_ifname(struct wpa_driver_wext_data *drv, + const char *ifname, int *flags) +{ + struct ifreq ifr; + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + if (ioctl(drv->ioctl_sock, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) { + perror("ioctl[SIOCGIFFLAGS]"); + return -1; + } + *flags = ifr.ifr_flags & 0xffff; + return 0; +} + + +/** + * wpa_driver_wext_get_ifflags - Get interface flags (SIOCGIFFLAGS) + * @drv: driver_wext private data + * @flags: Pointer to returned flags value + * Returns: 0 on success, -1 on failure + */ +int wpa_driver_wext_get_ifflags(struct wpa_driver_wext_data *drv, int *flags) +{ + return wpa_driver_wext_get_ifflags_ifname(drv, drv->ifname, flags); +} + + +static int wpa_driver_wext_set_ifflags_ifname(struct wpa_driver_wext_data *drv, + const char *ifname, int flags) +{ + struct ifreq ifr; + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + ifr.ifr_flags = flags & 0xffff; + if (ioctl(drv->ioctl_sock, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { + perror("SIOCSIFFLAGS"); + return -1; + } + return 0; +} + + +/** + * wpa_driver_wext_set_ifflags - Set interface flags (SIOCSIFFLAGS) + * @drv: driver_wext private data + * @flags: New value for flags + * Returns: 0 on success, -1 on failure + */ +int wpa_driver_wext_set_ifflags(struct wpa_driver_wext_data *drv, int flags) +{ + return wpa_driver_wext_set_ifflags_ifname(drv, drv->ifname, flags); +} + + +/** + * wpa_driver_wext_init - Initialize WE driver interface + * @ctx: context to be used when calling wpa_supplicant functions, + * e.g., wpa_supplicant_event() + * @ifname: interface name, e.g., wlan0 + * Returns: Pointer to private data, %NULL on failure + */ +void * wpa_driver_wext_init(void *ctx, const char *ifname) +{ + int s; + struct sockaddr_nl local; + struct wpa_driver_wext_data *drv; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->ctx = ctx; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + + drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->ioctl_sock < 0) { + perror("socket(PF_INET,SOCK_DGRAM)"); + os_free(drv); + return NULL; + } + + s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (s < 0) { + perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)"); + close(drv->ioctl_sock); + os_free(drv); + return NULL; + } + + os_memset(&local, 0, sizeof(local)); + local.nl_family = AF_NETLINK; + local.nl_groups = RTMGRP_LINK; + if (bind(s, (struct sockaddr *) &local, sizeof(local)) < 0) { + perror("bind(netlink)"); + close(s); + close(drv->ioctl_sock); + os_free(drv); + return NULL; + } + + eloop_register_read_sock(s, wpa_driver_wext_event_receive, drv, ctx); + drv->event_sock = s; + + drv->mlme_sock = -1; + + wpa_driver_wext_finish_drv_init(drv); + + return drv; +} + + +static void wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv) +{ + int flags; + + if (wpa_driver_wext_get_ifflags(drv, &flags) != 0) + printf("Could not get interface '%s' flags\n", drv->ifname); + else if (!(flags & IFF_UP)) { + if (wpa_driver_wext_set_ifflags(drv, flags | IFF_UP) != 0) { + printf("Could not set interface '%s' UP\n", + drv->ifname); + } else { + /* + * Wait some time to allow driver to initialize before + * starting configuring the driver. This seems to be + * needed at least some drivers that load firmware etc. + * when the interface is set up. + */ + wpa_printf(MSG_DEBUG, "Interface %s set UP - waiting " + "a second for the driver to complete " + "initialization", drv->ifname); + sleep(1); + } + } + + /* + * Make sure that the driver does not have any obsolete PMKID entries. + */ + wpa_driver_wext_flush_pmkid(drv); + + if (wpa_driver_wext_set_mode(drv, 0) < 0) { + printf("Could not configure driver to use managed mode\n"); + } + + wpa_driver_wext_get_range(drv); + + /* + * Unlock the driver's BSSID and force to a random SSID to clear any + * previous association the driver might have when the supplicant + * starts up. + */ + wpa_driver_wext_disconnect(drv); + + drv->ifindex = if_nametoindex(drv->ifname); + + if (os_strncmp(drv->ifname, "wlan", 4) == 0) { + /* + * Host AP driver may use both wlan# and wifi# interface in + * wireless events. Since some of the versions included WE-18 + * support, let's add the alternative ifindex also from + * driver_wext.c for the time being. This may be removed at + * some point once it is believed that old versions of the + * driver are not in use anymore. + */ + char ifname2[IFNAMSIZ + 1]; + os_strlcpy(ifname2, drv->ifname, sizeof(ifname2)); + os_memcpy(ifname2, "wifi", 4); + wpa_driver_wext_alternative_ifindex(drv, ifname2); + } + + wpa_driver_wext_send_oper_ifla(drv, 1, IF_OPER_DORMANT); +} + + +/** + * wpa_driver_wext_deinit - Deinitialize WE driver interface + * @priv: Pointer to private wext data from wpa_driver_wext_init() + * + * Shut down driver interface and processing of driver events. Free + * private data buffer if one was allocated in wpa_driver_wext_init(). + */ +void wpa_driver_wext_deinit(void *priv) +{ + struct wpa_driver_wext_data *drv = priv; + int flags; + + eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv, drv->ctx); + + /* + * Clear possibly configured driver parameters in order to make it + * easier to use the driver after wpa_supplicant has been terminated. + */ + wpa_driver_wext_disconnect(drv); + + wpa_driver_wext_send_oper_ifla(priv, 0, IF_OPER_UP); + + eloop_unregister_read_sock(drv->event_sock); + if (drv->mlme_sock >= 0) + eloop_unregister_read_sock(drv->mlme_sock); + + if (wpa_driver_wext_get_ifflags(drv, &flags) == 0) + (void) wpa_driver_wext_set_ifflags(drv, flags & ~IFF_UP); + + close(drv->event_sock); + close(drv->ioctl_sock); + if (drv->mlme_sock >= 0) + close(drv->mlme_sock); + os_free(drv->assoc_req_ies); + os_free(drv->assoc_resp_ies); + os_free(drv); +} + + +/** + * wpa_driver_wext_scan_timeout - Scan timeout to report scan completion + * @eloop_ctx: Unused + * @timeout_ctx: ctx argument given to wpa_driver_wext_init() + * + * This function can be used as registered timeout when starting a scan to + * generate a scan completed event if the driver does not report this. + */ +void wpa_driver_wext_scan_timeout(void *eloop_ctx, void *timeout_ctx) +{ + wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); + wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); +} + + +/** + * wpa_driver_wext_scan - Request the driver to initiate scan + * @priv: Pointer to private wext data from wpa_driver_wext_init() + * @ssid: Specific SSID to scan for (ProbeReq) or %NULL to scan for + * all SSIDs (either active scan with broadcast SSID or passive + * scan + * @ssid_len: Length of the SSID + * Returns: 0 on success, -1 on failure + */ +int wpa_driver_wext_scan(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_wext_data *drv = priv; + struct iwreq iwr; + int ret = 0, timeout; + struct iw_scan_req req; + + if (ssid_len > IW_ESSID_MAX_SIZE) { + wpa_printf(MSG_DEBUG, "%s: too long SSID (%lu)", + __FUNCTION__, (unsigned long) ssid_len); + return -1; + } + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + + if (ssid && ssid_len) { + os_memset(&req, 0, sizeof(req)); + req.essid_len = ssid_len; + req.bssid.sa_family = ARPHRD_ETHER; + os_memset(req.bssid.sa_data, 0xff, ETH_ALEN); + os_memcpy(req.essid, ssid, ssid_len); + iwr.u.data.pointer = (caddr_t) &req; + iwr.u.data.length = sizeof(req); + iwr.u.data.flags = IW_SCAN_THIS_ESSID; + } + + if (ioctl(drv->ioctl_sock, SIOCSIWSCAN, &iwr) < 0) { + perror("ioctl[SIOCSIWSCAN]"); + ret = -1; + } + + /* Not all drivers generate "scan completed" wireless event, so try to + * read results after a timeout. */ + timeout = 5; + if (drv->scan_complete_events) { + /* + * The driver seems to deliver SIOCGIWSCAN events to notify + * when scan is complete, so use longer timeout to avoid race + * conditions with scanning and following association request. + */ + timeout = 30; + } + wpa_printf(MSG_DEBUG, "Scan requested (ret=%d) - scan timeout %d " + "seconds", ret, timeout); + eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv, drv->ctx); + eloop_register_timeout(timeout, 0, wpa_driver_wext_scan_timeout, drv, + drv->ctx); + + return ret; +} + + +static u8 * wpa_driver_wext_giwscan(struct wpa_driver_wext_data *drv, + size_t *len) +{ + struct iwreq iwr; + u8 *res_buf; + size_t res_buf_len; + + res_buf_len = IW_SCAN_MAX_DATA; + for (;;) { + res_buf = os_malloc(res_buf_len); + if (res_buf == NULL) + return NULL; + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = res_buf; + iwr.u.data.length = res_buf_len; + + if (ioctl(drv->ioctl_sock, SIOCGIWSCAN, &iwr) == 0) + break; + + if (errno == E2BIG && res_buf_len < 65535) { + os_free(res_buf); + res_buf = NULL; + res_buf_len *= 2; + if (res_buf_len > 65535) + res_buf_len = 65535; /* 16-bit length field */ + wpa_printf(MSG_DEBUG, "Scan results did not fit - " + "trying larger buffer (%lu bytes)", + (unsigned long) res_buf_len); + } else { + perror("ioctl[SIOCGIWSCAN]"); + os_free(res_buf); + return NULL; + } + } + + if (iwr.u.data.length > res_buf_len) { + os_free(res_buf); + return NULL; + } + *len = iwr.u.data.length; + + return res_buf; +} + + +/* + * Data structure for collecting WEXT scan results. This is needed to allow + * the various methods of reporting IEs to be combined into a single IE buffer. + */ +struct wext_scan_data { + struct wpa_scan_res res; + u8 *ie; + size_t ie_len; + u8 ssid[32]; + size_t ssid_len; + int maxrate; +}; + + +static void wext_get_scan_mode(struct iw_event *iwe, + struct wext_scan_data *res) +{ + if (iwe->u.mode == IW_MODE_ADHOC) + res->res.caps |= IEEE80211_CAP_IBSS; + else if (iwe->u.mode == IW_MODE_MASTER || iwe->u.mode == IW_MODE_INFRA) + res->res.caps |= IEEE80211_CAP_ESS; +} + + +static void wext_get_scan_ssid(struct iw_event *iwe, + struct wext_scan_data *res, char *custom, + char *end) +{ + int ssid_len = iwe->u.essid.length; + if (custom + ssid_len > end) + return; + if (iwe->u.essid.flags && + ssid_len > 0 && + ssid_len <= IW_ESSID_MAX_SIZE) { + os_memcpy(res->ssid, custom, ssid_len); + res->ssid_len = ssid_len; + } +} + + +static void wext_get_scan_freq(struct iw_event *iwe, + struct wext_scan_data *res) +{ + int divi = 1000000, i; + + if (iwe->u.freq.e == 0) { + /* + * Some drivers do not report frequency, but a channel. + * Try to map this to frequency by assuming they are using + * IEEE 802.11b/g. But don't overwrite a previously parsed + * frequency if the driver sends both frequency and channel, + * since the driver may be sending an A-band channel that we + * don't handle here. + */ + + if (res->res.freq) + return; + + if (iwe->u.freq.m >= 1 && iwe->u.freq.m <= 13) { + res->res.freq = 2407 + 5 * iwe->u.freq.m; + return; + } else if (iwe->u.freq.m == 14) { + res->res.freq = 2484; + return; + } + } + + if (iwe->u.freq.e > 6) { + wpa_printf(MSG_DEBUG, "Invalid freq in scan results (BSSID=" + MACSTR " m=%d e=%d)", + MAC2STR(res->res.bssid), iwe->u.freq.m, + iwe->u.freq.e); + return; + } + + for (i = 0; i < iwe->u.freq.e; i++) + divi /= 10; + res->res.freq = iwe->u.freq.m / divi; +} + + +static void wext_get_scan_qual(struct iw_event *iwe, + struct wext_scan_data *res) +{ + res->res.qual = iwe->u.qual.qual; + res->res.noise = iwe->u.qual.noise; + res->res.level = iwe->u.qual.level; +} + + +static void wext_get_scan_encode(struct iw_event *iwe, + struct wext_scan_data *res) +{ + if (!(iwe->u.data.flags & IW_ENCODE_DISABLED)) + res->res.caps |= IEEE80211_CAP_PRIVACY; +} + + +static void wext_get_scan_rate(struct iw_event *iwe, + struct wext_scan_data *res, char *pos, + char *end) +{ + int maxrate; + char *custom = pos + IW_EV_LCP_LEN; + struct iw_param p; + size_t clen; + + clen = iwe->len; + if (custom + clen > end) + return; + maxrate = 0; + while (((ssize_t) clen) >= (ssize_t) sizeof(struct iw_param)) { + /* Note: may be misaligned, make a local, aligned copy */ + os_memcpy(&p, custom, sizeof(struct iw_param)); + if (p.value > maxrate) + maxrate = p.value; + clen -= sizeof(struct iw_param); + custom += sizeof(struct iw_param); + } + + /* Convert the maxrate from WE-style (b/s units) to + * 802.11 rates (500000 b/s units). + */ + res->maxrate = maxrate / 500000; +} + + +static void wext_get_scan_iwevgenie(struct iw_event *iwe, + struct wext_scan_data *res, char *custom, + char *end) +{ + char *genie, *gpos, *gend; + u8 *tmp; + + if (iwe->u.data.length == 0) + return; + + gpos = genie = custom; + gend = genie + iwe->u.data.length; + if (gend > end) { + wpa_printf(MSG_INFO, "IWEVGENIE overflow"); + return; + } + + tmp = os_realloc(res->ie, res->ie_len + gend - gpos); + if (tmp == NULL) + return; + os_memcpy(tmp + res->ie_len, gpos, gend - gpos); + res->ie = tmp; + res->ie_len += gend - gpos; +} + + +static void wext_get_scan_custom(struct iw_event *iwe, + struct wext_scan_data *res, char *custom, + char *end) +{ + size_t clen; + u8 *tmp; + + clen = iwe->u.data.length; + if (custom + clen > end) + return; + + if (clen > 7 && os_strncmp(custom, "wpa_ie=", 7) == 0) { + char *spos; + int bytes; + spos = custom + 7; + bytes = custom + clen - spos; + if (bytes & 1 || bytes == 0) + return; + bytes /= 2; + tmp = os_realloc(res->ie, res->ie_len + bytes); + if (tmp == NULL) + return; + hexstr2bin(spos, tmp + res->ie_len, bytes); + res->ie = tmp; + res->ie_len += bytes; + } else if (clen > 7 && os_strncmp(custom, "rsn_ie=", 7) == 0) { + char *spos; + int bytes; + spos = custom + 7; + bytes = custom + clen - spos; + if (bytes & 1 || bytes == 0) + return; + bytes /= 2; + tmp = os_realloc(res->ie, res->ie_len + bytes); + if (tmp == NULL) + return; + hexstr2bin(spos, tmp + res->ie_len, bytes); + res->ie = tmp; + res->ie_len += bytes; + } else if (clen > 4 && os_strncmp(custom, "tsf=", 4) == 0) { + char *spos; + int bytes; + u8 bin[8]; + spos = custom + 4; + bytes = custom + clen - spos; + if (bytes != 16) { + wpa_printf(MSG_INFO, "Invalid TSF length (%d)", bytes); + return; + } + bytes /= 2; + hexstr2bin(spos, bin, bytes); + res->res.tsf += WPA_GET_BE64(bin); + } +} + + +static int wext_19_iw_point(struct wpa_driver_wext_data *drv, u16 cmd) +{ + return drv->we_version_compiled > 18 && + (cmd == SIOCGIWESSID || cmd == SIOCGIWENCODE || + cmd == IWEVGENIE || cmd == IWEVCUSTOM); +} + + +static void wpa_driver_wext_add_scan_entry(struct wpa_scan_results *res, + struct wext_scan_data *data) +{ + struct wpa_scan_res **tmp; + struct wpa_scan_res *r; + size_t extra_len; + u8 *pos, *end, *ssid_ie = NULL, *rate_ie = NULL; + + /* Figure out whether we need to fake any IEs */ + pos = data->ie; + end = pos + data->ie_len; + while (pos && pos + 1 < end) { + if (pos + 2 + pos[1] > end) + break; + if (pos[0] == WLAN_EID_SSID) + ssid_ie = pos; + else if (pos[0] == WLAN_EID_SUPP_RATES) + rate_ie = pos; + else if (pos[0] == WLAN_EID_EXT_SUPP_RATES) + rate_ie = pos; + pos += 2 + pos[1]; + } + + extra_len = 0; + if (ssid_ie == NULL) + extra_len += 2 + data->ssid_len; + if (rate_ie == NULL && data->maxrate) + extra_len += 3; + + r = os_zalloc(sizeof(*r) + extra_len + data->ie_len); + if (r == NULL) + return; + os_memcpy(r, &data->res, sizeof(*r)); + r->ie_len = extra_len + data->ie_len; + pos = (u8 *) (r + 1); + if (ssid_ie == NULL) { + /* + * Generate a fake SSID IE since the driver did not report + * a full IE list. + */ + *pos++ = WLAN_EID_SSID; + *pos++ = data->ssid_len; + os_memcpy(pos, data->ssid, data->ssid_len); + pos += data->ssid_len; + } + if (rate_ie == NULL && data->maxrate) { + /* + * Generate a fake Supported Rates IE since the driver did not + * report a full IE list. + */ + *pos++ = WLAN_EID_SUPP_RATES; + *pos++ = 1; + *pos++ = data->maxrate; + } + if (data->ie) + os_memcpy(pos, data->ie, data->ie_len); + + tmp = os_realloc(res->res, + (res->num + 1) * sizeof(struct wpa_scan_res *)); + if (tmp == NULL) { + os_free(r); + return; + } + tmp[res->num++] = r; + res->res = tmp; +} + + +/** + * wpa_driver_wext_get_scan_results - Fetch the latest scan results + * @priv: Pointer to private wext data from wpa_driver_wext_init() + * Returns: Scan results on success, -1 on failure + */ +struct wpa_scan_results * wpa_driver_wext_get_scan_results(void *priv) +{ + struct wpa_driver_wext_data *drv = priv; + size_t ap_num = 0, len; + int first; + u8 *res_buf; + struct iw_event iwe_buf, *iwe = &iwe_buf; + char *pos, *end, *custom; + struct wpa_scan_results *res; + struct wext_scan_data data; + + res_buf = wpa_driver_wext_giwscan(drv, &len); + if (res_buf == NULL) + return NULL; + + ap_num = 0; + first = 1; + + res = os_zalloc(sizeof(*res)); + if (res == NULL) { + os_free(res_buf); + return NULL; + } + + pos = (char *) res_buf; + end = (char *) res_buf + len; + os_memset(&data, 0, sizeof(data)); + + while (pos + IW_EV_LCP_LEN <= end) { + /* Event data may be unaligned, so make a local, aligned copy + * before processing. */ + os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); + if (iwe->len <= IW_EV_LCP_LEN) + break; + + custom = pos + IW_EV_POINT_LEN; + if (wext_19_iw_point(drv, iwe->cmd)) { + /* WE-19 removed the pointer from struct iw_point */ + char *dpos = (char *) &iwe_buf.u.data.length; + int dlen = dpos - (char *) &iwe_buf; + os_memcpy(dpos, pos + IW_EV_LCP_LEN, + sizeof(struct iw_event) - dlen); + } else { + os_memcpy(&iwe_buf, pos, sizeof(struct iw_event)); + custom += IW_EV_POINT_OFF; + } + + switch (iwe->cmd) { + case SIOCGIWAP: + if (!first) + wpa_driver_wext_add_scan_entry(res, &data); + first = 0; + os_free(data.ie); + os_memset(&data, 0, sizeof(data)); + os_memcpy(data.res.bssid, + iwe->u.ap_addr.sa_data, ETH_ALEN); + break; + case SIOCGIWMODE: + wext_get_scan_mode(iwe, &data); + break; + case SIOCGIWESSID: + wext_get_scan_ssid(iwe, &data, custom, end); + break; + case SIOCGIWFREQ: + wext_get_scan_freq(iwe, &data); + break; + case IWEVQUAL: + wext_get_scan_qual(iwe, &data); + break; + case SIOCGIWENCODE: + wext_get_scan_encode(iwe, &data); + break; + case SIOCGIWRATE: + wext_get_scan_rate(iwe, &data, pos, end); + break; + case IWEVGENIE: + wext_get_scan_iwevgenie(iwe, &data, custom, end); + break; + case IWEVCUSTOM: + wext_get_scan_custom(iwe, &data, custom, end); + break; + } + + pos += iwe->len; + } + os_free(res_buf); + res_buf = NULL; + if (!first) + wpa_driver_wext_add_scan_entry(res, &data); + os_free(data.ie); + + wpa_printf(MSG_DEBUG, "Received %lu bytes of scan results (%lu BSSes)", + (unsigned long) len, (unsigned long) res->num); + + return res; +} + + +static int wpa_driver_wext_get_range(void *priv) +{ + struct wpa_driver_wext_data *drv = priv; + struct iw_range *range; + struct iwreq iwr; + int minlen; + size_t buflen; + + /* + * Use larger buffer than struct iw_range in order to allow the + * structure to grow in the future. + */ + buflen = sizeof(struct iw_range) + 500; + range = os_zalloc(buflen); + if (range == NULL) + return -1; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) range; + iwr.u.data.length = buflen; + + minlen = ((char *) &range->enc_capa) - (char *) range + + sizeof(range->enc_capa); + + if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) { + perror("ioctl[SIOCGIWRANGE]"); + os_free(range); + return -1; + } else if (iwr.u.data.length >= minlen && + range->we_version_compiled >= 18) { + wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d " + "WE(source)=%d enc_capa=0x%x", + range->we_version_compiled, + range->we_version_source, + range->enc_capa); + drv->has_capability = 1; + drv->we_version_compiled = range->we_version_compiled; + if (range->enc_capa & IW_ENC_CAPA_WPA) { + drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA | + WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK; + } + if (range->enc_capa & IW_ENC_CAPA_WPA2) { + drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK; + } + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40 | + WPA_DRIVER_CAPA_ENC_WEP104; + if (range->enc_capa & IW_ENC_CAPA_CIPHER_TKIP) + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP; + if (range->enc_capa & IW_ENC_CAPA_CIPHER_CCMP) + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP; + if (range->enc_capa & IW_ENC_CAPA_4WAY_HANDSHAKE) + drv->capa.flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE; + drv->capa.auth = WPA_DRIVER_AUTH_OPEN | + WPA_DRIVER_AUTH_SHARED | + WPA_DRIVER_AUTH_LEAP; + + wpa_printf(MSG_DEBUG, " capabilities: key_mgmt 0x%x enc 0x%x " + "flags 0x%x", + drv->capa.key_mgmt, drv->capa.enc, drv->capa.flags); + } else { + wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: too old (short) data - " + "assuming WPA is not supported"); + } + + os_free(range); + return 0; +} + + +static int wpa_driver_wext_set_wpa(void *priv, int enabled) +{ + struct wpa_driver_wext_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + return wpa_driver_wext_set_auth_param(drv, IW_AUTH_WPA_ENABLED, + enabled); +} + + +static int wpa_driver_wext_set_psk(struct wpa_driver_wext_data *drv, + const u8 *psk) +{ + struct iw_encode_ext *ext; + struct iwreq iwr; + int ret; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE)) + return 0; + + if (!psk) + return 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + + ext = os_zalloc(sizeof(*ext) + PMK_LEN); + if (ext == NULL) + return -1; + + iwr.u.encoding.pointer = (caddr_t) ext; + iwr.u.encoding.length = sizeof(*ext) + PMK_LEN; + ext->key_len = PMK_LEN; + os_memcpy(&ext->key, psk, ext->key_len); + ext->alg = IW_ENCODE_ALG_PMK; + + ret = ioctl(drv->ioctl_sock, SIOCSIWENCODEEXT, &iwr); + if (ret < 0) + perror("ioctl[SIOCSIWENCODEEXT] PMK"); + os_free(ext); + + return ret; +} + + +static int wpa_driver_wext_set_key_ext(void *priv, wpa_alg alg, + const u8 *addr, int key_idx, + int set_tx, const u8 *seq, + size_t seq_len, + const u8 *key, size_t key_len) +{ + struct wpa_driver_wext_data *drv = priv; + struct iwreq iwr; + int ret = 0; + struct iw_encode_ext *ext; + + if (seq_len > IW_ENCODE_SEQ_MAX_SIZE) { + wpa_printf(MSG_DEBUG, "%s: Invalid seq_len %lu", + __FUNCTION__, (unsigned long) seq_len); + return -1; + } + + ext = os_zalloc(sizeof(*ext) + key_len); + if (ext == NULL) + return -1; + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.encoding.flags = key_idx + 1; + iwr.u.encoding.flags |= IW_ENCODE_TEMP; + if (alg == WPA_ALG_NONE) + iwr.u.encoding.flags |= IW_ENCODE_DISABLED; + iwr.u.encoding.pointer = (caddr_t) ext; + iwr.u.encoding.length = sizeof(*ext) + key_len; + + if (addr == NULL || + os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0) + ext->ext_flags |= IW_ENCODE_EXT_GROUP_KEY; + if (set_tx) + ext->ext_flags |= IW_ENCODE_EXT_SET_TX_KEY; + + ext->addr.sa_family = ARPHRD_ETHER; + if (addr) + os_memcpy(ext->addr.sa_data, addr, ETH_ALEN); + else + os_memset(ext->addr.sa_data, 0xff, ETH_ALEN); + if (key && key_len) { + os_memcpy(ext + 1, key, key_len); + ext->key_len = key_len; + } + switch (alg) { + case WPA_ALG_NONE: + ext->alg = IW_ENCODE_ALG_NONE; + break; + case WPA_ALG_WEP: + ext->alg = IW_ENCODE_ALG_WEP; + break; + case WPA_ALG_TKIP: + ext->alg = IW_ENCODE_ALG_TKIP; + break; + case WPA_ALG_CCMP: + ext->alg = IW_ENCODE_ALG_CCMP; + break; + case WPA_ALG_PMK: + ext->alg = IW_ENCODE_ALG_PMK; + break; +#ifdef CONFIG_IEEE80211W + case WPA_ALG_IGTK: + ext->alg = IW_ENCODE_ALG_AES_CMAC; + break; +#endif /* CONFIG_IEEE80211W */ + default: + wpa_printf(MSG_DEBUG, "%s: Unknown algorithm %d", + __FUNCTION__, alg); + os_free(ext); + return -1; + } + + if (seq && seq_len) { + ext->ext_flags |= IW_ENCODE_EXT_RX_SEQ_VALID; + os_memcpy(ext->rx_seq, seq, seq_len); + } + + if (ioctl(drv->ioctl_sock, SIOCSIWENCODEEXT, &iwr) < 0) { + ret = errno == EOPNOTSUPP ? -2 : -1; + if (errno == ENODEV) { + /* + * ndiswrapper seems to be returning incorrect error + * code.. */ + ret = -2; + } + + perror("ioctl[SIOCSIWENCODEEXT]"); + } + + os_free(ext); + return ret; +} + + +/** + * wpa_driver_wext_set_key - Configure encryption key + * @priv: Pointer to private wext data from wpa_driver_wext_init() + * @priv: Private driver interface data + * @alg: Encryption algorithm (%WPA_ALG_NONE, %WPA_ALG_WEP, + * %WPA_ALG_TKIP, %WPA_ALG_CCMP); %WPA_ALG_NONE clears the key. + * @addr: Address of the peer STA or ff:ff:ff:ff:ff:ff for + * broadcast/default keys + * @key_idx: key index (0..3), usually 0 for unicast keys + * @set_tx: Configure this key as the default Tx key (only used when + * driver does not support separate unicast/individual key + * @seq: Sequence number/packet number, seq_len octets, the next + * packet number to be used for in replay protection; configured + * for Rx keys (in most cases, this is only used with broadcast + * keys and set to zero for unicast keys) + * @seq_len: Length of the seq, depends on the algorithm: + * TKIP: 6 octets, CCMP: 6 octets + * @key: Key buffer; TKIP: 16-byte temporal key, 8-byte Tx Mic key, + * 8-byte Rx Mic Key + * @key_len: Length of the key buffer in octets (WEP: 5 or 13, + * TKIP: 32, CCMP: 16) + * Returns: 0 on success, -1 on failure + * + * This function uses SIOCSIWENCODEEXT by default, but tries to use + * SIOCSIWENCODE if the extended ioctl fails when configuring a WEP key. + */ +int wpa_driver_wext_set_key(void *priv, wpa_alg alg, + const u8 *addr, int key_idx, + int set_tx, const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + struct wpa_driver_wext_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + wpa_printf(MSG_DEBUG, "%s: alg=%d key_idx=%d set_tx=%d seq_len=%lu " + "key_len=%lu", + __FUNCTION__, alg, key_idx, set_tx, + (unsigned long) seq_len, (unsigned long) key_len); + + ret = wpa_driver_wext_set_key_ext(drv, alg, addr, key_idx, set_tx, + seq, seq_len, key, key_len); + if (ret == 0) + return 0; + + if (ret == -2 && + (alg == WPA_ALG_NONE || alg == WPA_ALG_WEP)) { + wpa_printf(MSG_DEBUG, "Driver did not support " + "SIOCSIWENCODEEXT, trying SIOCSIWENCODE"); + ret = 0; + } else { + wpa_printf(MSG_DEBUG, "Driver did not support " + "SIOCSIWENCODEEXT"); + return ret; + } + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.encoding.flags = key_idx + 1; + iwr.u.encoding.flags |= IW_ENCODE_TEMP; + if (alg == WPA_ALG_NONE) + iwr.u.encoding.flags |= IW_ENCODE_DISABLED; + iwr.u.encoding.pointer = (caddr_t) key; + iwr.u.encoding.length = key_len; + + if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) { + perror("ioctl[SIOCSIWENCODE]"); + ret = -1; + } + + if (set_tx && alg != WPA_ALG_NONE) { + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.encoding.flags = key_idx + 1; + iwr.u.encoding.flags |= IW_ENCODE_TEMP; + iwr.u.encoding.pointer = (caddr_t) NULL; + iwr.u.encoding.length = 0; + if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) { + perror("ioctl[SIOCSIWENCODE] (set_tx)"); + ret = -1; + } + } + + return ret; +} + + +static int wpa_driver_wext_set_countermeasures(void *priv, + int enabled) +{ + struct wpa_driver_wext_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + return wpa_driver_wext_set_auth_param(drv, + IW_AUTH_TKIP_COUNTERMEASURES, + enabled); +} + + +static int wpa_driver_wext_set_drop_unencrypted(void *priv, + int enabled) +{ + struct wpa_driver_wext_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + drv->use_crypt = enabled; + return wpa_driver_wext_set_auth_param(drv, IW_AUTH_DROP_UNENCRYPTED, + enabled); +} + + +static int wpa_driver_wext_mlme(struct wpa_driver_wext_data *drv, + const u8 *addr, int cmd, int reason_code) +{ + struct iwreq iwr; + struct iw_mlme mlme; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + os_memset(&mlme, 0, sizeof(mlme)); + mlme.cmd = cmd; + mlme.reason_code = reason_code; + mlme.addr.sa_family = ARPHRD_ETHER; + os_memcpy(mlme.addr.sa_data, addr, ETH_ALEN); + iwr.u.data.pointer = (caddr_t) &mlme; + iwr.u.data.length = sizeof(mlme); + + if (ioctl(drv->ioctl_sock, SIOCSIWMLME, &iwr) < 0) { + perror("ioctl[SIOCSIWMLME]"); + ret = -1; + } + + return ret; +} + + +static void wpa_driver_wext_disconnect(struct wpa_driver_wext_data *drv) +{ + struct iwreq iwr; + const u8 null_bssid[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; + u8 ssid[32]; + int i; + + /* + * Only force-disconnect when the card is in infrastructure mode, + * otherwise the driver might interpret the cleared BSSID and random + * SSID as an attempt to create a new ad-hoc network. + */ + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + if (ioctl(drv->ioctl_sock, SIOCGIWMODE, &iwr) < 0) { + perror("ioctl[SIOCGIWMODE]"); + iwr.u.mode = IW_MODE_INFRA; + } + + if (iwr.u.mode == IW_MODE_INFRA) { + /* + * Clear the BSSID selection and set a random SSID to make sure + * the driver will not be trying to associate with something + * even if it does not understand SIOCSIWMLME commands (or + * tries to associate automatically after deauth/disassoc). + */ + wpa_driver_wext_set_bssid(drv, null_bssid); + + for (i = 0; i < 32; i++) + ssid[i] = rand() & 0xFF; + wpa_driver_wext_set_ssid(drv, ssid, 32); + } +} + + +static int wpa_driver_wext_deauthenticate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_wext_data *drv = priv; + int ret; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + ret = wpa_driver_wext_mlme(drv, addr, IW_MLME_DEAUTH, reason_code); + wpa_driver_wext_disconnect(drv); + return ret; +} + + +static int wpa_driver_wext_disassociate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_wext_data *drv = priv; + int ret; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + ret = wpa_driver_wext_mlme(drv, addr, IW_MLME_DISASSOC, reason_code); + wpa_driver_wext_disconnect(drv); + return ret; +} + + +static int wpa_driver_wext_set_gen_ie(void *priv, const u8 *ie, + size_t ie_len) +{ + struct wpa_driver_wext_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) ie; + iwr.u.data.length = ie_len; + + if (ioctl(drv->ioctl_sock, SIOCSIWGENIE, &iwr) < 0) { + perror("ioctl[SIOCSIWGENIE]"); + ret = -1; + } + + return ret; +} + + +int wpa_driver_wext_cipher2wext(int cipher) +{ + switch (cipher) { + case CIPHER_NONE: + return IW_AUTH_CIPHER_NONE; + case CIPHER_WEP40: + return IW_AUTH_CIPHER_WEP40; + case CIPHER_TKIP: + return IW_AUTH_CIPHER_TKIP; + case CIPHER_CCMP: + return IW_AUTH_CIPHER_CCMP; + case CIPHER_WEP104: + return IW_AUTH_CIPHER_WEP104; + default: + return 0; + } +} + + +int wpa_driver_wext_keymgmt2wext(int keymgmt) +{ + switch (keymgmt) { + case KEY_MGMT_802_1X: + case KEY_MGMT_802_1X_NO_WPA: + return IW_AUTH_KEY_MGMT_802_1X; + case KEY_MGMT_PSK: + return IW_AUTH_KEY_MGMT_PSK; + default: + return 0; + } +} + + +static int +wpa_driver_wext_auth_alg_fallback(struct wpa_driver_wext_data *drv, + struct wpa_driver_associate_params *params) +{ + struct iwreq iwr; + int ret = 0; + + wpa_printf(MSG_DEBUG, "WEXT: Driver did not support " + "SIOCSIWAUTH for AUTH_ALG, trying SIOCSIWENCODE"); + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + /* Just changing mode, not actual keys */ + iwr.u.encoding.flags = 0; + iwr.u.encoding.pointer = (caddr_t) NULL; + iwr.u.encoding.length = 0; + + /* + * Note: IW_ENCODE_{OPEN,RESTRICTED} can be interpreted to mean two + * different things. Here they are used to indicate Open System vs. + * Shared Key authentication algorithm. However, some drivers may use + * them to select between open/restricted WEP encrypted (open = allow + * both unencrypted and encrypted frames; restricted = only allow + * encrypted frames). + */ + + if (!drv->use_crypt) { + iwr.u.encoding.flags |= IW_ENCODE_DISABLED; + } else { + if (params->auth_alg & AUTH_ALG_OPEN_SYSTEM) + iwr.u.encoding.flags |= IW_ENCODE_OPEN; + if (params->auth_alg & AUTH_ALG_SHARED_KEY) + iwr.u.encoding.flags |= IW_ENCODE_RESTRICTED; + } + + if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) { + perror("ioctl[SIOCSIWENCODE]"); + ret = -1; + } + + return ret; +} + + +int wpa_driver_wext_associate(void *priv, + struct wpa_driver_associate_params *params) +{ + struct wpa_driver_wext_data *drv = priv; + int ret = 0; + int allow_unencrypted_eapol; + int value; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + /* + * If the driver did not support SIOCSIWAUTH, fallback to + * SIOCSIWENCODE here. + */ + if (drv->auth_alg_fallback && + wpa_driver_wext_auth_alg_fallback(drv, params) < 0) + ret = -1; + + if (!params->bssid && + wpa_driver_wext_set_bssid(drv, NULL) < 0) + ret = -1; + + /* TODO: should consider getting wpa version and cipher/key_mgmt suites + * from configuration, not from here, where only the selected suite is + * available */ + if (wpa_driver_wext_set_gen_ie(drv, params->wpa_ie, params->wpa_ie_len) + < 0) + ret = -1; + if (params->wpa_ie == NULL || params->wpa_ie_len == 0) + value = IW_AUTH_WPA_VERSION_DISABLED; + else if (params->wpa_ie[0] == WLAN_EID_RSN) + value = IW_AUTH_WPA_VERSION_WPA2; + else + value = IW_AUTH_WPA_VERSION_WPA; + if (wpa_driver_wext_set_auth_param(drv, + IW_AUTH_WPA_VERSION, value) < 0) + ret = -1; + value = wpa_driver_wext_cipher2wext(params->pairwise_suite); + if (wpa_driver_wext_set_auth_param(drv, + IW_AUTH_CIPHER_PAIRWISE, value) < 0) + ret = -1; + value = wpa_driver_wext_cipher2wext(params->group_suite); + if (wpa_driver_wext_set_auth_param(drv, + IW_AUTH_CIPHER_GROUP, value) < 0) + ret = -1; + value = wpa_driver_wext_keymgmt2wext(params->key_mgmt_suite); + if (wpa_driver_wext_set_auth_param(drv, + IW_AUTH_KEY_MGMT, value) < 0) + ret = -1; + value = params->key_mgmt_suite != KEY_MGMT_NONE || + params->pairwise_suite != CIPHER_NONE || + params->group_suite != CIPHER_NONE || + params->wpa_ie_len; + if (wpa_driver_wext_set_auth_param(drv, + IW_AUTH_PRIVACY_INVOKED, value) < 0) + ret = -1; + + /* Allow unencrypted EAPOL messages even if pairwise keys are set when + * not using WPA. IEEE 802.1X specifies that these frames are not + * encrypted, but WPA encrypts them when pairwise keys are in use. */ + if (params->key_mgmt_suite == KEY_MGMT_802_1X || + params->key_mgmt_suite == KEY_MGMT_PSK) + allow_unencrypted_eapol = 0; + else + allow_unencrypted_eapol = 1; + + if (wpa_driver_wext_set_psk(drv, params->psk) < 0) + ret = -1; + if (wpa_driver_wext_set_auth_param(drv, + IW_AUTH_RX_UNENCRYPTED_EAPOL, + allow_unencrypted_eapol) < 0) + ret = -1; +#ifdef CONFIG_IEEE80211W + switch (params->mgmt_frame_protection) { + case NO_MGMT_FRAME_PROTECTION: + value = IW_AUTH_MFP_DISABLED; + break; + case MGMT_FRAME_PROTECTION_OPTIONAL: + value = IW_AUTH_MFP_OPTIONAL; + break; + case MGMT_FRAME_PROTECTION_REQUIRED: + value = IW_AUTH_MFP_REQUIRED; + break; + }; + if (wpa_driver_wext_set_auth_param(drv, IW_AUTH_MFP, value) < 0) + ret = -1; +#endif /* CONFIG_IEEE80211W */ + if (params->freq && wpa_driver_wext_set_freq(drv, params->freq) < 0) + ret = -1; + if (wpa_driver_wext_set_ssid(drv, params->ssid, params->ssid_len) < 0) + ret = -1; + if (params->bssid && + wpa_driver_wext_set_bssid(drv, params->bssid) < 0) + ret = -1; + + return ret; +} + + +static int wpa_driver_wext_set_auth_alg(void *priv, int auth_alg) +{ + struct wpa_driver_wext_data *drv = priv; + int algs = 0, res; + + if (auth_alg & AUTH_ALG_OPEN_SYSTEM) + algs |= IW_AUTH_ALG_OPEN_SYSTEM; + if (auth_alg & AUTH_ALG_SHARED_KEY) + algs |= IW_AUTH_ALG_SHARED_KEY; + if (auth_alg & AUTH_ALG_LEAP) + algs |= IW_AUTH_ALG_LEAP; + if (algs == 0) { + /* at least one algorithm should be set */ + algs = IW_AUTH_ALG_OPEN_SYSTEM; + } + + res = wpa_driver_wext_set_auth_param(drv, IW_AUTH_80211_AUTH_ALG, + algs); + drv->auth_alg_fallback = res == -2; + return res; +} + + +/** + * wpa_driver_wext_set_mode - Set wireless mode (infra/adhoc), SIOCSIWMODE + * @priv: Pointer to private wext data from wpa_driver_wext_init() + * @mode: 0 = infra/BSS (associate with an AP), 1 = adhoc/IBSS + * Returns: 0 on success, -1 on failure + */ +int wpa_driver_wext_set_mode(void *priv, int mode) +{ + struct wpa_driver_wext_data *drv = priv; + struct iwreq iwr; + int ret = -1, flags; + unsigned int new_mode = mode ? IW_MODE_ADHOC : IW_MODE_INFRA; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.mode = new_mode; + if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) == 0) { + ret = 0; + goto done; + } + + if (errno != EBUSY) { + perror("ioctl[SIOCSIWMODE]"); + goto done; + } + + /* mac80211 doesn't allow mode changes while the device is up, so if + * the device isn't in the mode we're about to change to, take device + * down, try to set the mode again, and bring it back up. + */ + if (ioctl(drv->ioctl_sock, SIOCGIWMODE, &iwr) < 0) { + perror("ioctl[SIOCGIWMODE]"); + goto done; + } + + if (iwr.u.mode == new_mode) { + ret = 0; + goto done; + } + + if (wpa_driver_wext_get_ifflags(drv, &flags) == 0) { + (void) wpa_driver_wext_set_ifflags(drv, flags & ~IFF_UP); + + /* Try to set the mode again while the interface is down */ + iwr.u.mode = new_mode; + if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) < 0) + perror("ioctl[SIOCSIWMODE]"); + else + ret = 0; + + /* Ignore return value of get_ifflags to ensure that the device + * is always up like it was before this function was called. + */ + (void) wpa_driver_wext_get_ifflags(drv, &flags); + (void) wpa_driver_wext_set_ifflags(drv, flags | IFF_UP); + } + +done: + return ret; +} + + +static int wpa_driver_wext_pmksa(struct wpa_driver_wext_data *drv, + u32 cmd, const u8 *bssid, const u8 *pmkid) +{ + struct iwreq iwr; + struct iw_pmksa pmksa; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + os_memset(&pmksa, 0, sizeof(pmksa)); + pmksa.cmd = cmd; + pmksa.bssid.sa_family = ARPHRD_ETHER; + if (bssid) + os_memcpy(pmksa.bssid.sa_data, bssid, ETH_ALEN); + if (pmkid) + os_memcpy(pmksa.pmkid, pmkid, IW_PMKID_LEN); + iwr.u.data.pointer = (caddr_t) &pmksa; + iwr.u.data.length = sizeof(pmksa); + + if (ioctl(drv->ioctl_sock, SIOCSIWPMKSA, &iwr) < 0) { + if (errno != EOPNOTSUPP) + perror("ioctl[SIOCSIWPMKSA]"); + ret = -1; + } + + return ret; +} + + +static int wpa_driver_wext_add_pmkid(void *priv, const u8 *bssid, + const u8 *pmkid) +{ + struct wpa_driver_wext_data *drv = priv; + return wpa_driver_wext_pmksa(drv, IW_PMKSA_ADD, bssid, pmkid); +} + + +static int wpa_driver_wext_remove_pmkid(void *priv, const u8 *bssid, + const u8 *pmkid) +{ + struct wpa_driver_wext_data *drv = priv; + return wpa_driver_wext_pmksa(drv, IW_PMKSA_REMOVE, bssid, pmkid); +} + + +static int wpa_driver_wext_flush_pmkid(void *priv) +{ + struct wpa_driver_wext_data *drv = priv; + return wpa_driver_wext_pmksa(drv, IW_PMKSA_FLUSH, NULL, NULL); +} + + +int wpa_driver_wext_get_capa(void *priv, struct wpa_driver_capa *capa) +{ + struct wpa_driver_wext_data *drv = priv; + if (!drv->has_capability) + return -1; + os_memcpy(capa, &drv->capa, sizeof(*capa)); + return 0; +} + + +int wpa_driver_wext_alternative_ifindex(struct wpa_driver_wext_data *drv, + const char *ifname) +{ + if (ifname == NULL) { + drv->ifindex2 = -1; + return 0; + } + + drv->ifindex2 = if_nametoindex(ifname); + if (drv->ifindex2 <= 0) + return -1; + + wpa_printf(MSG_DEBUG, "Added alternative ifindex %d (%s) for " + "wireless events", drv->ifindex2, ifname); + + return 0; +} + + +int wpa_driver_wext_set_operstate(void *priv, int state) +{ + struct wpa_driver_wext_data *drv = priv; + + wpa_printf(MSG_DEBUG, "%s: operstate %d->%d (%s)", + __func__, drv->operstate, state, state ? "UP" : "DORMANT"); + drv->operstate = state; + return wpa_driver_wext_send_oper_ifla( + drv, -1, state ? IF_OPER_UP : IF_OPER_DORMANT); +} + + +int wpa_driver_wext_get_version(struct wpa_driver_wext_data *drv) +{ + return drv->we_version_compiled; +} + + +const struct wpa_driver_ops wpa_driver_wext_ops = { + .name = "wext", + .desc = "Linux wireless extensions (generic)", + .get_bssid = wpa_driver_wext_get_bssid, + .get_ssid = wpa_driver_wext_get_ssid, + .set_wpa = wpa_driver_wext_set_wpa, + .set_key = wpa_driver_wext_set_key, + .set_countermeasures = wpa_driver_wext_set_countermeasures, + .set_drop_unencrypted = wpa_driver_wext_set_drop_unencrypted, + .scan = wpa_driver_wext_scan, + .get_scan_results2 = wpa_driver_wext_get_scan_results, + .deauthenticate = wpa_driver_wext_deauthenticate, + .disassociate = wpa_driver_wext_disassociate, + .set_mode = wpa_driver_wext_set_mode, + .associate = wpa_driver_wext_associate, + .set_auth_alg = wpa_driver_wext_set_auth_alg, + .init = wpa_driver_wext_init, + .deinit = wpa_driver_wext_deinit, + .add_pmkid = wpa_driver_wext_add_pmkid, + .remove_pmkid = wpa_driver_wext_remove_pmkid, + .flush_pmkid = wpa_driver_wext_flush_pmkid, + .get_capa = wpa_driver_wext_get_capa, + .set_operstate = wpa_driver_wext_set_operstate, +}; diff --git a/src/drivers/driver_wext.h b/src/drivers/driver_wext.h new file mode 100644 index 0000000..b89c2cb --- /dev/null +++ b/src/drivers/driver_wext.h @@ -0,0 +1,82 @@ +/* + * WPA Supplicant - driver_wext exported functions + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef DRIVER_WEXT_H +#define DRIVER_WEXT_H + +#include <net/if.h> + +struct wpa_driver_wext_data { + void *ctx; + int event_sock; + int ioctl_sock; + int mlme_sock; + char ifname[IFNAMSIZ + 1]; + int ifindex; + int ifindex2; + int if_removed; + u8 *assoc_req_ies; + size_t assoc_req_ies_len; + u8 *assoc_resp_ies; + size_t assoc_resp_ies_len; + struct wpa_driver_capa capa; + int has_capability; + int we_version_compiled; + + /* for set_auth_alg fallback */ + int use_crypt; + int auth_alg_fallback; + + int operstate; + + char mlmedev[IFNAMSIZ + 1]; + + int scan_complete_events; +}; + +int wpa_driver_wext_get_ifflags(struct wpa_driver_wext_data *drv, int *flags); +int wpa_driver_wext_set_ifflags(struct wpa_driver_wext_data *drv, int flags); +int wpa_driver_wext_get_bssid(void *priv, u8 *bssid); +int wpa_driver_wext_set_bssid(void *priv, const u8 *bssid); +int wpa_driver_wext_get_ssid(void *priv, u8 *ssid); +int wpa_driver_wext_set_ssid(void *priv, const u8 *ssid, size_t ssid_len); +int wpa_driver_wext_set_freq(void *priv, int freq); +int wpa_driver_wext_set_mode(void *priv, int mode); +int wpa_driver_wext_set_key(void *priv, wpa_alg alg, + const u8 *addr, int key_idx, + int set_tx, const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len); +int wpa_driver_wext_scan(void *priv, const u8 *ssid, size_t ssid_len); +struct wpa_scan_results * wpa_driver_wext_get_scan_results(void *priv); + +void wpa_driver_wext_scan_timeout(void *eloop_ctx, void *timeout_ctx); + +int wpa_driver_wext_alternative_ifindex(struct wpa_driver_wext_data *drv, + const char *ifname); + +void * wpa_driver_wext_init(void *ctx, const char *ifname); +void wpa_driver_wext_deinit(void *priv); + +int wpa_driver_wext_set_operstate(void *priv, int state); +int wpa_driver_wext_get_version(struct wpa_driver_wext_data *drv); + +int wpa_driver_wext_associate(void *priv, + struct wpa_driver_associate_params *params); +int wpa_driver_wext_get_capa(void *priv, struct wpa_driver_capa *capa); +int wpa_driver_wext_set_auth_param(struct wpa_driver_wext_data *drv, + int idx, u32 value); +int wpa_driver_wext_cipher2wext(int cipher); +int wpa_driver_wext_keymgmt2wext(int keymgmt); + +#endif /* DRIVER_WEXT_H */ diff --git a/src/drivers/driver_wired.c b/src/drivers/driver_wired.c new file mode 100644 index 0000000..098991a --- /dev/null +++ b/src/drivers/driver_wired.c @@ -0,0 +1,286 @@ +/* + * WPA Supplicant - wired Ethernet driver interface + * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <sys/ioctl.h> +#include <net/if.h> +#ifdef __linux__ +#include <netpacket/packet.h> +#endif /* __linux__ */ +#if defined(__FreeBSD__) || defined(__DragonFly__) +#include <net/if_dl.h> +#endif /* defined(__FreeBSD__) || defined(__DragonFly__) */ + +#include "common.h" +#include "driver.h" + + +static const u8 pae_group_addr[ETH_ALEN] = +{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }; + + +struct wpa_driver_wired_data { + void *ctx; + int pf_sock; + char ifname[IFNAMSIZ + 1]; + int membership, multi, iff_allmulti, iff_up; +}; + + +static int wpa_driver_wired_get_ssid(void *priv, u8 *ssid) +{ + ssid[0] = 0; + return 0; +} + + +static int wpa_driver_wired_get_bssid(void *priv, u8 *bssid) +{ + /* Report PAE group address as the "BSSID" for wired connection. */ + os_memcpy(bssid, pae_group_addr, ETH_ALEN); + return 0; +} + + +static int wpa_driver_wired_get_ifflags(const char *ifname, int *flags) +{ + struct ifreq ifr; + int s; + + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket"); + return -1; + } + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) { + perror("ioctl[SIOCGIFFLAGS]"); + close(s); + return -1; + } + close(s); + *flags = ifr.ifr_flags & 0xffff; + return 0; +} + + +static int wpa_driver_wired_set_ifflags(const char *ifname, int flags) +{ + struct ifreq ifr; + int s; + + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket"); + return -1; + } + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + ifr.ifr_flags = flags & 0xffff; + if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { + perror("ioctl[SIOCSIFFLAGS]"); + close(s); + return -1; + } + close(s); + return 0; +} + + +static int wpa_driver_wired_multi(const char *ifname, const u8 *addr, int add) +{ + struct ifreq ifr; + int s; + + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket"); + return -1; + } + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); +#ifdef __linux__ + ifr.ifr_hwaddr.sa_family = AF_UNSPEC; + os_memcpy(ifr.ifr_hwaddr.sa_data, addr, ETH_ALEN); +#endif /* __linux__ */ +#if defined(__FreeBSD__) || defined(__DragonFly__) + { + struct sockaddr_dl *dlp; + dlp = (struct sockaddr_dl *) &ifr.ifr_addr; + dlp->sdl_len = sizeof(struct sockaddr_dl); + dlp->sdl_family = AF_LINK; + dlp->sdl_index = 0; + dlp->sdl_nlen = 0; + dlp->sdl_alen = ETH_ALEN; + dlp->sdl_slen = 0; + os_memcpy(LLADDR(dlp), addr, ETH_ALEN); + } +#endif /* defined(__FreeBSD__) || defined(__DragonFly__) */ +#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) + { + struct sockaddr *sap; + sap = (struct sockaddr *) &ifr.ifr_addr; + sap->sa_len = sizeof(struct sockaddr); + sap->sa_family = AF_UNSPEC; + os_memcpy(sap->sa_data, addr, ETH_ALEN); + } +#endif /* defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) */ + + if (ioctl(s, add ? SIOCADDMULTI : SIOCDELMULTI, (caddr_t) &ifr) < 0) { + perror("ioctl[SIOC{ADD/DEL}MULTI]"); + close(s); + return -1; + } + close(s); + return 0; +} + + +static int wpa_driver_wired_membership(struct wpa_driver_wired_data *drv, + const u8 *addr, int add) +{ +#ifdef __linux__ + struct packet_mreq mreq; + + if (drv->pf_sock == -1) + return -1; + + os_memset(&mreq, 0, sizeof(mreq)); + mreq.mr_ifindex = if_nametoindex(drv->ifname); + mreq.mr_type = PACKET_MR_MULTICAST; + mreq.mr_alen = ETH_ALEN; + os_memcpy(mreq.mr_address, addr, ETH_ALEN); + + if (setsockopt(drv->pf_sock, SOL_PACKET, + add ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP, + &mreq, sizeof(mreq)) < 0) { + perror("setsockopt"); + return -1; + } + return 0; +#else /* __linux__ */ + return -1; +#endif /* __linux__ */ +} + + +static void * wpa_driver_wired_init(void *ctx, const char *ifname) +{ + struct wpa_driver_wired_data *drv; + int flags; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + drv->ctx = ctx; + +#ifdef __linux__ + drv->pf_sock = socket(PF_PACKET, SOCK_DGRAM, 0); + if (drv->pf_sock < 0) + perror("socket(PF_PACKET)"); +#else /* __linux__ */ + drv->pf_sock = -1; +#endif /* __linux__ */ + + if (wpa_driver_wired_get_ifflags(ifname, &flags) == 0 && + !(flags & IFF_UP) && + wpa_driver_wired_set_ifflags(ifname, flags | IFF_UP) == 0) { + drv->iff_up = 1; + } + + if (wpa_driver_wired_membership(drv, pae_group_addr, 1) == 0) { + wpa_printf(MSG_DEBUG, "%s: Added multicast membership with " + "packet socket", __func__); + drv->membership = 1; + } else if (wpa_driver_wired_multi(ifname, pae_group_addr, 1) == 0) { + wpa_printf(MSG_DEBUG, "%s: Added multicast membership with " + "SIOCADDMULTI", __func__); + drv->multi = 1; + } else if (wpa_driver_wired_get_ifflags(ifname, &flags) < 0) { + wpa_printf(MSG_INFO, "%s: Could not get interface " + "flags", __func__); + os_free(drv); + return NULL; + } else if (flags & IFF_ALLMULTI) { + wpa_printf(MSG_DEBUG, "%s: Interface is already configured " + "for multicast", __func__); + } else if (wpa_driver_wired_set_ifflags(ifname, + flags | IFF_ALLMULTI) < 0) { + wpa_printf(MSG_INFO, "%s: Failed to enable allmulti", + __func__); + os_free(drv); + return NULL; + } else { + wpa_printf(MSG_DEBUG, "%s: Enabled allmulti mode", + __func__); + drv->iff_allmulti = 1; + } + + return drv; +} + + +static void wpa_driver_wired_deinit(void *priv) +{ + struct wpa_driver_wired_data *drv = priv; + int flags; + + if (drv->membership && + wpa_driver_wired_membership(drv, pae_group_addr, 0) < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to remove PAE multicast " + "group (PACKET)", __func__); + } + + if (drv->multi && + wpa_driver_wired_multi(drv->ifname, pae_group_addr, 0) < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to remove PAE multicast " + "group (SIOCDELMULTI)", __func__); + } + + if (drv->iff_allmulti && + (wpa_driver_wired_get_ifflags(drv->ifname, &flags) < 0 || + wpa_driver_wired_set_ifflags(drv->ifname, + flags & ~IFF_ALLMULTI) < 0)) { + wpa_printf(MSG_DEBUG, "%s: Failed to disable allmulti mode", + __func__); + } + + if (drv->iff_up && + wpa_driver_wired_get_ifflags(drv->ifname, &flags) == 0 && + (flags & IFF_UP) && + wpa_driver_wired_set_ifflags(drv->ifname, flags & ~IFF_UP) < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to set the interface down", + __func__); + } + + if (drv->pf_sock != -1) + close(drv->pf_sock); + + os_free(drv); +} + + +const struct wpa_driver_ops wpa_driver_wired_ops = { + .name = "wired", + .desc = "wpa_supplicant wired Ethernet driver", + .get_ssid = wpa_driver_wired_get_ssid, + .get_bssid = wpa_driver_wired_get_bssid, + .init = wpa_driver_wired_init, + .deinit = wpa_driver_wired_deinit, +}; diff --git a/src/drivers/ndis_events.c b/src/drivers/ndis_events.c new file mode 100644 index 0000000..f6eaa7c --- /dev/null +++ b/src/drivers/ndis_events.c @@ -0,0 +1,808 @@ +/* + * ndis_events - Receive NdisMIndicateStatus() events using WMI + * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#define _WIN32_WINNT 0x0400 + +#include "includes.h" + +#ifndef COBJMACROS +#define COBJMACROS +#endif /* COBJMACROS */ +#include <wbemidl.h> + +#include "common.h" + + +static int wmi_refcnt = 0; +static int wmi_first = 1; + +struct ndis_events_data { + IWbemObjectSink sink; + IWbemObjectSinkVtbl sink_vtbl; + + IWbemServices *pSvc; + IWbemLocator *pLoc; + + HANDLE read_pipe, write_pipe, event_avail; + UINT ref; + int terminating; + char *ifname; /* {GUID..} */ + WCHAR *adapter_desc; +}; + +#define BstrAlloc(x) (x) ? SysAllocString(x) : NULL +#define BstrFree(x) if (x) SysFreeString(x) + +/* WBEM / WMI wrapper functions, to perform in-place conversion of WCHARs to + * BSTRs */ +HRESULT STDMETHODCALLTYPE call_IWbemServices_ExecQuery( + IWbemServices *pSvc, LPCWSTR strQueryLanguage, LPCWSTR strQuery, + long lFlags, IWbemContext *pCtx, IEnumWbemClassObject **ppEnum) +{ + BSTR bsQueryLanguage, bsQuery; + HRESULT hr; + + bsQueryLanguage = BstrAlloc(strQueryLanguage); + bsQuery = BstrAlloc(strQuery); + + hr = IWbemServices_ExecQuery(pSvc, bsQueryLanguage, bsQuery, lFlags, + pCtx, ppEnum); + + BstrFree(bsQueryLanguage); + BstrFree(bsQuery); + + return hr; +} + + +HRESULT STDMETHODCALLTYPE call_IWbemServices_ExecNotificationQueryAsync( + IWbemServices *pSvc, LPCWSTR strQueryLanguage, LPCWSTR strQuery, + long lFlags, IWbemContext *pCtx, IWbemObjectSink *pResponseHandler) +{ + BSTR bsQueryLanguage, bsQuery; + HRESULT hr; + + bsQueryLanguage = BstrAlloc(strQueryLanguage); + bsQuery = BstrAlloc(strQuery); + + hr = IWbemServices_ExecNotificationQueryAsync(pSvc, bsQueryLanguage, + bsQuery, lFlags, pCtx, + pResponseHandler); + + BstrFree(bsQueryLanguage); + BstrFree(bsQuery); + + return hr; +} + + +HRESULT STDMETHODCALLTYPE call_IWbemLocator_ConnectServer( + IWbemLocator *pLoc, LPCWSTR strNetworkResource, LPCWSTR strUser, + LPCWSTR strPassword, LPCWSTR strLocale, long lSecurityFlags, + LPCWSTR strAuthority, IWbemContext *pCtx, IWbemServices **ppNamespace) +{ + BSTR bsNetworkResource, bsUser, bsPassword, bsLocale, bsAuthority; + HRESULT hr; + + bsNetworkResource = BstrAlloc(strNetworkResource); + bsUser = BstrAlloc(strUser); + bsPassword = BstrAlloc(strPassword); + bsLocale = BstrAlloc(strLocale); + bsAuthority = BstrAlloc(strAuthority); + + hr = IWbemLocator_ConnectServer(pLoc, bsNetworkResource, bsUser, + bsPassword, bsLocale, lSecurityFlags, + bsAuthority, pCtx, ppNamespace); + + BstrFree(bsNetworkResource); + BstrFree(bsUser); + BstrFree(bsPassword); + BstrFree(bsLocale); + BstrFree(bsAuthority); + + return hr; +} + + +enum event_types { EVENT_CONNECT, EVENT_DISCONNECT, EVENT_MEDIA_SPECIFIC, + EVENT_ADAPTER_ARRIVAL, EVENT_ADAPTER_REMOVAL }; + +static int ndis_events_get_adapter(struct ndis_events_data *events, + const char *ifname, const char *desc); + + +static int ndis_events_constructor(struct ndis_events_data *events) +{ + events->ref = 1; + + if (!CreatePipe(&events->read_pipe, &events->write_pipe, NULL, 512)) { + wpa_printf(MSG_ERROR, "CreatePipe() failed: %d", + (int) GetLastError()); + return -1; + } + events->event_avail = CreateEvent(NULL, TRUE, FALSE, NULL); + if (events->event_avail == NULL) { + wpa_printf(MSG_ERROR, "CreateEvent() failed: %d", + (int) GetLastError()); + CloseHandle(events->read_pipe); + CloseHandle(events->write_pipe); + return -1; + } + + return 0; +} + + +static void ndis_events_destructor(struct ndis_events_data *events) +{ + CloseHandle(events->read_pipe); + CloseHandle(events->write_pipe); + CloseHandle(events->event_avail); + IWbemServices_Release(events->pSvc); + IWbemLocator_Release(events->pLoc); + if (--wmi_refcnt == 0) + CoUninitialize(); +} + + +static HRESULT STDMETHODCALLTYPE +ndis_events_query_interface(IWbemObjectSink *this, REFIID riid, void **obj) +{ + *obj = NULL; + + if (IsEqualIID(riid, &IID_IUnknown) || + IsEqualIID(riid, &IID_IWbemObjectSink)) { + *obj = this; + IWbemObjectSink_AddRef(this); + return NOERROR; + } + + return E_NOINTERFACE; +} + + +static ULONG STDMETHODCALLTYPE ndis_events_add_ref(IWbemObjectSink *this) +{ + struct ndis_events_data *events = (struct ndis_events_data *) this; + return ++events->ref; +} + + +static ULONG STDMETHODCALLTYPE ndis_events_release(IWbemObjectSink *this) +{ + struct ndis_events_data *events = (struct ndis_events_data *) this; + + if (--events->ref != 0) + return events->ref; + + ndis_events_destructor(events); + wpa_printf(MSG_DEBUG, "ndis_events: terminated"); + os_free(events->adapter_desc); + os_free(events->ifname); + os_free(events); + return 0; +} + + +static int ndis_events_send_event(struct ndis_events_data *events, + enum event_types type, + char *data, size_t data_len) +{ + char buf[512], *pos, *end; + int _type; + DWORD written; + + end = buf + sizeof(buf); + _type = (int) type; + os_memcpy(buf, &_type, sizeof(_type)); + pos = buf + sizeof(_type); + + if (data) { + if (2 + data_len > (size_t) (end - pos)) { + wpa_printf(MSG_DEBUG, "Not enough room for send_event " + "data (%d)", data_len); + return -1; + } + *pos++ = data_len >> 8; + *pos++ = data_len & 0xff; + os_memcpy(pos, data, data_len); + pos += data_len; + } + + if (WriteFile(events->write_pipe, buf, pos - buf, &written, NULL)) { + SetEvent(events->event_avail); + return 0; + } + wpa_printf(MSG_INFO, "WriteFile() failed: %d", (int) GetLastError()); + return -1; +} + + +static void ndis_events_media_connect(struct ndis_events_data *events) +{ + wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaConnect"); + ndis_events_send_event(events, EVENT_CONNECT, NULL, 0); +} + + +static void ndis_events_media_disconnect(struct ndis_events_data *events) +{ + wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaDisconnect"); + ndis_events_send_event(events, EVENT_DISCONNECT, NULL, 0); +} + + +static void ndis_events_media_specific(struct ndis_events_data *events, + IWbemClassObject *pObj) +{ + VARIANT vt; + HRESULT hr; + LONG lower, upper, k; + UCHAR ch; + char *data, *pos; + size_t data_len; + + wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaSpecificIndication"); + + /* This is the StatusBuffer from NdisMIndicateStatus() call */ + hr = IWbemClassObject_Get(pObj, L"NdisStatusMediaSpecificIndication", + 0, &vt, NULL, NULL); + if (FAILED(hr)) { + wpa_printf(MSG_DEBUG, "Could not get " + "NdisStatusMediaSpecificIndication from " + "the object?!"); + return; + } + + SafeArrayGetLBound(V_ARRAY(&vt), 1, &lower); + SafeArrayGetUBound(V_ARRAY(&vt), 1, &upper); + data_len = upper - lower + 1; + data = os_malloc(data_len); + if (data == NULL) { + wpa_printf(MSG_DEBUG, "Failed to allocate buffer for event " + "data"); + VariantClear(&vt); + return; + } + + pos = data; + for (k = lower; k <= upper; k++) { + SafeArrayGetElement(V_ARRAY(&vt), &k, &ch); + *pos++ = ch; + } + wpa_hexdump(MSG_DEBUG, "MediaSpecificEvent", (u8 *) data, data_len); + + VariantClear(&vt); + + ndis_events_send_event(events, EVENT_MEDIA_SPECIFIC, data, data_len); + + os_free(data); +} + + +static void ndis_events_adapter_arrival(struct ndis_events_data *events) +{ + wpa_printf(MSG_DEBUG, "MSNdis_NotifyAdapterArrival"); + ndis_events_send_event(events, EVENT_ADAPTER_ARRIVAL, NULL, 0); +} + + +static void ndis_events_adapter_removal(struct ndis_events_data *events) +{ + wpa_printf(MSG_DEBUG, "MSNdis_NotifyAdapterRemoval"); + ndis_events_send_event(events, EVENT_ADAPTER_REMOVAL, NULL, 0); +} + + +static HRESULT STDMETHODCALLTYPE +ndis_events_indicate(IWbemObjectSink *this, long lObjectCount, + IWbemClassObject __RPC_FAR *__RPC_FAR *ppObjArray) +{ + struct ndis_events_data *events = (struct ndis_events_data *) this; + long i; + + if (events->terminating) { + wpa_printf(MSG_DEBUG, "ndis_events_indicate: Ignore " + "indication - terminating"); + return WBEM_NO_ERROR; + } + /* wpa_printf(MSG_DEBUG, "Notification received - %d object(s)", + lObjectCount); */ + + for (i = 0; i < lObjectCount; i++) { + IWbemClassObject *pObj = ppObjArray[i]; + HRESULT hr; + VARIANT vtClass, vt; + + hr = IWbemClassObject_Get(pObj, L"__CLASS", 0, &vtClass, NULL, + NULL); + if (FAILED(hr)) { + wpa_printf(MSG_DEBUG, "Failed to get __CLASS from " + "event."); + break; + } + /* wpa_printf(MSG_DEBUG, "CLASS: '%S'", vtClass.bstrVal); */ + + hr = IWbemClassObject_Get(pObj, L"InstanceName", 0, &vt, NULL, + NULL); + if (FAILED(hr)) { + wpa_printf(MSG_DEBUG, "Failed to get InstanceName " + "from event."); + VariantClear(&vtClass); + break; + } + + if (wcscmp(vtClass.bstrVal, + L"MSNdis_NotifyAdapterArrival") == 0) { + wpa_printf(MSG_DEBUG, "ndis_events_indicate: Try to " + "update adapter description since it may " + "have changed with new adapter instance"); + ndis_events_get_adapter(events, events->ifname, NULL); + } + + if (wcscmp(events->adapter_desc, vt.bstrVal) != 0) { + wpa_printf(MSG_DEBUG, "ndis_events_indicate: Ignore " + "indication for foreign adapter: " + "InstanceName: '%S' __CLASS: '%S'", + vt.bstrVal, vtClass.bstrVal); + VariantClear(&vtClass); + VariantClear(&vt); + continue; + } + VariantClear(&vt); + + if (wcscmp(vtClass.bstrVal, + L"MSNdis_StatusMediaSpecificIndication") == 0) { + ndis_events_media_specific(events, pObj); + } else if (wcscmp(vtClass.bstrVal, + L"MSNdis_StatusMediaConnect") == 0) { + ndis_events_media_connect(events); + } else if (wcscmp(vtClass.bstrVal, + L"MSNdis_StatusMediaDisconnect") == 0) { + ndis_events_media_disconnect(events); + } else if (wcscmp(vtClass.bstrVal, + L"MSNdis_NotifyAdapterArrival") == 0) { + ndis_events_adapter_arrival(events); + } else if (wcscmp(vtClass.bstrVal, + L"MSNdis_NotifyAdapterRemoval") == 0) { + ndis_events_adapter_removal(events); + } else { + wpa_printf(MSG_DEBUG, "Unepected event - __CLASS: " + "'%S'", vtClass.bstrVal); + } + + VariantClear(&vtClass); + } + + return WBEM_NO_ERROR; +} + + +static HRESULT STDMETHODCALLTYPE +ndis_events_set_status(IWbemObjectSink *this, long lFlags, HRESULT hResult, + BSTR strParam, IWbemClassObject __RPC_FAR *pObjParam) +{ + return WBEM_NO_ERROR; +} + + +static int notification_query(IWbemObjectSink *pDestSink, + IWbemServices *pSvc, const char *class_name) +{ + HRESULT hr; + WCHAR query[256]; + + _snwprintf(query, 256, + L"SELECT * FROM %S", class_name); + wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query); + hr = call_IWbemServices_ExecNotificationQueryAsync( + pSvc, L"WQL", query, 0, 0, pDestSink); + if (FAILED(hr)) { + wpa_printf(MSG_DEBUG, "ExecNotificationQueryAsync for %s " + "failed with hresult of 0x%x", + class_name, (int) hr); + return -1; + } + + return 0; +} + + +static int register_async_notification(IWbemObjectSink *pDestSink, + IWbemServices *pSvc) +{ + int i; + const char *class_list[] = { + "MSNdis_StatusMediaConnect", + "MSNdis_StatusMediaDisconnect", + "MSNdis_StatusMediaSpecificIndication", + "MSNdis_NotifyAdapterArrival", + "MSNdis_NotifyAdapterRemoval", + NULL + }; + + for (i = 0; class_list[i]; i++) { + if (notification_query(pDestSink, pSvc, class_list[i]) < 0) + return -1; + } + + return 0; +} + + +void ndis_events_deinit(struct ndis_events_data *events) +{ + events->terminating = 1; + IWbemServices_CancelAsyncCall(events->pSvc, &events->sink); + IWbemObjectSink_Release(&events->sink); + /* + * Rest of deinitialization is done in ndis_events_destructor() once + * all reference count drops to zero. + */ +} + + +static int ndis_events_use_desc(struct ndis_events_data *events, + const char *desc) +{ + char *tmp, *pos; + size_t len; + + if (desc == NULL) { + if (events->adapter_desc == NULL) + return -1; + /* Continue using old description */ + return 0; + } + + tmp = os_strdup(desc); + if (tmp == NULL) + return -1; + + pos = os_strstr(tmp, " (Microsoft's Packet Scheduler)"); + if (pos) + *pos = '\0'; + + len = os_strlen(tmp); + events->adapter_desc = os_malloc((len + 1) * sizeof(WCHAR)); + if (events->adapter_desc == NULL) { + os_free(tmp); + return -1; + } + _snwprintf(events->adapter_desc, len + 1, L"%S", tmp); + os_free(tmp); + return 0; +} + + +static int ndis_events_get_adapter(struct ndis_events_data *events, + const char *ifname, const char *desc) +{ + HRESULT hr; + IWbemServices *pSvc; +#define MAX_QUERY_LEN 256 + WCHAR query[MAX_QUERY_LEN]; + IEnumWbemClassObject *pEnumerator; + IWbemClassObject *pObj; + ULONG uReturned; + VARIANT vt; + int len, pos; + + /* + * Try to get adapter descriptor through WMI CIMv2 Win32_NetworkAdapter + * to have better probability of matching with InstanceName from + * MSNdis events. If this fails, use the provided description. + */ + + os_free(events->adapter_desc); + events->adapter_desc = NULL; + + hr = call_IWbemLocator_ConnectServer( + events->pLoc, L"ROOT\\CIMV2", NULL, NULL, 0, 0, 0, 0, &pSvc); + if (FAILED(hr)) { + wpa_printf(MSG_ERROR, "ndis_events: Could not connect to WMI " + "server (ROOT\\CIMV2) - error 0x%x", (int) hr); + return ndis_events_use_desc(events, desc); + } + wpa_printf(MSG_DEBUG, "ndis_events: Connected to ROOT\\CIMV2."); + + _snwprintf(query, MAX_QUERY_LEN, + L"SELECT Index FROM Win32_NetworkAdapterConfiguration " + L"WHERE SettingID='%S'", ifname); + wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query); + + hr = call_IWbemServices_ExecQuery( + pSvc, L"WQL", query, + WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, + NULL, &pEnumerator); + if (!SUCCEEDED(hr)) { + wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface " + "GUID from Win32_NetworkAdapterConfiguration: " + "0x%x", (int) hr); + IWbemServices_Release(pSvc); + return ndis_events_use_desc(events, desc); + } + + uReturned = 0; + hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1, + &pObj, &uReturned); + if (!SUCCEEDED(hr) || uReturned == 0) { + wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface " + "GUID from Win32_NetworkAdapterConfiguration: " + "0x%x", (int) hr); + IEnumWbemClassObject_Release(pEnumerator); + IWbemServices_Release(pSvc); + return ndis_events_use_desc(events, desc); + } + IEnumWbemClassObject_Release(pEnumerator); + + VariantInit(&vt); + hr = IWbemClassObject_Get(pObj, L"Index", 0, &vt, NULL, NULL); + if (!SUCCEEDED(hr)) { + wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Index from " + "Win32_NetworkAdapterConfiguration: 0x%x", + (int) hr); + IWbemServices_Release(pSvc); + return ndis_events_use_desc(events, desc); + } + + _snwprintf(query, MAX_QUERY_LEN, + L"SELECT Name,PNPDeviceID FROM Win32_NetworkAdapter WHERE " + L"Index=%d", + vt.uintVal); + wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query); + VariantClear(&vt); + IWbemClassObject_Release(pObj); + + hr = call_IWbemServices_ExecQuery( + pSvc, L"WQL", query, + WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, + NULL, &pEnumerator); + if (!SUCCEEDED(hr)) { + wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface " + "from Win32_NetworkAdapter: 0x%x", (int) hr); + IWbemServices_Release(pSvc); + return ndis_events_use_desc(events, desc); + } + + uReturned = 0; + hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1, + &pObj, &uReturned); + if (!SUCCEEDED(hr) || uReturned == 0) { + wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface " + "from Win32_NetworkAdapter: 0x%x", (int) hr); + IEnumWbemClassObject_Release(pEnumerator); + IWbemServices_Release(pSvc); + return ndis_events_use_desc(events, desc); + } + IEnumWbemClassObject_Release(pEnumerator); + + hr = IWbemClassObject_Get(pObj, L"Name", 0, &vt, NULL, NULL); + if (!SUCCEEDED(hr)) { + wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Name from " + "Win32_NetworkAdapter: 0x%x", (int) hr); + IWbemClassObject_Release(pObj); + IWbemServices_Release(pSvc); + return ndis_events_use_desc(events, desc); + } + + wpa_printf(MSG_DEBUG, "ndis_events: Win32_NetworkAdapter::Name='%S'", + vt.bstrVal); + events->adapter_desc = _wcsdup(vt.bstrVal); + VariantClear(&vt); + + /* + * Try to get even better candidate for matching with InstanceName + * from Win32_PnPEntity. This is needed at least for some USB cards + * that can change the InstanceName whenever being unplugged and + * plugged again. + */ + + hr = IWbemClassObject_Get(pObj, L"PNPDeviceID", 0, &vt, NULL, NULL); + if (!SUCCEEDED(hr)) { + wpa_printf(MSG_DEBUG, "ndis_events: Failed to get PNPDeviceID " + "from Win32_NetworkAdapter: 0x%x", (int) hr); + IWbemClassObject_Release(pObj); + IWbemServices_Release(pSvc); + if (events->adapter_desc == NULL) + return ndis_events_use_desc(events, desc); + return 0; /* use Win32_NetworkAdapter::Name */ + } + + wpa_printf(MSG_DEBUG, "ndis_events: Win32_NetworkAdapter::PNPDeviceID=" + "'%S'", vt.bstrVal); + + len = _snwprintf(query, MAX_QUERY_LEN, + L"SELECT Name FROM Win32_PnPEntity WHERE DeviceID='"); + if (len < 0 || len >= MAX_QUERY_LEN - 1) { + VariantClear(&vt); + IWbemClassObject_Release(pObj); + IWbemServices_Release(pSvc); + if (events->adapter_desc == NULL) + return ndis_events_use_desc(events, desc); + return 0; /* use Win32_NetworkAdapter::Name */ + } + + /* Escape \ as \\ */ + for (pos = 0; vt.bstrVal[pos] && len < MAX_QUERY_LEN - 2; pos++) { + if (vt.bstrVal[pos] == '\\') { + if (len >= MAX_QUERY_LEN - 3) + break; + query[len++] = '\\'; + } + query[len++] = vt.bstrVal[pos]; + } + query[len++] = L'\''; + query[len] = L'\0'; + VariantClear(&vt); + IWbemClassObject_Release(pObj); + wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query); + + hr = call_IWbemServices_ExecQuery( + pSvc, L"WQL", query, + WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, + NULL, &pEnumerator); + if (!SUCCEEDED(hr)) { + wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface " + "Name from Win32_PnPEntity: 0x%x", (int) hr); + IWbemServices_Release(pSvc); + if (events->adapter_desc == NULL) + return ndis_events_use_desc(events, desc); + return 0; /* use Win32_NetworkAdapter::Name */ + } + + uReturned = 0; + hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1, + &pObj, &uReturned); + if (!SUCCEEDED(hr) || uReturned == 0) { + wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface " + "from Win32_PnPEntity: 0x%x", (int) hr); + IEnumWbemClassObject_Release(pEnumerator); + IWbemServices_Release(pSvc); + if (events->adapter_desc == NULL) + return ndis_events_use_desc(events, desc); + return 0; /* use Win32_NetworkAdapter::Name */ + } + IEnumWbemClassObject_Release(pEnumerator); + + hr = IWbemClassObject_Get(pObj, L"Name", 0, &vt, NULL, NULL); + if (!SUCCEEDED(hr)) { + wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Name from " + "Win32_PnPEntity: 0x%x", (int) hr); + IWbemClassObject_Release(pObj); + IWbemServices_Release(pSvc); + if (events->adapter_desc == NULL) + return ndis_events_use_desc(events, desc); + return 0; /* use Win32_NetworkAdapter::Name */ + } + + wpa_printf(MSG_DEBUG, "ndis_events: Win32_PnPEntity::Name='%S'", + vt.bstrVal); + os_free(events->adapter_desc); + events->adapter_desc = _wcsdup(vt.bstrVal); + VariantClear(&vt); + + IWbemClassObject_Release(pObj); + + IWbemServices_Release(pSvc); + + if (events->adapter_desc == NULL) + return ndis_events_use_desc(events, desc); + + return 0; +} + + +struct ndis_events_data * +ndis_events_init(HANDLE *read_pipe, HANDLE *event_avail, + const char *ifname, const char *desc) +{ + HRESULT hr; + IWbemObjectSink *pSink; + struct ndis_events_data *events; + + events = os_zalloc(sizeof(*events)); + if (events == NULL) { + wpa_printf(MSG_ERROR, "Could not allocate sink for events."); + return NULL; + } + events->ifname = os_strdup(ifname); + if (events->ifname == NULL) { + os_free(events); + return NULL; + } + + if (wmi_refcnt++ == 0) { + hr = CoInitializeEx(0, COINIT_MULTITHREADED); + if (FAILED(hr)) { + wpa_printf(MSG_ERROR, "CoInitializeEx() failed - " + "returned 0x%x", (int) hr); + os_free(events); + return NULL; + } + } + + if (wmi_first) { + /* CoInitializeSecurity() must be called once and only once + * per process, so let's use wmi_first flag to protect against + * multiple calls. */ + wmi_first = 0; + + hr = CoInitializeSecurity(NULL, -1, NULL, NULL, + RPC_C_AUTHN_LEVEL_PKT_PRIVACY, + RPC_C_IMP_LEVEL_IMPERSONATE, + NULL, EOAC_SECURE_REFS, NULL); + if (FAILED(hr)) { + wpa_printf(MSG_ERROR, "CoInitializeSecurity() failed " + "- returned 0x%x", (int) hr); + os_free(events); + return NULL; + } + } + + hr = CoCreateInstance(&CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, + &IID_IWbemLocator, + (LPVOID *) (void *) &events->pLoc); + if (FAILED(hr)) { + wpa_printf(MSG_ERROR, "CoCreateInstance() failed - returned " + "0x%x", (int) hr); + CoUninitialize(); + os_free(events); + return NULL; + } + + if (ndis_events_get_adapter(events, ifname, desc) < 0) { + CoUninitialize(); + os_free(events); + return NULL; + } + wpa_printf(MSG_DEBUG, "ndis_events: use adapter descriptor '%S'", + events->adapter_desc); + + hr = call_IWbemLocator_ConnectServer( + events->pLoc, L"ROOT\\WMI", NULL, NULL, + 0, 0, 0, 0, &events->pSvc); + if (FAILED(hr)) { + wpa_printf(MSG_ERROR, "Could not connect to server - error " + "0x%x", (int) hr); + CoUninitialize(); + os_free(events->adapter_desc); + os_free(events); + return NULL; + } + wpa_printf(MSG_DEBUG, "Connected to ROOT\\WMI."); + + ndis_events_constructor(events); + pSink = &events->sink; + pSink->lpVtbl = &events->sink_vtbl; + events->sink_vtbl.QueryInterface = ndis_events_query_interface; + events->sink_vtbl.AddRef = ndis_events_add_ref; + events->sink_vtbl.Release = ndis_events_release; + events->sink_vtbl.Indicate = ndis_events_indicate; + events->sink_vtbl.SetStatus = ndis_events_set_status; + + if (register_async_notification(pSink, events->pSvc) < 0) { + wpa_printf(MSG_DEBUG, "Failed to register async " + "notifications"); + ndis_events_destructor(events); + os_free(events->adapter_desc); + os_free(events); + return NULL; + } + + *read_pipe = events->read_pipe; + *event_avail = events->event_avail; + + return events; +} diff --git a/src/drivers/priv_netlink.h b/src/drivers/priv_netlink.h new file mode 100644 index 0000000..2a31e25 --- /dev/null +++ b/src/drivers/priv_netlink.h @@ -0,0 +1,104 @@ +/* + * wpa_supplicant - Private copy of Linux netlink/rtnetlink definitions. + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef PRIV_NETLINK_H +#define PRIV_NETLINK_H + +/* + * This should be replaced with user space header once one is available with C + * library, etc.. + */ + +#ifndef IFF_LOWER_UP +#define IFF_LOWER_UP 0x10000 /* driver signals L1 up */ +#endif +#ifndef IFF_DORMANT +#define IFF_DORMANT 0x20000 /* driver signals dormant */ +#endif + +#ifndef IFLA_IFNAME +#define IFLA_IFNAME 3 +#endif +#ifndef IFLA_WIRELESS +#define IFLA_WIRELESS 11 +#endif +#ifndef IFLA_OPERSTATE +#define IFLA_OPERSTATE 16 +#endif +#ifndef IFLA_LINKMODE +#define IFLA_LINKMODE 17 +#define IF_OPER_DORMANT 5 +#define IF_OPER_UP 6 +#endif + +#define NLM_F_REQUEST 1 + +#define NETLINK_ROUTE 0 +#define RTMGRP_LINK 1 +#define RTM_BASE 0x10 +#define RTM_NEWLINK (RTM_BASE + 0) +#define RTM_DELLINK (RTM_BASE + 1) +#define RTM_SETLINK (RTM_BASE + 3) + +#define NLMSG_ALIGNTO 4 +#define NLMSG_ALIGN(len) (((len) + NLMSG_ALIGNTO - 1) & ~(NLMSG_ALIGNTO - 1)) +#define NLMSG_LENGTH(len) ((len) + NLMSG_ALIGN(sizeof(struct nlmsghdr))) +#define NLMSG_DATA(nlh) ((void*) (((char*) nlh) + NLMSG_LENGTH(0))) + +#define RTA_ALIGNTO 4 +#define RTA_ALIGN(len) (((len) + RTA_ALIGNTO - 1) & ~(RTA_ALIGNTO - 1)) +#define RTA_OK(rta,len) \ +((len) > 0 && (rta)->rta_len >= sizeof(struct rtattr) && \ +(rta)->rta_len <= (len)) +#define RTA_NEXT(rta,attrlen) \ +((attrlen) -= RTA_ALIGN((rta)->rta_len), \ +(struct rtattr *) (((char *)(rta)) + RTA_ALIGN((rta)->rta_len))) +#define RTA_LENGTH(len) (RTA_ALIGN(sizeof(struct rtattr)) + (len)) +#define RTA_DATA(rta) ((void *) (((char *) (rta)) + RTA_LENGTH(0))) + + +struct sockaddr_nl +{ + sa_family_t nl_family; + unsigned short nl_pad; + u32 nl_pid; + u32 nl_groups; +}; + +struct nlmsghdr +{ + u32 nlmsg_len; + u16 nlmsg_type; + u16 nlmsg_flags; + u32 nlmsg_seq; + u32 nlmsg_pid; +}; + +struct ifinfomsg +{ + unsigned char ifi_family; + unsigned char __ifi_pad; + unsigned short ifi_type; + int ifi_index; + unsigned ifi_flags; + unsigned ifi_change; +}; + +struct rtattr +{ + unsigned short rta_len; + unsigned short rta_type; +}; + +#endif /* PRIV_NETLINK_H */ diff --git a/src/drivers/radiotap.c b/src/drivers/radiotap.c new file mode 100644 index 0000000..804473f --- /dev/null +++ b/src/drivers/radiotap.c @@ -0,0 +1,287 @@ +/* + * Radiotap parser + * + * Copyright 2007 Andy Green <andy@warmcat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * + * Modified for userspace by Johannes Berg <johannes@sipsolutions.net> + * I only modified some things on top to ease syncing should bugs be found. + */ + +#include "includes.h" + +#include "common.h" +#include "radiotap_iter.h" + +#define le16_to_cpu le_to_host16 +#define le32_to_cpu le_to_host32 +#define __le32 uint32_t +#define ulong unsigned long +#define unlikely(cond) (cond) +#define get_unaligned(p) \ +({ \ + struct packed_dummy_struct { \ + typeof(*(p)) __val; \ + } __attribute__((packed)) *__ptr = (void *) (p); \ + \ + __ptr->__val; \ +}) + +/* function prototypes and related defs are in radiotap_iter.h */ + +/** + * ieee80211_radiotap_iterator_init - radiotap parser iterator initialization + * @iterator: radiotap_iterator to initialize + * @radiotap_header: radiotap header to parse + * @max_length: total length we can parse into (eg, whole packet length) + * + * Returns: 0 or a negative error code if there is a problem. + * + * This function initializes an opaque iterator struct which can then + * be passed to ieee80211_radiotap_iterator_next() to visit every radiotap + * argument which is present in the header. It knows about extended + * present headers and handles them. + * + * How to use: + * call __ieee80211_radiotap_iterator_init() to init a semi-opaque iterator + * struct ieee80211_radiotap_iterator (no need to init the struct beforehand) + * checking for a good 0 return code. Then loop calling + * __ieee80211_radiotap_iterator_next()... it returns either 0, + * -ENOENT if there are no more args to parse, or -EINVAL if there is a problem. + * The iterator's @this_arg member points to the start of the argument + * associated with the current argument index that is present, which can be + * found in the iterator's @this_arg_index member. This arg index corresponds + * to the IEEE80211_RADIOTAP_... defines. + * + * Radiotap header length: + * You can find the CPU-endian total radiotap header length in + * iterator->max_length after executing ieee80211_radiotap_iterator_init() + * successfully. + * + * Alignment Gotcha: + * You must take care when dereferencing iterator.this_arg + * for multibyte types... the pointer is not aligned. Use + * get_unaligned((type *)iterator.this_arg) to dereference + * iterator.this_arg for type "type" safely on all arches. + * + * Example code: + * See Documentation/networking/radiotap-headers.txt + */ + +int ieee80211_radiotap_iterator_init( + struct ieee80211_radiotap_iterator *iterator, + struct ieee80211_radiotap_header *radiotap_header, + int max_length) +{ + /* Linux only supports version 0 radiotap format */ + if (radiotap_header->it_version) + return -EINVAL; + + /* sanity check for allowed length and radiotap length field */ + if (max_length < le16_to_cpu(get_unaligned(&radiotap_header->it_len))) + return -EINVAL; + + iterator->rtheader = radiotap_header; + iterator->max_length = le16_to_cpu(get_unaligned( + &radiotap_header->it_len)); + iterator->arg_index = 0; + iterator->bitmap_shifter = le32_to_cpu(get_unaligned( + &radiotap_header->it_present)); + iterator->arg = (u8 *)radiotap_header + sizeof(*radiotap_header); + iterator->this_arg = NULL; + + /* find payload start allowing for extended bitmap(s) */ + + if (unlikely(iterator->bitmap_shifter & (1<<IEEE80211_RADIOTAP_EXT))) { + while (le32_to_cpu(get_unaligned((__le32 *)iterator->arg)) & + (1<<IEEE80211_RADIOTAP_EXT)) { + iterator->arg += sizeof(u32); + + /* + * check for insanity where the present bitmaps + * keep claiming to extend up to or even beyond the + * stated radiotap header length + */ + + if (((ulong)iterator->arg - (ulong)iterator->rtheader) + > (ulong)iterator->max_length) + return -EINVAL; + } + + iterator->arg += sizeof(u32); + + /* + * no need to check again for blowing past stated radiotap + * header length, because ieee80211_radiotap_iterator_next + * checks it before it is dereferenced + */ + } + + /* we are all initialized happily */ + + return 0; +} + + +/** + * ieee80211_radiotap_iterator_next - return next radiotap parser iterator arg + * @iterator: radiotap_iterator to move to next arg (if any) + * + * Returns: 0 if there is an argument to handle, + * -ENOENT if there are no more args or -EINVAL + * if there is something else wrong. + * + * This function provides the next radiotap arg index (IEEE80211_RADIOTAP_*) + * in @this_arg_index and sets @this_arg to point to the + * payload for the field. It takes care of alignment handling and extended + * present fields. @this_arg can be changed by the caller (eg, + * incremented to move inside a compound argument like + * IEEE80211_RADIOTAP_CHANNEL). The args pointed to are in + * little-endian format whatever the endianess of your CPU. + * + * Alignment Gotcha: + * You must take care when dereferencing iterator.this_arg + * for multibyte types... the pointer is not aligned. Use + * get_unaligned((type *)iterator.this_arg) to dereference + * iterator.this_arg for type "type" safely on all arches. + */ + +int ieee80211_radiotap_iterator_next( + struct ieee80211_radiotap_iterator *iterator) +{ + + /* + * small length lookup table for all radiotap types we heard of + * starting from b0 in the bitmap, so we can walk the payload + * area of the radiotap header + * + * There is a requirement to pad args, so that args + * of a given length must begin at a boundary of that length + * -- but note that compound args are allowed (eg, 2 x u16 + * for IEEE80211_RADIOTAP_CHANNEL) so total arg length is not + * a reliable indicator of alignment requirement. + * + * upper nybble: content alignment for arg + * lower nybble: content length for arg + */ + + static const u8 rt_sizes[] = { + [IEEE80211_RADIOTAP_TSFT] = 0x88, + [IEEE80211_RADIOTAP_FLAGS] = 0x11, + [IEEE80211_RADIOTAP_RATE] = 0x11, + [IEEE80211_RADIOTAP_CHANNEL] = 0x24, + [IEEE80211_RADIOTAP_FHSS] = 0x22, + [IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = 0x11, + [IEEE80211_RADIOTAP_DBM_ANTNOISE] = 0x11, + [IEEE80211_RADIOTAP_LOCK_QUALITY] = 0x22, + [IEEE80211_RADIOTAP_TX_ATTENUATION] = 0x22, + [IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = 0x22, + [IEEE80211_RADIOTAP_DBM_TX_POWER] = 0x11, + [IEEE80211_RADIOTAP_ANTENNA] = 0x11, + [IEEE80211_RADIOTAP_DB_ANTSIGNAL] = 0x11, + [IEEE80211_RADIOTAP_DB_ANTNOISE] = 0x11, + [IEEE80211_RADIOTAP_RX_FLAGS] = 0x22, + [IEEE80211_RADIOTAP_TX_FLAGS] = 0x22, + [IEEE80211_RADIOTAP_RTS_RETRIES] = 0x11, + [IEEE80211_RADIOTAP_DATA_RETRIES] = 0x11, + /* + * add more here as they are defined in + * include/net/ieee80211_radiotap.h + */ + }; + + /* + * for every radiotap entry we can at + * least skip (by knowing the length)... + */ + + while (iterator->arg_index < (int) sizeof(rt_sizes)) { + int hit = 0; + int pad; + + if (!(iterator->bitmap_shifter & 1)) + goto next_entry; /* arg not present */ + + /* + * arg is present, account for alignment padding + * 8-bit args can be at any alignment + * 16-bit args must start on 16-bit boundary + * 32-bit args must start on 32-bit boundary + * 64-bit args must start on 64-bit boundary + * + * note that total arg size can differ from alignment of + * elements inside arg, so we use upper nybble of length + * table to base alignment on + * + * also note: these alignments are ** relative to the + * start of the radiotap header **. There is no guarantee + * that the radiotap header itself is aligned on any + * kind of boundary. + * + * the above is why get_unaligned() is used to dereference + * multibyte elements from the radiotap area + */ + + pad = (((ulong)iterator->arg) - + ((ulong)iterator->rtheader)) & + ((rt_sizes[iterator->arg_index] >> 4) - 1); + + if (pad) + iterator->arg += + (rt_sizes[iterator->arg_index] >> 4) - pad; + + /* + * this is what we will return to user, but we need to + * move on first so next call has something fresh to test + */ + iterator->this_arg_index = iterator->arg_index; + iterator->this_arg = iterator->arg; + hit = 1; + + /* internally move on the size of this arg */ + iterator->arg += rt_sizes[iterator->arg_index] & 0x0f; + + /* + * check for insanity where we are given a bitmap that + * claims to have more arg content than the length of the + * radiotap section. We will normally end up equalling this + * max_length on the last arg, never exceeding it. + */ + + if (((ulong)iterator->arg - (ulong)iterator->rtheader) > + (ulong) iterator->max_length) + return -EINVAL; + + next_entry: + iterator->arg_index++; + if (unlikely((iterator->arg_index & 31) == 0)) { + /* completed current u32 bitmap */ + if (iterator->bitmap_shifter & 1) { + /* b31 was set, there is more */ + /* move to next u32 bitmap */ + iterator->bitmap_shifter = le32_to_cpu( + get_unaligned(iterator->next_bitmap)); + iterator->next_bitmap++; + } else + /* no more bitmaps: end */ + iterator->arg_index = sizeof(rt_sizes); + } else /* just try the next bit */ + iterator->bitmap_shifter >>= 1; + + /* if we found a valid arg earlier, return it now */ + if (hit) + return 0; + } + + /* we don't know how to handle any more args, we're done */ + return -ENOENT; +} diff --git a/src/drivers/radiotap.h b/src/drivers/radiotap.h new file mode 100644 index 0000000..508264c --- /dev/null +++ b/src/drivers/radiotap.h @@ -0,0 +1,242 @@ +/* $FreeBSD: src/sys/net80211/ieee80211_radiotap.h,v 1.5 2005/01/22 20:12:05 sam Exp $ */ +/* $NetBSD: ieee80211_radiotap.h,v 1.11 2005/06/22 06:16:02 dyoung Exp $ */ + +/*- + * Copyright (c) 2003, 2004 David Young. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of David Young may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY DAVID YOUNG ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DAVID + * YOUNG BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +/* + * Modifications to fit into the linux IEEE 802.11 stack, + * Mike Kershaw (dragorn@kismetwireless.net) + */ + +#ifndef IEEE80211RADIOTAP_H +#define IEEE80211RADIOTAP_H + +#include <stdint.h> + +/* Base version of the radiotap packet header data */ +#define PKTHDR_RADIOTAP_VERSION 0 + +/* A generic radio capture format is desirable. There is one for + * Linux, but it is neither rigidly defined (there were not even + * units given for some fields) nor easily extensible. + * + * I suggest the following extensible radio capture format. It is + * based on a bitmap indicating which fields are present. + * + * I am trying to describe precisely what the application programmer + * should expect in the following, and for that reason I tell the + * units and origin of each measurement (where it applies), or else I + * use sufficiently weaselly language ("is a monotonically nondecreasing + * function of...") that I cannot set false expectations for lawyerly + * readers. + */ + +/* The radio capture header precedes the 802.11 header. + * All data in the header is little endian on all platforms. + */ +struct ieee80211_radiotap_header { + uint8_t it_version; /* Version 0. Only increases + * for drastic changes, + * introduction of compatible + * new fields does not count. + */ + uint8_t it_pad; + uint16_t it_len; /* length of the whole + * header in bytes, including + * it_version, it_pad, + * it_len, and data fields. + */ + uint32_t it_present; /* A bitmap telling which + * fields are present. Set bit 31 + * (0x80000000) to extend the + * bitmap by another 32 bits. + * Additional extensions are made + * by setting bit 31. + */ +}; + +/* Name Data type Units + * ---- --------- ----- + * + * IEEE80211_RADIOTAP_TSFT __le64 microseconds + * + * Value in microseconds of the MAC's 64-bit 802.11 Time + * Synchronization Function timer when the first bit of the + * MPDU arrived at the MAC. For received frames, only. + * + * IEEE80211_RADIOTAP_CHANNEL 2 x uint16_t MHz, bitmap + * + * Tx/Rx frequency in MHz, followed by flags (see below). + * + * IEEE80211_RADIOTAP_FHSS uint16_t see below + * + * For frequency-hopping radios, the hop set (first byte) + * and pattern (second byte). + * + * IEEE80211_RADIOTAP_RATE u8 500kb/s + * + * Tx/Rx data rate + * + * IEEE80211_RADIOTAP_DBM_ANTSIGNAL s8 decibels from + * one milliwatt (dBm) + * + * RF signal power at the antenna, decibel difference from + * one milliwatt. + * + * IEEE80211_RADIOTAP_DBM_ANTNOISE s8 decibels from + * one milliwatt (dBm) + * + * RF noise power at the antenna, decibel difference from one + * milliwatt. + * + * IEEE80211_RADIOTAP_DB_ANTSIGNAL u8 decibel (dB) + * + * RF signal power at the antenna, decibel difference from an + * arbitrary, fixed reference. + * + * IEEE80211_RADIOTAP_DB_ANTNOISE u8 decibel (dB) + * + * RF noise power at the antenna, decibel difference from an + * arbitrary, fixed reference point. + * + * IEEE80211_RADIOTAP_LOCK_QUALITY uint16_t unitless + * + * Quality of Barker code lock. Unitless. Monotonically + * nondecreasing with "better" lock strength. Called "Signal + * Quality" in datasheets. (Is there a standard way to measure + * this?) + * + * IEEE80211_RADIOTAP_TX_ATTENUATION uint16_t unitless + * + * Transmit power expressed as unitless distance from max + * power set at factory calibration. 0 is max power. + * Monotonically nondecreasing with lower power levels. + * + * IEEE80211_RADIOTAP_DB_TX_ATTENUATION uint16_t decibels (dB) + * + * Transmit power expressed as decibel distance from max power + * set at factory calibration. 0 is max power. Monotonically + * nondecreasing with lower power levels. + * + * IEEE80211_RADIOTAP_DBM_TX_POWER s8 decibels from + * one milliwatt (dBm) + * + * Transmit power expressed as dBm (decibels from a 1 milliwatt + * reference). This is the absolute power level measured at + * the antenna port. + * + * IEEE80211_RADIOTAP_FLAGS u8 bitmap + * + * Properties of transmitted and received frames. See flags + * defined below. + * + * IEEE80211_RADIOTAP_ANTENNA u8 antenna index + * + * Unitless indication of the Rx/Tx antenna for this packet. + * The first antenna is antenna 0. + * + * IEEE80211_RADIOTAP_RX_FLAGS uint16_t bitmap + * + * Properties of received frames. See flags defined below. + * + * IEEE80211_RADIOTAP_TX_FLAGS uint16_t bitmap + * + * Properties of transmitted frames. See flags defined below. + * + * IEEE80211_RADIOTAP_RTS_RETRIES u8 data + * + * Number of rts retries a transmitted frame used. + * + * IEEE80211_RADIOTAP_DATA_RETRIES u8 data + * + * Number of unicast retries a transmitted frame used. + * + */ +enum ieee80211_radiotap_type { + IEEE80211_RADIOTAP_TSFT = 0, + IEEE80211_RADIOTAP_FLAGS = 1, + IEEE80211_RADIOTAP_RATE = 2, + IEEE80211_RADIOTAP_CHANNEL = 3, + IEEE80211_RADIOTAP_FHSS = 4, + IEEE80211_RADIOTAP_DBM_ANTSIGNAL = 5, + IEEE80211_RADIOTAP_DBM_ANTNOISE = 6, + IEEE80211_RADIOTAP_LOCK_QUALITY = 7, + IEEE80211_RADIOTAP_TX_ATTENUATION = 8, + IEEE80211_RADIOTAP_DB_TX_ATTENUATION = 9, + IEEE80211_RADIOTAP_DBM_TX_POWER = 10, + IEEE80211_RADIOTAP_ANTENNA = 11, + IEEE80211_RADIOTAP_DB_ANTSIGNAL = 12, + IEEE80211_RADIOTAP_DB_ANTNOISE = 13, + IEEE80211_RADIOTAP_RX_FLAGS = 14, + IEEE80211_RADIOTAP_TX_FLAGS = 15, + IEEE80211_RADIOTAP_RTS_RETRIES = 16, + IEEE80211_RADIOTAP_DATA_RETRIES = 17, + IEEE80211_RADIOTAP_EXT = 31 +}; + +/* Channel flags. */ +#define IEEE80211_CHAN_TURBO 0x0010 /* Turbo channel */ +#define IEEE80211_CHAN_CCK 0x0020 /* CCK channel */ +#define IEEE80211_CHAN_OFDM 0x0040 /* OFDM channel */ +#define IEEE80211_CHAN_2GHZ 0x0080 /* 2 GHz spectrum channel. */ +#define IEEE80211_CHAN_5GHZ 0x0100 /* 5 GHz spectrum channel */ +#define IEEE80211_CHAN_PASSIVE 0x0200 /* Only passive scan allowed */ +#define IEEE80211_CHAN_DYN 0x0400 /* Dynamic CCK-OFDM channel */ +#define IEEE80211_CHAN_GFSK 0x0800 /* GFSK channel (FHSS PHY) */ + +/* For IEEE80211_RADIOTAP_FLAGS */ +#define IEEE80211_RADIOTAP_F_CFP 0x01 /* sent/received + * during CFP + */ +#define IEEE80211_RADIOTAP_F_SHORTPRE 0x02 /* sent/received + * with short + * preamble + */ +#define IEEE80211_RADIOTAP_F_WEP 0x04 /* sent/received + * with WEP encryption + */ +#define IEEE80211_RADIOTAP_F_FRAG 0x08 /* sent/received + * with fragmentation + */ +#define IEEE80211_RADIOTAP_F_FCS 0x10 /* frame includes FCS */ +#define IEEE80211_RADIOTAP_F_DATAPAD 0x20 /* frame has padding between + * 802.11 header and payload + * (to 32-bit boundary) + */ +/* For IEEE80211_RADIOTAP_RX_FLAGS */ +#define IEEE80211_RADIOTAP_F_RX_BADFCS 0x0001 /* frame failed crc check */ + +/* For IEEE80211_RADIOTAP_TX_FLAGS */ +#define IEEE80211_RADIOTAP_F_TX_FAIL 0x0001 /* failed due to excessive + * retries */ +#define IEEE80211_RADIOTAP_F_TX_CTS 0x0002 /* used cts 'protection' */ +#define IEEE80211_RADIOTAP_F_TX_RTS 0x0004 /* used rts/cts handshake */ + +#endif /* IEEE80211_RADIOTAP_H */ diff --git a/src/drivers/radiotap_iter.h b/src/drivers/radiotap_iter.h new file mode 100644 index 0000000..92a798a --- /dev/null +++ b/src/drivers/radiotap_iter.h @@ -0,0 +1,41 @@ +#ifndef __RADIOTAP_ITER_H +#define __RADIOTAP_ITER_H + +#include "radiotap.h" + +/* Radiotap header iteration + * implemented in radiotap.c + */ +/** + * struct ieee80211_radiotap_iterator - tracks walk thru present radiotap args + * @rtheader: pointer to the radiotap header we are walking through + * @max_length: length of radiotap header in cpu byte ordering + * @this_arg_index: IEEE80211_RADIOTAP_... index of current arg + * @this_arg: pointer to current radiotap arg + * @arg_index: internal next argument index + * @arg: internal next argument pointer + * @next_bitmap: internal pointer to next present u32 + * @bitmap_shifter: internal shifter for curr u32 bitmap, b0 set == arg present + */ + +struct ieee80211_radiotap_iterator { + struct ieee80211_radiotap_header *rtheader; + int max_length; + int this_arg_index; + unsigned char *this_arg; + + int arg_index; + unsigned char *arg; + uint32_t *next_bitmap; + uint32_t bitmap_shifter; +}; + +extern int ieee80211_radiotap_iterator_init( + struct ieee80211_radiotap_iterator *iterator, + struct ieee80211_radiotap_header *radiotap_header, + int max_length); + +extern int ieee80211_radiotap_iterator_next( + struct ieee80211_radiotap_iterator *iterator); + +#endif /* __RADIOTAP_ITER_H */ diff --git a/src/eap_common/.gitignore b/src/eap_common/.gitignore deleted file mode 100644 index a438335..0000000 --- a/src/eap_common/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.d diff --git a/src/eap_common/eap_defs.h b/src/eap_common/eap_defs.h index 0ac95b5..0efe7ab 100644 --- a/src/eap_common/eap_defs.h +++ b/src/eap_common/eap_defs.h @@ -67,7 +67,7 @@ typedef enum { EAP_TYPE_SAKE = 48 /* RFC 4763 */, EAP_TYPE_IKEV2 = 49 /* RFC 5106 */, EAP_TYPE_AKA_PRIME = 50 /* draft-arkko-eap-aka-kdf-10.txt */, - EAP_TYPE_GPSK = 51 /* draft-ietf-emu-eap-gpsk-17.txt */, + EAP_TYPE_GPSK = 51 /* RFC 5433 */, EAP_TYPE_EXPANDED = 254 /* RFC 3748 */ } EapType; diff --git a/src/eap_common/eap_fast_common.h b/src/eap_common/eap_fast_common.h index 257123e..c85fd37 100644 --- a/src/eap_common/eap_fast_common.h +++ b/src/eap_common/eap_fast_common.h @@ -24,8 +24,7 @@ #define TLS_EXT_PAC_OPAQUE 35 /* - * draft-cam-winget-eap-fast-provisioning-04.txt: - * Section 4.2.1 - Formats for PAC TLV Attributes / Type Field + * RFC 5422: Section 4.2.1 - Formats for PAC TLV Attributes / Type Field * Note: bit 0x8000 (Mandatory) and bit 0x4000 (Reserved) are also defined * in the general PAC TLV format (Section 4.2). */ @@ -59,10 +58,7 @@ struct pac_tlv_hdr { #define EAP_FAST_PAC_KEY_LEN 32 -/* draft-cam-winget-eap-fast-provisioning-04.txt: 4.2.6 PAC-Type TLV - * Note: Machine Authentication PAC and User Authorization PAC were removed in - * draft-cam-winget-eap-fast-provisioning-03.txt - */ +/* RFC 5422: 4.2.6 PAC-Type TLV */ #define PAC_TYPE_TUNNEL_PAC 1 /* Application Specific Short Lived PACs (only in volatile storage) */ /* User Authorization PAC */ @@ -73,8 +69,8 @@ struct pac_tlv_hdr { /* - * draft-cam-winget-eap-fast-provisioning-04.txt: - * Section 3.4 - Key Derivations Used in the EAP-FAST Provisioning Exchange + * RFC 5422: + * Section 3.3 - Key Derivations Used in the EAP-FAST Provisioning Exchange */ struct eap_fast_key_block_provisioning { /* Extra key material after TLS key_block */ diff --git a/src/eap_common/eap_tlv_common.h b/src/eap_common/eap_tlv_common.h index e3836d5..f86015d 100644 --- a/src/eap_common/eap_tlv_common.h +++ b/src/eap_common/eap_tlv_common.h @@ -24,8 +24,7 @@ #define EAP_TLV_URI_TLV 8 #define EAP_TLV_EAP_PAYLOAD_TLV 9 #define EAP_TLV_INTERMEDIATE_RESULT_TLV 10 -#define EAP_TLV_PAC_TLV 11 /* draft-cam-winget-eap-fast-provisioning-04.txt, - * Section 4.2 */ +#define EAP_TLV_PAC_TLV 11 /* RFC 5422, Section 4.2 */ #define EAP_TLV_CRYPTO_BINDING_TLV 12 #define EAP_TLV_CALLING_STATION_ID_TLV 13 #define EAP_TLV_CALLED_STATION_ID_TLV 14 @@ -99,7 +98,7 @@ struct eap_tlv_request_action_tlv { be16 action; } STRUCT_PACKED; -/* draft-cam-winget-eap-fast-provisiong-04.txt, Section 4.2.6 - PAC-Type TLV */ +/* RFC 5422, Section 4.2.6 - PAC-Type TLV */ struct eap_tlv_pac_type_tlv { be16 tlv_type; /* PAC_TYPE_PAC_TYPE */ be16 length; diff --git a/src/eap_peer/.gitignore b/src/eap_peer/.gitignore deleted file mode 100644 index a438335..0000000 --- a/src/eap_peer/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.d diff --git a/src/eap_peer/eap_fast.c b/src/eap_peer/eap_fast.c index 07e345f..d008670 100644 --- a/src/eap_peer/eap_fast.c +++ b/src/eap_peer/eap_fast.c @@ -918,10 +918,7 @@ static int eap_fast_parse_pac_info(struct eap_fast_pac *entry, int type, entry->a_id_info_len = len; break; case PAC_TYPE_PAC_TYPE: - /* - * draft-cam-winget-eap-fast-provisioning-04.txt, - * Section 4.2.6 - PAC-Type TLV - */ + /* RFC 5422, Section 4.2.6 - PAC-Type TLV */ if (len != 2) { wpa_printf(MSG_INFO, "EAP-FAST: Invalid PAC-Type " "length %lu (expected 2)", @@ -961,7 +958,7 @@ static int eap_fast_process_pac_info(struct eap_fast_pac *entry) size_t left, len; int type; - /* draft-cam-winget-eap-fast-provisioning-04.txt, Section 4.2.4 */ + /* RFC 5422, Section 4.2.4 */ /* PAC-Type defaults to Tunnel PAC (Type 1) */ entry->pac_type = PAC_TYPE_TUNNEL_PAC; diff --git a/src/eap_peer/eap_gpsk.c b/src/eap_peer/eap_gpsk.c index 9126e1c..f6a1955 100644 --- a/src/eap_peer/eap_gpsk.c +++ b/src/eap_peer/eap_gpsk.c @@ -1,5 +1,5 @@ /* - * EAP peer method: EAP-GPSK (draft-ietf-emu-eap-gpsk-08.txt) + * EAP peer method: EAP-GPSK (RFC 5433) * Copyright (c) 2006-2008, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify diff --git a/src/eap_peer/eap_methods.h b/src/eap_peer/eap_methods.h index c11bd8c..9fd9b51 100644 --- a/src/eap_peer/eap_methods.h +++ b/src/eap_peer/eap_methods.h @@ -62,6 +62,11 @@ static inline void eap_peer_unregister_methods(void) { } +static inline char ** eap_get_names_as_string_array(size_t *num) +{ + return NULL; +} + #endif /* IEEE8021X_EAPOL */ diff --git a/src/eap_peer/eap_sim.c b/src/eap_peer/eap_sim.c index a7e49f8..5e30d1f 100644 --- a/src/eap_peer/eap_sim.c +++ b/src/eap_peer/eap_sim.c @@ -468,8 +468,6 @@ static struct wpabuf * eap_sim_response_notification(struct eap_sim_data *data, wpa_printf(MSG_DEBUG, "Generating EAP-SIM Notification (id=%d)", id); msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_SIM, EAP_SIM_SUBTYPE_NOTIFICATION); - wpa_printf(MSG_DEBUG, " AT_NOTIFICATION"); - eap_sim_msg_add(msg, EAP_SIM_AT_NOTIFICATION, notification, NULL, 0); if (k_aut && data->reauth) { wpa_printf(MSG_DEBUG, " AT_IV"); wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); diff --git a/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c index 19afb90..186feaa 100644 --- a/src/eap_peer/eap_tls_common.c +++ b/src/eap_peer/eap_tls_common.c @@ -45,6 +45,18 @@ static int eap_tls_check_blob(struct eap_sm *sm, const char **name, } +static void eap_tls_params_flags(struct tls_connection_params *params, + const char *txt) +{ + if (txt == NULL) + return; + if (os_strstr(txt, "tls_allow_md5=1")) + params->flags |= TLS_CONN_ALLOW_SIGN_RSA_MD5; + if (os_strstr(txt, "tls_disable_time_checks=1")) + params->flags |= TLS_CONN_DISABLE_TIME_CHECKS; +} + + static void eap_tls_params_from_conf1(struct tls_connection_params *params, struct eap_peer_config *config) { @@ -62,6 +74,7 @@ static void eap_tls_params_from_conf1(struct tls_connection_params *params, params->key_id = config->key_id; params->cert_id = config->cert_id; params->ca_cert_id = config->ca_cert_id; + eap_tls_params_flags(params, config->phase1); } @@ -82,6 +95,7 @@ static void eap_tls_params_from_conf2(struct tls_connection_params *params, params->key_id = config->key2_id; params->cert_id = config->cert2_id; params->ca_cert_id = config->ca_cert2_id; + eap_tls_params_flags(params, config->phase2); } diff --git a/src/eap_peer/eap_tnc.c b/src/eap_peer/eap_tnc.c index 0a3a01c..c560015 100644 --- a/src/eap_peer/eap_tnc.c +++ b/src/eap_peer/eap_tnc.c @@ -295,7 +295,7 @@ static struct wpabuf * eap_tnc_process(struct eap_sm *sm, void *priv, wpa_printf(MSG_DEBUG, "EAP-TNC: Server did not use " "start flag in the first message"); ret->ignore = TRUE; - return NULL; + goto fail; } tncc_init_connection(data->tncc); @@ -308,7 +308,7 @@ static struct wpabuf * eap_tnc_process(struct eap_sm *sm, void *priv, wpa_printf(MSG_DEBUG, "EAP-TNC: Server used start " "flag again"); ret->ignore = TRUE; - return NULL; + goto fail; } res = tncc_process_if_tnccs(data->tncc, @@ -317,7 +317,7 @@ static struct wpabuf * eap_tnc_process(struct eap_sm *sm, void *priv, switch (res) { case TNCCS_PROCESS_ERROR: ret->ignore = TRUE; - return NULL; + goto fail; case TNCCS_PROCESS_OK_NO_RECOMMENDATION: case TNCCS_RECOMMENDATION_ERROR: wpa_printf(MSG_DEBUG, "EAP-TNC: No " @@ -404,6 +404,11 @@ static struct wpabuf * eap_tnc_process(struct eap_sm *sm, void *priv, data->out_buf = resp; data->state = PROC_MSG; return eap_tnc_build_msg(data, ret, id); + +fail: + if (data->in_buf == &tmpbuf) + data->in_buf = NULL; + return NULL; } diff --git a/src/eap_peer/eap_ttls.c b/src/eap_peer/eap_ttls.c index e1a0fbd..0851f8b 100644 --- a/src/eap_peer/eap_ttls.c +++ b/src/eap_peer/eap_ttls.c @@ -842,7 +842,7 @@ static int eap_ttls_phase2_request_pap(struct eap_sm *sm, /* User-Password; in RADIUS, this is encrypted, but EAP-TTLS encrypts * the data, so no separate encryption is used in the AVP itself. * However, the password is padded to obfuscate its length. */ - pad = (16 - (password_len & 15)) & 15; + pad = password_len == 0 ? 16 : (16 - (password_len & 15)) & 15; pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_USER_PASSWORD, 0, 1, password_len + pad); os_memcpy(pos, password, password_len); diff --git a/src/eap_peer/eap_wsc.c b/src/eap_peer/eap_wsc.c index 17e42f4..7c8ad2f 100644 --- a/src/eap_peer/eap_wsc.c +++ b/src/eap_peer/eap_wsc.c @@ -144,7 +144,7 @@ static void * eap_wsc_init(struct eap_sm *sm) if (registrar && cfg.pin) { wps_registrar_add_pin(data->wps_ctx->registrar, NULL, - cfg.pin, cfg.pin_len); + cfg.pin, cfg.pin_len, 0); } return data; diff --git a/src/eap_peer/tncc.c b/src/eap_peer/tncc.c index 662662d..eaaa168 100644 --- a/src/eap_peer/tncc.c +++ b/src/eap_peer/tncc.c @@ -1106,6 +1106,7 @@ static struct tnc_if_imc * tncc_parse_imc(char *start, char *end, int *error) if (pos >= end || *pos != ' ') { wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMC line '%s' " "(no space after name)", start); + os_free(imc->name); os_free(imc); return NULL; } diff --git a/src/eap_server/.gitignore b/src/eap_server/.gitignore deleted file mode 100644 index a438335..0000000 --- a/src/eap_server/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.d diff --git a/src/eap_server/eap.c b/src/eap_server/eap.c index dea91e6..897adc3 100644 --- a/src/eap_server/eap.c +++ b/src/eap_server/eap.c @@ -573,6 +573,13 @@ SM_STATE(EAP, SUCCESS2) } sm->eap_if.eapSuccess = TRUE; + + /* + * Start reauthentication with identity request even though we know the + * previously used identity. This is needed to get reauthentication + * started properly. + */ + sm->start_reauth = TRUE; } @@ -1070,7 +1077,7 @@ static EapType eap_sm_Policy_getNextMethod(struct eap_sm *sm, int *vendor) static int eap_sm_Policy_getDecision(struct eap_sm *sm) { - if (!sm->eap_server && sm->identity) { + if (!sm->eap_server && sm->identity && !sm->start_reauth) { wpa_printf(MSG_DEBUG, "EAP: getDecision: -> PASSTHROUGH"); return DECISION_PASSTHROUGH; } @@ -1091,7 +1098,8 @@ static int eap_sm_Policy_getDecision(struct eap_sm *sm) return DECISION_FAILURE; } - if ((sm->user == NULL || sm->update_user) && sm->identity) { + if ((sm->user == NULL || sm->update_user) && sm->identity && + !sm->start_reauth) { /* * Allow Identity method to be started once to allow identity * selection hint to be sent from the authentication server, @@ -1118,6 +1126,7 @@ static int eap_sm_Policy_getDecision(struct eap_sm *sm) } sm->update_user = FALSE; } + sm->start_reauth = FALSE; if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS && (sm->user->methods[sm->user_eap_method_index].vendor != @@ -1252,7 +1261,7 @@ void eap_server_sm_deinit(struct eap_sm *sm) sm->m->reset(sm, sm->eap_method_priv); wpabuf_free(sm->eap_if.eapReqData); os_free(sm->eap_if.eapKeyData); - os_free(sm->lastReqData); + wpabuf_free(sm->lastReqData); wpabuf_free(sm->eap_if.eapRespData); os_free(sm->identity); os_free(sm->pac_opaque_encr_key); diff --git a/src/eap_server/eap_fast.c b/src/eap_server/eap_fast.c index b474c99..c06f396 100644 --- a/src/eap_server/eap_fast.c +++ b/src/eap_server/eap_fast.c @@ -73,6 +73,10 @@ struct eap_fast_data { }; +static int eap_fast_process_phase2_start(struct eap_sm *sm, + struct eap_fast_data *data); + + static const char * eap_fast_state_txt(int state) { switch (state) { @@ -804,11 +808,48 @@ static struct wpabuf * eap_fast_build_pac(struct eap_sm *sm, } +static int eap_fast_encrypt_phase2(struct eap_sm *sm, + struct eap_fast_data *data, + struct wpabuf *plain, int piggyback) +{ + struct wpabuf *encr; + + wpa_hexdump_buf_key(MSG_DEBUG, "EAP-FAST: Encrypting Phase 2 TLVs", + plain); + encr = eap_server_tls_encrypt(sm, &data->ssl, wpabuf_mhead(plain), + wpabuf_len(plain)); + wpabuf_free(plain); + + if (data->ssl.out_buf && piggyback) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Piggyback Phase 2 data " + "(len=%d) with last Phase 1 Message (len=%d " + "used=%d)", + (int) wpabuf_len(encr), + (int) wpabuf_len(data->ssl.out_buf), + (int) data->ssl.out_used); + if (wpabuf_resize(&data->ssl.out_buf, wpabuf_len(encr)) < 0) { + wpa_printf(MSG_WARNING, "EAP-FAST: Failed to resize " + "output buffer"); + wpabuf_free(encr); + return -1; + } + wpabuf_put_buf(data->ssl.out_buf, encr); + wpabuf_free(encr); + } else { + wpabuf_free(data->ssl.out_buf); + data->ssl.out_used = 0; + data->ssl.out_buf = encr; + } + + return 0; +} + + static struct wpabuf * eap_fast_buildReq(struct eap_sm *sm, void *priv, u8 id) { struct eap_fast_data *data = priv; struct wpabuf *req = NULL; - struct wpabuf *encr; + int piggyback = 0; if (data->ssl.state == FRAG_ACK) { return eap_server_tls_build_ack(id, EAP_TYPE_FAST, @@ -827,6 +868,19 @@ static struct wpabuf * eap_fast_buildReq(struct eap_sm *sm, void *priv, u8 id) if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { if (eap_fast_phase1_done(sm, data) < 0) return NULL; + if (data->state == PHASE2_START) { + /* + * Try to generate Phase 2 data to piggyback + * with the end of Phase 1 to avoid extra + * roundtrip. + */ + wpa_printf(MSG_DEBUG, "EAP-FAST: Try to start " + "Phase 2"); + if (eap_fast_process_phase2_start(sm, data)) + break; + req = eap_fast_build_phase2_req(sm, data, id); + piggyback = 1; + } } break; case PHASE2_ID: @@ -856,18 +910,9 @@ static struct wpabuf * eap_fast_buildReq(struct eap_sm *sm, void *priv, u8 id) return NULL; } - if (req) { - wpa_hexdump_buf_key(MSG_DEBUG, "EAP-FAST: Encrypting Phase 2 " - "TLVs", req); - encr = eap_server_tls_encrypt(sm, &data->ssl, - wpabuf_mhead(req), - wpabuf_len(req)); - wpabuf_free(req); - - wpabuf_free(data->ssl.out_buf); - data->ssl.out_used = 0; - data->ssl.out_buf = encr; - } + if (req && + eap_fast_encrypt_phase2(sm, data, req, piggyback) < 0) + return NULL; return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_FAST, data->fast_version, id); @@ -1443,8 +1488,8 @@ static int eap_fast_process_phase1(struct eap_sm *sm, } -static void eap_fast_process_phase2_start(struct eap_sm *sm, - struct eap_fast_data *data) +static int eap_fast_process_phase2_start(struct eap_sm *sm, + struct eap_fast_data *data) { u8 next_type; @@ -1474,7 +1519,7 @@ static void eap_fast_process_phase2_start(struct eap_sm *sm, next_type = EAP_TYPE_IDENTITY; } - eap_fast_phase2_init(sm, data, next_type); + return eap_fast_phase2_init(sm, data, next_type); } diff --git a/src/eap_server/eap_gpsk.c b/src/eap_server/eap_gpsk.c index c87d452..d0c7559 100644 --- a/src/eap_server/eap_gpsk.c +++ b/src/eap_server/eap_gpsk.c @@ -1,5 +1,5 @@ /* - * hostapd / EAP-GPSK (draft-ietf-emu-eap-gpsk-08.txt) server + * hostapd / EAP-GPSK (RFC 5433) server * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify diff --git a/src/eap_server/eap_i.h b/src/eap_server/eap_i.h index 61f564d..d52b86f 100644 --- a/src/eap_server/eap_i.h +++ b/src/eap_server/eap_i.h @@ -183,6 +183,8 @@ struct eap_sm { int tnc; struct wps_context *wps; struct wpabuf *assoc_wps_ie; + + Boolean start_reauth; }; int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len, diff --git a/src/eap_server/eap_tls_common.c b/src/eap_server/eap_tls_common.c index befc1bf..bda1184 100644 --- a/src/eap_server/eap_tls_common.c +++ b/src/eap_server/eap_tls_common.c @@ -344,7 +344,7 @@ struct wpabuf * eap_server_tls_encrypt(struct eap_sm *sm, size_t buf_len; /* reserve some extra room for encryption overhead */ - buf_len = plain_len + 200; + buf_len = plain_len + 300; buf = wpabuf_alloc(buf_len); if (buf == NULL) return NULL; diff --git a/src/eap_server/eap_tnc.c b/src/eap_server/eap_tnc.c index 834685b..4cb3ecf 100644 --- a/src/eap_server/eap_tnc.c +++ b/src/eap_server/eap_tnc.c @@ -500,7 +500,7 @@ static void eap_tnc_process(struct eap_sm *sm, void *priv, static Boolean eap_tnc_isDone(struct eap_sm *sm, void *priv) { struct eap_tnc_data *data = priv; - return data->state == DONE; + return data->state == DONE || data->state == FAIL; } diff --git a/src/eap_server/eap_ttls.c b/src/eap_server/eap_ttls.c index b097ab2..21e4b21 100644 --- a/src/eap_server/eap_ttls.c +++ b/src/eap_server/eap_ttls.c @@ -954,7 +954,7 @@ static int eap_ttls_phase2_eap_init(struct eap_sm *sm, sm->init_phase2 = 1; data->phase2_priv = data->phase2_method->init(sm); sm->init_phase2 = 0; - return 0; + return data->phase2_priv == NULL ? -1 : 0; } @@ -1045,6 +1045,11 @@ static void eap_ttls_process_phase2_eap_response(struct eap_sm *sm, next_type = sm->user->methods[0].method; sm->user_eap_method_index = 1; wpa_printf(MSG_DEBUG, "EAP-TTLS: try EAP type %d", next_type); + if (eap_ttls_phase2_eap_init(sm, data, next_type)) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to initialize " + "EAP type %d", next_type); + eap_ttls_state(data, FAILURE); + } break; case PHASE2_METHOD: if (data->ttls_version > 0) { @@ -1066,12 +1071,6 @@ static void eap_ttls_process_phase2_eap_response(struct eap_sm *sm, __func__, data->state); break; } - - if (eap_ttls_phase2_eap_init(sm, data, next_type)) { - wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to initialize EAP " - "type %d", next_type); - eap_ttls_state(data, FAILURE); - } } diff --git a/src/eapol_supp/.gitignore b/src/eapol_supp/.gitignore deleted file mode 100644 index a438335..0000000 --- a/src/eapol_supp/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.d diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c index c832b5a..d163049 100644 --- a/src/eapol_supp/eapol_supp_sm.c +++ b/src/eapol_supp/eapol_supp_sm.c @@ -282,7 +282,12 @@ SM_STATE(SUPP_PAE, CONNECTING) * delay authentication. Use a short timeout to send the first * EAPOL-Start if Authenticator does not start authentication. */ +#ifdef CONFIG_WPS + /* Reduce latency on starting WPS negotiation. */ + sm->startWhen = 1; +#else /* CONFIG_WPS */ sm->startWhen = 3; +#endif /* CONFIG_WPS */ } eapol_enable_timer_tick(sm); sm->eapolEap = FALSE; @@ -737,8 +742,8 @@ static void eapol_sm_processKey(struct eapol_sm *sm) os_memcpy(ekey + IEEE8021X_KEY_IV_LEN, keydata.encr_key, encr_key_len); os_memcpy(datakey, key + 1, key_len); - rc4(datakey, key_len, ekey, - IEEE8021X_KEY_IV_LEN + encr_key_len); + rc4_skip(ekey, IEEE8021X_KEY_IV_LEN + encr_key_len, 0, + datakey, key_len); wpa_hexdump_key(MSG_DEBUG, "EAPOL: Decrypted(RC4) key", datakey, key_len); } else if (key_len == 0) { diff --git a/src/hlr_auc_gw/.gitignore b/src/hlr_auc_gw/.gitignore deleted file mode 100644 index a438335..0000000 --- a/src/hlr_auc_gw/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.d diff --git a/src/hlr_auc_gw/hlr_auc_gw.c b/src/hlr_auc_gw/hlr_auc_gw.c index 3d914e6..e318903 100644 --- a/src/hlr_auc_gw/hlr_auc_gw.c +++ b/src/hlr_auc_gw/hlr_auc_gw.c @@ -532,7 +532,7 @@ static void aka_req_auth(int s, struct sockaddr_un *from, socklen_t fromlen, static void aka_auts(int s, struct sockaddr_un *from, socklen_t fromlen, char *imsi) { - char *auts, *rand; + char *auts, *__rand; u8 _auts[EAP_AKA_AUTS_LEN], _rand[EAP_AKA_RAND_LEN], sqn[6]; struct milenage_parameters *m; @@ -543,14 +543,14 @@ static void aka_auts(int s, struct sockaddr_un *from, socklen_t fromlen, return; *auts++ = '\0'; - rand = strchr(auts, ' '); - if (rand == NULL) + __rand = strchr(auts, ' '); + if (__rand == NULL) return; - *rand++ = '\0'; + *__rand++ = '\0'; - printf("AKA-AUTS: IMSI=%s AUTS=%s RAND=%s\n", imsi, auts, rand); + printf("AKA-AUTS: IMSI=%s AUTS=%s RAND=%s\n", imsi, auts, __rand); if (hexstr2bin(auts, _auts, EAP_AKA_AUTS_LEN) || - hexstr2bin(rand, _rand, EAP_AKA_RAND_LEN)) { + hexstr2bin(__rand, _rand, EAP_AKA_RAND_LEN)) { printf("Could not parse AUTS/RAND\n"); return; } diff --git a/src/l2_packet/Makefile b/src/l2_packet/Makefile new file mode 100644 index 0000000..cffba62 --- /dev/null +++ b/src/l2_packet/Makefile @@ -0,0 +1,9 @@ +all: + @echo Nothing to be made. + +clean: + for d in $(SUBDIRS); do make -C $$d clean; done + rm -f *~ *.o *.d + +install: + @echo Nothing to be made. diff --git a/src/l2_packet/l2_packet_freebsd.c b/src/l2_packet/l2_packet_freebsd.c new file mode 100644 index 0000000..d1034aa --- /dev/null +++ b/src/l2_packet/l2_packet_freebsd.c @@ -0,0 +1,285 @@ +/* + * WPA Supplicant - Layer2 packet handling with FreeBSD + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * Copyright (c) 2005, Sam Leffler <sam@errno.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#ifdef __APPLE__ +#include <net/bpf.h> +#endif /* __APPLE__ */ +#include <pcap.h> + +#include <sys/ioctl.h> +#include <sys/sysctl.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/route.h> +#include <netinet/in.h> + +#include "common.h" +#include "eloop.h" +#include "l2_packet.h" + + +static const u8 pae_group_addr[ETH_ALEN] = +{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }; + +struct l2_packet_data { + pcap_t *pcap; + char ifname[100]; + u8 own_addr[ETH_ALEN]; + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len); + void *rx_callback_ctx; + int l2_hdr; /* whether to include layer 2 (Ethernet) header data + * buffers */ +}; + + +int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr) +{ + os_memcpy(addr, l2->own_addr, ETH_ALEN); + return 0; +} + + +int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, + const u8 *buf, size_t len) +{ + if (!l2->l2_hdr) { + int ret; + struct l2_ethhdr *eth = os_malloc(sizeof(*eth) + len); + if (eth == NULL) + return -1; + os_memcpy(eth->h_dest, dst_addr, ETH_ALEN); + os_memcpy(eth->h_source, l2->own_addr, ETH_ALEN); + eth->h_proto = htons(proto); + os_memcpy(eth + 1, buf, len); + ret = pcap_inject(l2->pcap, (u8 *) eth, len + sizeof(*eth)); + os_free(eth); + return ret; + } else + return pcap_inject(l2->pcap, buf, len); +} + + +static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct l2_packet_data *l2 = eloop_ctx; + pcap_t *pcap = sock_ctx; + struct pcap_pkthdr hdr; + const u_char *packet; + struct l2_ethhdr *ethhdr; + unsigned char *buf; + size_t len; + + packet = pcap_next(pcap, &hdr); + + if (packet == NULL || hdr.caplen < sizeof(*ethhdr)) + return; + + ethhdr = (struct l2_ethhdr *) packet; + if (l2->l2_hdr) { + buf = (unsigned char *) ethhdr; + len = hdr.caplen; + } else { + buf = (unsigned char *) (ethhdr + 1); + len = hdr.caplen - sizeof(*ethhdr); + } + l2->rx_callback(l2->rx_callback_ctx, ethhdr->h_source, buf, len); +} + + +static int l2_packet_init_libpcap(struct l2_packet_data *l2, + unsigned short protocol) +{ + bpf_u_int32 pcap_maskp, pcap_netp; + char pcap_filter[200], pcap_err[PCAP_ERRBUF_SIZE]; + struct bpf_program pcap_fp; + + pcap_lookupnet(l2->ifname, &pcap_netp, &pcap_maskp, pcap_err); + l2->pcap = pcap_open_live(l2->ifname, 2500, 0, 10, pcap_err); + if (l2->pcap == NULL) { + fprintf(stderr, "pcap_open_live: %s\n", pcap_err); + fprintf(stderr, "ifname='%s'\n", l2->ifname); + return -1; + } + if (pcap_datalink(l2->pcap) != DLT_EN10MB && + pcap_set_datalink(l2->pcap, DLT_EN10MB) < 0) { + fprintf(stderr, "pcap_set_datalink(DLT_EN10MB): %s\n", + pcap_geterr(l2->pcap)); + return -1; + } + os_snprintf(pcap_filter, sizeof(pcap_filter), + "not ether src " MACSTR " and " + "( ether dst " MACSTR " or ether dst " MACSTR " ) and " + "ether proto 0x%x", + MAC2STR(l2->own_addr), /* do not receive own packets */ + MAC2STR(l2->own_addr), MAC2STR(pae_group_addr), + protocol); + if (pcap_compile(l2->pcap, &pcap_fp, pcap_filter, 1, pcap_netp) < 0) { + fprintf(stderr, "pcap_compile: %s\n", pcap_geterr(l2->pcap)); + return -1; + } + + if (pcap_setfilter(l2->pcap, &pcap_fp) < 0) { + fprintf(stderr, "pcap_setfilter: %s\n", pcap_geterr(l2->pcap)); + return -1; + } + + pcap_freecode(&pcap_fp); + /* + * When libpcap uses BPF we must enable "immediate mode" to + * receive frames right away; otherwise the system may + * buffer them for us. + */ + { + unsigned int on = 1; + if (ioctl(pcap_fileno(l2->pcap), BIOCIMMEDIATE, &on) < 0) { + fprintf(stderr, "%s: cannot enable immediate mode on " + "interface %s: %s\n", + __func__, l2->ifname, strerror(errno)); + /* XXX should we fail? */ + } + } + + eloop_register_read_sock(pcap_get_selectable_fd(l2->pcap), + l2_packet_receive, l2, l2->pcap); + + return 0; +} + + +static int eth_get(const char *device, u8 ea[ETH_ALEN]) +{ + struct if_msghdr *ifm; + struct sockaddr_dl *sdl; + u_char *p, *buf; + size_t len; + int mib[] = { CTL_NET, AF_ROUTE, 0, AF_LINK, NET_RT_IFLIST, 0 }; + + if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) + return -1; + if ((buf = os_malloc(len)) == NULL) + return -1; + if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) { + os_free(buf); + return -1; + } + for (p = buf; p < buf + len; p += ifm->ifm_msglen) { + ifm = (struct if_msghdr *)p; + sdl = (struct sockaddr_dl *)(ifm + 1); + if (ifm->ifm_type != RTM_IFINFO || + (ifm->ifm_addrs & RTA_IFP) == 0) + continue; + if (sdl->sdl_family != AF_LINK || sdl->sdl_nlen == 0 || + os_memcmp(sdl->sdl_data, device, sdl->sdl_nlen) != 0) + continue; + os_memcpy(ea, LLADDR(sdl), sdl->sdl_alen); + break; + } + os_free(buf); + + if (p >= buf + len) { + errno = ESRCH; + return -1; + } + return 0; +} + + +struct l2_packet_data * l2_packet_init( + const char *ifname, const u8 *own_addr, unsigned short protocol, + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len), + void *rx_callback_ctx, int l2_hdr) +{ + struct l2_packet_data *l2; + + l2 = os_zalloc(sizeof(struct l2_packet_data)); + if (l2 == NULL) + return NULL; + os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname)); + l2->rx_callback = rx_callback; + l2->rx_callback_ctx = rx_callback_ctx; + l2->l2_hdr = l2_hdr; + + if (eth_get(l2->ifname, l2->own_addr) < 0) { + fprintf(stderr, "Failed to get link-level address for " + "interface '%s'.\n", l2->ifname); + os_free(l2); + return NULL; + } + + if (l2_packet_init_libpcap(l2, protocol)) { + os_free(l2); + return NULL; + } + + return l2; +} + + +void l2_packet_deinit(struct l2_packet_data *l2) +{ + if (l2 != NULL) { + if (l2->pcap) { + eloop_unregister_read_sock( + pcap_get_selectable_fd(l2->pcap)); + pcap_close(l2->pcap); + } + os_free(l2); + } +} + + +int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len) +{ + pcap_if_t *devs, *dev; + struct pcap_addr *addr; + struct sockaddr_in *saddr; + int found = 0; + char err[PCAP_ERRBUF_SIZE + 1]; + + if (pcap_findalldevs(&devs, err) < 0) { + wpa_printf(MSG_DEBUG, "pcap_findalldevs: %s\n", err); + return -1; + } + + for (dev = devs; dev && !found; dev = dev->next) { + if (os_strcmp(dev->name, l2->ifname) != 0) + continue; + + addr = dev->addresses; + while (addr) { + saddr = (struct sockaddr_in *) addr->addr; + if (saddr && saddr->sin_family == AF_INET) { + os_strlcpy(buf, inet_ntoa(saddr->sin_addr), + len); + found = 1; + break; + } + addr = addr->next; + } + } + + pcap_freealldevs(devs); + + return found ? 0 : -1; +} + + +void l2_packet_notify_auth_start(struct l2_packet_data *l2) +{ +} diff --git a/src/l2_packet/l2_packet_linux.c b/src/l2_packet/l2_packet_linux.c new file mode 100644 index 0000000..48d1bde --- /dev/null +++ b/src/l2_packet/l2_packet_linux.c @@ -0,0 +1,200 @@ +/* + * WPA Supplicant - Layer2 packet handling with Linux packet sockets + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <sys/ioctl.h> +#include <netpacket/packet.h> +#include <net/if.h> + +#include "common.h" +#include "eloop.h" +#include "l2_packet.h" + + +struct l2_packet_data { + int fd; /* packet socket for EAPOL frames */ + char ifname[IFNAMSIZ + 1]; + int ifindex; + u8 own_addr[ETH_ALEN]; + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len); + void *rx_callback_ctx; + int l2_hdr; /* whether to include layer 2 (Ethernet) header data + * buffers */ +}; + + +int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr) +{ + os_memcpy(addr, l2->own_addr, ETH_ALEN); + return 0; +} + + +int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, + const u8 *buf, size_t len) +{ + int ret; + if (l2 == NULL) + return -1; + if (l2->l2_hdr) { + ret = send(l2->fd, buf, len, 0); + if (ret < 0) + perror("l2_packet_send - send"); + } else { + struct sockaddr_ll ll; + os_memset(&ll, 0, sizeof(ll)); + ll.sll_family = AF_PACKET; + ll.sll_ifindex = l2->ifindex; + ll.sll_protocol = htons(proto); + ll.sll_halen = ETH_ALEN; + os_memcpy(ll.sll_addr, dst_addr, ETH_ALEN); + ret = sendto(l2->fd, buf, len, 0, (struct sockaddr *) &ll, + sizeof(ll)); + if (ret < 0) + perror("l2_packet_send - sendto"); + } + return ret; +} + + +static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct l2_packet_data *l2 = eloop_ctx; + u8 buf[2300]; + int res; + struct sockaddr_ll ll; + socklen_t fromlen; + + os_memset(&ll, 0, sizeof(ll)); + fromlen = sizeof(ll); + res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &ll, + &fromlen); + if (res < 0) { + perror("l2_packet_receive - recvfrom"); + return; + } + + l2->rx_callback(l2->rx_callback_ctx, ll.sll_addr, buf, res); +} + + +struct l2_packet_data * l2_packet_init( + const char *ifname, const u8 *own_addr, unsigned short protocol, + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len), + void *rx_callback_ctx, int l2_hdr) +{ + struct l2_packet_data *l2; + struct ifreq ifr; + struct sockaddr_ll ll; + + l2 = os_zalloc(sizeof(struct l2_packet_data)); + if (l2 == NULL) + return NULL; + os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname)); + l2->rx_callback = rx_callback; + l2->rx_callback_ctx = rx_callback_ctx; + l2->l2_hdr = l2_hdr; + + l2->fd = socket(PF_PACKET, l2_hdr ? SOCK_RAW : SOCK_DGRAM, + htons(protocol)); + if (l2->fd < 0) { + perror("socket(PF_PACKET)"); + os_free(l2); + return NULL; + } + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, l2->ifname, sizeof(ifr.ifr_name)); + if (ioctl(l2->fd, SIOCGIFINDEX, &ifr) < 0) { + perror("ioctl[SIOCGIFINDEX]"); + close(l2->fd); + os_free(l2); + return NULL; + } + l2->ifindex = ifr.ifr_ifindex; + + os_memset(&ll, 0, sizeof(ll)); + ll.sll_family = PF_PACKET; + ll.sll_ifindex = ifr.ifr_ifindex; + ll.sll_protocol = htons(protocol); + if (bind(l2->fd, (struct sockaddr *) &ll, sizeof(ll)) < 0) { + perror("bind[PF_PACKET]"); + close(l2->fd); + os_free(l2); + return NULL; + } + + if (ioctl(l2->fd, SIOCGIFHWADDR, &ifr) < 0) { + perror("ioctl[SIOCGIFHWADDR]"); + close(l2->fd); + os_free(l2); + return NULL; + } + os_memcpy(l2->own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); + + eloop_register_read_sock(l2->fd, l2_packet_receive, l2, NULL); + + return l2; +} + + +void l2_packet_deinit(struct l2_packet_data *l2) +{ + if (l2 == NULL) + return; + + if (l2->fd >= 0) { + eloop_unregister_read_sock(l2->fd); + close(l2->fd); + } + + os_free(l2); +} + + +int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len) +{ + int s; + struct ifreq ifr; + struct sockaddr_in *saddr; + size_t res; + + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket"); + return -1; + } + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, l2->ifname, sizeof(ifr.ifr_name)); + if (ioctl(s, SIOCGIFADDR, &ifr) < 0) { + if (errno != EADDRNOTAVAIL) + perror("ioctl[SIOCGIFADDR]"); + close(s); + return -1; + } + close(s); + saddr = aliasing_hide_typecast(&ifr.ifr_addr, struct sockaddr_in); + if (saddr->sin_family != AF_INET) + return -1; + res = os_strlcpy(buf, inet_ntoa(saddr->sin_addr), len); + if (res >= len) + return -1; + return 0; +} + + +void l2_packet_notify_auth_start(struct l2_packet_data *l2) +{ +} diff --git a/src/l2_packet/l2_packet_ndis.c b/src/l2_packet/l2_packet_ndis.c new file mode 100644 index 0000000..7de5880 --- /dev/null +++ b/src/l2_packet/l2_packet_ndis.c @@ -0,0 +1,516 @@ +/* + * WPA Supplicant - Layer2 packet handling with Microsoft NDISUIO + * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * This implementation requires Windows specific event loop implementation, + * i.e., eloop_win.c. In addition, the NDISUIO connection is shared with + * driver_ndis.c, so only that driver interface can be used and + * CONFIG_USE_NDISUIO must be defined. + * + * WinXP version of the code uses overlapped I/O and a single threaded design + * with callback functions from I/O code. WinCE version uses a separate RX + * thread that blocks on ReadFile() whenever the media status is connected. + */ + +#include "includes.h" +#include <winsock2.h> +#include <ntddndis.h> + +#ifdef _WIN32_WCE +#include <winioctl.h> +#include <nuiouser.h> +#endif /* _WIN32_WCE */ + +#include "common.h" +#include "eloop.h" +#include "l2_packet.h" + +#ifndef _WIN32_WCE +/* from nuiouser.h */ +#define FSCTL_NDISUIO_BASE FILE_DEVICE_NETWORK +#define _NDISUIO_CTL_CODE(_Function, _Method, _Access) \ + CTL_CODE(FSCTL_NDISUIO_BASE, _Function, _Method, _Access) +#define IOCTL_NDISUIO_SET_ETHER_TYPE \ + _NDISUIO_CTL_CODE(0x202, METHOD_BUFFERED, \ + FILE_READ_ACCESS | FILE_WRITE_ACCESS) +#endif /* _WIN32_WCE */ + +/* From driver_ndis.c to shared the handle to NDISUIO */ +HANDLE driver_ndis_get_ndisuio_handle(void); + +/* + * NDISUIO supports filtering of only one ethertype at the time, so we must + * fake support for two (EAPOL and RSN pre-auth) by switching to pre-auth + * whenever wpa_supplicant is trying to pre-authenticate and then switching + * back to EAPOL when pre-authentication has been completed. + */ + +struct l2_packet_data; + +struct l2_packet_ndisuio_global { + int refcount; + unsigned short first_proto; + struct l2_packet_data *l2[2]; +#ifdef _WIN32_WCE + HANDLE rx_thread; + HANDLE stop_request; + HANDLE ready_for_read; + HANDLE rx_processed; +#endif /* _WIN32_WCE */ +}; + +static struct l2_packet_ndisuio_global *l2_ndisuio_global = NULL; + +struct l2_packet_data { + char ifname[100]; + u8 own_addr[ETH_ALEN]; + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len); + void *rx_callback_ctx; + int l2_hdr; /* whether to include layer 2 (Ethernet) header in calls to + * rx_callback and l2_packet_send() */ + HANDLE rx_avail; +#ifndef _WIN32_WCE + OVERLAPPED rx_overlapped; +#endif /* _WIN32_WCE */ + u8 rx_buf[1514]; + DWORD rx_written; +}; + + +int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr) +{ + os_memcpy(addr, l2->own_addr, ETH_ALEN); + return 0; +} + + +int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, + const u8 *buf, size_t len) +{ + BOOL res; + DWORD written; + struct l2_ethhdr *eth; +#ifndef _WIN32_WCE + OVERLAPPED overlapped; +#endif /* _WIN32_WCE */ + OVERLAPPED *o; + + if (l2 == NULL) + return -1; + +#ifdef _WIN32_WCE + o = NULL; +#else /* _WIN32_WCE */ + os_memset(&overlapped, 0, sizeof(overlapped)); + o = &overlapped; +#endif /* _WIN32_WCE */ + + if (l2->l2_hdr) { + res = WriteFile(driver_ndis_get_ndisuio_handle(), buf, len, + &written, o); + } else { + size_t mlen = sizeof(*eth) + len; + eth = os_malloc(mlen); + if (eth == NULL) + return -1; + + os_memcpy(eth->h_dest, dst_addr, ETH_ALEN); + os_memcpy(eth->h_source, l2->own_addr, ETH_ALEN); + eth->h_proto = htons(proto); + os_memcpy(eth + 1, buf, len); + res = WriteFile(driver_ndis_get_ndisuio_handle(), eth, mlen, + &written, o); + os_free(eth); + } + + if (!res) { + DWORD err = GetLastError(); +#ifndef _WIN32_WCE + if (err == ERROR_IO_PENDING) { + /* For now, just assume that the packet will be sent in + * time before the next write happens. This could be + * cleaned up at some point to actually wait for + * completion before starting new writes. + */ + return 0; + } +#endif /* _WIN32_WCE */ + wpa_printf(MSG_DEBUG, "L2(NDISUIO): WriteFile failed: %d", + (int) GetLastError()); + return -1; + } + + return 0; +} + + +static void l2_packet_callback(struct l2_packet_data *l2); + +#ifdef _WIN32_WCE +static void l2_packet_rx_thread_try_read(struct l2_packet_data *l2) +{ + HANDLE handles[2]; + + wpa_printf(MSG_MSGDUMP, "l2_packet_rx_thread: -> ReadFile"); + if (!ReadFile(driver_ndis_get_ndisuio_handle(), l2->rx_buf, + sizeof(l2->rx_buf), &l2->rx_written, NULL)) { + DWORD err = GetLastError(); + wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: ReadFile failed: " + "%d", (int) err); + /* + * ReadFile on NDISUIO/WinCE returns ERROR_DEVICE_NOT_CONNECTED + * error whenever the connection is not up. Yield the thread to + * avoid triggering a busy loop. Connection event should stop + * us from looping for long, but we need to allow enough CPU + * for the main thread to process the media disconnection. + */ + Sleep(100); + return; + } + + wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: Read %d byte packet", + (int) l2->rx_written); + + /* + * Notify the main thread about the availability of a frame and wait + * for the frame to be processed. + */ + SetEvent(l2->rx_avail); + handles[0] = l2_ndisuio_global->stop_request; + handles[1] = l2_ndisuio_global->rx_processed; + WaitForMultipleObjects(2, handles, FALSE, INFINITE); + ResetEvent(l2_ndisuio_global->rx_processed); +} + + +static DWORD WINAPI l2_packet_rx_thread(LPVOID arg) +{ + struct l2_packet_data *l2 = arg; + DWORD res; + HANDLE handles[2]; + int run = 1; + + wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread started"); + handles[0] = l2_ndisuio_global->stop_request; + handles[1] = l2_ndisuio_global->ready_for_read; + + /* + * Unfortunately, NDISUIO on WinCE does not seem to support waiting + * on the handle. There do not seem to be anything else that we could + * wait for either. If one were to modify NDISUIO to set a named event + * whenever packets are available, this event could be used here to + * avoid having to poll for new packets or we could even move to use a + * single threaded design. + * + * In addition, NDISUIO on WinCE is returning + * ERROR_DEVICE_NOT_CONNECTED whenever ReadFile() is attempted while + * the adapter is not in connected state. For now, we are just using a + * local event to allow ReadFile calls only after having received NDIS + * media connect event. This event could be easily converted to handle + * another event if the protocol driver is replaced with somewhat more + * useful design. + */ + + while (l2_ndisuio_global && run) { + res = WaitForMultipleObjects(2, handles, FALSE, INFINITE); + switch (res) { + case WAIT_OBJECT_0: + wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: Received " + "request to stop RX thread"); + run = 0; + break; + case WAIT_OBJECT_0 + 1: + l2_packet_rx_thread_try_read(l2); + break; + case WAIT_FAILED: + default: + wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: " + "WaitForMultipleObjects failed: %d", + (int) GetLastError()); + run = 0; + break; + } + } + + wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread stopped"); + + return 0; +} +#else /* _WIN32_WCE */ +static int l2_ndisuio_start_read(struct l2_packet_data *l2, int recursive) +{ + os_memset(&l2->rx_overlapped, 0, sizeof(l2->rx_overlapped)); + l2->rx_overlapped.hEvent = l2->rx_avail; + if (!ReadFile(driver_ndis_get_ndisuio_handle(), l2->rx_buf, + sizeof(l2->rx_buf), &l2->rx_written, &l2->rx_overlapped)) + { + DWORD err = GetLastError(); + if (err != ERROR_IO_PENDING) { + wpa_printf(MSG_DEBUG, "L2(NDISUIO): ReadFile failed: " + "%d", (int) err); + return -1; + } + /* + * Once read is completed, l2_packet_rx_event() will be + * called. + */ + } else { + wpa_printf(MSG_DEBUG, "L2(NDISUIO): ReadFile returned data " + "without wait for completion"); + if (!recursive) + l2_packet_callback(l2); + } + + return 0; +} +#endif /* _WIN32_WCE */ + + +static void l2_packet_callback(struct l2_packet_data *l2) +{ + const u8 *rx_buf, *rx_src; + size_t rx_len; + struct l2_ethhdr *ethhdr = (struct l2_ethhdr *) l2->rx_buf; + + wpa_printf(MSG_DEBUG, "L2(NDISUIO): Read %d bytes", + (int) l2->rx_written); + + if (l2->l2_hdr || l2->rx_written < sizeof(*ethhdr)) { + rx_buf = (u8 *) ethhdr; + rx_len = l2->rx_written; + } else { + rx_buf = (u8 *) (ethhdr + 1); + rx_len = l2->rx_written - sizeof(*ethhdr); + } + rx_src = ethhdr->h_source; + + l2->rx_callback(l2->rx_callback_ctx, rx_src, rx_buf, rx_len); +#ifndef _WIN32_WCE + l2_ndisuio_start_read(l2, 1); +#endif /* _WIN32_WCE */ +} + + +static void l2_packet_rx_event(void *eloop_data, void *user_data) +{ + struct l2_packet_data *l2 = eloop_data; + + if (l2_ndisuio_global) + l2 = l2_ndisuio_global->l2[l2_ndisuio_global->refcount - 1]; + + ResetEvent(l2->rx_avail); + +#ifndef _WIN32_WCE + if (!GetOverlappedResult(driver_ndis_get_ndisuio_handle(), + &l2->rx_overlapped, &l2->rx_written, FALSE)) { + wpa_printf(MSG_DEBUG, "L2(NDISUIO): GetOverlappedResult " + "failed: %d", (int) GetLastError()); + return; + } +#endif /* _WIN32_WCE */ + + l2_packet_callback(l2); + +#ifdef _WIN32_WCE + SetEvent(l2_ndisuio_global->rx_processed); +#endif /* _WIN32_WCE */ +} + + +static int l2_ndisuio_set_ether_type(unsigned short protocol) +{ + USHORT proto = htons(protocol); + DWORD written; + + if (!DeviceIoControl(driver_ndis_get_ndisuio_handle(), + IOCTL_NDISUIO_SET_ETHER_TYPE, &proto, + sizeof(proto), NULL, 0, &written, NULL)) { + wpa_printf(MSG_ERROR, "L2(NDISUIO): " + "IOCTL_NDISUIO_SET_ETHER_TYPE failed: %d", + (int) GetLastError()); + return -1; + } + + return 0; +} + + +struct l2_packet_data * l2_packet_init( + const char *ifname, const u8 *own_addr, unsigned short protocol, + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len), + void *rx_callback_ctx, int l2_hdr) +{ + struct l2_packet_data *l2; + + if (l2_ndisuio_global == NULL) { + l2_ndisuio_global = os_zalloc(sizeof(*l2_ndisuio_global)); + if (l2_ndisuio_global == NULL) + return NULL; + l2_ndisuio_global->first_proto = protocol; + } + if (l2_ndisuio_global->refcount >= 2) { + wpa_printf(MSG_ERROR, "L2(NDISUIO): Not more than two " + "simultaneous connections allowed"); + return NULL; + } + l2_ndisuio_global->refcount++; + + l2 = os_zalloc(sizeof(struct l2_packet_data)); + if (l2 == NULL) + return NULL; + l2_ndisuio_global->l2[l2_ndisuio_global->refcount - 1] = l2; + + os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname)); + l2->rx_callback = rx_callback; + l2->rx_callback_ctx = rx_callback_ctx; + l2->l2_hdr = l2_hdr; + + if (own_addr) + os_memcpy(l2->own_addr, own_addr, ETH_ALEN); + + if (l2_ndisuio_set_ether_type(protocol) < 0) { + os_free(l2); + return NULL; + } + + if (l2_ndisuio_global->refcount > 1) { + wpa_printf(MSG_DEBUG, "L2(NDISUIO): Temporarily setting " + "filtering ethertype to %04x", protocol); + if (l2_ndisuio_global->l2[0]) + l2->rx_avail = l2_ndisuio_global->l2[0]->rx_avail; + return l2; + } + + l2->rx_avail = CreateEvent(NULL, TRUE, FALSE, NULL); + if (l2->rx_avail == NULL) { + os_free(l2); + return NULL; + } + + eloop_register_event(l2->rx_avail, sizeof(l2->rx_avail), + l2_packet_rx_event, l2, NULL); + +#ifdef _WIN32_WCE + l2_ndisuio_global->stop_request = CreateEvent(NULL, TRUE, FALSE, NULL); + /* + * This event is being set based on media connect/disconnect + * notifications in driver_ndis.c. + */ + l2_ndisuio_global->ready_for_read = + CreateEvent(NULL, TRUE, FALSE, TEXT("WpaSupplicantConnected")); + l2_ndisuio_global->rx_processed = CreateEvent(NULL, TRUE, FALSE, NULL); + if (l2_ndisuio_global->stop_request == NULL || + l2_ndisuio_global->ready_for_read == NULL || + l2_ndisuio_global->rx_processed == NULL) { + if (l2_ndisuio_global->stop_request) { + CloseHandle(l2_ndisuio_global->stop_request); + l2_ndisuio_global->stop_request = NULL; + } + if (l2_ndisuio_global->ready_for_read) { + CloseHandle(l2_ndisuio_global->ready_for_read); + l2_ndisuio_global->ready_for_read = NULL; + } + if (l2_ndisuio_global->rx_processed) { + CloseHandle(l2_ndisuio_global->rx_processed); + l2_ndisuio_global->rx_processed = NULL; + } + eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail)); + os_free(l2); + return NULL; + } + + l2_ndisuio_global->rx_thread = CreateThread(NULL, 0, + l2_packet_rx_thread, l2, 0, + NULL); + if (l2_ndisuio_global->rx_thread == NULL) { + wpa_printf(MSG_INFO, "L2(NDISUIO): Failed to create RX " + "thread: %d", (int) GetLastError()); + eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail)); + CloseHandle(l2_ndisuio_global->stop_request); + l2_ndisuio_global->stop_request = NULL; + os_free(l2); + return NULL; + } +#else /* _WIN32_WCE */ + l2_ndisuio_start_read(l2, 0); +#endif /* _WIN32_WCE */ + + return l2; +} + + +void l2_packet_deinit(struct l2_packet_data *l2) +{ + if (l2 == NULL) + return; + + if (l2_ndisuio_global) { + l2_ndisuio_global->refcount--; + l2_ndisuio_global->l2[l2_ndisuio_global->refcount] = NULL; + if (l2_ndisuio_global->refcount) { + wpa_printf(MSG_DEBUG, "L2(NDISUIO): restore filtering " + "ethertype to %04x", + l2_ndisuio_global->first_proto); + l2_ndisuio_set_ether_type( + l2_ndisuio_global->first_proto); + return; + } + +#ifdef _WIN32_WCE + wpa_printf(MSG_DEBUG, "L2(NDISUIO): Waiting for RX thread to " + "stop"); + SetEvent(l2_ndisuio_global->stop_request); + /* + * Cancel pending ReadFile() in the RX thread (if we were still + * connected at this point). + */ + if (!DeviceIoControl(driver_ndis_get_ndisuio_handle(), + IOCTL_CANCEL_READ, NULL, 0, NULL, 0, NULL, + NULL)) { + wpa_printf(MSG_DEBUG, "L2(NDISUIO): IOCTL_CANCEL_READ " + "failed: %d", (int) GetLastError()); + /* RX thread will exit blocking ReadFile once NDISUIO + * notices that the adapter is disconnected. */ + } + WaitForSingleObject(l2_ndisuio_global->rx_thread, INFINITE); + wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread exited"); + CloseHandle(l2_ndisuio_global->rx_thread); + CloseHandle(l2_ndisuio_global->stop_request); + CloseHandle(l2_ndisuio_global->ready_for_read); + CloseHandle(l2_ndisuio_global->rx_processed); +#endif /* _WIN32_WCE */ + + os_free(l2_ndisuio_global); + l2_ndisuio_global = NULL; + } + +#ifndef _WIN32_WCE + CancelIo(driver_ndis_get_ndisuio_handle()); +#endif /* _WIN32_WCE */ + + eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail)); + CloseHandle(l2->rx_avail); + os_free(l2); +} + + +int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len) +{ + return -1; +} + + +void l2_packet_notify_auth_start(struct l2_packet_data *l2) +{ +} diff --git a/src/l2_packet/l2_packet_none.c b/src/l2_packet/l2_packet_none.c new file mode 100644 index 0000000..5e3f6e9 --- /dev/null +++ b/src/l2_packet/l2_packet_none.c @@ -0,0 +1,123 @@ +/* + * WPA Supplicant - Layer2 packet handling example with dummy functions + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * This file can be used as a starting point for layer2 packet implementation. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "l2_packet.h" + + +struct l2_packet_data { + char ifname[17]; + u8 own_addr[ETH_ALEN]; + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len); + void *rx_callback_ctx; + int l2_hdr; /* whether to include layer 2 (Ethernet) header data + * buffers */ + int fd; +}; + + +int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr) +{ + os_memcpy(addr, l2->own_addr, ETH_ALEN); + return 0; +} + + +int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, + const u8 *buf, size_t len) +{ + if (l2 == NULL) + return -1; + + /* + * TODO: Send frame (may need different implementation depending on + * whether l2->l2_hdr is set). + */ + + return 0; +} + + +static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct l2_packet_data *l2 = eloop_ctx; + u8 buf[2300]; + int res; + + /* TODO: receive frame (e.g., recv() using sock */ + buf[0] = 0; + res = 0; + + l2->rx_callback(l2->rx_callback_ctx, NULL /* TODO: src addr */, + buf, res); +} + + +struct l2_packet_data * l2_packet_init( + const char *ifname, const u8 *own_addr, unsigned short protocol, + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len), + void *rx_callback_ctx, int l2_hdr) +{ + struct l2_packet_data *l2; + + l2 = os_zalloc(sizeof(struct l2_packet_data)); + if (l2 == NULL) + return NULL; + os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname)); + l2->rx_callback = rx_callback; + l2->rx_callback_ctx = rx_callback_ctx; + l2->l2_hdr = l2_hdr; + + /* + * TODO: open connection for receiving frames + */ + l2->fd = -1; + eloop_register_read_sock(l2->fd, l2_packet_receive, l2, NULL); + + return l2; +} + + +void l2_packet_deinit(struct l2_packet_data *l2) +{ + if (l2 == NULL) + return; + + if (l2->fd >= 0) { + eloop_unregister_read_sock(l2->fd); + /* TODO: close connection */ + } + + os_free(l2); +} + + +int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len) +{ + /* TODO: get interface IP address */ + return -1; +} + + +void l2_packet_notify_auth_start(struct l2_packet_data *l2) +{ + /* This function can be left empty */ +} diff --git a/src/l2_packet/l2_packet_pcap.c b/src/l2_packet/l2_packet_pcap.c new file mode 100644 index 0000000..8156e29 --- /dev/null +++ b/src/l2_packet/l2_packet_pcap.c @@ -0,0 +1,386 @@ +/* + * WPA Supplicant - Layer2 packet handling with libpcap/libdnet and WinPcap + * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#ifndef CONFIG_NATIVE_WINDOWS +#include <sys/ioctl.h> +#endif /* CONFIG_NATIVE_WINDOWS */ +#include <pcap.h> +#ifndef CONFIG_WINPCAP +#include <dnet.h> +#endif /* CONFIG_WINPCAP */ + +#include "common.h" +#include "eloop.h" +#include "l2_packet.h" + + +static const u8 pae_group_addr[ETH_ALEN] = +{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }; + +struct l2_packet_data { + pcap_t *pcap; +#ifdef CONFIG_WINPCAP + unsigned int num_fast_poll; +#else /* CONFIG_WINPCAP */ + eth_t *eth; +#endif /* CONFIG_WINPCAP */ + char ifname[100]; + u8 own_addr[ETH_ALEN]; + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len); + void *rx_callback_ctx; + int l2_hdr; /* whether to include layer 2 (Ethernet) header in calls + * to rx_callback */ +}; + + +int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr) +{ + os_memcpy(addr, l2->own_addr, ETH_ALEN); + return 0; +} + + +#ifndef CONFIG_WINPCAP +static int l2_packet_init_libdnet(struct l2_packet_data *l2) +{ + eth_addr_t own_addr; + + l2->eth = eth_open(l2->ifname); + if (!l2->eth) { + printf("Failed to open interface '%s'.\n", l2->ifname); + perror("eth_open"); + return -1; + } + + if (eth_get(l2->eth, &own_addr) < 0) { + printf("Failed to get own hw address from interface '%s'.\n", + l2->ifname); + perror("eth_get"); + eth_close(l2->eth); + l2->eth = NULL; + return -1; + } + os_memcpy(l2->own_addr, own_addr.data, ETH_ALEN); + + return 0; +} +#endif /* CONFIG_WINPCAP */ + + +int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, + const u8 *buf, size_t len) +{ + int ret; + struct l2_ethhdr *eth; + + if (l2 == NULL) + return -1; + + if (l2->l2_hdr) { +#ifdef CONFIG_WINPCAP + ret = pcap_sendpacket(l2->pcap, buf, len); +#else /* CONFIG_WINPCAP */ + ret = eth_send(l2->eth, buf, len); +#endif /* CONFIG_WINPCAP */ + } else { + size_t mlen = sizeof(*eth) + len; + eth = os_malloc(mlen); + if (eth == NULL) + return -1; + + os_memcpy(eth->h_dest, dst_addr, ETH_ALEN); + os_memcpy(eth->h_source, l2->own_addr, ETH_ALEN); + eth->h_proto = htons(proto); + os_memcpy(eth + 1, buf, len); + +#ifdef CONFIG_WINPCAP + ret = pcap_sendpacket(l2->pcap, (u8 *) eth, mlen); +#else /* CONFIG_WINPCAP */ + ret = eth_send(l2->eth, (u8 *) eth, mlen); +#endif /* CONFIG_WINPCAP */ + + os_free(eth); + } + + return ret; +} + + +#ifndef CONFIG_WINPCAP +static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct l2_packet_data *l2 = eloop_ctx; + pcap_t *pcap = sock_ctx; + struct pcap_pkthdr hdr; + const u_char *packet; + struct l2_ethhdr *ethhdr; + unsigned char *buf; + size_t len; + + packet = pcap_next(pcap, &hdr); + + if (packet == NULL || hdr.caplen < sizeof(*ethhdr)) + return; + + ethhdr = (struct l2_ethhdr *) packet; + if (l2->l2_hdr) { + buf = (unsigned char *) ethhdr; + len = hdr.caplen; + } else { + buf = (unsigned char *) (ethhdr + 1); + len = hdr.caplen - sizeof(*ethhdr); + } + l2->rx_callback(l2->rx_callback_ctx, ethhdr->h_source, buf, len); +} +#endif /* CONFIG_WINPCAP */ + + +#ifdef CONFIG_WINPCAP +static void l2_packet_receive_cb(u_char *user, const struct pcap_pkthdr *hdr, + const u_char *pkt_data) +{ + struct l2_packet_data *l2 = (struct l2_packet_data *) user; + struct l2_ethhdr *ethhdr; + unsigned char *buf; + size_t len; + + if (pkt_data == NULL || hdr->caplen < sizeof(*ethhdr)) + return; + + ethhdr = (struct l2_ethhdr *) pkt_data; + if (l2->l2_hdr) { + buf = (unsigned char *) ethhdr; + len = hdr->caplen; + } else { + buf = (unsigned char *) (ethhdr + 1); + len = hdr->caplen - sizeof(*ethhdr); + } + l2->rx_callback(l2->rx_callback_ctx, ethhdr->h_source, buf, len); + /* + * Use shorter poll interval for 3 seconds to reduce latency during key + * handshake. + */ + l2->num_fast_poll = 3 * 50; +} + + +static void l2_packet_receive_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct l2_packet_data *l2 = eloop_ctx; + pcap_t *pcap = timeout_ctx; + int timeout; + + if (l2->num_fast_poll > 0) { + timeout = 20000; + l2->num_fast_poll--; + } else + timeout = 100000; + + /* Register new timeout before calling l2_packet_receive() since + * receive handler may free this l2_packet instance (which will + * cancel this timeout). */ + eloop_register_timeout(0, timeout, l2_packet_receive_timeout, + l2, pcap); + pcap_dispatch(pcap, 10, l2_packet_receive_cb, (u_char *) l2); +} +#endif /* CONFIG_WINPCAP */ + + +static int l2_packet_init_libpcap(struct l2_packet_data *l2, + unsigned short protocol) +{ + bpf_u_int32 pcap_maskp, pcap_netp; + char pcap_filter[200], pcap_err[PCAP_ERRBUF_SIZE]; + struct bpf_program pcap_fp; + +#ifdef CONFIG_WINPCAP + char ifname[128]; + os_snprintf(ifname, sizeof(ifname), "\\Device\\NPF_%s", l2->ifname); + pcap_lookupnet(ifname, &pcap_netp, &pcap_maskp, pcap_err); + l2->pcap = pcap_open_live(ifname, 2500, 0, 10, pcap_err); + if (l2->pcap == NULL) { + fprintf(stderr, "pcap_open_live: %s\n", pcap_err); + fprintf(stderr, "ifname='%s'\n", ifname); + return -1; + } + if (pcap_setnonblock(l2->pcap, 1, pcap_err) < 0) + fprintf(stderr, "pcap_setnonblock: %s\n", + pcap_geterr(l2->pcap)); +#else /* CONFIG_WINPCAP */ + pcap_lookupnet(l2->ifname, &pcap_netp, &pcap_maskp, pcap_err); + l2->pcap = pcap_open_live(l2->ifname, 2500, 0, 10, pcap_err); + if (l2->pcap == NULL) { + fprintf(stderr, "pcap_open_live: %s\n", pcap_err); + fprintf(stderr, "ifname='%s'\n", l2->ifname); + return -1; + } + if (pcap_datalink(l2->pcap) != DLT_EN10MB && + pcap_set_datalink(l2->pcap, DLT_EN10MB) < 0) { + fprintf(stderr, "pcap_set_datalink(DLT_EN10MB): %s\n", + pcap_geterr(l2->pcap)); + return -1; + } +#endif /* CONFIG_WINPCAP */ + os_snprintf(pcap_filter, sizeof(pcap_filter), + "not ether src " MACSTR " and " + "( ether dst " MACSTR " or ether dst " MACSTR " ) and " + "ether proto 0x%x", + MAC2STR(l2->own_addr), /* do not receive own packets */ + MAC2STR(l2->own_addr), MAC2STR(pae_group_addr), + protocol); + if (pcap_compile(l2->pcap, &pcap_fp, pcap_filter, 1, pcap_netp) < 0) { + fprintf(stderr, "pcap_compile: %s\n", pcap_geterr(l2->pcap)); + return -1; + } + + if (pcap_setfilter(l2->pcap, &pcap_fp) < 0) { + fprintf(stderr, "pcap_setfilter: %s\n", pcap_geterr(l2->pcap)); + return -1; + } + + pcap_freecode(&pcap_fp); +#ifdef BIOCIMMEDIATE + /* + * When libpcap uses BPF we must enable "immediate mode" to + * receive frames right away; otherwise the system may + * buffer them for us. + */ + { + unsigned int on = 1; + if (ioctl(pcap_fileno(l2->pcap), BIOCIMMEDIATE, &on) < 0) { + fprintf(stderr, "%s: cannot enable immediate mode on " + "interface %s: %s\n", + __func__, l2->ifname, strerror(errno)); + /* XXX should we fail? */ + } + } +#endif /* BIOCIMMEDIATE */ + +#ifdef CONFIG_WINPCAP + eloop_register_timeout(0, 100000, l2_packet_receive_timeout, + l2, l2->pcap); +#else /* CONFIG_WINPCAP */ + eloop_register_read_sock(pcap_get_selectable_fd(l2->pcap), + l2_packet_receive, l2, l2->pcap); +#endif /* CONFIG_WINPCAP */ + + return 0; +} + + +struct l2_packet_data * l2_packet_init( + const char *ifname, const u8 *own_addr, unsigned short protocol, + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len), + void *rx_callback_ctx, int l2_hdr) +{ + struct l2_packet_data *l2; + + l2 = os_zalloc(sizeof(struct l2_packet_data)); + if (l2 == NULL) + return NULL; + os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname)); + l2->rx_callback = rx_callback; + l2->rx_callback_ctx = rx_callback_ctx; + l2->l2_hdr = l2_hdr; + +#ifdef CONFIG_WINPCAP + if (own_addr) + os_memcpy(l2->own_addr, own_addr, ETH_ALEN); +#else /* CONFIG_WINPCAP */ + if (l2_packet_init_libdnet(l2)) + return NULL; +#endif /* CONFIG_WINPCAP */ + + if (l2_packet_init_libpcap(l2, protocol)) { +#ifndef CONFIG_WINPCAP + eth_close(l2->eth); +#endif /* CONFIG_WINPCAP */ + os_free(l2); + return NULL; + } + + return l2; +} + + +void l2_packet_deinit(struct l2_packet_data *l2) +{ + if (l2 == NULL) + return; + +#ifdef CONFIG_WINPCAP + eloop_cancel_timeout(l2_packet_receive_timeout, l2, l2->pcap); +#else /* CONFIG_WINPCAP */ + if (l2->eth) + eth_close(l2->eth); + eloop_unregister_read_sock(pcap_get_selectable_fd(l2->pcap)); +#endif /* CONFIG_WINPCAP */ + if (l2->pcap) + pcap_close(l2->pcap); + os_free(l2); +} + + +int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len) +{ + pcap_if_t *devs, *dev; + struct pcap_addr *addr; + struct sockaddr_in *saddr; + int found = 0; + char err[PCAP_ERRBUF_SIZE + 1]; + + if (pcap_findalldevs(&devs, err) < 0) { + wpa_printf(MSG_DEBUG, "pcap_findalldevs: %s\n", err); + return -1; + } + + for (dev = devs; dev && !found; dev = dev->next) { + if (os_strcmp(dev->name, l2->ifname) != 0) + continue; + + addr = dev->addresses; + while (addr) { + saddr = (struct sockaddr_in *) addr->addr; + if (saddr && saddr->sin_family == AF_INET) { + os_strlcpy(buf, inet_ntoa(saddr->sin_addr), + len); + found = 1; + break; + } + addr = addr->next; + } + } + + pcap_freealldevs(devs); + + return found ? 0 : -1; +} + + +void l2_packet_notify_auth_start(struct l2_packet_data *l2) +{ +#ifdef CONFIG_WINPCAP + /* + * Use shorter poll interval for 3 seconds to reduce latency during key + * handshake. + */ + l2->num_fast_poll = 3 * 50; + eloop_cancel_timeout(l2_packet_receive_timeout, l2, l2->pcap); + eloop_register_timeout(0, 10000, l2_packet_receive_timeout, + l2, l2->pcap); +#endif /* CONFIG_WINPCAP */ +} diff --git a/src/l2_packet/l2_packet_privsep.c b/src/l2_packet/l2_packet_privsep.c new file mode 100644 index 0000000..c0e7c49 --- /dev/null +++ b/src/l2_packet/l2_packet_privsep.c @@ -0,0 +1,267 @@ +/* + * WPA Supplicant - Layer2 packet handling with privilege separation + * Copyright (c) 2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <sys/un.h> + +#include "common.h" +#include "eloop.h" +#include "l2_packet.h" +#include "privsep_commands.h" + + +struct l2_packet_data { + int fd; /* UNIX domain socket for privsep access */ + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len); + void *rx_callback_ctx; + u8 own_addr[ETH_ALEN]; + char *own_socket_path; + struct sockaddr_un priv_addr; +}; + + +static int wpa_priv_cmd(struct l2_packet_data *l2, int cmd, + const void *data, size_t data_len) +{ + struct msghdr msg; + struct iovec io[2]; + + io[0].iov_base = &cmd; + io[0].iov_len = sizeof(cmd); + io[1].iov_base = (u8 *) data; + io[1].iov_len = data_len; + + os_memset(&msg, 0, sizeof(msg)); + msg.msg_iov = io; + msg.msg_iovlen = data ? 2 : 1; + msg.msg_name = &l2->priv_addr; + msg.msg_namelen = sizeof(l2->priv_addr); + + if (sendmsg(l2->fd, &msg, 0) < 0) { + perror("L2: sendmsg(cmd)"); + return -1; + } + + return 0; +} + + +int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr) +{ + os_memcpy(addr, l2->own_addr, ETH_ALEN); + return 0; +} + + +int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, + const u8 *buf, size_t len) +{ + struct msghdr msg; + struct iovec io[4]; + int cmd = PRIVSEP_CMD_L2_SEND; + + io[0].iov_base = &cmd; + io[0].iov_len = sizeof(cmd); + io[1].iov_base = &dst_addr; + io[1].iov_len = ETH_ALEN; + io[2].iov_base = &proto; + io[2].iov_len = 2; + io[3].iov_base = (u8 *) buf; + io[3].iov_len = len; + + os_memset(&msg, 0, sizeof(msg)); + msg.msg_iov = io; + msg.msg_iovlen = 4; + msg.msg_name = &l2->priv_addr; + msg.msg_namelen = sizeof(l2->priv_addr); + + if (sendmsg(l2->fd, &msg, 0) < 0) { + perror("L2: sendmsg(packet_send)"); + return -1; + } + + return 0; +} + + +static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct l2_packet_data *l2 = eloop_ctx; + u8 buf[2300]; + int res; + struct sockaddr_un from; + socklen_t fromlen = sizeof(from); + + os_memset(&from, 0, sizeof(from)); + res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &from, + &fromlen); + if (res < 0) { + perror("l2_packet_receive - recvfrom"); + return; + } + if (res < ETH_ALEN) { + wpa_printf(MSG_DEBUG, "L2: Too show packet received"); + return; + } + + if (from.sun_family != AF_UNIX || + os_strncmp(from.sun_path, l2->priv_addr.sun_path, + sizeof(from.sun_path)) != 0) { + wpa_printf(MSG_DEBUG, "L2: Received message from unexpected " + "source"); + return; + } + + l2->rx_callback(l2->rx_callback_ctx, buf, buf + ETH_ALEN, + res - ETH_ALEN); +} + + +struct l2_packet_data * l2_packet_init( + const char *ifname, const u8 *own_addr, unsigned short protocol, + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len), + void *rx_callback_ctx, int l2_hdr) +{ + struct l2_packet_data *l2; + char *own_dir = "/tmp"; + char *priv_dir = "/var/run/wpa_priv"; + size_t len; + static unsigned int counter = 0; + struct sockaddr_un addr; + fd_set rfds; + struct timeval tv; + int res; + u8 reply[ETH_ALEN + 1]; + int reg_cmd[2]; + + l2 = os_zalloc(sizeof(struct l2_packet_data)); + if (l2 == NULL) + return NULL; + l2->rx_callback = rx_callback; + l2->rx_callback_ctx = rx_callback_ctx; + + len = os_strlen(own_dir) + 50; + l2->own_socket_path = os_malloc(len); + if (l2->own_socket_path == NULL) { + os_free(l2); + return NULL; + } + os_snprintf(l2->own_socket_path, len, "%s/wpa_privsep-l2-%d-%d", + own_dir, getpid(), counter++); + + l2->priv_addr.sun_family = AF_UNIX; + os_snprintf(l2->priv_addr.sun_path, sizeof(l2->priv_addr.sun_path), + "%s/%s", priv_dir, ifname); + + l2->fd = socket(PF_UNIX, SOCK_DGRAM, 0); + if (l2->fd < 0) { + perror("socket(PF_UNIX)"); + os_free(l2->own_socket_path); + l2->own_socket_path = NULL; + os_free(l2); + return NULL; + } + + os_memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + os_strlcpy(addr.sun_path, l2->own_socket_path, sizeof(addr.sun_path)); + if (bind(l2->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("bind(PF_UNIX)"); + goto fail; + } + + reg_cmd[0] = protocol; + reg_cmd[1] = l2_hdr; + if (wpa_priv_cmd(l2, PRIVSEP_CMD_L2_REGISTER, reg_cmd, sizeof(reg_cmd)) + < 0) { + wpa_printf(MSG_ERROR, "L2: Failed to register with wpa_priv"); + goto fail; + } + + FD_ZERO(&rfds); + FD_SET(l2->fd, &rfds); + tv.tv_sec = 5; + tv.tv_usec = 0; + res = select(l2->fd + 1, &rfds, NULL, NULL, &tv); + if (res < 0 && errno != EINTR) { + perror("select"); + goto fail; + } + + if (FD_ISSET(l2->fd, &rfds)) { + res = recv(l2->fd, reply, sizeof(reply), 0); + if (res < 0) { + perror("recv"); + goto fail; + } + } else { + wpa_printf(MSG_DEBUG, "L2: Timeout while waiting for " + "registration reply"); + goto fail; + } + + if (res != ETH_ALEN) { + wpa_printf(MSG_DEBUG, "L2: Unexpected registration reply " + "(len=%d)", res); + } + os_memcpy(l2->own_addr, reply, ETH_ALEN); + + eloop_register_read_sock(l2->fd, l2_packet_receive, l2, NULL); + + return l2; + +fail: + close(l2->fd); + l2->fd = -1; + unlink(l2->own_socket_path); + os_free(l2->own_socket_path); + l2->own_socket_path = NULL; + os_free(l2); + return NULL; +} + + +void l2_packet_deinit(struct l2_packet_data *l2) +{ + if (l2 == NULL) + return; + + if (l2->fd >= 0) { + wpa_priv_cmd(l2, PRIVSEP_CMD_L2_UNREGISTER, NULL, 0); + eloop_unregister_read_sock(l2->fd); + close(l2->fd); + } + + if (l2->own_socket_path) { + unlink(l2->own_socket_path); + os_free(l2->own_socket_path); + } + + os_free(l2); +} + + +int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len) +{ + /* TODO */ + return -1; +} + + +void l2_packet_notify_auth_start(struct l2_packet_data *l2) +{ + wpa_priv_cmd(l2, PRIVSEP_CMD_L2_NOTIFY_AUTH_START, NULL, 0); +} diff --git a/src/l2_packet/l2_packet_winpcap.c b/src/l2_packet/l2_packet_winpcap.c new file mode 100644 index 0000000..f76b386 --- /dev/null +++ b/src/l2_packet/l2_packet_winpcap.c @@ -0,0 +1,341 @@ +/* + * WPA Supplicant - Layer2 packet handling with WinPcap RX thread + * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * This l2_packet implementation is explicitly for WinPcap and Windows events. + * l2_packet_pcap.c has support for WinPcap, but it requires polling to receive + * frames which means relatively long latency for EAPOL RX processing. The + * implementation here uses a separate thread to allow WinPcap to be receiving + * all the time to reduce latency for EAPOL receiving from about 100 ms to 3 ms + * when comparing l2_packet_pcap.c to l2_packet_winpcap.c. Extra sleep of 50 ms + * is added in to receive thread whenever no EAPOL frames has been received for + * a while. Whenever an EAPOL handshake is expected, this sleep is removed. + * + * The RX thread receives a frame and signals main thread through Windows event + * about the availability of a new frame. Processing the received frame is + * synchronized with pair of Windows events so that no extra buffer or queuing + * mechanism is needed. This implementation requires Windows specific event + * loop implementation, i.e., eloop_win.c. + * + * WinPcap has pcap_getevent() that could, in theory at least, be used to + * implement this kind of waiting with a simpler single-thread design. However, + * that event handle is not really signaled immediately when receiving each + * frame, so it does not really work for this kind of use. + */ + +#include "includes.h" +#include <pcap.h> + +#include "common.h" +#include "eloop.h" +#include "l2_packet.h" + + +static const u8 pae_group_addr[ETH_ALEN] = +{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }; + +/* + * Number of pcap_dispatch() iterations to do without extra wait after each + * received EAPOL packet or authentication notification. This is used to reduce + * latency for EAPOL receive. + */ +static const size_t no_wait_count = 750; + +struct l2_packet_data { + pcap_t *pcap; + unsigned int num_fast_poll; + char ifname[100]; + u8 own_addr[ETH_ALEN]; + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len); + void *rx_callback_ctx; + int l2_hdr; /* whether to include layer 2 (Ethernet) header in calls to + * rx_callback and l2_packet_send() */ + int running; + HANDLE rx_avail, rx_done, rx_thread, rx_thread_done, rx_notify; + u8 *rx_buf, *rx_src; + size_t rx_len; + size_t rx_no_wait; +}; + + +int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr) +{ + os_memcpy(addr, l2->own_addr, ETH_ALEN); + return 0; +} + + +int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, + const u8 *buf, size_t len) +{ + int ret; + struct l2_ethhdr *eth; + + if (l2 == NULL) + return -1; + + if (l2->l2_hdr) { + ret = pcap_sendpacket(l2->pcap, buf, len); + } else { + size_t mlen = sizeof(*eth) + len; + eth = os_malloc(mlen); + if (eth == NULL) + return -1; + + os_memcpy(eth->h_dest, dst_addr, ETH_ALEN); + os_memcpy(eth->h_source, l2->own_addr, ETH_ALEN); + eth->h_proto = htons(proto); + os_memcpy(eth + 1, buf, len); + ret = pcap_sendpacket(l2->pcap, (u8 *) eth, mlen); + os_free(eth); + } + + return ret; +} + + +/* pcap_dispatch() callback for the RX thread */ +static void l2_packet_receive_cb(u_char *user, const struct pcap_pkthdr *hdr, + const u_char *pkt_data) +{ + struct l2_packet_data *l2 = (struct l2_packet_data *) user; + struct l2_ethhdr *ethhdr; + + if (pkt_data == NULL || hdr->caplen < sizeof(*ethhdr)) + return; + + ethhdr = (struct l2_ethhdr *) pkt_data; + if (l2->l2_hdr) { + l2->rx_buf = (u8 *) ethhdr; + l2->rx_len = hdr->caplen; + } else { + l2->rx_buf = (u8 *) (ethhdr + 1); + l2->rx_len = hdr->caplen - sizeof(*ethhdr); + } + l2->rx_src = ethhdr->h_source; + SetEvent(l2->rx_avail); + WaitForSingleObject(l2->rx_done, INFINITE); + ResetEvent(l2->rx_done); + l2->rx_no_wait = no_wait_count; +} + + +/* main RX loop that is running in a separate thread */ +static DWORD WINAPI l2_packet_receive_thread(LPVOID arg) +{ + struct l2_packet_data *l2 = arg; + + while (l2->running) { + pcap_dispatch(l2->pcap, 1, l2_packet_receive_cb, + (u_char *) l2); + if (l2->rx_no_wait > 0) + l2->rx_no_wait--; + if (WaitForSingleObject(l2->rx_notify, + l2->rx_no_wait ? 0 : 50) == + WAIT_OBJECT_0) { + l2->rx_no_wait = no_wait_count; + ResetEvent(l2->rx_notify); + } + } + SetEvent(l2->rx_thread_done); + ExitThread(0); + return 0; +} + + +/* main thread RX event handler */ +static void l2_packet_rx_event(void *eloop_data, void *user_data) +{ + struct l2_packet_data *l2 = eloop_data; + l2->rx_callback(l2->rx_callback_ctx, l2->rx_src, l2->rx_buf, + l2->rx_len); + ResetEvent(l2->rx_avail); + SetEvent(l2->rx_done); +} + + +static int l2_packet_init_libpcap(struct l2_packet_data *l2, + unsigned short protocol) +{ + bpf_u_int32 pcap_maskp, pcap_netp; + char pcap_filter[200], pcap_err[PCAP_ERRBUF_SIZE]; + struct bpf_program pcap_fp; + + pcap_lookupnet(l2->ifname, &pcap_netp, &pcap_maskp, pcap_err); + l2->pcap = pcap_open_live(l2->ifname, 2500, 0, 1, pcap_err); + if (l2->pcap == NULL) { + fprintf(stderr, "pcap_open_live: %s\n", pcap_err); + fprintf(stderr, "ifname='%s'\n", l2->ifname); + return -1; + } + os_snprintf(pcap_filter, sizeof(pcap_filter), + "not ether src " MACSTR " and " + "( ether dst " MACSTR " or ether dst " MACSTR " ) and " + "ether proto 0x%x", + MAC2STR(l2->own_addr), /* do not receive own packets */ + MAC2STR(l2->own_addr), MAC2STR(pae_group_addr), + protocol); + if (pcap_compile(l2->pcap, &pcap_fp, pcap_filter, 1, pcap_netp) < 0) { + fprintf(stderr, "pcap_compile: %s\n", pcap_geterr(l2->pcap)); + return -1; + } + + if (pcap_setfilter(l2->pcap, &pcap_fp) < 0) { + fprintf(stderr, "pcap_setfilter: %s\n", pcap_geterr(l2->pcap)); + return -1; + } + + pcap_freecode(&pcap_fp); + + return 0; +} + + +struct l2_packet_data * l2_packet_init( + const char *ifname, const u8 *own_addr, unsigned short protocol, + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len), + void *rx_callback_ctx, int l2_hdr) +{ + struct l2_packet_data *l2; + DWORD thread_id; + + l2 = os_zalloc(sizeof(struct l2_packet_data)); + if (l2 == NULL) + return NULL; + if (os_strncmp(ifname, "\\Device\\NPF_", 12) == 0) + os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname)); + else + os_snprintf(l2->ifname, sizeof(l2->ifname), "\\Device\\NPF_%s", + ifname); + l2->rx_callback = rx_callback; + l2->rx_callback_ctx = rx_callback_ctx; + l2->l2_hdr = l2_hdr; + + if (own_addr) + os_memcpy(l2->own_addr, own_addr, ETH_ALEN); + + if (l2_packet_init_libpcap(l2, protocol)) { + os_free(l2); + return NULL; + } + + l2->rx_avail = CreateEvent(NULL, TRUE, FALSE, NULL); + l2->rx_done = CreateEvent(NULL, TRUE, FALSE, NULL); + l2->rx_notify = CreateEvent(NULL, TRUE, FALSE, NULL); + if (l2->rx_avail == NULL || l2->rx_done == NULL || + l2->rx_notify == NULL) { + CloseHandle(l2->rx_avail); + CloseHandle(l2->rx_done); + CloseHandle(l2->rx_notify); + pcap_close(l2->pcap); + os_free(l2); + return NULL; + } + + eloop_register_event(l2->rx_avail, sizeof(l2->rx_avail), + l2_packet_rx_event, l2, NULL); + + l2->running = 1; + l2->rx_thread = CreateThread(NULL, 0, l2_packet_receive_thread, l2, 0, + &thread_id); + + return l2; +} + + +static void l2_packet_deinit_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct l2_packet_data *l2 = eloop_ctx; + + if (l2->rx_thread_done && + WaitForSingleObject(l2->rx_thread_done, 2000) != WAIT_OBJECT_0) { + wpa_printf(MSG_DEBUG, "l2_packet_winpcap: RX thread did not " + "exit - kill it\n"); + TerminateThread(l2->rx_thread, 0); + } + CloseHandle(l2->rx_thread_done); + CloseHandle(l2->rx_thread); + if (l2->pcap) + pcap_close(l2->pcap); + eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail)); + CloseHandle(l2->rx_avail); + CloseHandle(l2->rx_done); + CloseHandle(l2->rx_notify); + os_free(l2); +} + + +void l2_packet_deinit(struct l2_packet_data *l2) +{ + if (l2 == NULL) + return; + + l2->rx_thread_done = CreateEvent(NULL, TRUE, FALSE, NULL); + + l2->running = 0; + pcap_breakloop(l2->pcap); + + /* + * RX thread may be waiting in l2_packet_receive_cb() for l2->rx_done + * event and this event is set in l2_packet_rx_event(). However, + * l2_packet_deinit() may end up being called from l2->rx_callback(), + * so we need to return from here and complete deinitialization in + * a registered timeout to avoid having to forcefully kill the RX + * thread. + */ + eloop_register_timeout(0, 0, l2_packet_deinit_timeout, l2, NULL); +} + + +int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len) +{ + pcap_if_t *devs, *dev; + struct pcap_addr *addr; + struct sockaddr_in *saddr; + int found = 0; + char err[PCAP_ERRBUF_SIZE + 1]; + + if (pcap_findalldevs(&devs, err) < 0) { + wpa_printf(MSG_DEBUG, "pcap_findalldevs: %s\n", err); + return -1; + } + + for (dev = devs; dev && !found; dev = dev->next) { + if (os_strcmp(dev->name, l2->ifname) != 0) + continue; + + addr = dev->addresses; + while (addr) { + saddr = (struct sockaddr_in *) addr->addr; + if (saddr && saddr->sin_family == AF_INET) { + os_strlcpy(buf, inet_ntoa(saddr->sin_addr), + len); + found = 1; + break; + } + addr = addr->next; + } + } + + pcap_freealldevs(devs); + + return found ? 0 : -1; +} + + +void l2_packet_notify_auth_start(struct l2_packet_data *l2) +{ + if (l2) + SetEvent(l2->rx_notify); +} diff --git a/src/radius/.gitignore b/src/radius/.gitignore deleted file mode 100644 index a438335..0000000 --- a/src/radius/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.d diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c index 673e97e..826acad 100644 --- a/src/radius/radius_client.c +++ b/src/radius/radius_client.c @@ -917,6 +917,22 @@ static void radius_retry_primary_timer(void *eloop_ctx, void *timeout_ctx) } +static int radius_client_disable_pmtu_discovery(int s) +{ + int r = -1; +#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) + /* Turn off Path MTU discovery on IPv4/UDP sockets. */ + int action = IP_PMTUDISC_DONT; + r = setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &action, + sizeof(action)); + if (r == -1) + wpa_printf(MSG_ERROR, "Failed to set IP_MTU_DISCOVER: " + "%s", strerror(errno)); +#endif + return r; +} + + static int radius_client_init_auth(struct radius_client_data *radius) { struct hostapd_radius_servers *conf = radius->conf; @@ -925,8 +941,10 @@ static int radius_client_init_auth(struct radius_client_data *radius) radius->auth_serv_sock = socket(PF_INET, SOCK_DGRAM, 0); if (radius->auth_serv_sock < 0) perror("socket[PF_INET,SOCK_DGRAM]"); - else + else { + radius_client_disable_pmtu_discovery(radius->auth_serv_sock); ok++; + } #ifdef CONFIG_IPV6 radius->auth_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0); @@ -975,8 +993,10 @@ static int radius_client_init_acct(struct radius_client_data *radius) radius->acct_serv_sock = socket(PF_INET, SOCK_DGRAM, 0); if (radius->acct_serv_sock < 0) perror("socket[PF_INET,SOCK_DGRAM]"); - else + else { + radius_client_disable_pmtu_discovery(radius->acct_serv_sock); ok++; + } #ifdef CONFIG_IPV6 radius->acct_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0); @@ -1060,6 +1080,12 @@ void radius_client_deinit(struct radius_client_data *radius) eloop_unregister_read_sock(radius->auth_serv_sock); if (radius->acct_serv_sock >= 0) eloop_unregister_read_sock(radius->acct_serv_sock); +#ifdef CONFIG_IPV6 + if (radius->auth_serv_sock6 >= 0) + eloop_unregister_read_sock(radius->auth_serv_sock6); + if (radius->acct_serv_sock6 >= 0) + eloop_unregister_read_sock(radius->acct_serv_sock6); +#endif /* CONFIG_IPV6 */ eloop_cancel_timeout(radius_retry_primary_timer, radius, NULL); diff --git a/src/radius/radius_server.c b/src/radius/radius_server.c index 1bfb93c..4f399bc 100644 --- a/src/radius/radius_server.c +++ b/src/radius/radius_server.c @@ -117,7 +117,8 @@ wpa_hexdump_ascii(MSG_MSGDUMP, "RADIUS SRV: " args) static void radius_server_session_timeout(void *eloop_ctx, void *timeout_ctx); - +static void radius_server_session_remove_timeout(void *eloop_ctx, + void *timeout_ctx); static struct radius_client * @@ -179,6 +180,7 @@ static void radius_server_session_free(struct radius_server_data *data, struct radius_session *sess) { eloop_cancel_timeout(radius_server_session_timeout, data, sess); + eloop_cancel_timeout(radius_server_session_remove_timeout, data, sess); eap_server_sm_deinit(sess->eap); if (sess->last_msg) { radius_msg_free(sess->last_msg); @@ -194,9 +196,6 @@ static void radius_server_session_free(struct radius_server_data *data, } -static void radius_server_session_remove_timeout(void *eloop_ctx, - void *timeout_ctx); - static void radius_server_session_remove(struct radius_server_data *data, struct radius_session *sess) { @@ -493,6 +492,7 @@ static int radius_server_request(struct radius_server_data *data, unsigned int state; struct radius_session *sess; struct radius_msg *reply; + int is_complete = 0; if (force_sess) sess = force_sess; @@ -603,6 +603,9 @@ static int radius_server_request(struct radius_server_data *data, return -1; } + if (sess->eap_if->eapSuccess || sess->eap_if->eapFail) + is_complete = 1; + reply = radius_server_encapsulate_eap(data, client, sess, msg); if (reply) { @@ -644,7 +647,7 @@ static int radius_server_request(struct radius_server_data *data, client->counters.packets_dropped++; } - if (sess->eap_if->eapSuccess || sess->eap_if->eapFail) { + if (is_complete) { RADIUS_DEBUG("Removing completed session 0x%x after timeout", sess->sess_id); eloop_cancel_timeout(radius_server_session_remove_timeout, @@ -663,7 +666,13 @@ static void radius_server_receive_auth(int sock, void *eloop_ctx, { struct radius_server_data *data = eloop_ctx; u8 *buf = NULL; - struct sockaddr_storage from; + union { + struct sockaddr_storage ss; + struct sockaddr_in sin; +#ifdef CONFIG_IPV6 + struct sockaddr_in6 sin6; +#endif /* CONFIG_IPV6 */ + } from; socklen_t fromlen; int len; struct radius_client *client = NULL; @@ -678,7 +687,7 @@ static void radius_server_receive_auth(int sock, void *eloop_ctx, fromlen = sizeof(from); len = recvfrom(sock, buf, RADIUS_MAX_MSG_LEN, 0, - (struct sockaddr *) &from, &fromlen); + (struct sockaddr *) &from.ss, &fromlen); if (len < 0) { perror("recvfrom[radius_server]"); goto fail; @@ -686,28 +695,26 @@ static void radius_server_receive_auth(int sock, void *eloop_ctx, #ifdef CONFIG_IPV6 if (data->ipv6) { - struct sockaddr_in6 *from6 = (struct sockaddr_in6 *) &from; - if (inet_ntop(AF_INET6, &from6->sin6_addr, abuf, sizeof(abuf)) - == NULL) + if (inet_ntop(AF_INET6, &from.sin6.sin6_addr, abuf, + sizeof(abuf)) == NULL) abuf[0] = '\0'; - from_port = ntohs(from6->sin6_port); + from_port = ntohs(from.sin6.sin6_port); RADIUS_DEBUG("Received %d bytes from %s:%d", len, abuf, from_port); client = radius_server_get_client(data, (struct in_addr *) - &from6->sin6_addr, 1); + &from.sin6.sin6_addr, 1); } #endif /* CONFIG_IPV6 */ if (!data->ipv6) { - struct sockaddr_in *from4 = (struct sockaddr_in *) &from; - os_strlcpy(abuf, inet_ntoa(from4->sin_addr), sizeof(abuf)); - from_port = ntohs(from4->sin_port); + os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf)); + from_port = ntohs(from.sin.sin_port); RADIUS_DEBUG("Received %d bytes from %s:%d", len, abuf, from_port); - client = radius_server_get_client(data, &from4->sin_addr, 0); + client = radius_server_get_client(data, &from.sin.sin_addr, 0); } RADIUS_DUMP("Received data", buf, len); @@ -765,6 +772,22 @@ fail: } +static int radius_server_disable_pmtu_discovery(int s) +{ + int r = -1; +#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) + /* Turn off Path MTU discovery on IPv4/UDP sockets. */ + int action = IP_PMTUDISC_DONT; + r = setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &action, + sizeof(action)); + if (r == -1) + wpa_printf(MSG_ERROR, "Failed to set IP_MTU_DISCOVER: " + "%s", strerror(errno)); +#endif + return r; +} + + static int radius_server_open_socket(int port) { int s; @@ -776,6 +799,8 @@ static int radius_server_open_socket(int port) return -1; } + radius_server_disable_pmtu_discovery(s); + os_memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(port); diff --git a/src/rsn_supp/.gitignore b/src/rsn_supp/.gitignore deleted file mode 100644 index a438335..0000000 --- a/src/rsn_supp/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.d diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index b221476..e611fc5 100644 --- a/src/rsn_supp/wpa.c +++ b/src/rsn_supp/wpa.c @@ -358,14 +358,15 @@ static int wpa_derive_ptk(struct wpa_sm *sm, const unsigned char *src_addr, const struct wpa_eapol_key *key, struct wpa_ptk *ptk) { + size_t ptk_len = sm->pairwise_cipher == WPA_CIPHER_CCMP ? 48 : 64; #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->key_mgmt)) - return wpa_derive_ptk_ft(sm, src_addr, key, ptk); + return wpa_derive_ptk_ft(sm, src_addr, key, ptk, ptk_len); #endif /* CONFIG_IEEE80211R */ wpa_pmk_to_ptk(sm->pmk, sm->pmk_len, "Pairwise key expansion", sm->own_addr, sm->bssid, sm->snonce, key->key_nonce, - (u8 *) ptk, sizeof(*ptk), + (u8 *) ptk, ptk_len, wpa_key_mgmt_sha256(sm->key_mgmt)); return 0; } @@ -407,13 +408,13 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm, #endif /* CONFIG_NO_WPA2 */ if (wpa_supplicant_get_pmk(sm, src_addr, ie.pmkid)) - return; + goto failed; if (sm->renew_snonce) { if (os_get_random(sm->snonce, WPA_NONCE_LEN)) { wpa_msg(sm->ctx->ctx, MSG_WARNING, "WPA: Failed to get random data for SNonce"); - return; + goto failed; } sm->renew_snonce = 0; wpa_hexdump(MSG_DEBUG, "WPA: Renewed SNonce", @@ -433,9 +434,13 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm, if (wpa_supplicant_send_2_of_4(sm, sm->bssid, key, ver, sm->snonce, sm->assoc_wpa_ie, sm->assoc_wpa_ie_len, ptk)) - return; + goto failed; os_memcpy(sm->anonce, key->key_nonce, WPA_NONCE_LEN); + return; + +failed: + wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED); } @@ -537,7 +542,8 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm, if (wpa_sm_set_key(sm, alg, sm->bssid, 0, 1, key_rsc, rsclen, (u8 *) sm->ptk.tk1, keylen) < 0) { wpa_printf(MSG_WARNING, "WPA: Failed to set PTK to the " - "driver."); + "driver (alg=%d keylen=%d bssid=" MACSTR ")", + alg, keylen, MAC2STR(sm->bssid)); return -1; } @@ -647,7 +653,8 @@ static int wpa_supplicant_install_gtk(struct wpa_sm *sm, gd->keyidx, gd->tx, key_rsc, gd->key_rsc_len, _gtk, gd->gtk_len) < 0) { wpa_printf(MSG_WARNING, "WPA: Failed to set GTK to " - "the driver."); + "the driver (alg=%d keylen=%d keyidx=%d)", + gd->alg, gd->gtk_len, gd->keyidx); return -1; } @@ -944,30 +951,30 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, wpa_supplicant_parse_ies(pos, len, &ie); if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { wpa_printf(MSG_WARNING, "WPA: GTK IE in unencrypted key data"); - return; + goto failed; } #ifdef CONFIG_IEEE80211W if (ie.igtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { wpa_printf(MSG_WARNING, "WPA: IGTK KDE in unencrypted key " "data"); - return; + goto failed; } if (ie.igtk && ie.igtk_len != sizeof(struct wpa_igtk_kde)) { wpa_printf(MSG_WARNING, "WPA: Invalid IGTK KDE length %lu", (unsigned long) ie.igtk_len); - return; + goto failed; } #endif /* CONFIG_IEEE80211W */ if (wpa_supplicant_validate_ie(sm, sm->bssid, &ie) < 0) - return; + goto failed; if (os_memcmp(sm->anonce, key->key_nonce, WPA_NONCE_LEN) != 0) { wpa_printf(MSG_WARNING, "WPA: ANonce from message 1 of 4-Way " "Handshake differs from 3 of 4-Way Handshake - drop" " packet (src=" MACSTR ")", MAC2STR(sm->bssid)); - return; + goto failed; } keylen = WPA_GET_BE16(key->key_length); @@ -977,7 +984,7 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, wpa_printf(MSG_WARNING, "WPA: Invalid CCMP key length " "%d (src=" MACSTR ")", keylen, MAC2STR(sm->bssid)); - return; + goto failed; } break; case WPA_CIPHER_TKIP: @@ -985,14 +992,15 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, wpa_printf(MSG_WARNING, "WPA: Invalid TKIP key length " "%d (src=" MACSTR ")", keylen, MAC2STR(sm->bssid)); - return; + goto failed; } break; } if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info, - NULL, 0, &sm->ptk)) - return; + NULL, 0, &sm->ptk)) { + goto failed; + } /* SNonce was successfully used in msg 3/4, so mark it to be renewed * for the next 4-Way Handshake. If msg 3 is received again, the old @@ -1000,7 +1008,8 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, sm->renew_snonce = 1; if (key_info & WPA_KEY_INFO_INSTALL) { - wpa_supplicant_install_ptk(sm, key); + if (wpa_supplicant_install_ptk(sm, key)) + goto failed; } if (key_info & WPA_KEY_INFO_SECURE) { @@ -1015,10 +1024,18 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, wpa_supplicant_pairwise_gtk(sm, key, ie.gtk, ie.gtk_len, key_info) < 0) { wpa_printf(MSG_INFO, "RSN: Failed to configure GTK"); + goto failed; } - if (ieee80211w_set_keys(sm, &ie) < 0) + if (ieee80211w_set_keys(sm, &ie) < 0) { wpa_printf(MSG_INFO, "RSN: Failed to configure IGTK"); + goto failed; + } + + return; + +failed: + wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED); } @@ -1209,11 +1226,11 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm, wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE); if (ret) - return; + goto failed; if (wpa_supplicant_install_gtk(sm, &gd, key->key_rsc) || wpa_supplicant_send_2_of_2(sm, key, ver, key_info)) - return; + goto failed; if (rekey) { wpa_msg(sm->ctx->ctx, MSG_INFO, "WPA: Group rekeying " @@ -1226,6 +1243,10 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm, key_info & WPA_KEY_INFO_SECURE); } + return; + +failed: + wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED); } @@ -1468,9 +1489,9 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, key_info = WPA_GET_BE16(key->key_info); ver = key_info & WPA_KEY_INFO_TYPE_MASK; if (ver != WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 && -#ifdef CONFIG_IEEE80211R +#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) ver != WPA_KEY_INFO_TYPE_AES_128_CMAC && -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */ ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { wpa_printf(MSG_INFO, "WPA: Unsupported EAPOL-Key descriptor " "version %d.", ver); diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c index c89b89a..557b311 100644 --- a/src/rsn_supp/wpa_ft.c +++ b/src/rsn_supp/wpa_ft.c @@ -25,7 +25,7 @@ int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr, const struct wpa_eapol_key *key, - struct wpa_ptk *ptk) + struct wpa_ptk *ptk, size_t ptk_len) { u8 pmk_r1_name[WPA_PMK_NAME_LEN]; u8 ptk_name[WPA_PMK_NAME_LEN]; @@ -50,8 +50,8 @@ int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr, wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN); wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, anonce, sm->own_addr, sm->bssid, pmk_r1_name, - (u8 *) ptk, sizeof(*ptk), ptk_name); - wpa_hexdump_key(MSG_DEBUG, "FT: PTK", (u8 *) ptk, sizeof(*ptk)); + (u8 *) ptk, ptk_len, ptk_name); + wpa_hexdump_key(MSG_DEBUG, "FT: PTK", (u8 *) ptk, ptk_len); wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN); return 0; @@ -455,7 +455,7 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, int ft_action, const u8 *target_ap) { u8 *ft_ies; - size_t ft_ies_len; + size_t ft_ies_len, ptk_len; struct wpa_ft_ies parse; struct rsn_mdie *mdie; struct rsn_ftie *ftie; @@ -545,11 +545,12 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, sm->pmk_r1_name, WPA_PMK_NAME_LEN); bssid = target_ap; + ptk_len = sm->pairwise_cipher == WPA_CIPHER_CCMP ? 48 : 64; wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, ftie->anonce, sm->own_addr, bssid, sm->pmk_r1_name, - (u8 *) &sm->ptk, sizeof(sm->ptk), ptk_name); + (u8 *) &sm->ptk, ptk_len, ptk_name); wpa_hexdump_key(MSG_DEBUG, "FT: PTK", - (u8 *) &sm->ptk, sizeof(sm->ptk)); + (u8 *) &sm->ptk, ptk_len); wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN); ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, ftie->anonce, diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h index 95348da..e0dc6bd 100644 --- a/src/rsn_supp/wpa_i.h +++ b/src/rsn_supp/wpa_i.h @@ -240,6 +240,6 @@ int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst, int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr, const struct wpa_eapol_key *key, - struct wpa_ptk *ptk); + struct wpa_ptk *ptk, size_t ptk_len); #endif /* WPA_I_H */ diff --git a/src/tls/.gitignore b/src/tls/.gitignore deleted file mode 100644 index a438335..0000000 --- a/src/tls/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.d diff --git a/src/tls/rsa.c b/src/tls/rsa.c index bfc0d52..4965a2a 100644 --- a/src/tls/rsa.c +++ b/src/tls/rsa.c @@ -35,6 +35,7 @@ struct crypto_rsa_key { }; +#ifdef EAP_TLS_FUNCS static const u8 * crypto_rsa_parse_integer(const u8 *pos, const u8 *end, struct bignum *num) { @@ -223,6 +224,7 @@ error: crypto_rsa_free(key); return NULL; } +#endif /* EAP_TLS_FUNCS */ /** diff --git a/src/tls/tlsv1_client.c b/src/tls/tlsv1_client.c index 302e3ee..0bf1174 100644 --- a/src/tls/tlsv1_client.c +++ b/src/tls/tlsv1_client.c @@ -620,6 +620,17 @@ int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers) suites[count++] = TLS_DH_anon_WITH_3DES_EDE_CBC_SHA; suites[count++] = TLS_DH_anon_WITH_RC4_128_MD5; suites[count++] = TLS_DH_anon_WITH_DES_CBC_SHA; + + /* + * Cisco AP (at least 350 and 1200 series) local authentication + * server does not know how to search cipher suites from the + * list and seem to require that the last entry in the list is + * the one that it wants to use. However, TLS specification + * requires the list to be in the client preference order. As a + * workaround, add anon-DH AES-128-SHA1 again at the end of the + * list to allow the Cisco code to find it. + */ + suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA; conn->num_cipher_suites = count; } diff --git a/src/tls/tlsv1_server_read.c b/src/tls/tlsv1_server_read.c index 0e299d8..397d74a 100644 --- a/src/tls/tlsv1_server_read.c +++ b/src/tls/tlsv1_server_read.c @@ -40,6 +40,7 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, u16 cipher_suite; u16 num_suites; int compr_null_found; + u16 ext_type, ext_len; if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " @@ -183,10 +184,7 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, } if (end - pos >= 2) { - u16 ext_len; - /* Extension client_hello_extension_list<0..2^16-1> */ - ext_len = WPA_GET_BE16(pos); pos += 2; @@ -195,7 +193,7 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, if (end - pos != ext_len) { wpa_printf(MSG_DEBUG, "TLSv1: Invalid ClientHello " "extension list length %u (expected %u)", - ext_len, end - pos); + ext_len, (unsigned int) (end - pos)); goto decode_error; } @@ -207,8 +205,6 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, */ while (pos < end) { - u16 ext_type, ext_len; - if (end - pos < 2) { wpa_printf(MSG_DEBUG, "TLSv1: Invalid " "extension_type field"); @@ -520,7 +516,7 @@ static int tls_process_client_key_exchange_rsa( out, &outlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to decrypt " "PreMasterSecret (encr_len=%d outlen=%lu)", - end - pos, (unsigned long) outlen); + (int) (end - pos), (unsigned long) outlen); use_random = 1; } diff --git a/src/utils/.gitignore b/src/utils/.gitignore deleted file mode 100644 index a438335..0000000 --- a/src/utils/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.d diff --git a/src/utils/base64.c b/src/utils/base64.c index 0eadb81..13fc511 100644 --- a/src/utils/base64.c +++ b/src/utils/base64.c @@ -43,6 +43,8 @@ unsigned char * base64_encode(const unsigned char *src, size_t len, olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */ olen += olen / 72; /* line feeds */ olen++; /* nul termination */ + if (olen < len) + return NULL; /* integer overflow */ out = os_malloc(olen); if (out == NULL) return NULL; diff --git a/src/utils/common.c b/src/utils/common.c index cb373c3..9a46ebe 100644 --- a/src/utils/common.c +++ b/src/utils/common.c @@ -325,3 +325,9 @@ const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len) } return ssid_txt; } + + +void * __hide_aliasing_typecast(void *foo) +{ + return foo; +} diff --git a/src/utils/common.h b/src/utils/common.h index d0a2eb3..d649391 100644 --- a/src/utils/common.h +++ b/src/utils/common.h @@ -22,17 +22,24 @@ #include <byteswap.h> #endif /* __linux__ */ -#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || \ + defined(__OpenBSD__) #include <sys/types.h> #include <sys/endian.h> #define __BYTE_ORDER _BYTE_ORDER #define __LITTLE_ENDIAN _LITTLE_ENDIAN #define __BIG_ENDIAN _BIG_ENDIAN +#ifdef __OpenBSD__ +#define bswap_16 swap16 +#define bswap_32 swap32 +#define bswap_64 swap64 +#else /* __OpenBSD__ */ #define bswap_16 bswap16 #define bswap_32 bswap32 #define bswap_64 bswap64 +#endif /* __OpenBSD__ */ #endif /* defined(__FreeBSD__) || defined(__NetBSD__) || - * defined(__DragonFly__) */ + * defined(__DragonFly__) || defined(__OpenBSD__) */ #ifdef __APPLE__ #include <sys/types.h> @@ -435,4 +442,17 @@ static inline int is_zero_ether_addr(const u8 *a) #include "wpa_debug.h" + +/* + * gcc 4.4 ends up generating strict-aliasing warnings about some very common + * networking socket uses that do not really result in a real problem and + * cannot be easily avoided with union-based type-punning due to struct + * definitions including another struct in system header files. To avoid having + * to fully disable strict-aliasing warnings, provide a mechanism to hide the + * typecast from aliasing for now. A cleaner solution will hopefully be found + * in the future to handle these cases. + */ +void * __hide_aliasing_typecast(void *foo); +#define aliasing_hide_typecast(a,t) (t *) __hide_aliasing_typecast((a)) + #endif /* COMMON_H */ diff --git a/src/utils/eloop_none.c b/src/utils/eloop_none.c new file mode 100644 index 0000000..215030b --- /dev/null +++ b/src/utils/eloop_none.c @@ -0,0 +1,410 @@ +/* + * Event loop - empty template (basic structure, but no OS specific operations) + * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" + + +struct eloop_sock { + int sock; + void *eloop_data; + void *user_data; + void (*handler)(int sock, void *eloop_ctx, void *sock_ctx); +}; + +struct eloop_timeout { + struct os_time time; + void *eloop_data; + void *user_data; + void (*handler)(void *eloop_ctx, void *sock_ctx); + struct eloop_timeout *next; +}; + +struct eloop_signal { + int sig; + void *user_data; + void (*handler)(int sig, void *eloop_ctx, void *signal_ctx); + int signaled; +}; + +struct eloop_data { + void *user_data; + + int max_sock, reader_count; + struct eloop_sock *readers; + + struct eloop_timeout *timeout; + + int signal_count; + struct eloop_signal *signals; + int signaled; + int pending_terminate; + + int terminate; + int reader_table_changed; +}; + +static struct eloop_data eloop; + + +int eloop_init(void *user_data) +{ + memset(&eloop, 0, sizeof(eloop)); + eloop.user_data = user_data; + return 0; +} + + +int eloop_register_read_sock(int sock, + void (*handler)(int sock, void *eloop_ctx, + void *sock_ctx), + void *eloop_data, void *user_data) +{ + struct eloop_sock *tmp; + + tmp = (struct eloop_sock *) + realloc(eloop.readers, + (eloop.reader_count + 1) * sizeof(struct eloop_sock)); + if (tmp == NULL) + return -1; + + tmp[eloop.reader_count].sock = sock; + tmp[eloop.reader_count].eloop_data = eloop_data; + tmp[eloop.reader_count].user_data = user_data; + tmp[eloop.reader_count].handler = handler; + eloop.reader_count++; + eloop.readers = tmp; + if (sock > eloop.max_sock) + eloop.max_sock = sock; + eloop.reader_table_changed = 1; + + return 0; +} + + +void eloop_unregister_read_sock(int sock) +{ + int i; + + if (eloop.readers == NULL || eloop.reader_count == 0) + return; + + for (i = 0; i < eloop.reader_count; i++) { + if (eloop.readers[i].sock == sock) + break; + } + if (i == eloop.reader_count) + return; + if (i != eloop.reader_count - 1) { + memmove(&eloop.readers[i], &eloop.readers[i + 1], + (eloop.reader_count - i - 1) * + sizeof(struct eloop_sock)); + } + eloop.reader_count--; + eloop.reader_table_changed = 1; +} + + +int eloop_register_timeout(unsigned int secs, unsigned int usecs, + void (*handler)(void *eloop_ctx, void *timeout_ctx), + void *eloop_data, void *user_data) +{ + struct eloop_timeout *timeout, *tmp, *prev; + + timeout = (struct eloop_timeout *) malloc(sizeof(*timeout)); + if (timeout == NULL) + return -1; + os_get_time(&timeout->time); + timeout->time.sec += secs; + timeout->time.usec += usecs; + while (timeout->time.usec >= 1000000) { + timeout->time.sec++; + timeout->time.usec -= 1000000; + } + timeout->eloop_data = eloop_data; + timeout->user_data = user_data; + timeout->handler = handler; + timeout->next = NULL; + + if (eloop.timeout == NULL) { + eloop.timeout = timeout; + return 0; + } + + prev = NULL; + tmp = eloop.timeout; + while (tmp != NULL) { + if (os_time_before(&timeout->time, &tmp->time)) + break; + prev = tmp; + tmp = tmp->next; + } + + if (prev == NULL) { + timeout->next = eloop.timeout; + eloop.timeout = timeout; + } else { + timeout->next = prev->next; + prev->next = timeout; + } + + return 0; +} + + +int eloop_cancel_timeout(void (*handler)(void *eloop_ctx, void *sock_ctx), + void *eloop_data, void *user_data) +{ + struct eloop_timeout *timeout, *prev, *next; + int removed = 0; + + prev = NULL; + timeout = eloop.timeout; + while (timeout != NULL) { + next = timeout->next; + + if (timeout->handler == handler && + (timeout->eloop_data == eloop_data || + eloop_data == ELOOP_ALL_CTX) && + (timeout->user_data == user_data || + user_data == ELOOP_ALL_CTX)) { + if (prev == NULL) + eloop.timeout = next; + else + prev->next = next; + free(timeout); + removed++; + } else + prev = timeout; + + timeout = next; + } + + return removed; +} + + +int eloop_is_timeout_registered(void (*handler)(void *eloop_ctx, + void *timeout_ctx), + void *eloop_data, void *user_data) +{ + struct eloop_timeout *tmp; + + tmp = eloop.timeout; + while (tmp != NULL) { + if (tmp->handler == handler && + tmp->eloop_data == eloop_data && + tmp->user_data == user_data) + return 1; + + tmp = tmp->next; + } + + return 0; +} + + +/* TODO: replace with suitable signal handler */ +#if 0 +static void eloop_handle_signal(int sig) +{ + int i; + + eloop.signaled++; + for (i = 0; i < eloop.signal_count; i++) { + if (eloop.signals[i].sig == sig) { + eloop.signals[i].signaled++; + break; + } + } +} +#endif + + +static void eloop_process_pending_signals(void) +{ + int i; + + if (eloop.signaled == 0) + return; + eloop.signaled = 0; + + if (eloop.pending_terminate) { + eloop.pending_terminate = 0; + } + + for (i = 0; i < eloop.signal_count; i++) { + if (eloop.signals[i].signaled) { + eloop.signals[i].signaled = 0; + eloop.signals[i].handler(eloop.signals[i].sig, + eloop.user_data, + eloop.signals[i].user_data); + } + } +} + + +int eloop_register_signal(int sig, + void (*handler)(int sig, void *eloop_ctx, + void *signal_ctx), + void *user_data) +{ + struct eloop_signal *tmp; + + tmp = (struct eloop_signal *) + realloc(eloop.signals, + (eloop.signal_count + 1) * + sizeof(struct eloop_signal)); + if (tmp == NULL) + return -1; + + tmp[eloop.signal_count].sig = sig; + tmp[eloop.signal_count].user_data = user_data; + tmp[eloop.signal_count].handler = handler; + tmp[eloop.signal_count].signaled = 0; + eloop.signal_count++; + eloop.signals = tmp; + + /* TODO: register signal handler */ + + return 0; +} + + +int eloop_register_signal_terminate(void (*handler)(int sig, void *eloop_ctx, + void *signal_ctx), + void *user_data) +{ +#if 0 + /* TODO: for example */ + int ret = eloop_register_signal(SIGINT, handler, user_data); + if (ret == 0) + ret = eloop_register_signal(SIGTERM, handler, user_data); + return ret; +#endif + return 0; +} + + +int eloop_register_signal_reconfig(void (*handler)(int sig, void *eloop_ctx, + void *signal_ctx), + void *user_data) +{ +#if 0 + /* TODO: for example */ + return eloop_register_signal(SIGHUP, handler, user_data); +#endif + return 0; +} + + +void eloop_run(void) +{ + int i; + struct os_time tv, now; + + while (!eloop.terminate && + (eloop.timeout || eloop.reader_count > 0)) { + if (eloop.timeout) { + os_get_time(&now); + if (os_time_before(&now, &eloop.timeout->time)) + os_time_sub(&eloop.timeout->time, &now, &tv); + else + tv.sec = tv.usec = 0; + } + + /* + * TODO: wait for any event (read socket ready, timeout (tv), + * signal + */ + os_sleep(1, 0); /* just a dummy wait for testing */ + + eloop_process_pending_signals(); + + /* check if some registered timeouts have occurred */ + if (eloop.timeout) { + struct eloop_timeout *tmp; + + os_get_time(&now); + if (!os_time_before(&now, &eloop.timeout->time)) { + tmp = eloop.timeout; + eloop.timeout = eloop.timeout->next; + tmp->handler(tmp->eloop_data, + tmp->user_data); + free(tmp); + } + + } + + eloop.reader_table_changed = 0; + for (i = 0; i < eloop.reader_count; i++) { + /* + * TODO: call each handler that has pending data to + * read + */ + if (0 /* TODO: eloop.readers[i].sock ready */) { + eloop.readers[i].handler( + eloop.readers[i].sock, + eloop.readers[i].eloop_data, + eloop.readers[i].user_data); + if (eloop.reader_table_changed) + break; + } + } + } +} + + +void eloop_terminate(void) +{ + eloop.terminate = 1; +} + + +void eloop_destroy(void) +{ + struct eloop_timeout *timeout, *prev; + + timeout = eloop.timeout; + while (timeout != NULL) { + prev = timeout; + timeout = timeout->next; + free(prev); + } + free(eloop.readers); + free(eloop.signals); +} + + +int eloop_terminated(void) +{ + return eloop.terminate; +} + + +void eloop_wait_for_read_sock(int sock) +{ + /* + * TODO: wait for the file descriptor to have something available for + * reading + */ +} + + +void * eloop_get_user_data(void) +{ + return eloop.user_data; +} diff --git a/src/utils/eloop_win.c b/src/utils/eloop_win.c new file mode 100644 index 0000000..c95aa76 --- /dev/null +++ b/src/utils/eloop_win.c @@ -0,0 +1,622 @@ +/* + * Event loop based on Windows events and WaitForMultipleObjects + * Copyright (c) 2002-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <winsock2.h> + +#include "common.h" +#include "eloop.h" + + +struct eloop_sock { + int sock; + void *eloop_data; + void *user_data; + eloop_sock_handler handler; + WSAEVENT event; +}; + +struct eloop_event { + void *eloop_data; + void *user_data; + eloop_event_handler handler; + HANDLE event; +}; + +struct eloop_timeout { + struct os_time time; + void *eloop_data; + void *user_data; + eloop_timeout_handler handler; + struct eloop_timeout *next; +}; + +struct eloop_signal { + int sig; + void *user_data; + eloop_signal_handler handler; + int signaled; +}; + +struct eloop_data { + void *user_data; + + int max_sock; + size_t reader_count; + struct eloop_sock *readers; + + size_t event_count; + struct eloop_event *events; + + struct eloop_timeout *timeout; + + int signal_count; + struct eloop_signal *signals; + int signaled; + int pending_terminate; + + int terminate; + int reader_table_changed; + + struct eloop_signal term_signal; + HANDLE term_event; + + HANDLE *handles; + size_t num_handles; +}; + +static struct eloop_data eloop; + + +int eloop_init(void *user_data) +{ + os_memset(&eloop, 0, sizeof(eloop)); + eloop.user_data = user_data; + eloop.num_handles = 1; + eloop.handles = os_malloc(eloop.num_handles * + sizeof(eloop.handles[0])); + if (eloop.handles == NULL) + return -1; + + eloop.term_event = CreateEvent(NULL, FALSE, FALSE, NULL); + if (eloop.term_event == NULL) { + printf("CreateEvent() failed: %d\n", + (int) GetLastError()); + os_free(eloop.handles); + return -1; + } + + return 0; +} + + +static int eloop_prepare_handles(void) +{ + HANDLE *n; + + if (eloop.num_handles > eloop.reader_count + eloop.event_count + 8) + return 0; + n = os_realloc(eloop.handles, + eloop.num_handles * 2 * sizeof(eloop.handles[0])); + if (n == NULL) + return -1; + eloop.handles = n; + eloop.num_handles *= 2; + return 0; +} + + +int eloop_register_read_sock(int sock, eloop_sock_handler handler, + void *eloop_data, void *user_data) +{ + WSAEVENT event; + struct eloop_sock *tmp; + + if (eloop_prepare_handles()) + return -1; + + event = WSACreateEvent(); + if (event == WSA_INVALID_EVENT) { + printf("WSACreateEvent() failed: %d\n", WSAGetLastError()); + return -1; + } + + if (WSAEventSelect(sock, event, FD_READ)) { + printf("WSAEventSelect() failed: %d\n", WSAGetLastError()); + WSACloseEvent(event); + return -1; + } + tmp = os_realloc(eloop.readers, + (eloop.reader_count + 1) * sizeof(struct eloop_sock)); + if (tmp == NULL) { + WSAEventSelect(sock, event, 0); + WSACloseEvent(event); + return -1; + } + + tmp[eloop.reader_count].sock = sock; + tmp[eloop.reader_count].eloop_data = eloop_data; + tmp[eloop.reader_count].user_data = user_data; + tmp[eloop.reader_count].handler = handler; + tmp[eloop.reader_count].event = event; + eloop.reader_count++; + eloop.readers = tmp; + if (sock > eloop.max_sock) + eloop.max_sock = sock; + eloop.reader_table_changed = 1; + + return 0; +} + + +void eloop_unregister_read_sock(int sock) +{ + size_t i; + + if (eloop.readers == NULL || eloop.reader_count == 0) + return; + + for (i = 0; i < eloop.reader_count; i++) { + if (eloop.readers[i].sock == sock) + break; + } + if (i == eloop.reader_count) + return; + + WSAEventSelect(eloop.readers[i].sock, eloop.readers[i].event, 0); + WSACloseEvent(eloop.readers[i].event); + + if (i != eloop.reader_count - 1) { + os_memmove(&eloop.readers[i], &eloop.readers[i + 1], + (eloop.reader_count - i - 1) * + sizeof(struct eloop_sock)); + } + eloop.reader_count--; + eloop.reader_table_changed = 1; +} + + +int eloop_register_event(void *event, size_t event_size, + eloop_event_handler handler, + void *eloop_data, void *user_data) +{ + struct eloop_event *tmp; + HANDLE h = event; + + if (event_size != sizeof(HANDLE) || h == INVALID_HANDLE_VALUE) + return -1; + + if (eloop_prepare_handles()) + return -1; + + tmp = os_realloc(eloop.events, + (eloop.event_count + 1) * sizeof(struct eloop_event)); + if (tmp == NULL) + return -1; + + tmp[eloop.event_count].eloop_data = eloop_data; + tmp[eloop.event_count].user_data = user_data; + tmp[eloop.event_count].handler = handler; + tmp[eloop.event_count].event = h; + eloop.event_count++; + eloop.events = tmp; + + return 0; +} + + +void eloop_unregister_event(void *event, size_t event_size) +{ + size_t i; + HANDLE h = event; + + if (eloop.events == NULL || eloop.event_count == 0 || + event_size != sizeof(HANDLE)) + return; + + for (i = 0; i < eloop.event_count; i++) { + if (eloop.events[i].event == h) + break; + } + if (i == eloop.event_count) + return; + + if (i != eloop.event_count - 1) { + os_memmove(&eloop.events[i], &eloop.events[i + 1], + (eloop.event_count - i - 1) * + sizeof(struct eloop_event)); + } + eloop.event_count--; +} + + +int eloop_register_timeout(unsigned int secs, unsigned int usecs, + eloop_timeout_handler handler, + void *eloop_data, void *user_data) +{ + struct eloop_timeout *timeout, *tmp, *prev; + + timeout = os_malloc(sizeof(*timeout)); + if (timeout == NULL) + return -1; + os_get_time(&timeout->time); + timeout->time.sec += secs; + timeout->time.usec += usecs; + while (timeout->time.usec >= 1000000) { + timeout->time.sec++; + timeout->time.usec -= 1000000; + } + timeout->eloop_data = eloop_data; + timeout->user_data = user_data; + timeout->handler = handler; + timeout->next = NULL; + + if (eloop.timeout == NULL) { + eloop.timeout = timeout; + return 0; + } + + prev = NULL; + tmp = eloop.timeout; + while (tmp != NULL) { + if (os_time_before(&timeout->time, &tmp->time)) + break; + prev = tmp; + tmp = tmp->next; + } + + if (prev == NULL) { + timeout->next = eloop.timeout; + eloop.timeout = timeout; + } else { + timeout->next = prev->next; + prev->next = timeout; + } + + return 0; +} + + +int eloop_cancel_timeout(eloop_timeout_handler handler, + void *eloop_data, void *user_data) +{ + struct eloop_timeout *timeout, *prev, *next; + int removed = 0; + + prev = NULL; + timeout = eloop.timeout; + while (timeout != NULL) { + next = timeout->next; + + if (timeout->handler == handler && + (timeout->eloop_data == eloop_data || + eloop_data == ELOOP_ALL_CTX) && + (timeout->user_data == user_data || + user_data == ELOOP_ALL_CTX)) { + if (prev == NULL) + eloop.timeout = next; + else + prev->next = next; + os_free(timeout); + removed++; + } else + prev = timeout; + + timeout = next; + } + + return removed; +} + + +int eloop_is_timeout_registered(eloop_timeout_handler handler, + void *eloop_data, void *user_data) +{ + struct eloop_timeout *tmp; + + tmp = eloop.timeout; + while (tmp != NULL) { + if (tmp->handler == handler && + tmp->eloop_data == eloop_data && + tmp->user_data == user_data) + return 1; + + tmp = tmp->next; + } + + return 0; +} + + +/* TODO: replace with suitable signal handler */ +#if 0 +static void eloop_handle_signal(int sig) +{ + int i; + + eloop.signaled++; + for (i = 0; i < eloop.signal_count; i++) { + if (eloop.signals[i].sig == sig) { + eloop.signals[i].signaled++; + break; + } + } +} +#endif + + +static void eloop_process_pending_signals(void) +{ + int i; + + if (eloop.signaled == 0) + return; + eloop.signaled = 0; + + if (eloop.pending_terminate) { + eloop.pending_terminate = 0; + } + + for (i = 0; i < eloop.signal_count; i++) { + if (eloop.signals[i].signaled) { + eloop.signals[i].signaled = 0; + eloop.signals[i].handler(eloop.signals[i].sig, + eloop.user_data, + eloop.signals[i].user_data); + } + } + + if (eloop.term_signal.signaled) { + eloop.term_signal.signaled = 0; + eloop.term_signal.handler(eloop.term_signal.sig, + eloop.user_data, + eloop.term_signal.user_data); + } +} + + +int eloop_register_signal(int sig, eloop_signal_handler handler, + void *user_data) +{ + struct eloop_signal *tmp; + + tmp = os_realloc(eloop.signals, + (eloop.signal_count + 1) * + sizeof(struct eloop_signal)); + if (tmp == NULL) + return -1; + + tmp[eloop.signal_count].sig = sig; + tmp[eloop.signal_count].user_data = user_data; + tmp[eloop.signal_count].handler = handler; + tmp[eloop.signal_count].signaled = 0; + eloop.signal_count++; + eloop.signals = tmp; + + /* TODO: register signal handler */ + + return 0; +} + + +#ifndef _WIN32_WCE +static BOOL eloop_handle_console_ctrl(DWORD type) +{ + switch (type) { + case CTRL_C_EVENT: + case CTRL_BREAK_EVENT: + eloop.signaled++; + eloop.term_signal.signaled++; + SetEvent(eloop.term_event); + return TRUE; + default: + return FALSE; + } +} +#endif /* _WIN32_WCE */ + + +int eloop_register_signal_terminate(eloop_signal_handler handler, + void *user_data) +{ +#ifndef _WIN32_WCE + if (SetConsoleCtrlHandler((PHANDLER_ROUTINE) eloop_handle_console_ctrl, + TRUE) == 0) { + printf("SetConsoleCtrlHandler() failed: %d\n", + (int) GetLastError()); + return -1; + } +#endif /* _WIN32_WCE */ + + eloop.term_signal.handler = handler; + eloop.term_signal.user_data = user_data; + + return 0; +} + + +int eloop_register_signal_reconfig(eloop_signal_handler handler, + void *user_data) +{ + /* TODO */ + return 0; +} + + +void eloop_run(void) +{ + struct os_time tv, now; + DWORD count, ret, timeout, err; + size_t i; + + while (!eloop.terminate && + (eloop.timeout || eloop.reader_count > 0 || + eloop.event_count > 0)) { + tv.sec = tv.usec = 0; + if (eloop.timeout) { + os_get_time(&now); + if (os_time_before(&now, &eloop.timeout->time)) + os_time_sub(&eloop.timeout->time, &now, &tv); + } + + count = 0; + for (i = 0; i < eloop.event_count; i++) + eloop.handles[count++] = eloop.events[i].event; + + for (i = 0; i < eloop.reader_count; i++) + eloop.handles[count++] = eloop.readers[i].event; + + if (eloop.term_event) + eloop.handles[count++] = eloop.term_event; + + if (eloop.timeout) + timeout = tv.sec * 1000 + tv.usec / 1000; + else + timeout = INFINITE; + + if (count > MAXIMUM_WAIT_OBJECTS) { + printf("WaitForMultipleObjects: Too many events: " + "%d > %d (ignoring extra events)\n", + (int) count, MAXIMUM_WAIT_OBJECTS); + count = MAXIMUM_WAIT_OBJECTS; + } +#ifdef _WIN32_WCE + ret = WaitForMultipleObjects(count, eloop.handles, FALSE, + timeout); +#else /* _WIN32_WCE */ + ret = WaitForMultipleObjectsEx(count, eloop.handles, FALSE, + timeout, TRUE); +#endif /* _WIN32_WCE */ + err = GetLastError(); + + eloop_process_pending_signals(); + + /* check if some registered timeouts have occurred */ + if (eloop.timeout) { + struct eloop_timeout *tmp; + + os_get_time(&now); + if (!os_time_before(&now, &eloop.timeout->time)) { + tmp = eloop.timeout; + eloop.timeout = eloop.timeout->next; + tmp->handler(tmp->eloop_data, + tmp->user_data); + os_free(tmp); + } + + } + + if (ret == WAIT_FAILED) { + printf("WaitForMultipleObjects(count=%d) failed: %d\n", + (int) count, (int) err); + os_sleep(1, 0); + continue; + } + +#ifndef _WIN32_WCE + if (ret == WAIT_IO_COMPLETION) + continue; +#endif /* _WIN32_WCE */ + + if (ret == WAIT_TIMEOUT) + continue; + + while (ret >= WAIT_OBJECT_0 && + ret < WAIT_OBJECT_0 + eloop.event_count) { + eloop.events[ret].handler( + eloop.events[ret].eloop_data, + eloop.events[ret].user_data); + ret = WaitForMultipleObjects(eloop.event_count, + eloop.handles, FALSE, 0); + } + + eloop.reader_table_changed = 0; + for (i = 0; i < eloop.reader_count; i++) { + WSANETWORKEVENTS events; + if (WSAEnumNetworkEvents(eloop.readers[i].sock, + eloop.readers[i].event, + &events) == 0 && + (events.lNetworkEvents & FD_READ)) { + eloop.readers[i].handler( + eloop.readers[i].sock, + eloop.readers[i].eloop_data, + eloop.readers[i].user_data); + if (eloop.reader_table_changed) + break; + } + } + } +} + + +void eloop_terminate(void) +{ + eloop.terminate = 1; + SetEvent(eloop.term_event); +} + + +void eloop_destroy(void) +{ + struct eloop_timeout *timeout, *prev; + + timeout = eloop.timeout; + while (timeout != NULL) { + prev = timeout; + timeout = timeout->next; + os_free(prev); + } + os_free(eloop.readers); + os_free(eloop.signals); + if (eloop.term_event) + CloseHandle(eloop.term_event); + os_free(eloop.handles); + eloop.handles = NULL; + os_free(eloop.events); + eloop.events = NULL; +} + + +int eloop_terminated(void) +{ + return eloop.terminate; +} + + +void eloop_wait_for_read_sock(int sock) +{ + WSAEVENT event; + + event = WSACreateEvent(); + if (event == WSA_INVALID_EVENT) { + printf("WSACreateEvent() failed: %d\n", WSAGetLastError()); + return; + } + + if (WSAEventSelect(sock, event, FD_READ)) { + printf("WSAEventSelect() failed: %d\n", WSAGetLastError()); + WSACloseEvent(event); + return ; + } + + WaitForSingleObject(event, INFINITE); + WSAEventSelect(sock, event, 0); + WSACloseEvent(event); +} + + +void * eloop_get_user_data(void) +{ + return eloop.user_data; +} diff --git a/src/utils/os_none.c b/src/utils/os_none.c new file mode 100644 index 0000000..bab8f17 --- /dev/null +++ b/src/utils/os_none.c @@ -0,0 +1,226 @@ +/* + * wpa_supplicant/hostapd / Empty OS specific functions + * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * This file can be used as a starting point when adding a new OS target. The + * functions here do not really work as-is since they are just empty or only + * return an error value. os_internal.c can be used as another starting point + * or reference since it has example implementation of many of these functions. + */ + +#include "includes.h" + +#include "os.h" + +void os_sleep(os_time_t sec, os_time_t usec) +{ +} + + +int os_get_time(struct os_time *t) +{ + return -1; +} + + +int os_mktime(int year, int month, int day, int hour, int min, int sec, + os_time_t *t) +{ + return -1; +} + + +int os_daemonize(const char *pid_file) +{ + return -1; +} + + +void os_daemonize_terminate(const char *pid_file) +{ +} + + +int os_get_random(unsigned char *buf, size_t len) +{ + return -1; +} + + +unsigned long os_random(void) +{ + return 0; +} + + +char * os_rel2abs_path(const char *rel_path) +{ + return NULL; /* strdup(rel_path) can be used here */ +} + + +int os_program_init(void) +{ + return 0; +} + + +void os_program_deinit(void) +{ +} + + +int os_setenv(const char *name, const char *value, int overwrite) +{ + return -1; +} + + +int os_unsetenv(const char *name) +{ + return -1; +} + + +char * os_readfile(const char *name, size_t *len) +{ + return NULL; +} + + +void * os_zalloc(size_t size) +{ + return NULL; +} + + +#ifdef OS_NO_C_LIB_DEFINES +void * os_malloc(size_t size) +{ + return NULL; +} + + +void * os_realloc(void *ptr, size_t size) +{ + return NULL; +} + + +void os_free(void *ptr) +{ +} + + +void * os_memcpy(void *dest, const void *src, size_t n) +{ + return dest; +} + + +void * os_memmove(void *dest, const void *src, size_t n) +{ + return dest; +} + + +void * os_memset(void *s, int c, size_t n) +{ + return s; +} + + +int os_memcmp(const void *s1, const void *s2, size_t n) +{ + return 0; +} + + +char * os_strdup(const char *s) +{ + return NULL; +} + + +size_t os_strlen(const char *s) +{ + return 0; +} + + +int os_strcasecmp(const char *s1, const char *s2) +{ + /* + * Ignoring case is not required for main functionality, so just use + * the case sensitive version of the function. + */ + return os_strcmp(s1, s2); +} + + +int os_strncasecmp(const char *s1, const char *s2, size_t n) +{ + /* + * Ignoring case is not required for main functionality, so just use + * the case sensitive version of the function. + */ + return os_strncmp(s1, s2, n); +} + + +char * os_strchr(const char *s, int c) +{ + return NULL; +} + + +char * os_strrchr(const char *s, int c) +{ + return NULL; +} + + +int os_strcmp(const char *s1, const char *s2) +{ + return 0; +} + + +int os_strncmp(const char *s1, const char *s2, size_t n) +{ + return 0; +} + + +char * os_strncpy(char *dest, const char *src, size_t n) +{ + return dest; +} + + +size_t os_strlcpy(char *dest, const char *src, size_t size) +{ + return 0; +} + + +char * os_strstr(const char *haystack, const char *needle) +{ + return NULL; +} + + +int os_snprintf(char *str, size_t size, const char *format, ...) +{ + return 0; +} +#endif /* OS_NO_C_LIB_DEFINES */ diff --git a/src/utils/os_unix.c b/src/utils/os_unix.c index 060892d..bc2fc40 100644 --- a/src/utils/os_unix.c +++ b/src/utils/os_unix.c @@ -227,7 +227,8 @@ int os_setenv(const char *name, const char *value, int overwrite) int os_unsetenv(const char *name) { -#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__) +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__) || \ + defined(__OpenBSD__) unsetenv(name); return 0; #else diff --git a/src/utils/os_win32.c b/src/utils/os_win32.c new file mode 100644 index 0000000..0740964 --- /dev/null +++ b/src/utils/os_win32.c @@ -0,0 +1,222 @@ +/* + * wpa_supplicant/hostapd / OS specific functions for Win32 systems + * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <winsock2.h> +#include <wincrypt.h> + +#include "os.h" + +void os_sleep(os_time_t sec, os_time_t usec) +{ + if (sec) + Sleep(sec * 1000); + if (usec) + Sleep(usec / 1000); +} + + +int os_get_time(struct os_time *t) +{ +#define EPOCHFILETIME (116444736000000000ULL) + FILETIME ft; + LARGE_INTEGER li; + ULONGLONG tt; + +#ifdef _WIN32_WCE + SYSTEMTIME st; + + GetSystemTime(&st); + SystemTimeToFileTime(&st, &ft); +#else /* _WIN32_WCE */ + GetSystemTimeAsFileTime(&ft); +#endif /* _WIN32_WCE */ + li.LowPart = ft.dwLowDateTime; + li.HighPart = ft.dwHighDateTime; + tt = (li.QuadPart - EPOCHFILETIME) / 10; + t->sec = (os_time_t) (tt / 1000000); + t->usec = (os_time_t) (tt % 1000000); + + return 0; +} + + +int os_mktime(int year, int month, int day, int hour, int min, int sec, + os_time_t *t) +{ + struct tm tm, *tm1; + time_t t_local, t1, t2; + os_time_t tz_offset; + + if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 || + hour < 0 || hour > 23 || min < 0 || min > 59 || sec < 0 || + sec > 60) + return -1; + + memset(&tm, 0, sizeof(tm)); + tm.tm_year = year - 1900; + tm.tm_mon = month - 1; + tm.tm_mday = day; + tm.tm_hour = hour; + tm.tm_min = min; + tm.tm_sec = sec; + + t_local = mktime(&tm); + + /* figure out offset to UTC */ + tm1 = localtime(&t_local); + if (tm1) { + t1 = mktime(tm1); + tm1 = gmtime(&t_local); + if (tm1) { + t2 = mktime(tm1); + tz_offset = t2 - t1; + } else + tz_offset = 0; + } else + tz_offset = 0; + + *t = (os_time_t) t_local - tz_offset; + return 0; +} + + +int os_daemonize(const char *pid_file) +{ + /* TODO */ + return -1; +} + + +void os_daemonize_terminate(const char *pid_file) +{ +} + + +int os_get_random(unsigned char *buf, size_t len) +{ + HCRYPTPROV prov; + BOOL ret; + + if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT)) + return -1; + + ret = CryptGenRandom(prov, len, buf); + CryptReleaseContext(prov, 0); + + return ret ? 0 : -1; +} + + +unsigned long os_random(void) +{ + return rand(); +} + + +char * os_rel2abs_path(const char *rel_path) +{ + return _strdup(rel_path); +} + + +int os_program_init(void) +{ +#ifdef CONFIG_NATIVE_WINDOWS + WSADATA wsaData; + if (WSAStartup(MAKEWORD(2, 0), &wsaData)) { + printf("Could not find a usable WinSock.dll\n"); + return -1; + } +#endif /* CONFIG_NATIVE_WINDOWS */ + return 0; +} + + +void os_program_deinit(void) +{ +#ifdef CONFIG_NATIVE_WINDOWS + WSACleanup(); +#endif /* CONFIG_NATIVE_WINDOWS */ +} + + +int os_setenv(const char *name, const char *value, int overwrite) +{ + return -1; +} + + +int os_unsetenv(const char *name) +{ + return -1; +} + + +char * os_readfile(const char *name, size_t *len) +{ + FILE *f; + char *buf; + + f = fopen(name, "rb"); + if (f == NULL) + return NULL; + + fseek(f, 0, SEEK_END); + *len = ftell(f); + fseek(f, 0, SEEK_SET); + + buf = malloc(*len); + if (buf == NULL) { + fclose(f); + return NULL; + } + + fread(buf, 1, *len, f); + fclose(f); + + return buf; +} + + +void * os_zalloc(size_t size) +{ + return calloc(1, size); +} + + +size_t os_strlcpy(char *dest, const char *src, size_t siz) +{ + const char *s = src; + size_t left = siz; + + if (left) { + /* Copy string up to the maximum size of the dest buffer */ + while (--left != 0) { + if ((*dest++ = *s++) == '\0') + break; + } + } + + if (left == 0) { + /* Not enough room for the string; force NUL-termination */ + if (siz != 0) + *dest = '\0'; + while (*s++) + ; /* determine total src string length */ + } + + return s - src - 1; +} diff --git a/src/utils/wpa_debug.c b/src/utils/wpa_debug.c index e17cf06..4dd0732 100644 --- a/src/utils/wpa_debug.c +++ b/src/utils/wpa_debug.c @@ -56,7 +56,7 @@ void wpa_debug_print_timestamp(void) * * Note: New line '\n' is added to the end of the text when printing to stdout. */ -void wpa_printf(int level, char *fmt, ...) +void wpa_printf(int level, const char *fmt, ...) { va_list ap; @@ -267,7 +267,7 @@ void wpa_msg_register_cb(wpa_msg_cb_func func) } -void wpa_msg(void *ctx, int level, char *fmt, ...) +void wpa_msg(void *ctx, int level, const char *fmt, ...) { va_list ap; char *buf; @@ -288,6 +288,30 @@ void wpa_msg(void *ctx, int level, char *fmt, ...) wpa_msg_cb(ctx, level, buf, len); os_free(buf); } + + +void wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...) +{ + va_list ap; + char *buf; + const int buflen = 2048; + int len; + + if (!wpa_msg_cb) + return; + + buf = os_malloc(buflen); + if (buf == NULL) { + wpa_printf(MSG_ERROR, "wpa_msg_ctrl: Failed to allocate " + "message buffer"); + return; + } + va_start(ap, fmt); + len = vsnprintf(buf, buflen, fmt, ap); + va_end(ap); + wpa_msg_cb(ctx, level, buf, len); + os_free(buf); +} #endif /* CONFIG_NO_WPA_MSG */ diff --git a/src/utils/wpa_debug.h b/src/utils/wpa_debug.h index 2d23f06..b4010d5 100644 --- a/src/utils/wpa_debug.h +++ b/src/utils/wpa_debug.h @@ -60,7 +60,7 @@ void wpa_debug_print_timestamp(void); * * Note: New line '\n' is added to the end of the text when printing to stdout. */ -void wpa_printf(int level, char *fmt, ...) +void wpa_printf(int level, const char *fmt, ...) PRINTF_FORMAT(2, 3); /** @@ -141,6 +141,7 @@ void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf, #ifdef CONFIG_NO_WPA_MSG #define wpa_msg(args...) do { } while (0) +#define wpa_msg_ctrl(args...) do { } while (0) #define wpa_msg_register_cb(f) do { } while (0) #else /* CONFIG_NO_WPA_MSG */ /** @@ -157,7 +158,22 @@ void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf, * * Note: New line '\n' is added to the end of the text when printing to stdout. */ -void wpa_msg(void *ctx, int level, char *fmt, ...) PRINTF_FORMAT(3, 4); +void wpa_msg(void *ctx, int level, const char *fmt, ...) PRINTF_FORMAT(3, 4); + +/** + * wpa_msg_ctrl - Conditional printf for ctrl_iface monitors + * @ctx: Pointer to context data; this is the ctx variable registered + * with struct wpa_driver_ops::init() + * @level: priority level (MSG_*) of the message + * @fmt: printf format string, followed by optional arguments + * + * This function is used to print conditional debugging and error messages. + * This function is like wpa_msg(), but it sends the output only to the + * attached ctrl_iface monitors. In other words, it can be used for frequent + * events that do not need to be sent to syslog. + */ +void wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...) +PRINTF_FORMAT(3, 4); typedef void (*wpa_msg_cb_func)(void *ctx, int level, const char *txt, size_t len); diff --git a/src/utils/wpabuf.c b/src/utils/wpabuf.c index c544179..8181912 100644 --- a/src/utils/wpabuf.c +++ b/src/utils/wpabuf.c @@ -29,6 +29,10 @@ static void wpabuf_overflow(const struct wpabuf *buf, size_t len) int wpabuf_resize(struct wpabuf **_buf, size_t add_len) { struct wpabuf *buf = *_buf; + if (buf == NULL) { + *_buf = wpabuf_alloc(add_len); + return *_buf == NULL ? -1 : 0; + } if (buf->used + add_len > buf->size) { unsigned char *nbuf; if (buf->ext_data) { diff --git a/src/wps/.gitignore b/src/wps/.gitignore deleted file mode 100644 index a438335..0000000 --- a/src/wps/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.d diff --git a/src/wps/httpread.c b/src/wps/httpread.c index 313b468..0d7165e 100644 --- a/src/wps/httpread.c +++ b/src/wps/httpread.c @@ -206,7 +206,8 @@ static int httpread_hdr_option_analyze( h->got_content_length = 1; return 0; } - if (word_eq(hbp, "TRANSFER_ENCODING:")) { + if (word_eq(hbp, "TRANSFER_ENCODING:") || + word_eq(hbp, "TRANSFER-ENCODING:")) { while (isgraph(*hbp)) hbp++; while (*hbp == ' ' || *hbp == '\t') @@ -214,7 +215,7 @@ static int httpread_hdr_option_analyze( /* There should (?) be no encodings of interest * other than chunked... */ - if (os_strncmp(hbp, "CHUNKED", 7)) { + if (word_eq(hbp, "CHUNKED")) { h->chunked = 1; h->in_chunk_data = 0; /* ignore possible ;<parameters> */ @@ -513,6 +514,8 @@ static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) * consists of chunks each with a header, ending with * an ending header. */ + if (nread == 0) + goto get_more; if (!h->got_body) { /* Here to get (more of) body */ /* ensure we have enough room for worst case for body diff --git a/src/wps/wps.h b/src/wps/wps.h index e0f2b2d..faf32c4 100644 --- a/src/wps/wps.h +++ b/src/wps/wps.h @@ -42,7 +42,7 @@ struct upnp_wps_device_sm; * @key_idx: Key index * @key: Key * @key_len: Key length in octets - * @mac_addr: MAC address of the peer + * @mac_addr: MAC address of the Credential receiver * @cred_attr: Unparsed Credential attribute data (used only in cred_cb()); * this may be %NULL, if not used * @cred_attr_len: Length of cred_attr in octets @@ -266,6 +266,11 @@ struct wps_registrar_config { * to be set with a suitable Credential and skip_cred_build being used. */ int disable_auto_conf; + + /** + * static_wep_only - Whether the BSS supports only static WEP + */ + int static_wep_only; }; @@ -291,7 +296,17 @@ enum wps_event { /** * WPS_EV_PWD_AUTH_FAIL - Password authentication failed */ - WPS_EV_PWD_AUTH_FAIL + WPS_EV_PWD_AUTH_FAIL, + + /** + * WPS_EV_PBC_OVERLAP - PBC session overlap detected + */ + WPS_EV_PBC_OVERLAP, + + /** + * WPS_EV_PBC_TIMEOUT - PBC walktime expired before protocol run start + */ + WPS_EV_PBC_TIMEOUT }; /** @@ -500,7 +515,7 @@ wps_registrar_init(struct wps_context *wps, const struct wps_registrar_config *cfg); void wps_registrar_deinit(struct wps_registrar *reg); int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *uuid, - const u8 *pin, size_t pin_len); + const u8 *pin, size_t pin_len, int timeout); int wps_registrar_invalidate_pin(struct wps_registrar *reg, const u8 *uuid); int wps_registrar_unlock_pin(struct wps_registrar *reg, const u8 *uuid); int wps_registrar_button_pushed(struct wps_registrar *reg); diff --git a/src/wps/wps_attr_parse.c b/src/wps/wps_attr_parse.c index 25ff251..f50ae39 100644 --- a/src/wps/wps_attr_parse.c +++ b/src/wps/wps_attr_parse.c @@ -381,6 +381,14 @@ static int wps_set_attr(struct wps_parse_attr *attr, u16 type, attr->eap_identity = pos; attr->eap_identity_len = len; break; + case ATTR_AP_SETUP_LOCKED: + if (len != 1) { + wpa_printf(MSG_DEBUG, "WPS: Invalid AP Setup Locked " + "length %u", len); + return -1; + } + attr->ap_setup_locked = pos; + break; default: wpa_printf(MSG_DEBUG, "WPS: Unsupported attribute type 0x%x " "len=%u", type, len); diff --git a/src/wps/wps_common.c b/src/wps/wps_common.c index 48af303..4b45f00 100644 --- a/src/wps/wps_common.c +++ b/src/wps/wps_common.c @@ -128,56 +128,6 @@ int wps_derive_keys(struct wps_data *wps) } -int wps_derive_mgmt_keys(struct wps_data *wps) -{ - u8 nonces[2 * WPS_NONCE_LEN]; - u8 keys[WPS_MGMTAUTHKEY_LEN + WPS_MGMTENCKEY_LEN]; - u8 hash[SHA256_MAC_LEN]; - const u8 *addr[2]; - size_t len[2]; - const char *auth_label = "WFA-WLAN-Management-MgmtAuthKey"; - const char *enc_label = "WFA-WLAN-Management-MgmtEncKey"; - - /* MgmtAuthKey || MgmtEncKey = - * kdf(EMSK, N1 || N2 || "WFA-WLAN-Management-Keys", 384) */ - os_memcpy(nonces, wps->nonce_e, WPS_NONCE_LEN); - os_memcpy(nonces + WPS_NONCE_LEN, wps->nonce_r, WPS_NONCE_LEN); - wps_kdf(wps->emsk, nonces, sizeof(nonces), "WFA-WLAN-Management-Keys", - keys, sizeof(keys)); - os_memcpy(wps->mgmt_auth_key, keys, WPS_MGMTAUTHKEY_LEN); - os_memcpy(wps->mgmt_enc_key, keys + WPS_MGMTAUTHKEY_LEN, - WPS_MGMTENCKEY_LEN); - - addr[0] = nonces; - len[0] = sizeof(nonces); - - /* MgmtEncKeyID = first 128 bits of - * SHA-256(N1 || N2 || "WFA-WLAN-Management-MgmtAuthKey") */ - addr[1] = (const u8 *) auth_label; - len[1] = os_strlen(auth_label); - sha256_vector(2, addr, len, hash); - os_memcpy(wps->mgmt_auth_key_id, hash, WPS_MGMT_KEY_ID_LEN); - - /* MgmtEncKeyID = first 128 bits of - * SHA-256(N1 || N2 || "WFA-WLAN-Management-MgmtEncKey") */ - addr[1] = (const u8 *) enc_label; - len[1] = os_strlen(enc_label); - sha256_vector(2, addr, len, hash); - os_memcpy(wps->mgmt_enc_key_id, hash, WPS_MGMT_KEY_ID_LEN); - - wpa_hexdump_key(MSG_DEBUG, "WPS: MgmtAuthKey", - wps->mgmt_auth_key, WPS_MGMTAUTHKEY_LEN); - wpa_hexdump(MSG_DEBUG, "WPS: MgmtAuthKeyID", - wps->mgmt_auth_key_id, WPS_MGMT_KEY_ID_LEN); - wpa_hexdump_key(MSG_DEBUG, "WPS: MgmtEncKey", - wps->mgmt_enc_key, WPS_MGMTENCKEY_LEN); - wpa_hexdump(MSG_DEBUG, "WPS: MgmtEncKeyID", - wps->mgmt_enc_key_id, WPS_MGMT_KEY_ID_LEN); - - return 0; -} - - void wps_derive_psk(struct wps_data *wps, const u8 *dev_passwd, size_t dev_passwd_len) { @@ -335,3 +285,21 @@ void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part) data.pwd_auth_fail.part = part; wps->event_cb(wps->cb_ctx, WPS_EV_PWD_AUTH_FAIL, &data); } + + +void wps_pbc_overlap_event(struct wps_context *wps) +{ + if (wps->event_cb == NULL) + return; + + wps->event_cb(wps->cb_ctx, WPS_EV_PBC_OVERLAP, NULL); +} + + +void wps_pbc_timeout_event(struct wps_context *wps) +{ + if (wps->event_cb == NULL) + return; + + wps->event_cb(wps->cb_ctx, WPS_EV_PBC_TIMEOUT, NULL); +} diff --git a/src/wps/wps_enrollee.c b/src/wps/wps_enrollee.c index 179f7db..5cb3e1e 100644 --- a/src/wps/wps_enrollee.c +++ b/src/wps/wps_enrollee.c @@ -41,7 +41,7 @@ static int wps_build_wps_state(struct wps_data *wps, struct wpabuf *msg) state); wpabuf_put_be16(msg, ATTR_WPS_STATE); wpabuf_put_be16(msg, 1); - wpabuf_put_u8(msg, WPS_STATE_NOT_CONFIGURED); + wpabuf_put_u8(msg, state); return 0; } @@ -521,10 +521,6 @@ static int wps_process_pubkey(struct wps_data *wps, const u8 *pk, if (wps_derive_keys(wps) < 0) return -1; - if (wps->request_type == WPS_REQ_WLAN_MANAGER_REGISTRAR && - wps_derive_mgmt_keys(wps) < 0) - return -1; - return 0; } @@ -650,6 +646,21 @@ static int wps_process_cred_e(struct wps_data *wps, const u8 *cred, wps_process_cred(&attr, &wps->cred)) return -1; + if (os_memcmp(wps->cred.mac_addr, wps->wps->dev.mac_addr, ETH_ALEN) != + 0) { + wpa_printf(MSG_DEBUG, "WPS: MAC Address in the Credential (" + MACSTR ") does not match with own address (" MACSTR + ")", MAC2STR(wps->cred.mac_addr), + MAC2STR(wps->wps->dev.mac_addr)); + /* + * In theory, this could be consider fatal error, but there are + * number of deployed implementations using other address here + * due to unclarity in the specification. For interoperability + * reasons, allow this to be processed since we do not really + * use the MAC Address information for anything. + */ + } + if (wps->wps->cred_cb) { wps->cred.cred_attr = cred - 4; wps->cred.cred_attr_len = cred_len + 4; @@ -700,6 +711,21 @@ static int wps_process_ap_settings_e(struct wps_data *wps, wpa_printf(MSG_INFO, "WPS: Received new AP configuration from " "Registrar"); + if (os_memcmp(cred.mac_addr, wps->wps->dev.mac_addr, ETH_ALEN) != + 0) { + wpa_printf(MSG_DEBUG, "WPS: MAC Address in the AP Settings (" + MACSTR ") does not match with own address (" MACSTR + ")", MAC2STR(cred.mac_addr), + MAC2STR(wps->wps->dev.mac_addr)); + /* + * In theory, this could be consider fatal error, but there are + * number of deployed implementations using other address here + * due to unclarity in the specification. For interoperability + * reasons, allow this to be processed since we do not really + * use the MAC Address information for anything. + */ + } + if (wps->wps->cred_cb) { cred.cred_attr = wpabuf_head(attrs); cred.cred_attr_len = wpabuf_len(attrs); @@ -1159,6 +1185,17 @@ enum wps_process_res wps_enrollee_process_msg(struct wps_data *wps, "op_code=%d)", (unsigned long) wpabuf_len(msg), op_code); + if (op_code == WSC_UPnP) { + /* Determine the OpCode based on message type attribute */ + struct wps_parse_attr attr; + if (wps_parse_msg(msg, &attr) == 0 && attr.msg_type) { + if (*attr.msg_type == WPS_WSC_ACK) + op_code = WSC_ACK; + else if (*attr.msg_type == WPS_WSC_NACK) + op_code = WSC_NACK; + } + } + switch (op_code) { case WSC_MSG: case WSC_UPnP: diff --git a/src/wps/wps_i.h b/src/wps/wps_i.h index 85adf28..3317a2c 100644 --- a/src/wps/wps_i.h +++ b/src/wps/wps_i.h @@ -62,10 +62,6 @@ struct wps_data { u8 authkey[WPS_AUTHKEY_LEN]; u8 keywrapkey[WPS_KEYWRAPKEY_LEN]; u8 emsk[WPS_EMSK_LEN]; - u8 mgmt_auth_key[WPS_MGMTAUTHKEY_LEN]; - u8 mgmt_auth_key_id[WPS_MGMT_KEY_ID_LEN]; - u8 mgmt_enc_key[WPS_MGMTENCKEY_LEN]; - u8 mgmt_enc_key_id[WPS_MGMT_KEY_ID_LEN]; struct wpabuf *last_msg; @@ -146,6 +142,7 @@ struct wps_parse_attr { const u8 *selected_registrar; /* 1 octet (Bool) */ const u8 *request_type; /* 1 octet */ const u8 *response_type; /* 1 octet */ + const u8 *ap_setup_locked; /* 1 octet */ /* variable length fields */ const u8 *manufacturer; @@ -182,7 +179,6 @@ struct wps_parse_attr { void wps_kdf(const u8 *key, const u8 *label_prefix, size_t label_prefix_len, const char *label, u8 *res, size_t res_len); int wps_derive_keys(struct wps_data *wps); -int wps_derive_mgmt_keys(struct wps_data *wps); void wps_derive_psk(struct wps_data *wps, const u8 *dev_passwd, size_t dev_passwd_len); struct wpabuf * wps_decrypt_encr_settings(struct wps_data *wps, const u8 *encr, @@ -190,6 +186,8 @@ struct wpabuf * wps_decrypt_encr_settings(struct wps_data *wps, const u8 *encr, void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg); void wps_success_event(struct wps_context *wps); void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part); +void wps_pbc_overlap_event(struct wps_context *wps); +void wps_pbc_timeout_event(struct wps_context *wps); /* wps_attr_parse.c */ int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr); diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c index f7eebdd..f34c9e9 100644 --- a/src/wps/wps_registrar.c +++ b/src/wps/wps_registrar.c @@ -23,6 +23,7 @@ #include "wps_dev_attr.h" #include "wps_upnp.h" +#define WPS_WORKAROUNDS struct wps_uuid_pin { struct wps_uuid_pin *next; @@ -30,7 +31,10 @@ struct wps_uuid_pin { int wildcard_uuid; u8 *pin; size_t pin_len; - int locked; +#define PIN_LOCKED BIT(0) +#define PIN_EXPIRES BIT(1) + int flags; + struct os_time expiration; }; @@ -98,6 +102,9 @@ struct wps_registrar { int disable_auto_conf; int sel_reg_dev_password_id_override; int sel_reg_config_methods_override; + int static_wep_only; + + int force_pbc_overlap; }; @@ -376,6 +383,7 @@ wps_registrar_init(struct wps_context *wps, reg->disable_auto_conf = cfg->disable_auto_conf; reg->sel_reg_dev_password_id_override = -1; reg->sel_reg_config_methods_override = -1; + reg->static_wep_only = cfg->static_wep_only; if (wps_set_ie(reg)) { wps_registrar_deinit(reg); @@ -409,10 +417,11 @@ void wps_registrar_deinit(struct wps_registrar *reg) * @uuid: UUID-E or %NULL for wildcard (any UUID) * @pin: PIN (Device Password) * @pin_len: Length of pin in octets + * @timeout: Time (in seconds) when the PIN will be invalidated; 0 = no timeout * Returns: 0 on success, -1 on failure */ int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *uuid, - const u8 *pin, size_t pin_len) + const u8 *pin, size_t pin_len, int timeout) { struct wps_uuid_pin *p; @@ -431,20 +440,59 @@ int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *uuid, os_memcpy(p->pin, pin, pin_len); p->pin_len = pin_len; + if (timeout) { + p->flags |= PIN_EXPIRES; + os_get_time(&p->expiration); + p->expiration.sec += timeout; + } + p->next = reg->pins; reg->pins = p; - wpa_printf(MSG_DEBUG, "WPS: A new PIN configured"); + wpa_printf(MSG_DEBUG, "WPS: A new PIN configured (timeout=%d)", + timeout); wpa_hexdump(MSG_DEBUG, "WPS: UUID", uuid, WPS_UUID_LEN); wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: PIN", pin, pin_len); reg->selected_registrar = 1; reg->pbc = 0; wps_set_ie(reg); + eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL); + eloop_register_timeout(WPS_PBC_WALK_TIME, 0, + wps_registrar_set_selected_timeout, + reg, NULL); return 0; } +static void wps_registrar_expire_pins(struct wps_registrar *reg) +{ + struct wps_uuid_pin *pin, *prev, *del; + struct os_time now; + + os_get_time(&now); + prev = NULL; + pin = reg->pins; + while (pin) { + if ((pin->flags & PIN_EXPIRES) && + os_time_before(&pin->expiration, &now)) { + if (prev == NULL) + reg->pins = pin->next; + else + prev->next = pin->next; + del = pin; + pin = pin->next; + wpa_hexdump(MSG_DEBUG, "WPS: Expired PIN for UUID", + del->uuid, WPS_UUID_LEN); + wps_free_pin(del); + continue; + } + prev = pin; + pin = pin->next; + } +} + + /** * wps_registrar_invalidate_pin - Invalidate a PIN for a specific UUID-E * @reg: Registrar data from wps_registrar_init() @@ -481,6 +529,8 @@ static const u8 * wps_registrar_get_pin(struct wps_registrar *reg, { struct wps_uuid_pin *pin; + wps_registrar_expire_pins(reg); + pin = reg->pins; while (pin) { if (!pin->wildcard_uuid && @@ -512,13 +562,13 @@ static const u8 * wps_registrar_get_pin(struct wps_registrar *reg, * Lock the PIN to avoid attacks based on concurrent re-use of the PIN * that could otherwise avoid PIN invalidations. */ - if (pin->locked) { + if (pin->flags & PIN_LOCKED) { wpa_printf(MSG_DEBUG, "WPS: Selected PIN locked - do not " "allow concurrent re-use"); return NULL; } *pin_len = pin->pin_len; - pin->locked = 1; + pin->flags |= PIN_LOCKED; return pin->pin; } @@ -545,7 +595,7 @@ int wps_registrar_unlock_pin(struct wps_registrar *reg, const u8 *uuid) "wildcard PIN"); return wps_registrar_invalidate_pin(reg, uuid); } - pin->locked = 0; + pin->flags &= ~PIN_LOCKED; return 0; } pin = pin->next; @@ -568,6 +618,7 @@ static void wps_registrar_pbc_timeout(void *eloop_ctx, void *timeout_ctx) struct wps_registrar *reg = eloop_ctx; wpa_printf(MSG_DEBUG, "WPS: PBC timed out - disable PBC mode"); + wps_pbc_timeout_event(reg->wps); wps_registrar_stop_pbc(reg); } @@ -586,9 +637,11 @@ int wps_registrar_button_pushed(struct wps_registrar *reg) if (wps_registrar_pbc_overlap(reg, NULL, NULL)) { wpa_printf(MSG_DEBUG, "WPS: PBC overlap - do not start PBC " "mode"); + wps_pbc_overlap_event(reg->wps); return -1; } wpa_printf(MSG_DEBUG, "WPS: Button pushed - PBC mode started"); + reg->force_pbc_overlap = 0; reg->selected_registrar = 1; reg->pbc = 1; wps_set_ie(reg); @@ -607,6 +660,14 @@ static void wps_registrar_pbc_completed(struct wps_registrar *reg) wps_registrar_stop_pbc(reg); } +static void wps_registrar_pin_completed(struct wps_registrar *reg) +{ + wpa_printf(MSG_DEBUG, "WPS: PIN completed using internal Registrar"); + eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL); + reg->selected_registrar = 0; + wps_set_ie(reg); +} + /** * wps_registrar_probe_req_rx - Notify Registrar of Probe Request @@ -648,8 +709,18 @@ void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr, wpa_printf(MSG_DEBUG, "WPS: Probe Request for PBC received from " MACSTR, MAC2STR(addr)); + if (attr.uuid_e == NULL) { + wpa_printf(MSG_DEBUG, "WPS: Invalid Probe Request WPS IE: No " + "UUID-E included"); + return; + } wps_registrar_add_pbc_session(reg, addr, attr.uuid_e); + if (wps_registrar_pbc_overlap(reg, addr, attr.uuid_e)) { + wpa_printf(MSG_DEBUG, "WPS: PBC session overlap detected"); + reg->force_pbc_overlap = 1; + wps_pbc_overlap_event(reg->wps); + } } @@ -777,6 +848,28 @@ static int wps_set_ie(struct wps_registrar *reg) return -1; } + if (reg->static_wep_only) { + /* + * Windows XP and Vista clients can get confused about + * EAP-Identity/Request when they probe the network with + * EAPOL-Start. In such a case, they may assume the network is + * using IEEE 802.1X and prompt user for a certificate while + * the correct (non-WPS) behavior would be to ask for the + * static WEP key. As a workaround, use Microsoft Provisioning + * IE to advertise that legacy 802.1X is not supported. + */ + const u8 ms_wps[7] = { + WLAN_EID_VENDOR_SPECIFIC, 5, + /* Microsoft Provisioning IE (00:50:f2:5) */ + 0x00, 0x50, 0xf2, 5, + 0x00 /* no legacy 802.1X or MS WPS */ + }; + wpa_printf(MSG_DEBUG, "WPS: Add Microsoft Provisioning IE " + "into Beacon/Probe Response frames"); + wpabuf_put_data(beacon, ms_wps, sizeof(ms_wps)); + wpabuf_put_data(probe, ms_wps, sizeof(ms_wps)); + } + ret = wps_cb_set_ie(reg, beacon, probe); wpabuf_free(beacon); wpabuf_free(probe); @@ -1033,8 +1126,10 @@ static int wps_build_cred(struct wps_data *wps, struct wpabuf *msg) } } wps->cred.encr_type = wps->encr_type; - /* Set MAC address in the Credential to be the AP's address (BSSID) */ - os_memcpy(wps->cred.mac_addr, wps->wps->dev.mac_addr, ETH_ALEN); + /* + * Set MAC address in the Credential to be the Enrollee's MAC address + */ + os_memcpy(wps->cred.mac_addr, wps->mac_addr_e, ETH_ALEN); if (wps->wps->wps_state == WPS_STATE_NOT_CONFIGURED && wps->wps->ap && !wps->wps->registrar->disable_auto_conf) { @@ -1159,14 +1254,15 @@ static struct wpabuf * wps_build_m2(struct wps_data *wps) static struct wpabuf * wps_build_m2d(struct wps_data *wps) { struct wpabuf *msg; - u16 err = WPS_CFG_NO_ERROR; + u16 err = wps->config_error; wpa_printf(MSG_DEBUG, "WPS: Building Message M2D"); msg = wpabuf_alloc(1000); if (msg == NULL) return NULL; - if (wps->wps->ap && wps->wps->ap_setup_locked) + if (wps->wps->ap && wps->wps->ap_setup_locked && + err == WPS_CFG_NO_ERROR) err = WPS_CFG_SETUP_LOCKED; if (wps_build_version(msg) || @@ -1368,8 +1464,18 @@ struct wpabuf * wps_registrar_get_msg(struct wps_data *wps, else wps->wps->upnp_msgs = NULL; msg = p->msg; + switch (p->type) { + case WPS_WSC_ACK: + *op_code = WSC_ACK; + break; + case WPS_WSC_NACK: + *op_code = WSC_NACK; + break; + default: + *op_code = WSC_MSG; + break; + } os_free(p); - *op_code = WSC_MSG; if (wps->ext_reg == 0) wps->ext_reg = 1; return msg; @@ -1654,7 +1760,21 @@ static int wps_process_auth_type_flags(struct wps_data *wps, const u8 *auth) wpa_printf(MSG_DEBUG, "WPS: No match in supported " "authentication types (own 0x%x Enrollee 0x%x)", wps->wps->auth_types, auth_types); +#ifdef WPS_WORKAROUNDS + /* + * Some deployed implementations seem to advertise incorrect + * information in this attribute. For example, Linksys WRT350N + * seems to have a byteorder bug that breaks this negotiation. + * In order to interoperate with existing implementations, + * assume that the Enrollee supports everything we do. + */ + wpa_printf(MSG_DEBUG, "WPS: Workaround - assume Enrollee " + "does not advertise supported authentication types " + "correctly"); + wps->auth_type = wps->wps->auth_types; +#else /* WPS_WORKAROUNDS */ return -1; +#endif /* WPS_WORKAROUNDS */ } return 0; @@ -1678,8 +1798,23 @@ static int wps_process_encr_type_flags(struct wps_data *wps, const u8 *encr) wps->encr_type = wps->wps->encr_types & encr_types; if (wps->encr_type == 0) { wpa_printf(MSG_DEBUG, "WPS: No match in supported " - "encryption types"); + "encryption types (own 0x%x Enrollee 0x%x)", + wps->wps->encr_types, encr_types); +#ifdef WPS_WORKAROUNDS + /* + * Some deployed implementations seem to advertise incorrect + * information in this attribute. For example, Linksys WRT350N + * seems to have a byteorder bug that breaks this negotiation. + * In order to interoperate with existing implementations, + * assume that the Enrollee supports everything we do. + */ + wpa_printf(MSG_DEBUG, "WPS: Workaround - assume Enrollee " + "does not advertise supported encryption types " + "correctly"); + wps->encr_type = wps->wps->encr_types; +#else /* WPS_WORKAROUNDS */ return -1; +#endif /* WPS_WORKAROUNDS */ } return 0; @@ -1806,11 +1941,15 @@ static enum wps_process_res wps_process_m1(struct wps_data *wps, } if (wps->dev_pw_id == DEV_PW_PUSHBUTTON) { - if (wps_registrar_pbc_overlap(wps->wps->registrar, + if (wps->wps->registrar->force_pbc_overlap || + wps_registrar_pbc_overlap(wps->wps->registrar, wps->mac_addr_e, wps->uuid_e)) { wpa_printf(MSG_DEBUG, "WPS: PBC overlap - deny PBC " "negotiation"); wps->state = SEND_M2D; + wps->config_error = WPS_CFG_MULTIPLE_PBC_DETECTED; + wps_pbc_overlap_event(wps->wps); + wps->wps->registrar->force_pbc_overlap = 1; return WPS_CONTINUE; } wps_registrar_add_pbc_session(wps->wps->registrar, @@ -1836,6 +1975,14 @@ static enum wps_process_res wps_process_m3(struct wps_data *wps, return WPS_CONTINUE; } + if (wps->pbc && wps->wps->registrar->force_pbc_overlap) { + wpa_printf(MSG_DEBUG, "WPS: Reject negotiation due to PBC " + "session overlap"); + wps->state = SEND_WSC_NACK; + wps->config_error = WPS_CFG_MULTIPLE_PBC_DETECTED; + return WPS_CONTINUE; + } + if (wps_process_registrar_nonce(wps, attr->registrar_nonce) || wps_process_authenticator(wps, attr->authenticator, msg) || wps_process_e_hash1(wps, attr->e_hash1) || @@ -1865,6 +2012,14 @@ static enum wps_process_res wps_process_m5(struct wps_data *wps, return WPS_CONTINUE; } + if (wps->pbc && wps->wps->registrar->force_pbc_overlap) { + wpa_printf(MSG_DEBUG, "WPS: Reject negotiation due to PBC " + "session overlap"); + wps->state = SEND_WSC_NACK; + wps->config_error = WPS_CFG_MULTIPLE_PBC_DETECTED; + return WPS_CONTINUE; + } + if (wps_process_registrar_nonce(wps, attr->registrar_nonce) || wps_process_authenticator(wps, attr->authenticator, msg)) { wps->state = SEND_WSC_NACK; @@ -1896,6 +2051,28 @@ static enum wps_process_res wps_process_m5(struct wps_data *wps, } +static void wps_sta_cred_cb(struct wps_data *wps) +{ + /* + * Update credential to only include a single authentication and + * encryption type in case the AP configuration includes more than one + * option. + */ + if (wps->cred.auth_type & WPS_AUTH_WPA2PSK) + wps->cred.auth_type = WPS_AUTH_WPA2PSK; + else if (wps->cred.auth_type & WPS_AUTH_WPAPSK) + wps->cred.auth_type = WPS_AUTH_WPAPSK; + if (wps->cred.encr_type & WPS_ENCR_AES) + wps->cred.encr_type = WPS_ENCR_AES; + else if (wps->cred.encr_type & WPS_ENCR_TKIP) + wps->cred.encr_type = WPS_ENCR_TKIP; + wpa_printf(MSG_DEBUG, "WPS: Update local configuration based on the " + "AP configuration"); + if (wps->wps->cred_cb) + wps->wps->cred_cb(wps->wps->cb_ctx, &wps->cred); +} + + static int wps_process_ap_settings_r(struct wps_data *wps, struct wps_parse_attr *attr) { @@ -1908,12 +2085,21 @@ static int wps_process_ap_settings_r(struct wps_data *wps, wpa_printf(MSG_INFO, "WPS: Received old AP configuration from AP"); +#if 0 /* * TODO: Provide access to AP settings and allow changes before sending * out M8. For now, just copy the settings unchanged into M8. */ return 0; +#else + /* + * For now, use the AP PIN only to receive the current AP settings, + * not to reconfigure the AP. + */ + wps_sta_cred_cb(wps); + return 1; +#endif } @@ -1933,6 +2119,14 @@ static enum wps_process_res wps_process_m7(struct wps_data *wps, return WPS_CONTINUE; } + if (wps->pbc && wps->wps->registrar->force_pbc_overlap) { + wpa_printf(MSG_DEBUG, "WPS: Reject negotiation due to PBC " + "session overlap"); + wps->state = SEND_WSC_NACK; + wps->config_error = WPS_CFG_MULTIPLE_PBC_DETECTED; + return WPS_CONTINUE; + } + if (wps_process_registrar_nonce(wps, attr->registrar_nonce) || wps_process_authenticator(wps, attr->authenticator, msg)) { wps->state = SEND_WSC_NACK; @@ -2281,12 +2475,8 @@ static enum wps_process_res wps_process_wsc_done(struct wps_data *wps, wps->new_psk = NULL; } - if (!wps->wps->ap) { - wpa_printf(MSG_DEBUG, "WPS: Update local configuration based " - "on the modified AP configuration"); - if (wps->wps->cred_cb) - wps->wps->cred_cb(wps->wps->cb_ctx, &wps->cred); - } + if (!wps->wps->ap) + wps_sta_cred_cb(wps); if (wps->new_psk) { if (wps_cb_new_psk(wps->wps->registrar, wps->mac_addr_e, @@ -2304,6 +2494,8 @@ static enum wps_process_res wps_process_wsc_done(struct wps_data *wps, wps_registrar_remove_pbc_session(wps->wps->registrar, wps->mac_addr_e, wps->uuid_e); wps_registrar_pbc_completed(wps->wps->registrar); + } else { + wps_registrar_pin_completed(wps->wps->registrar); } wps_success_event(wps->wps); @@ -2333,7 +2525,8 @@ enum wps_process_res wps_registrar_process_msg(struct wps_data *wps, wps_registrar_free_pending_m2(wps->wps); if (wps->wps->wps_upnp && wps->ext_reg && wps->wps->upnp_msgs == NULL && - (op_code == WSC_MSG || op_code == WSC_Done)) { + (op_code == WSC_MSG || op_code == WSC_Done || op_code == WSC_NACK)) + { struct wps_parse_attr attr; int type; if (wps_parse_msg(msg, &attr) < 0 || attr.msg_type == NULL) @@ -2401,7 +2594,6 @@ static void wps_registrar_set_selected_timeout(void *eloop_ctx, * wps_registrar_set_selected_registrar - Notification of SetSelectedRegistrar * @reg: Registrar data from wps_registrar_init() * @msg: Received message from SetSelectedRegistrar - * @msg_len: Length of msg in octets * Returns: 0 on success, -1 on failure * * This function is called when an AP receives a SetSelectedRegistrar UPnP diff --git a/src/wps/wps_upnp.c b/src/wps/wps_upnp.c index 6d2de0e..4c6aac2 100644 --- a/src/wps/wps_upnp.c +++ b/src/wps/wps_upnp.c @@ -47,9 +47,6 @@ * -- Needs renaming with module prefix to avoid polluting the debugger * namespace and causing possible collisions with other static fncs * and structure declarations when using the debugger. - * -- Just what should be in the first event message sent after subscription - * for the WLANEvent field? If i pass it empty, Vista replies with OK - * but apparently barfs on the message. * -- The http error code generation is pretty bogus, hopefully noone cares. * * Author: Ted Merrill, Atheros Communications, based upon earlier work @@ -641,6 +638,27 @@ struct subscription * subscription_find(struct upnp_wps_device_sm *sm, } +static struct wpabuf * build_fake_wsc_ack(void) +{ + struct wpabuf *msg = wpabuf_alloc(100); + if (msg == NULL) + return NULL; + wpabuf_put_u8(msg, UPNP_WPS_WLANEVENT_TYPE_EAP); + wpabuf_put_str(msg, "00:00:00:00:00:00"); + wps_build_version(msg); + wps_build_msg_type(msg, WPS_WSC_ACK); + /* Enrollee Nonce */ + wpabuf_put_be16(msg, ATTR_ENROLLEE_NONCE); + wpabuf_put_be16(msg, WPS_NONCE_LEN); + wpabuf_put(msg, WPS_NONCE_LEN); + /* Registrar Nonce */ + wpabuf_put_be16(msg, ATTR_REGISTRAR_NONCE); + wpabuf_put_be16(msg, WPS_NONCE_LEN); + wpabuf_put(msg, WPS_NONCE_LEN); + return msg; +} + + /* subscription_first_event -- send format/queue event that is automatically * sent on a new subscription. */ @@ -665,6 +683,28 @@ static int subscription_first_event(struct subscription *s) const char *tail = "</e:propertyset>\n"; char txt[10]; + if (s->sm->wlanevent == NULL) { + /* + * There has been no events before the subscription. However, + * UPnP device architecture specification requires all the + * evented variables to be included, so generate a dummy event + * for this particular case using a WSC_ACK and all-zeros + * nonces. The ER (UPnP control point) will ignore this, but at + * least it will learn that WLANEvent variable will be used in + * event notifications in the future. + */ + struct wpabuf *msg; + wpa_printf(MSG_DEBUG, "WPS UPnP: Use a fake WSC_ACK as the " + "initial WLANEvent"); + msg = build_fake_wsc_ack(); + if (msg) { + s->sm->wlanevent = (char *) + base64_encode(wpabuf_head(msg), + wpabuf_len(msg), NULL); + wpabuf_free(msg); + } + } + wlan_event = s->sm->wlanevent; if (wlan_event == NULL || *wlan_event == '\0') { wpa_printf(MSG_DEBUG, "WPS UPnP: WLANEvent not known for " @@ -694,13 +734,13 @@ static int subscription_first_event(struct subscription *s) /** - * subscription_start - Rremember a UPnP control point to send events to. + * subscription_start - Remember a UPnP control point to send events to. * @sm: WPS UPnP state machine from upnp_wps_device_init() - * @callback_urls: malloc' mem given to the subscription + * @callback_urls: Callback URLs * Returns: %NULL on error, or pointer to new subscription structure. */ struct subscription * subscription_start(struct upnp_wps_device_sm *sm, - char *callback_urls) + const char *callback_urls) { struct subscription *s; time_t now = time(NULL); @@ -740,7 +780,6 @@ struct subscription * subscription_start(struct upnp_wps_device_sm *sm, } wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription %p started with %s", s, callback_urls); - os_free(callback_urls); /* Schedule sending this */ event_send_all_later(sm); return s; @@ -832,6 +871,50 @@ fail: } +#ifdef __FreeBSD__ +#include <sys/sysctl.h> +#include <net/route.h> +#include <net/if_dl.h> + +static int eth_get(const char *device, u8 ea[ETH_ALEN]) +{ + struct if_msghdr *ifm; + struct sockaddr_dl *sdl; + u_char *p, *buf; + size_t len; + int mib[] = { CTL_NET, AF_ROUTE, 0, AF_LINK, NET_RT_IFLIST, 0 }; + + if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) + return -1; + if ((buf = os_malloc(len)) == NULL) + return -1; + if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) { + os_free(buf); + return -1; + } + for (p = buf; p < buf + len; p += ifm->ifm_msglen) { + ifm = (struct if_msghdr *)p; + sdl = (struct sockaddr_dl *)(ifm + 1); + if (ifm->ifm_type != RTM_IFINFO || + (ifm->ifm_addrs & RTA_IFP) == 0) + continue; + if (sdl->sdl_family != AF_LINK || sdl->sdl_nlen == 0 || + os_memcmp(sdl->sdl_data, device, sdl->sdl_nlen) != 0) + continue; + os_memcpy(ea, LLADDR(sdl), sdl->sdl_alen); + break; + } + os_free(buf); + + if (p >= buf + len) { + errno = ESRCH; + return -1; + } + return 0; +} +#endif /* __FreeBSD__ */ + + /** * get_netif_info - Get hw and IP addresses for network device * @net_if: Selected network interface name @@ -870,6 +953,7 @@ static int get_netif_info(const char *net_if, unsigned *ip_addr, in_addr.s_addr = *ip_addr; os_snprintf(*ip_addr_text, 16, "%s", inet_ntoa(in_addr)); +#ifdef __linux__ os_strlcpy(req.ifr_name, net_if, sizeof(req.ifr_name)); if (ioctl(sock, SIOCGIFHWADDR, &req) < 0) { wpa_printf(MSG_ERROR, "WPS UPnP: SIOCGIFHWADDR failed: " @@ -877,6 +961,14 @@ static int get_netif_info(const char *net_if, unsigned *ip_addr, goto fail; } os_memcpy(mac, req.ifr_addr.sa_data, 6); +#elif defined(__FreeBSD__) + if (eth_get(net_if, mac) < 0) { + wpa_printf(MSG_ERROR, "WPS UPnP: Failed to get MAC address"); + goto fail; + } +#else +#error MAC address fetch not implemented +#endif os_snprintf(*mac_addr_text, 18, MACSTR, MAC2STR(req.ifr_addr.sa_data)); close(sock); @@ -914,8 +1006,7 @@ void upnp_wps_device_stop(struct upnp_wps_device_sm *sm) subscription_destroy(s); } - advertisement_state_machine_stop(sm); - /* TODO: send byebye notifications */ + advertisement_state_machine_stop(sm, 1); event_send_stop_all(sm); os_free(sm->wlanevent); diff --git a/src/wps/wps_upnp_event.c b/src/wps/wps_upnp_event.c index f278f35..4122a87 100644 --- a/src/wps/wps_upnp_event.c +++ b/src/wps/wps_upnp_event.c @@ -89,7 +89,7 @@ static void event_clean(struct wps_event_ *e) /* event_delete -- delete single unqueued event * (be sure to dequeue first if need be) */ -void event_delete(struct wps_event_ *e) +static void event_delete(struct wps_event_ *e) { event_clean(e); wpabuf_free(e->data); @@ -432,7 +432,7 @@ static int event_send_start(struct subscription *s) /* event_send_all_later_handler -- actually send events as needed */ -void event_send_all_later_handler(void *eloop_data, void *user_ctx) +static void event_send_all_later_handler(void *eloop_data, void *user_ctx) { struct upnp_wps_device_sm *sm = user_ctx; struct subscription *s; diff --git a/src/wps/wps_upnp_i.h b/src/wps/wps_upnp_i.h index d4b6569..ba4ec20 100644 --- a/src/wps/wps_upnp_i.h +++ b/src/wps/wps_upnp_i.h @@ -160,7 +160,7 @@ struct upnp_wps_device_sm { /* wps_upnp.c */ void format_date(struct wpabuf *buf); struct subscription * subscription_start(struct upnp_wps_device_sm *sm, - char *callback_urls); + const char *callback_urls); struct subscription * subscription_renew(struct upnp_wps_device_sm *sm, const u8 uuid[UUID_LEN]); void subscription_unlink(struct subscription *s); @@ -172,7 +172,8 @@ int send_wpabuf(int fd, struct wpabuf *buf); /* wps_upnp_ssdp.c */ void msearchreply_state_machine_stop(struct advertisement_state_machine *a); int advertisement_state_machine_start(struct upnp_wps_device_sm *sm); -void advertisement_state_machine_stop(struct upnp_wps_device_sm *sm); +void advertisement_state_machine_stop(struct upnp_wps_device_sm *sm, + int send_byebye); void ssdp_listener_stop(struct upnp_wps_device_sm *sm); int ssdp_listener_start(struct upnp_wps_device_sm *sm); int add_ssdp_network(char *net_if); @@ -185,7 +186,6 @@ void web_listener_stop(struct upnp_wps_device_sm *sm); /* wps_upnp_event.c */ int event_add(struct subscription *s, const struct wpabuf *data); -void event_delete(struct wps_event_ *e); void event_delete_all(struct subscription *s); void event_send_all_later(struct upnp_wps_device_sm *sm); void event_send_stop_all(struct upnp_wps_device_sm *sm); diff --git a/src/wps/wps_upnp_ssdp.c b/src/wps/wps_upnp_ssdp.c index 6a94c59..c1dc99d 100644 --- a/src/wps/wps_upnp_ssdp.c +++ b/src/wps/wps_upnp_ssdp.c @@ -229,10 +229,41 @@ static void advertisement_state_machine_handler(void *eloop_data, /** * advertisement_state_machine_stop - Stop SSDP advertisements * @sm: WPS UPnP state machine from upnp_wps_device_init() + * @send_byebye: Send byebye advertisement messages immediately */ -void advertisement_state_machine_stop(struct upnp_wps_device_sm *sm) +void advertisement_state_machine_stop(struct upnp_wps_device_sm *sm, + int send_byebye) { + struct advertisement_state_machine *a = &sm->advertisement; + int islast = 0; + struct wpabuf *msg; + struct sockaddr_in dest; + eloop_cancel_timeout(advertisement_state_machine_handler, NULL, sm); + if (!send_byebye || sm->multicast_sd < 0) + return; + + a->type = ADVERTISE_DOWN; + a->state = 0; + a->sm = sm; + + os_memset(&dest, 0, sizeof(dest)); + dest.sin_family = AF_INET; + dest.sin_addr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS); + dest.sin_port = htons(UPNP_MULTICAST_PORT); + + while (!islast) { + msg = next_advertisement(a, &islast); + if (msg == NULL) + break; + if (sendto(sm->multicast_sd, wpabuf_head(msg), wpabuf_len(msg), + 0, (struct sockaddr *) &dest, sizeof(dest)) < 0) { + wpa_printf(MSG_INFO, "WPS UPnP: Advertisement sendto " + "failed: %d (%s)", errno, strerror(errno)); + } + wpabuf_free(msg); + a->state++; + } } @@ -318,7 +349,7 @@ int advertisement_state_machine_start(struct upnp_wps_device_sm *sm) struct advertisement_state_machine *a = &sm->advertisement; int next_timeout_msec; - advertisement_state_machine_stop(sm); + advertisement_state_machine_stop(sm, 0); /* * Start out advertising down, this automatically switches @@ -461,7 +492,7 @@ static void msearchreply_state_machine_start(struct upnp_wps_device_sm *sm, a->type = MSEARCH_REPLY; a->state = 0; a->sm = sm; - os_memcpy(&a->client, client, sizeof(client)); + os_memcpy(&a->client, client, sizeof(*client)); /* Wait time depending on MX value */ next_timeout_msec = (1000 * mx * (os_random() & 0xFF)) >> 8; next_timeout_sec = next_timeout_msec / 1000; @@ -784,6 +815,7 @@ fail: */ int add_ssdp_network(char *net_if) { +#ifdef __linux__ int ret = -1; int sock = -1; struct rtentry rt; @@ -798,11 +830,11 @@ int add_ssdp_network(char *net_if) goto fail; rt.rt_dev = net_if; - sin = (struct sockaddr_in *) &rt.rt_dst; + sin = aliasing_hide_typecast(&rt.rt_dst, struct sockaddr_in); sin->sin_family = AF_INET; sin->sin_port = 0; sin->sin_addr.s_addr = inet_addr(SSDP_TARGET); - sin = (struct sockaddr_in *) &rt.rt_genmask; + sin = aliasing_hide_typecast(&rt.rt_genmask, struct sockaddr_in); sin->sin_family = AF_INET; sin->sin_port = 0; sin->sin_addr.s_addr = inet_addr(SSDP_NETMASK); @@ -826,6 +858,9 @@ fail: close(sock); return ret; +#else /* __linux__ */ + return 0; +#endif /* __linux__ */ } diff --git a/src/wps/wps_upnp_web.c b/src/wps/wps_upnp_web.c index db47c29..b637454 100644 --- a/src/wps/wps_upnp_web.c +++ b/src/wps/wps_upnp_web.c @@ -1578,7 +1578,6 @@ static void web_connection_parse_subscribe(struct web_connection *c, ret = HTTP_INTERNAL_SERVER_ERROR; goto error; } - callback_urls = NULL; /* is now owned by subscription */ } else { ret = HTTP_PRECONDITION_FAILED; goto error; @@ -1630,6 +1629,7 @@ error: http_put_empty(buf, ret); send_wpabuf(c->sd, buf); wpabuf_free(buf); + os_free(callback_urls); } diff --git a/wpa_supplicant/.gitignore b/wpa_supplicant/.gitignore deleted file mode 100644 index e7e034c..0000000 --- a/wpa_supplicant/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -*.d -.config -eapol_test -preauth_test -wpa_cli -wpa_passphrase -wpa_supplicant -wpa_priv diff --git a/wpa_supplicant/ChangeLog b/wpa_supplicant/ChangeLog index 8f5b6fb..ab99514 100644 --- a/wpa_supplicant/ChangeLog +++ b/wpa_supplicant/ChangeLog @@ -1,5 +1,28 @@ ChangeLog for wpa_supplicant +2010-01-12 - v0.6.10 + * fixed SHA-256 based key derivation function to match with the + standard when using CCMP (for IEEE 802.11r and IEEE 802.11w) + (note: this breaks interoperability with previous version) [Bug 307] + * changed driver_wext to disconnect at init/deinit to clear state + * added explicit disconnect on 4-way handshake failures + * added WPS workarounds for known interoperability issues with broken, + deployed implementation + * update IEEE 802.11w implementation to match with the published + standard + * do not send WPS M8 message when learning current AP configuration as + an external Registrar + * added a workaround for race condition between receive EAPOL frames + and association events + * fixed compilation with newer GnuTLS versions + * fixed PKCS#12 use with OpenSSL 1.0.0 + +2009-03-23 - v0.6.9 + * driver_ndis: add PAE group address to the multicast address list to + fix wired IEEE 802.1X authentication + * fixed IEEE 802.11r key derivation function to match with the standard + (note: this breaks interoperability with previous version) [Bug 303] + 2009-02-15 - v0.6.8 * increased wpa_cli ping interval to 5 seconds and made this configurable with a new command line options (-G<seconds>) diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile new file mode 100644 index 0000000..5a88fb3 --- /dev/null +++ b/wpa_supplicant/Makefile @@ -0,0 +1,1268 @@ +ifndef CC +CC=gcc +endif + +ifndef CFLAGS +CFLAGS = -MMD -O2 -Wall -g +endif + +export LIBDIR ?= /usr/local/lib/ +export BINDIR ?= /usr/local/sbin/ + +CFLAGS += -I../src +CFLAGS += -I../src/crypto +CFLAGS += -I../src/utils +CFLAGS += -I../src/common +CFLAGS += -I../src/rsn_supp + +ALL=wpa_supplicant wpa_passphrase wpa_cli + +all: verify_config $(ALL) dynamic_eap_methods + +verify_config: + @if [ ! -r .config ]; then \ + echo 'Building wpa_supplicant requires a configuration file'; \ + echo '(.config). See README for more instructions. You can'; \ + echo 'run "cp defconfig .config" to create an example'; \ + echo 'configuration.'; \ + exit 1; \ + fi + +mkconfig: + @if [ -e .config ]; then \ + echo '.config exists - did not replace it'; \ + exit 1; \ + fi + echo CONFIG_DRIVER_HOSTAP=y >> .config + echo CONFIG_DRIVER_WEXT=y >> .config + +install: all + mkdir -p $(DESTDIR)$(BINDIR) + for i in $(ALL); do cp $$i $(DESTDIR)$(BINDIR)/$$i; done + $(MAKE) -C ../src install + +OBJS = config.o +OBJS += ../src/utils/common.o +OBJS += ../src/utils/wpa_debug.o +OBJS += ../src/utils/wpabuf.o +OBJS += ../src/crypto/md5.o +OBJS += ../src/crypto/rc4.o +OBJS += ../src/crypto/md4.o +OBJS += ../src/crypto/sha1.o +OBJS += ../src/crypto/des.o +OBJS_p = wpa_passphrase.o +OBJS_p += ../src/utils/common.o +OBJS_p += ../src/utils/wpa_debug.o +OBJS_p += ../src/crypto/md5.o +OBJS_p += ../src/crypto/md4.o +OBJS_p += ../src/crypto/sha1.o +OBJS_p += ../src/crypto/des.o +OBJS_c = wpa_cli.o ../src/common/wpa_ctrl.o + +-include .config + +ifndef CONFIG_OS +ifdef CONFIG_NATIVE_WINDOWS +CONFIG_OS=win32 +else +CONFIG_OS=unix +endif +endif + +ifeq ($(CONFIG_OS), internal) +CFLAGS += -DOS_NO_C_LIB_DEFINES +endif + +OBJS += ../src/utils/os_$(CONFIG_OS).o +OBJS_p += ../src/utils/os_$(CONFIG_OS).o +OBJS_c += ../src/utils/os_$(CONFIG_OS).o + +ifndef CONFIG_ELOOP +CONFIG_ELOOP=eloop +endif +OBJS += ../src/utils/$(CONFIG_ELOOP).o + + +ifdef CONFIG_EAPOL_TEST +CFLAGS += -Werror -DEAPOL_TEST +endif + +ifndef CONFIG_BACKEND +CONFIG_BACKEND=file +endif + +ifeq ($(CONFIG_BACKEND), file) +OBJS += config_file.o +ifndef CONFIG_NO_CONFIG_BLOBS +NEED_BASE64=y +endif +CFLAGS += -DCONFIG_BACKEND_FILE +endif + +ifeq ($(CONFIG_BACKEND), winreg) +OBJS += config_winreg.o +endif + +ifeq ($(CONFIG_BACKEND), none) +OBJS += config_none.o +endif + +ifdef CONFIG_NO_CONFIG_WRITE +CFLAGS += -DCONFIG_NO_CONFIG_WRITE +endif + +ifdef CONFIG_NO_CONFIG_BLOBS +CFLAGS += -DCONFIG_NO_CONFIG_BLOBS +endif + +ifdef CONFIG_NO_SCAN_PROCESSING +CFLAGS += -DCONFIG_NO_SCAN_PROCESSING +endif + +ifdef CONFIG_DRIVER_HOSTAP +CFLAGS += -DCONFIG_DRIVER_HOSTAP +OBJS_d += ../src/drivers/driver_hostap.o +CONFIG_WIRELESS_EXTENSION=y +endif + +ifdef CONFIG_DRIVER_WEXT +CFLAGS += -DCONFIG_DRIVER_WEXT +CONFIG_WIRELESS_EXTENSION=y +endif + +ifdef CONFIG_DRIVER_NL80211 +CFLAGS += -DCONFIG_DRIVER_NL80211 +OBJS_d += ../src/drivers/driver_nl80211.o +LIBS += -lnl +ifdef CONFIG_CLIENT_MLME +OBJS_d += ../src/drivers/radiotap.o +endif +endif + +ifdef CONFIG_DRIVER_PRISM54 +CFLAGS += -DCONFIG_DRIVER_PRISM54 +OBJS_d += ../src/drivers/driver_prism54.o +CONFIG_WIRELESS_EXTENSION=y +endif + +ifdef CONFIG_DRIVER_HERMES +CFLAGS += -DCONFIG_DRIVER_HERMES +OBJS_d += ../src/drivers/driver_hermes.o +CONFIG_WIRELESS_EXTENSION=y +endif + +ifdef CONFIG_DRIVER_MADWIFI +CFLAGS += -DCONFIG_DRIVER_MADWIFI +OBJS_d += ../src/drivers/driver_madwifi.o +CONFIG_WIRELESS_EXTENSION=y +endif + +ifdef CONFIG_DRIVER_ATMEL +CFLAGS += -DCONFIG_DRIVER_ATMEL +OBJS_d += ../src/drivers/driver_atmel.o +CONFIG_WIRELESS_EXTENSION=y +endif + +ifdef CONFIG_DRIVER_NDISWRAPPER +CFLAGS += -DCONFIG_DRIVER_NDISWRAPPER +OBJS_d += ../src/drivers/driver_ndiswrapper.o +CONFIG_WIRELESS_EXTENSION=y +endif + +ifdef CONFIG_DRIVER_RALINK +CFLAGS += -DCONFIG_DRIVER_RALINK +OBJS_d += ../src/drivers/driver_ralink.o +endif + +ifdef CONFIG_DRIVER_BROADCOM +CFLAGS += -DCONFIG_DRIVER_BROADCOM +OBJS_d += ../src/drivers/driver_broadcom.o +endif + +ifdef CONFIG_DRIVER_IPW +CFLAGS += -DCONFIG_DRIVER_IPW +OBJS_d += ../src/drivers/driver_ipw.o +CONFIG_WIRELESS_EXTENSION=y +endif + +ifdef CONFIG_DRIVER_BSD +CFLAGS += -DCONFIG_DRIVER_BSD +OBJS_d += ../src/drivers/driver_bsd.o +ifndef CONFIG_L2_PACKET +CONFIG_L2_PACKET=freebsd +endif +endif + +ifdef CONFIG_DRIVER_NDIS +CFLAGS += -DCONFIG_DRIVER_NDIS +OBJS_d += ../src/drivers/driver_ndis.o +ifdef CONFIG_NDIS_EVENTS_INTEGRATED +OBJS_d += ../src/drivers/driver_ndis_.o +endif +ifndef CONFIG_L2_PACKET +CONFIG_L2_PACKET=pcap +endif +CONFIG_WINPCAP=y +ifdef CONFIG_USE_NDISUIO +CFLAGS += -DCONFIG_USE_NDISUIO +endif +endif + +ifdef CONFIG_DRIVER_WIRED +CFLAGS += -DCONFIG_DRIVER_WIRED +OBJS_d += ../src/drivers/driver_wired.o +endif + +ifdef CONFIG_DRIVER_TEST +CFLAGS += -DCONFIG_DRIVER_TEST +OBJS_d += ../src/drivers/driver_test.o +endif + +ifdef CONFIG_DRIVER_OSX +CFLAGS += -DCONFIG_DRIVER_OSX +OBJS_d += ../src/drivers/driver_osx.o +LDFLAGS += -framework CoreFoundation +LDFLAGS += -F/System/Library/PrivateFrameworks -framework Apple80211 +endif + +ifdef CONFIG_DRIVER_PS3 +CFLAGS += -DCONFIG_DRIVER_PS3 -m64 +OBJS_d += ../src/drivers/driver_ps3.o +LDFLAGS += -m64 +endif + +ifdef CONFIG_DRIVER_IPHONE +CFLAGS += -DCONFIG_DRIVER_IPHONE +OBJS_d += ../src/drivers/driver_iphone.o +OBJS_d += ../src/drivers/MobileApple80211.o +LIBS += -framework CoreFoundation +endif + +ifdef CONFIG_DRIVER_ROBOSWITCH +CFLAGS += -DCONFIG_DRIVER_ROBOSWITCH +OBJS_d += ../src/drivers/driver_roboswitch.o +endif + +ifndef CONFIG_L2_PACKET +CONFIG_L2_PACKET=linux +endif + +OBJS_l2 += ../src/l2_packet/l2_packet_$(CONFIG_L2_PACKET).o + +ifeq ($(CONFIG_L2_PACKET), pcap) +ifdef CONFIG_WINPCAP +CFLAGS += -DCONFIG_WINPCAP +LIBS += -lwpcap -lpacket +LIBS_w += -lwpcap +else +LIBS += -ldnet -lpcap +endif +endif + +ifeq ($(CONFIG_L2_PACKET), winpcap) +LIBS += -lwpcap -lpacket +LIBS_w += -lwpcap +endif + +ifeq ($(CONFIG_L2_PACKET), freebsd) +LIBS += -lpcap +endif + +ifdef CONFIG_EAP_TLS +# EAP-TLS +ifeq ($(CONFIG_EAP_TLS), dyn) +CFLAGS += -DEAP_TLS_DYNAMIC +EAPDYN += ../src/eap_peer/eap_tls.so +else +CFLAGS += -DEAP_TLS +OBJS += ../src/eap_peer/eap_tls.o +OBJS_h += ../src/eap_server/eap_tls.o +endif +TLS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_PEAP +# EAP-PEAP +ifeq ($(CONFIG_EAP_PEAP), dyn) +CFLAGS += -DEAP_PEAP_DYNAMIC +EAPDYN += ../src/eap_peer/eap_peap.so +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_peap.o +endif +TLS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_TTLS +# EAP-TTLS +ifeq ($(CONFIG_EAP_TTLS), dyn) +CFLAGS += -DEAP_TTLS_DYNAMIC +EAPDYN += ../src/eap_peer/eap_ttls.so +else +CFLAGS += -DEAP_TTLS +OBJS += ../src/eap_peer/eap_ttls.o +OBJS_h += ../src/eap_server/eap_ttls.o +endif +MS_FUNCS=y +TLS_FUNCS=y +CHAP=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_MD5 +# EAP-MD5 +ifeq ($(CONFIG_EAP_MD5), dyn) +CFLAGS += -DEAP_MD5_DYNAMIC +EAPDYN += ../src/eap_peer/eap_md5.so +else +CFLAGS += -DEAP_MD5 +OBJS += ../src/eap_peer/eap_md5.o +OBJS_h += ../src/eap_server/eap_md5.o +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) +CFLAGS += -DEAP_MSCHAPv2_DYNAMIC +EAPDYN += ../src/eap_peer/eap_mschapv2.so +EAPDYN += ../src/eap_peer/mschapv2.so +else +CFLAGS += -DEAP_MSCHAPv2 +OBJS += ../src/eap_peer/eap_mschapv2.o +OBJS += ../src/eap_peer/mschapv2.o +OBJS_h += ../src/eap_server/eap_mschapv2.o +endif +MS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_GTC +# EAP-GTC +ifeq ($(CONFIG_EAP_GTC), dyn) +CFLAGS += -DEAP_GTC_DYNAMIC +EAPDYN += ../src/eap_peer/eap_gtc.so +else +CFLAGS += -DEAP_GTC +OBJS += ../src/eap_peer/eap_gtc.o +OBJS_h += ../src/eap_server/eap_gtc.o +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_OTP +# EAP-OTP +ifeq ($(CONFIG_EAP_OTP), dyn) +CFLAGS += -DEAP_OTP_DYNAMIC +EAPDYN += ../src/eap_peer/eap_otp.so +else +CFLAGS += -DEAP_OTP +OBJS += ../src/eap_peer/eap_otp.o +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_SIM +# EAP-SIM +ifeq ($(CONFIG_EAP_SIM), dyn) +CFLAGS += -DEAP_SIM_DYNAMIC +EAPDYN += ../src/eap_peer/eap_sim.so +else +CFLAGS += -DEAP_SIM +OBJS += ../src/eap_peer/eap_sim.o +OBJS_h += ../src/eap_server/eap_sim.o +endif +CONFIG_IEEE8021X_EAPOL=y +CONFIG_EAP_SIM_COMMON=y +endif + +ifdef CONFIG_EAP_LEAP +# EAP-LEAP +ifeq ($(CONFIG_EAP_LEAP), dyn) +CFLAGS += -DEAP_LEAP_DYNAMIC +EAPDYN += ../src/eap_peer/eap_leap.so +else +CFLAGS += -DEAP_LEAP +OBJS += ../src/eap_peer/eap_leap.o +endif +MS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_PSK +# EAP-PSK +ifeq ($(CONFIG_EAP_PSK), dyn) +CFLAGS += -DEAP_PSK_DYNAMIC +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_psk.o +endif +CONFIG_IEEE8021X_EAPOL=y +NEED_AES=y +endif + +ifdef CONFIG_EAP_AKA +# EAP-AKA +ifeq ($(CONFIG_EAP_AKA), dyn) +CFLAGS += -DEAP_AKA_DYNAMIC +EAPDYN += ../src/eap_peer/eap_aka.so +else +CFLAGS += -DEAP_AKA +OBJS += ../src/eap_peer/eap_aka.o +OBJS_h += ../src/eap_server/eap_aka.o +endif +CONFIG_IEEE8021X_EAPOL=y +CONFIG_EAP_SIM_COMMON=y +endif + +ifdef CONFIG_EAP_AKA_PRIME +# EAP-AKA' +ifeq ($(CONFIG_EAP_AKA_PRIME), dyn) +CFLAGS += -DEAP_AKA_PRIME_DYNAMIC +else +CFLAGS += -DEAP_AKA_PRIME +endif +NEED_SHA256=y +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 + +ifdef CONFIG_EAP_FAST +# EAP-FAST +ifeq ($(CONFIG_EAP_FAST), dyn) +CFLAGS += -DEAP_FAST_DYNAMIC +EAPDYN += ../src/eap_peer/eap_fast.so +EAPDYN += ../src/eap_common/eap_fast_common.o +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_fast.o +endif +TLS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +NEED_T_PRF=y +endif + +ifdef CONFIG_EAP_PAX +# EAP-PAX +ifeq ($(CONFIG_EAP_PAX), dyn) +CFLAGS += -DEAP_PAX_DYNAMIC +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_pax.o +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_SAKE +# EAP-SAKE +ifeq ($(CONFIG_EAP_SAKE), dyn) +CFLAGS += -DEAP_SAKE_DYNAMIC +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_sake.o +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_GPSK +# EAP-GPSK +ifeq ($(CONFIG_EAP_GPSK), dyn) +CFLAGS += -DEAP_GPSK_DYNAMIC +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_gpsk.o +endif +CONFIG_IEEE8021X_EAPOL=y +ifdef CONFIG_EAP_GPSK_SHA256 +CFLAGS += -DEAP_GPSK_SHA256 +endif +NEED_SHA256=y +endif + +ifdef CONFIG_WPS +# EAP-WSC +CFLAGS += -DCONFIG_WPS -DEAP_WSC +OBJS += wps_supplicant.o +OBJS += ../src/utils/uuid.o +OBJS += ../src/eap_peer/eap_wsc.o ../src/eap_common/eap_wsc_common.o +OBJS += ../src/wps/wps.o +OBJS += ../src/wps/wps_common.o +OBJS += ../src/wps/wps_attr_parse.o +OBJS += ../src/wps/wps_attr_build.o +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_wsc.o +CONFIG_IEEE8021X_EAPOL=y +NEED_DH_GROUPS=y +NEED_SHA256=y +NEED_BASE64=y +NEED_CRYPTO=y +NEED_80211_COMMON=y + +ifdef CONFIG_WPS_UPNP +CFLAGS += -DCONFIG_WPS_UPNP +OBJS += ../src/wps/wps_upnp.o +OBJS += ../src/wps/wps_upnp_ssdp.o +OBJS += ../src/wps/wps_upnp_web.o +OBJS += ../src/wps/wps_upnp_event.o +OBJS += ../src/wps/httpread.o +endif + +endif + +ifdef CONFIG_EAP_IKEV2 +# EAP-IKEv2 +ifeq ($(CONFIG_EAP_IKEV2), dyn) +CFLAGS += -DEAP_IKEV2_DYNAMIC +EAPDYN += ../src/eap_peer/eap_ikev2.so ../src/eap_peer/ikev2.o +EAPDYN += ../src/eap_common/eap_ikev2_common.o ../src/eap_common/ikev2_common.o +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_ikev2.o +OBJS_h += ../src/eap_server/ikev2.o +endif +CONFIG_IEEE8021X_EAPOL=y +NEED_DH_GROUPS=y +NEED_DH_GROUPS_ALL=y +endif + +ifdef CONFIG_EAP_VENDOR_TEST +ifeq ($(CONFIG_EAP_VENDOR_TEST), dyn) +CFLAGS += -DEAP_VENDOR_TEST_DYNAMIC +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_vendor_test.o +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_TNC +# EAP-TNC +CFLAGS += -DEAP_TNC +OBJS += ../src/eap_peer/eap_tnc.o +OBJS += ../src/eap_peer/tncc.o +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) +CFLAGS += -DIEEE8021X_EAPOL +OBJS += ../src/eapol_supp/eapol_supp_sm.o ../src/eap_peer/eap.o ../src/eap_common/eap_common.o ../src/eap_peer/eap_methods.o +ifdef CONFIG_DYNAMIC_EAP_METHODS +CFLAGS += -DCONFIG_DYNAMIC_EAP_METHODS +LIBS += -ldl -rdynamic +endif +endif + +ifdef CONFIG_EAP_SERVER +CFLAGS += -DEAP_SERVER +OBJS_h += ../src/eap_server/eap.o +OBJS_h += ../src/eap_server/eap_identity.o +OBJS_h += ../src/eap_server/eap_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 += ../hostapd/eapol_sm.o +OBJS_h += ../hostapd/ieee802_1x.o +endif + +ifdef CONFIG_WPA_AUTHENTICATOR +OBJS_h += ../hostapd/wpa.o +OBJS_h += ../hostapd/wpa_auth_ie.o +ifdef CONFIG_IEEE80211R +OBJS_h += ../hostapd/wpa_ft.o +endif +ifdef CONFIG_PEERKEY +OBJS_h += ../hostapd/peerkey.o +endif +endif + +ifdef CONFIG_PCSC +# PC/SC interface for smartcards (USIM, GSM SIM) +CFLAGS += -DPCSC_FUNCS -I/usr/include/PCSC +OBJS += ../src/utils/pcsc_funcs.o +# -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 +CFLAGS += -DCONFIG_SIM_SIMULATOR +NEED_MILENAGE=y +endif + +ifdef CONFIG_USIM_SIMULATOR +CFLAGS += -DCONFIG_USIM_SIMULATOR +NEED_MILENAGE=y +endif + +ifdef NEED_MILENAGE +OBJS += ../src/hlr_auc_gw/milenage.o +endif + +ifndef CONFIG_TLS +CONFIG_TLS=openssl +endif + +ifeq ($(CONFIG_TLS), internal) +ifndef CONFIG_CRYPTO +CONFIG_CRYPTO=internal +endif +endif +ifeq ($(CONFIG_CRYPTO), libtomcrypt) +CFLAGS += -DCONFIG_INTERNAL_X509 +endif +ifeq ($(CONFIG_CRYPTO), internal) +CFLAGS += -DCONFIG_INTERNAL_X509 +endif + + +ifdef TLS_FUNCS +# Shared TLS functions (needed for EAP_TLS, EAP_PEAP, EAP_TTLS, and EAP_FAST) +CFLAGS += -DEAP_TLS_FUNCS +OBJS += ../src/eap_peer/eap_tls_common.o +OBJS_h += ../src/eap_server/eap_tls_common.o +NEED_TLS_PRF=y +ifeq ($(CONFIG_TLS), openssl) +CFLAGS += -DEAP_TLS_OPENSSL +OBJS += ../src/crypto/tls_openssl.o +LIBS += -lssl -lcrypto +LIBS_p += -lcrypto +endif +ifeq ($(CONFIG_TLS), gnutls) +OBJS += ../src/crypto/tls_gnutls.o +LIBS += -lgnutls -lgcrypt -lgpg-error +LIBS_p += -lgcrypt +ifdef CONFIG_GNUTLS_EXTRA +CFLAGS += -DCONFIG_GNUTLS_EXTRA +LIBS += -lgnutls-extra +endif +endif +ifeq ($(CONFIG_TLS), schannel) +OBJS += ../src/crypto/tls_schannel.o +endif +ifeq ($(CONFIG_TLS), internal) +OBJS += ../src/crypto/tls_internal.o +OBJS += ../src/tls/tlsv1_common.o ../src/tls/tlsv1_record.o +OBJS += ../src/tls/tlsv1_cred.o ../src/tls/tlsv1_client.o +OBJS += ../src/tls/tlsv1_client_write.o ../src/tls/tlsv1_client_read.o +OBJS += ../src/tls/asn1.o ../src/tls/rsa.o ../src/tls/x509v3.o +OBJS_p += ../src/tls/asn1.o ../src/tls/rsa.o +OBJS_p += ../src/crypto/rc4.o ../src/crypto/aes_wrap.o ../src/crypto/aes.o +NEED_BASE64=y +NEED_TLS_PRF=y +CFLAGS += -DCONFIG_TLS_INTERNAL +CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT +ifeq ($(CONFIG_CRYPTO), internal) +endif +ifeq ($(CONFIG_CRYPTO), libtomcrypt) +LIBS += -ltomcrypt -ltfm +LIBS_p += -ltomcrypt -ltfm +endif +endif +ifeq ($(CONFIG_TLS), none) +OBJS += ../src/crypto/tls_none.o +CFLAGS += -DEAP_TLS_NONE +CONFIG_INTERNAL_AES=y +CONFIG_INTERNAL_SHA1=y +CONFIG_INTERNAL_MD5=y +CONFIG_INTERNAL_SHA256=y +endif +ifdef CONFIG_SMARTCARD +ifndef CONFIG_NATIVE_WINDOWS +ifneq ($(CONFIG_L2_PACKET), freebsd) +LIBS += -ldl +endif +endif +endif +NEED_CRYPTO=y +else +OBJS += ../src/crypto/tls_none.o +endif + +ifdef CONFIG_PKCS12 +CFLAGS += -DPKCS12_FUNCS +endif + +ifdef CONFIG_SMARTCARD +CFLAGS += -DCONFIG_SMARTCARD +endif + +ifdef MS_FUNCS +OBJS += ../src/crypto/ms_funcs.o +NEED_CRYPTO=y +endif + +ifdef CHAP +OBJS += ../src/eap_common/chap.o +endif + +ifdef NEED_CRYPTO +ifndef TLS_FUNCS +ifeq ($(CONFIG_TLS), openssl) +LIBS += -lcrypto +LIBS_p += -lcrypto +endif +ifeq ($(CONFIG_TLS), gnutls) +LIBS += -lgcrypt +LIBS_p += -lgcrypt +endif +ifeq ($(CONFIG_TLS), schannel) +endif +ifeq ($(CONFIG_TLS), internal) +ifeq ($(CONFIG_CRYPTO), libtomcrypt) +LIBS += -ltomcrypt -ltfm +LIBS_p += -ltomcrypt -ltfm +endif +endif +endif +ifeq ($(CONFIG_TLS), openssl) +OBJS += ../src/crypto/crypto_openssl.o +OBJS_p += ../src/crypto/crypto_openssl.o +CONFIG_INTERNAL_SHA256=y +endif +ifeq ($(CONFIG_TLS), gnutls) +OBJS += ../src/crypto/crypto_gnutls.o +OBJS_p += ../src/crypto/crypto_gnutls.o +CONFIG_INTERNAL_SHA256=y +endif +ifeq ($(CONFIG_TLS), schannel) +OBJS += ../src/crypto/crypto_cryptoapi.o +OBJS_p += ../src/crypto/crypto_cryptoapi.o +CONFIG_INTERNAL_SHA256=y +endif +ifeq ($(CONFIG_TLS), internal) +ifeq ($(CONFIG_CRYPTO), libtomcrypt) +OBJS += ../src/crypto/crypto_libtomcrypt.o +OBJS_p += ../src/crypto/crypto_libtomcrypt.o +CONFIG_INTERNAL_SHA256=y +endif +ifeq ($(CONFIG_CRYPTO), internal) +OBJS += ../src/crypto/crypto_internal.o ../src/tls/bignum.o +OBJS_p += ../src/crypto/crypto_internal.o ../src/tls/bignum.o +CFLAGS += -DCONFIG_CRYPTO_INTERNAL +ifdef CONFIG_INTERNAL_LIBTOMMATH +CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH +ifdef CONFIG_INTERNAL_LIBTOMMATH_FAST +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 +endif +ifeq ($(CONFIG_CRYPTO), cryptoapi) +OBJS += ../src/crypto/crypto_cryptoapi.o +OBJS_p += ../src/crypto/crypto_cryptoapi.o +CFLAGS += -DCONFIG_CRYPTO_CRYPTOAPI +CONFIG_INTERNAL_SHA256=y +endif +endif +ifeq ($(CONFIG_TLS), none) +OBJS += ../src/crypto/crypto_none.o +OBJS_p += ../src/crypto/crypto_none.o +CONFIG_INTERNAL_SHA256=y +endif +else +CONFIG_INTERNAL_AES=y +CONFIG_INTERNAL_SHA1=y +CONFIG_INTERNAL_MD5=y +endif + +ifdef CONFIG_INTERNAL_AES +CFLAGS += -DINTERNAL_AES +endif +ifdef CONFIG_INTERNAL_SHA1 +CFLAGS += -DINTERNAL_SHA1 +endif +ifdef CONFIG_INTERNAL_SHA256 +CFLAGS += -DINTERNAL_SHA256 +endif +ifdef CONFIG_INTERNAL_MD5 +CFLAGS += -DINTERNAL_MD5 +endif +ifdef CONFIG_INTERNAL_MD4 +CFLAGS += -DINTERNAL_MD4 +endif +ifdef CONFIG_INTERNAL_DES +CFLAGS += -DINTERNAL_DES +endif + +ifdef CONFIG_IEEE80211R +NEED_SHA256=y +endif + +ifdef CONFIG_IEEE80211W +CFLAGS += -DCONFIG_IEEE80211W +NEED_SHA256=y +endif + +ifdef NEED_SHA256 +OBJS += ../src/crypto/sha256.o +CFLAGS += -DNEED_SHA256 +endif + +ifdef CONFIG_WIRELESS_EXTENSION +OBJS_d += ../src/drivers/driver_wext.o +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 +CFLAGS += -DCONFIG_CTRL_IFACE +ifeq ($(CONFIG_CTRL_IFACE), unix) +CFLAGS += -DCONFIG_CTRL_IFACE_UNIX +endif +ifeq ($(CONFIG_CTRL_IFACE), udp) +CFLAGS += -DCONFIG_CTRL_IFACE_UDP +endif +ifeq ($(CONFIG_CTRL_IFACE), named_pipe) +CFLAGS += -DCONFIG_CTRL_IFACE_NAMED_PIPE +endif +OBJS += ctrl_iface.o ctrl_iface_$(CONFIG_CTRL_IFACE).o +endif + +ifdef CONFIG_CTRL_IFACE_DBUS +CFLAGS += -DCONFIG_CTRL_IFACE_DBUS -DDBUS_API_SUBJECT_TO_CHANGE +OBJS += ctrl_iface_dbus.o ctrl_iface_dbus_handlers.o dbus_dict_helpers.o +ifndef DBUS_LIBS +DBUS_LIBS := $(shell pkg-config --libs dbus-1) +endif +LIBS += $(DBUS_LIBS) +ifndef DBUS_INCLUDE +DBUS_INCLUDE := $(shell pkg-config --cflags dbus-1) +endif +dbus_version=$(subst ., ,$(shell pkg-config --modversion dbus-1)) +DBUS_VERSION_MAJOR=$(word 1,$(dbus_version)) +DBUS_VERSION_MINOR=$(word 2,$(dbus_version)) +ifeq ($(DBUS_VERSION_MAJOR),) +DBUS_VERSION_MAJOR=0 +endif +ifeq ($(DBUS_VERSION_MINOR),) +DBUS_VERSION_MINOR=0 +endif +DBUS_INCLUDE += -DDBUS_VERSION_MAJOR=$(DBUS_VERSION_MAJOR) +DBUS_INCLUDE += -DDBUS_VERSION_MINOR=$(DBUS_VERSION_MINOR) +CFLAGS += $(DBUS_INCLUDE) +endif + +ifdef CONFIG_READLINE +CFLAGS += -DCONFIG_READLINE +LIBS_c += -lncurses -lreadline +endif + +ifdef CONFIG_NATIVE_WINDOWS +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 +CFLAGS += -DCONFIG_NO_STDOUT_DEBUG +ifndef CONFIG_CTRL_IFACE +CFLAGS += -DCONFIG_NO_WPA_MSG +endif +endif + +ifdef CONFIG_IPV6 +# for eapol_test only +CFLAGS += -DCONFIG_IPV6 +endif + +ifdef CONFIG_PEERKEY +CFLAGS += -DCONFIG_PEERKEY +endif + +ifdef CONFIG_IEEE80211R +CFLAGS += -DCONFIG_IEEE80211R +OBJS += ../src/rsn_supp/wpa_ft.o +endif + +ifndef CONFIG_NO_WPA +OBJS += ../src/rsn_supp/wpa.o +OBJS += ../src/rsn_supp/preauth.o +OBJS += ../src/rsn_supp/pmksa_cache.o +OBJS += ../src/rsn_supp/peerkey.o +OBJS += ../src/rsn_supp/wpa_ie.o +OBJS += ../src/common/wpa_common.o +NEED_AES=y +else +CFLAGS += -DCONFIG_NO_WPA -DCONFIG_NO_WPA2 +endif + +ifdef CONFIG_NO_WPA2 +CFLAGS += -DCONFIG_NO_WPA2 +endif + +ifdef CONFIG_NO_WPA_PASSPHRASE +CFLAGS += -DCONFIG_NO_PBKDF2 +endif + +ifdef CONFIG_NO_AES_EXTRAS +CFLAGS += -DCONFIG_NO_AES_WRAP +CFLAGS += -DCONFIG_NO_AES_CTR -DCONFIG_NO_AES_OMAC1 +CFLAGS += -DCONFIG_NO_AES_EAX -DCONFIG_NO_AES_CBC +CFLAGS += -DCONFIG_NO_AES_ENCRYPT +CFLAGS += -DCONFIG_NO_AES_ENCRYPT_BLOCK +endif + +ifdef NEED_AES +OBJS += ../src/crypto/aes_wrap.o ../src/crypto/aes.o +endif + +ifdef NEED_DH_GROUPS +OBJS += ../src/crypto/dh_groups.o +ifdef NEED_DH_GROUPS_ALL +CFLAGS += -DALL_DH_GROUPS +endif +endif + +ifndef NEED_FIPS186_2_PRF +CFLAGS += -DCONFIG_NO_FIPS186_2_PRF +endif + +ifndef NEED_T_PRF +CFLAGS += -DCONFIG_NO_T_PRF +endif + +ifndef NEED_TLS_PRF +CFLAGS += -DCONFIG_NO_TLS_PRF +endif + +ifdef NEED_BASE64 +OBJS += ../src/utils/base64.o +endif + +ifdef CONFIG_CLIENT_MLME +OBJS += mlme.o ../src/common/ieee802_11_common.o +CFLAGS += -DCONFIG_CLIENT_MLME +endif + +ifndef CONFIG_MAIN +CONFIG_MAIN=main +endif + +ifdef CONFIG_DEBUG_SYSLOG +CFLAGS += -DCONFIG_DEBUG_SYSLOG +endif + +ifdef CONFIG_DEBUG_FILE +CFLAGS += -DCONFIG_DEBUG_FILE +endif + +ifdef CONFIG_DELAYED_MIC_ERROR_REPORT +CFLAGS += -DCONFIG_DELAYED_MIC_ERROR_REPORT +endif + +OBJS += ../src/drivers/scan_helpers.o + +OBJS_wpa_rm := ctrl_iface.o mlme.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 ../src/radius/radius.o ../src/radius/radius_client.o +OBJS_t += ../src/utils/ip_addr.o +OBJS_t2 := $(OBJS) $(OBJS_l2) preauth_test.o +OBJS += $(CONFIG_MAIN).o + +ifdef CONFIG_PRIVSEP +OBJS_priv += $(OBJS_d) ../src/drivers/drivers.o ../src/drivers/scan_helpers.o +OBJS_priv += $(OBJS_l2) +OBJS_priv += ../src/utils/os_$(CONFIG_OS).o +OBJS_priv += ../src/utils/$(CONFIG_ELOOP).o +OBJS_priv += ../src/utils/common.o +OBJS_priv += ../src/utils/wpa_debug.o +OBJS_priv += ../src/utils/wpabuf.o +OBJS_priv += wpa_priv.o +ifdef CONFIG_DRIVER_TEST +OBJS_priv += ../src/crypto/sha1.o +OBJS_priv += ../src/crypto/md5.o +ifeq ($(CONFIG_TLS), openssl) +OBJS_priv += ../src/crypto/crypto_openssl.o +endif +ifeq ($(CONFIG_TLS), gnutls) +OBJS_priv += ../src/crypto/crypto_gnutls.o +endif +ifeq ($(CONFIG_TLS), internal) +ifeq ($(CONFIG_CRYPTO), libtomcrypt) +OBJS_priv += ../src/crypto/crypto_libtomcrypt.o +else +OBJS_priv += ../src/crypto/crypto_internal.o +endif +endif +endif # CONFIG_DRIVER_TEST +OBJS += ../src/l2_packet/l2_packet_privsep.o +OBJS += ../src/drivers/driver_privsep.o +EXTRA_progs += wpa_priv +else +OBJS += $(OBJS_d) ../src/drivers/drivers.o +OBJS += $(OBJS_l2) +endif + +ifdef CONFIG_NDIS_EVENTS_INTEGRATED +CFLAGS += -DCONFIG_NDIS_EVENTS_INTEGRATED +OBJS += ../src/drivers/ndis_events.o +EXTRALIBS += -loleaut32 -lole32 -luuid +ifdef PLATFORMSDKLIB +EXTRALIBS += $(PLATFORMSDKLIB)/WbemUuid.Lib +else +EXTRALIBS += WbemUuid.Lib +endif +endif + +ifndef LDO +LDO=$(CC) +endif + +dynamic_eap_methods: $(EAPDYN) + +wpa_priv: $(OBJS_priv) + $(LDO) $(LDFLAGS) -o wpa_priv $(OBJS_priv) $(LIBS) + +wpa_supplicant: .config $(OBJS) $(EXTRA_progs) + $(LDO) $(LDFLAGS) -o wpa_supplicant $(OBJS) $(LIBS) $(EXTRALIBS) + +eapol_test: .config $(OBJS_t) + $(LDO) $(LDFLAGS) -o eapol_test $(OBJS_t) $(LIBS) + +preauth_test: .config $(OBJS_t2) + $(LDO) $(LDFLAGS) -o preauth_test $(OBJS_t2) $(LIBS) + +wpa_passphrase: $(OBJS_p) + $(LDO) $(LDFLAGS) -o wpa_passphrase $(OBJS_p) $(LIBS_p) + +wpa_cli: $(OBJS_c) + $(LDO) $(LDFLAGS) -o wpa_cli $(OBJS_c) $(LIBS_c) + +link_test: $(OBJS) $(OBJS_h) tests/link_test.o + $(LDO) $(LDFLAGS) -o link_test $(OBJS) $(OBJS_h) tests/link_test.o $(LIBS) + +test_wpa: $(OBJS_wpa) $(OBJS_h) + $(LDO) $(LDFLAGS) -o test_wpa $(OBJS_wpa) $(LIBS) + +OBJSa=../src/tls/asn1_test.o ../src/tls/asn1.o ../src/tls/x509v3.o ../src/utils/common.o ../src/utils/wpa_debug.o ../src/utils/os_unix.o \ + ../src/crypto/crypto_$(CONFIG_CRYPTO).o ../src/crypto/md5.o ../src/crypto/sha1.o \ + ../src/crypto/rc4.o ../src/crypto/des.o ../src/crypto/aes_wrap.o \ + ../src/crypto/aes.o ../src/tls/bignum.o ../src/tls/rsa.o +asn1_test: $(OBJSa) + $(LDO) $(LDFLAGS) -o asn1_test $(OBJSa) + +OBJSx=tests/test_x509v3.o ../src/tls/asn1.o ../src/tls/x509v3.o \ + ../src/utils/common.o ../src/utils/wpa_debug.o ../src/utils/os_unix.o \ + ../src/crypto/crypto_$(CONFIG_CRYPTO).o \ + ../src/crypto/md5.o ../src/crypto/sha1.o ../src/crypto/aes.o \ + ../src/crypto/rc4.o ../src/crypto/des.o ../src/crypto/aes_wrap.o \ + ../src/crypto/sha256.o \ + ../src/tls/bignum.o ../src/tls/rsa.o +test_x509v3: $(OBJSx) + $(LDO) $(LDFLAGS) -o test_x509v3 $(OBJSx) + +win_if_list: win_if_list.c + $(LDO) $(LDFLAGS) -o $@ win_if_list.c $(CFLAGS) $(LIBS_w) + +eap_psk.so: ../src/eap_peer/eap_psk.c ../src/eap_common/eap_psk_common.c + $(CC) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \ + -Deap_peer_psk_register=eap_peer_method_dynamic_init + +eap_pax.so: ../src/eap_peer/eap_pax.c ../src/eap_common/eap_pax_common.c + $(CC) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \ + -Deap_peer_pax_register=eap_peer_method_dynamic_init + +eap_sake.so: ../src/eap_peer/eap_sake.c ../src/eap_common/eap_sake_common.c + $(CC) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \ + -Deap_peer_sake_register=eap_peer_method_dynamic_init + +eap_wsc.so: ../src/eap_peer/eap_wsc.c ../src/eap_common/eap_wsc_common.c ../src/wps/wps.c + $(CC) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \ + -Deap_peer_wsc_register=eap_peer_method_dynamic_init + +eap_ikev2.so: ../src/eap_peer/eap_ikev2.c ../src/eap_peer/ikev2.c ../src/eap_common/eap_ikev2_common.o ../src/eap_common/ikev2_common.c + $(CC) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \ + -Deap_peer_ikev2_register=eap_peer_method_dynamic_init + +%.so: %.c + $(CC) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $< \ + -D$(*F:eap_%=eap_peer_%)_register=eap_peer_method_dynamic_init + +Q=@ +E=echo +ifeq ($(V), 1) +Q= +E=true +endif + +%.o: %.c + $(Q)$(CC) -c -o $@ $(CFLAGS) $< + @$(E) " CC " $< + +wpa_supplicant.exe: wpa_supplicant + mv -f $< $@ +wpa_cli.exe: wpa_cli + mv -f $< $@ +wpa_passphrase.exe: wpa_passphrase + mv -f $< $@ +win_if_list.exe: win_if_list + mv -f $< $@ +eapol_test.exe: eapol_test + mv -f $< $@ + +WINALL=wpa_supplicant.exe wpa_cli.exe wpa_passphrase.exe win_if_list.exe + +windows-bin: $(WINALL) + $(STRIP) $(WINALL) + +wpa_gui/Makefile: + qmake -o wpa_gui/Makefile wpa_gui/wpa_gui.pro + +wpa_gui: wpa_gui/Makefile + $(MAKE) -C wpa_gui + +wpa_gui-qt4/Makefile: + qmake -o wpa_gui-qt4/Makefile wpa_gui-qt4/wpa_gui.pro + +wpa_gui-qt4: wpa_gui-qt4/Makefile + $(MAKE) -C wpa_gui-qt4 + +TEST_MS_FUNCS_OBJS = ../src/crypto/crypto_openssl.o ../src/crypto/sha1.o ../src/crypto/md5.o \ + ../src/utils/os_unix.o ../src/crypto/rc4.o tests/test_ms_funcs.o +test-ms_funcs: $(TEST_MS_FUNCS_OBJS) + $(LDO) $(LDFLAGS) -o $@ $(TEST_MS_FUNCS_OBJS) $(LIBS) -lcrypto + ./test-ms_funcs + rm test-ms_funcs + +TEST_SHA1_OBJS = ../src/crypto/sha1.o ../src/crypto/md5.o tests/test_sha1.o #../src/crypto/crypto_openssl.o +test-sha1: $(TEST_SHA1_OBJS) + $(LDO) $(LDFLAGS) -o $@ $(TEST_SHA1_OBJS) $(LIBS) + ./test-sha1 + rm test-sha1 + +TEST_SHA256_OBJS = ../src/crypto/sha256.o ../src/crypto/md5.o tests/test_sha256.o ../src/utils/os_unix.o ../src/crypto/crypto_openssl.o +test-sha256: $(TEST_SHA256_OBJS) + $(LDO) $(LDFLAGS) -o $@ $(TEST_SHA256_OBJS) $(LIBS) + ./test-sha256 + rm test-sha256 + +TEST_AES_OBJS = ../src/crypto/aes_wrap.o ../src/crypto/aes.o tests/test_aes.o +test-aes: $(TEST_AES_OBJS) + $(LDO) $(LDFLAGS) -o $@ $(TEST_AES_OBJS) $(LIBS) + ./test-aes + rm test-aes + +TEST_EAP_SIM_COMMON_OBJS = ../src/crypto/sha1.o ../src/crypto/md5.o \ + ../src/crypto/aes_wrap.o ../src/utils/common.o ../src/utils/os_unix.o \ + ../src/utils/wpa_debug.o ../src/crypto/aes.o \ + tests/test_eap_sim_common.o +test-eap_sim_common: $(TEST_EAP_SIM_COMMON_OBJS) + $(LDO) $(LDFLAGS) -o $@ $(TEST_AES_OBJS) $(LIBS) + ./test-eap_sim_common + rm test-eap_sim_common + +TEST_MD4_OBJS = ../src/crypto/md4.o tests/test_md4.o #../src/crypto/crypto_openssl.o +test-md4: $(TEST_MD4_OBJS) + $(LDO) $(LDFLAGS) -o $@ $(TEST_MD4_OBJS) $(LIBS) + ./test-md4 + rm test-md4 + +TEST_MD5_OBJS = ../src/crypto/md5.o tests/test_md5.o #../src/crypto/crypto_openssl.o +test-md5: $(TEST_MD5_OBJS) + $(LDO) $(LDFLAGS) -o $@ $(TEST_MD5_OBJS) $(LIBS) + ./test-md5 + rm test-md5 + +tests: test-ms_funcs test-sha1 test-aes test-eap_sim_common test-md4 test-md5 + +clean: + $(MAKE) -C ../src clean + rm -f core *~ *.o *.d eap_*.so $(ALL) $(WINALL) eapol_test preauth_test + rm -f wpa_priv + +%.eps: %.fig + fig2dev -L eps $*.fig $*.eps + +%.png: %.fig + fig2dev -L png -m 3 $*.fig | pngtopnm | pnmscale 0.4 | pnmtopng \ + > $*.png + +docs-pics: doc/wpa_supplicant.png doc/wpa_supplicant.eps + +docs: docs-pics + (cd ..; doxygen wpa_supplicant/doc/doxygen.full; cd wpa_supplicant) + $(MAKE) -C doc/latex + cp doc/latex/refman.pdf wpa_supplicant-devel.pdf + +docs-fast: docs-pics + (cd ..; doxygen wpa_supplicant/doc/doxygen.fast; cd wpa_supplicant) + +clean-docs: + rm -rf doc/latex doc/html + rm -f doc/wpa_supplicant.{eps,png} wpa_supplicant-devel.pdf + +-include $(OBJS:%.o=%.d) diff --git a/wpa_supplicant/README b/wpa_supplicant/README index 2b94c23..b282150 100644 --- a/wpa_supplicant/README +++ b/wpa_supplicant/README @@ -397,10 +397,8 @@ CONFIG_PCSC=y Following options can be added to .config to select which driver interfaces are included. Hermes driver interface needs to be downloaded -from Agere (see above). CONFIG_WIRELESS_EXTENSION will be used -automatically if any of the selected drivers need it. +from Agere (see above). -CONFIG_WIRELESS_EXTENSION=y CONFIG_DRIVER_HOSTAP=y CONFIG_DRIVER_HERMES=y CONFIG_DRIVER_MADWIFI=y @@ -426,7 +424,6 @@ CONFIG_DRIVER_BROADCOM=y CONFIG_DRIVER_IPW=y CONFIG_DRIVER_BSD=y CONFIG_DRIVER_NDIS=y -CONFIG_WIRELESS_EXTENSION=y CONFIG_IEEE8021X_EAPOL=y CONFIG_EAP_MD5=y CONFIG_EAP_MSCHAPV2=y @@ -520,11 +517,11 @@ drivers: hostap = Host AP driver (Intersil Prism2/2.5/3) [default] (this can also be used with Linuxant DriverLoader) hermes = Agere Systems Inc. driver (Hermes-I/Hermes-II) - madwifi = MADWIFI 802.11 support (Atheros, etc.) + madwifi = MADWIFI 802.11 support (Atheros, etc.) (deprecated; use wext) atmel = ATMEL AT76C5XXx (USB, PCMCIA) wext = Linux wireless extensions (generic) ralink = Ralink Client driver - ndiswrapper = Linux ndiswrapper + ndiswrapper = Linux ndiswrapper (deprecated; use wext) broadcom = Broadcom wl.o driver ipw = Intel ipw2100/2200 driver (old; use wext with Linux 2.6.13 or newer) wired = wpa_supplicant wired Ethernet driver diff --git a/wpa_supplicant/README-WPS b/wpa_supplicant/README-WPS index 7fd358d..6b826a7 100644 --- a/wpa_supplicant/README-WPS +++ b/wpa_supplicant/README-WPS @@ -131,13 +131,18 @@ negotiation which will generate a new WPA PSK in the same way as the PIN method described above. -If the client wants to operation in the Registrar role to configure an +If the client wants to operate in the Registrar role to configure an AP, wpa_supplicant is notified over the control interface, e.g., with wpa_cli: wpa_cli wps_reg <AP BSSID> <AP PIN> (example: wpa_cli wps_reg 02:34:56:78:9a:bc 12345670) +This is currently only used to fetch the current AP settings instead +of actually changing them. The main difference with the wps_pin +command is that wps_reg uses the AP PIN (e.g., from a label on the AP) +instead of a PIN generated at the client. + Scanning -------- diff --git a/wpa_supplicant/README-Windows.txt b/wpa_supplicant/README-Windows.txt new file mode 100644 index 0000000..292223d --- /dev/null +++ b/wpa_supplicant/README-Windows.txt @@ -0,0 +1,450 @@ +wpa_supplicant for Windows +========================== + +Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi> and contributors +All Rights Reserved. + +This program is dual-licensed under both the GPL version 2 and BSD +license. Either license may be used at your option. + +This product includes software developed by the OpenSSL Project +for use in the OpenSSL Toolkit (http://www.openssl.org/). This +product includes cryptographic software written by Eric Young +(eay@cryptsoft.com). + + +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 + + +Binary version +-------------- + +Compiled binary version of the wpa_supplicant and additional tools is +available from http://w1.fi/wpa_supplicant/. These binaries can be +used after installing WinPcap. + +wpa_gui uses Qt 4 framework and may need additional dynamic libraries +(DLLs). These libraries are available from +http://w1.fi/wpa_supplicant/qt4/wpa_gui-qt433-windows-dll.zip +You can copy the DLL files from this ZIP package into the same directory +with wpa_gui.exe to allow wpa_gui to be started. + + +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. + + + +License information for third party software used in this product: + + OpenSSL License + --------------- + +/* ==================================================================== + * Copyright (c) 1998-2004 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ + + Original SSLeay License + ----------------------- + +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + + + + Qt Open Source Edition + ---------------------- + +The Qt GUI Toolkit is Copyright (C) 1994-2007 Trolltech ASA. +Qt Open Source Edition is licensed under GPL version 2. + +Source code for the library is available at +http://w1.fi/wpa_supplicant/qt4/qt-win-opensource-src-4.3.3.zip diff --git a/wpa_supplicant/config_winreg.c b/wpa_supplicant/config_winreg.c new file mode 100644 index 0000000..456d417 --- /dev/null +++ b/wpa_supplicant/config_winreg.c @@ -0,0 +1,980 @@ +/* + * WPA Supplicant / Configuration backend: Windows registry + * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * This file implements a configuration backend for Windows registry. All the + * configuration information is stored in the registry and the format for + * network configuration fields is same as described in the sample + * configuration file, wpa_supplicant.conf. + * + * Configuration data is in + * \a 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. + * + * Example network configuration block: + * \verbatim +HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant\configs\test\networks\0000 + ssid="example" + key_mgmt=WPA-PSK +\endverbatim + */ + +#include "includes.h" + +#include "common.h" +#include "uuid.h" +#include "config.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 + +#ifdef UNICODE +#define TSTR "%S" +#else /* UNICODE */ +#define TSTR "%s" +#endif /* UNICODE */ + + +static int wpa_config_read_blobs(struct wpa_config *config, HKEY hk) +{ + struct wpa_config_blob *blob; + int errors = 0; + HKEY bhk; + LONG ret; + DWORD i; + + ret = RegOpenKeyEx(hk, TEXT("blobs"), 0, KEY_QUERY_VALUE, &bhk); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "Could not open wpa_supplicant config " + "blobs key"); + return 0; /* assume no blobs */ + } + + for (i = 0; ; i++) { +#define TNAMELEN 255 + TCHAR name[TNAMELEN]; + char data[4096]; + DWORD namelen, datalen, type; + + namelen = TNAMELEN; + datalen = sizeof(data); + ret = RegEnumValue(bhk, i, name, &namelen, NULL, &type, + (LPBYTE) data, &datalen); + + if (ret == ERROR_NO_MORE_ITEMS) + break; + + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "RegEnumValue failed: 0x%x", + (unsigned int) ret); + break; + } + + if (namelen >= TNAMELEN) + namelen = TNAMELEN - 1; + name[namelen] = TEXT('\0'); + wpa_unicode2ascii_inplace(name); + + if (datalen >= sizeof(data)) + datalen = sizeof(data) - 1; + + wpa_printf(MSG_MSGDUMP, "blob %d: field='%s' len %d", + (int) i, name, (int) datalen); + + blob = os_zalloc(sizeof(*blob)); + if (blob == NULL) { + errors++; + break; + } + blob->name = os_strdup((char *) name); + blob->data = os_malloc(datalen); + if (blob->name == NULL || blob->data == NULL) { + wpa_config_free_blob(blob); + errors++; + break; + } + os_memcpy(blob->data, data, datalen); + blob->len = datalen; + + wpa_config_set_blob(config, blob); + } + + RegCloseKey(bhk); + + return errors ? -1 : 0; +} + + +static int wpa_config_read_reg_dword(HKEY hk, const TCHAR *name, int *_val) +{ + DWORD val, buflen; + LONG ret; + + buflen = sizeof(val); + ret = RegQueryValueEx(hk, name, NULL, NULL, (LPBYTE) &val, &buflen); + if (ret == ERROR_SUCCESS && buflen == sizeof(val)) { + wpa_printf(MSG_DEBUG, TSTR "=%d", name, (int) val); + *_val = val; + return 0; + } + + return -1; +} + + +static char * wpa_config_read_reg_string(HKEY hk, const TCHAR *name) +{ + DWORD buflen; + LONG ret; + TCHAR *val; + + buflen = 0; + ret = RegQueryValueEx(hk, name, NULL, NULL, NULL, &buflen); + if (ret != ERROR_SUCCESS) + return NULL; + val = os_malloc(buflen); + if (val == NULL) + return NULL; + + ret = RegQueryValueEx(hk, name, NULL, NULL, (LPBYTE) val, &buflen); + if (ret != ERROR_SUCCESS) { + os_free(val); + return NULL; + } + + wpa_unicode2ascii_inplace(val); + wpa_printf(MSG_DEBUG, TSTR "=%s", name, (char *) val); + return (char *) val; +} + + +#ifdef CONFIG_WPS +static int wpa_config_read_global_uuid(struct wpa_config *config, HKEY hk) +{ + char *str; + int ret = 0; + + str = wpa_config_read_reg_string(hk, TEXT("uuid")); + if (str == NULL) + return 0; + + if (uuid_str2bin(str, config->uuid)) + ret = -1; + + os_free(str); + + return ret; +} + + +static int wpa_config_read_global_os_version(struct wpa_config *config, + HKEY hk) +{ + char *str; + int ret = 0; + + str = wpa_config_read_reg_string(hk, TEXT("os_version")); + if (str == NULL) + return 0; + + if (hexstr2bin(str, config->os_version, 4)) + ret = -1; + + os_free(str); + + return ret; +} +#endif /* CONFIG_WPS */ + + +static int wpa_config_read_global(struct wpa_config *config, HKEY hk) +{ + int errors = 0; + + wpa_config_read_reg_dword(hk, TEXT("ap_scan"), &config->ap_scan); + wpa_config_read_reg_dword(hk, TEXT("fast_reauth"), + &config->fast_reauth); + wpa_config_read_reg_dword(hk, TEXT("dot11RSNAConfigPMKLifetime"), + (int *) &config->dot11RSNAConfigPMKLifetime); + wpa_config_read_reg_dword(hk, + TEXT("dot11RSNAConfigPMKReauthThreshold"), + (int *) + &config->dot11RSNAConfigPMKReauthThreshold); + wpa_config_read_reg_dword(hk, TEXT("dot11RSNAConfigSATimeout"), + (int *) &config->dot11RSNAConfigSATimeout); + wpa_config_read_reg_dword(hk, TEXT("update_config"), + &config->update_config); + + if (wpa_config_read_reg_dword(hk, TEXT("eapol_version"), + &config->eapol_version) == 0) { + if (config->eapol_version < 1 || + config->eapol_version > 2) { + wpa_printf(MSG_ERROR, "Invalid EAPOL version (%d)", + config->eapol_version); + errors++; + } + } + + config->ctrl_interface = wpa_config_read_reg_string( + hk, TEXT("ctrl_interface")); + +#ifdef CONFIG_WPS + if (wpa_config_read_global_uuid(config, hk)) + errors++; + config->device_name = wpa_config_read_reg_string( + hk, TEXT("device_name")); + config->manufacturer = wpa_config_read_reg_string( + hk, TEXT("manufacturer")); + config->model_name = wpa_config_read_reg_string( + hk, TEXT("model_name")); + config->serial_number = wpa_config_read_reg_string( + hk, TEXT("serial_number")); + config->device_type = wpa_config_read_reg_string( + hk, TEXT("device_type")); + if (wpa_config_read_global_os_version(config, hk)) + errors++; + wpa_config_read_reg_dword(hk, TEXT("wps_cred_processing"), + &config->wps_cred_processing); +#endif /* CONFIG_WPS */ + + return errors ? -1 : 0; +} + + +static struct wpa_ssid * wpa_config_read_network(HKEY hk, const TCHAR *netw, + int id) +{ + HKEY nhk; + LONG ret; + DWORD i; + struct wpa_ssid *ssid; + int errors = 0; + + ret = RegOpenKeyEx(hk, netw, 0, KEY_QUERY_VALUE, &nhk); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "Could not open wpa_supplicant config " + "network '" TSTR "'", netw); + return NULL; + } + + wpa_printf(MSG_MSGDUMP, "Start of a new network '" TSTR "'", netw); + ssid = os_zalloc(sizeof(*ssid)); + if (ssid == NULL) { + RegCloseKey(nhk); + return NULL; + } + ssid->id = id; + + wpa_config_set_network_defaults(ssid); + + for (i = 0; ; i++) { + TCHAR name[255], data[1024]; + DWORD namelen, datalen, type; + + namelen = 255; + datalen = sizeof(data); + ret = RegEnumValue(nhk, i, name, &namelen, NULL, &type, + (LPBYTE) data, &datalen); + + if (ret == ERROR_NO_MORE_ITEMS) + break; + + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_ERROR, "RegEnumValue failed: 0x%x", + (unsigned int) ret); + break; + } + + if (namelen >= 255) + namelen = 255 - 1; + name[namelen] = TEXT('\0'); + + if (datalen >= 1024) + datalen = 1024 - 1; + data[datalen] = TEXT('\0'); + + wpa_unicode2ascii_inplace(name); + wpa_unicode2ascii_inplace(data); + if (wpa_config_set(ssid, (char *) name, (char *) data, 0) < 0) + errors++; + } + + RegCloseKey(nhk); + + if (ssid->passphrase) { + if (ssid->psk_set) { + wpa_printf(MSG_ERROR, "Both PSK and passphrase " + "configured for network '" TSTR "'.", netw); + errors++; + } + wpa_config_update_psk(ssid); + } + + if ((ssid->key_mgmt & (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_FT_PSK | + WPA_KEY_MGMT_PSK_SHA256)) && + !ssid->psk_set) { + wpa_printf(MSG_ERROR, "WPA-PSK accepted for key management, " + "but no PSK configured for network '" TSTR "'.", + netw); + errors++; + } + + if ((ssid->group_cipher & WPA_CIPHER_CCMP) && + !(ssid->pairwise_cipher & WPA_CIPHER_CCMP) && + !(ssid->pairwise_cipher & WPA_CIPHER_NONE)) { + /* Group cipher cannot be stronger than the pairwise cipher. */ + wpa_printf(MSG_DEBUG, "Removed CCMP from group cipher " + "list since it was not allowed for pairwise " + "cipher for network '" TSTR "'.", netw); + ssid->group_cipher &= ~WPA_CIPHER_CCMP; + } + + if (errors) { + wpa_config_free_ssid(ssid); + ssid = NULL; + } + + return ssid; +} + + +static int wpa_config_read_networks(struct wpa_config *config, HKEY hk) +{ + HKEY nhk; + struct wpa_ssid *ssid, *tail = NULL, *head = NULL; + int errors = 0; + LONG ret; + DWORD i; + + ret = RegOpenKeyEx(hk, TEXT("networks"), 0, KEY_ENUMERATE_SUB_KEYS, + &nhk); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_ERROR, "Could not open wpa_supplicant networks " + "registry key"); + return -1; + } + + for (i = 0; ; i++) { + TCHAR name[255]; + DWORD namelen; + + namelen = 255; + ret = RegEnumKeyEx(nhk, i, name, &namelen, NULL, NULL, NULL, + NULL); + + if (ret == ERROR_NO_MORE_ITEMS) + break; + + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "RegEnumKeyEx failed: 0x%x", + (unsigned int) ret); + break; + } + + if (namelen >= 255) + namelen = 255 - 1; + name[namelen] = '\0'; + + ssid = wpa_config_read_network(nhk, name, i); + if (ssid == NULL) { + wpa_printf(MSG_ERROR, "Failed to parse network " + "profile '%s'.", name); + errors++; + continue; + } + if (head == NULL) { + head = tail = ssid; + } else { + tail->next = ssid; + tail = ssid; + } + if (wpa_config_add_prio_network(config, ssid)) { + wpa_printf(MSG_ERROR, "Failed to add network profile " + "'%s' to priority list.", name); + errors++; + continue; + } + } + + RegCloseKey(nhk); + + config->ssid = head; + + return errors ? -1 : 0; +} + + +struct wpa_config * wpa_config_read(const char *name) +{ + TCHAR buf[256]; + int errors = 0; + struct wpa_config *config; + HKEY hk; + LONG ret; + + config = wpa_config_alloc_empty(NULL, NULL); + if (config == NULL) + return NULL; + wpa_printf(MSG_DEBUG, "Reading configuration profile '%s'", name); + +#ifdef UNICODE + _snwprintf(buf, 256, WPA_KEY_PREFIX TEXT("\\configs\\%S"), name); +#else /* UNICODE */ + os_snprintf(buf, 256, WPA_KEY_PREFIX TEXT("\\configs\\%s"), name); +#endif /* UNICODE */ + + ret = RegOpenKeyEx(WPA_KEY_ROOT, buf, 0, KEY_QUERY_VALUE, &hk); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_ERROR, "Could not open wpa_supplicant " + "configuration registry HKLM\\" TSTR, buf); + os_free(config); + return NULL; + } + + if (wpa_config_read_global(config, hk)) + errors++; + + if (wpa_config_read_networks(config, hk)) + errors++; + + if (wpa_config_read_blobs(config, hk)) + errors++; + + wpa_config_debug_dump_networks(config); + + RegCloseKey(hk); + + if (errors) { + wpa_config_free(config); + config = NULL; + } + + return config; +} + + +static int wpa_config_write_reg_dword(HKEY hk, const TCHAR *name, int val, + int def) +{ + LONG ret; + DWORD _val = val; + + if (val == def) { + RegDeleteValue(hk, name); + return 0; + } + + ret = RegSetValueEx(hk, name, 0, REG_DWORD, (LPBYTE) &_val, + sizeof(_val)); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_ERROR, "WINREG: Failed to set %s=%d: error %d", + name, val, (int) GetLastError()); + return -1; + } + + return 0; +} + + +static int wpa_config_write_reg_string(HKEY hk, const char *name, + const char *val) +{ + LONG ret; + TCHAR *_name, *_val; + + _name = wpa_strdup_tchar(name); + if (_name == NULL) + return -1; + + if (val == NULL) { + RegDeleteValue(hk, _name); + os_free(_name); + return 0; + } + + _val = wpa_strdup_tchar(val); + if (_val == NULL) { + os_free(_name); + return -1; + } + ret = RegSetValueEx(hk, _name, 0, REG_SZ, (BYTE *) _val, + (os_strlen(val) + 1) * sizeof(TCHAR)); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_ERROR, "WINREG: Failed to set %s='%s': " + "error %d", name, val, (int) GetLastError()); + os_free(_name); + os_free(_val); + return -1; + } + + os_free(_name); + os_free(_val); + return 0; +} + + +static int wpa_config_write_global(struct wpa_config *config, HKEY hk) +{ +#ifdef CONFIG_CTRL_IFACE + wpa_config_write_reg_string(hk, "ctrl_interface", + config->ctrl_interface); +#endif /* CONFIG_CTRL_IFACE */ + + wpa_config_write_reg_dword(hk, TEXT("eapol_version"), + config->eapol_version, + DEFAULT_EAPOL_VERSION); + wpa_config_write_reg_dword(hk, TEXT("ap_scan"), config->ap_scan, + DEFAULT_AP_SCAN); + wpa_config_write_reg_dword(hk, TEXT("fast_reauth"), + config->fast_reauth, DEFAULT_FAST_REAUTH); + wpa_config_write_reg_dword(hk, TEXT("dot11RSNAConfigPMKLifetime"), + config->dot11RSNAConfigPMKLifetime, 0); + wpa_config_write_reg_dword(hk, + TEXT("dot11RSNAConfigPMKReauthThreshold"), + config->dot11RSNAConfigPMKReauthThreshold, + 0); + wpa_config_write_reg_dword(hk, TEXT("dot11RSNAConfigSATimeout"), + config->dot11RSNAConfigSATimeout, 0); + wpa_config_write_reg_dword(hk, TEXT("update_config"), + config->update_config, + 0); +#ifdef CONFIG_WPS + if (!is_nil_uuid(config->uuid)) { + char buf[40]; + uuid_bin2str(config->uuid, buf, sizeof(buf)); + wpa_config_write_reg_string(hk, "uuid", buf); + } + wpa_config_write_reg_string(hk, "device_name", config->device_name); + wpa_config_write_reg_string(hk, "manufacturer", config->manufacturer); + wpa_config_write_reg_string(hk, "model_name", config->model_name); + wpa_config_write_reg_string(hk, "model_number", config->model_number); + wpa_config_write_reg_string(hk, "serial_number", + config->serial_number); + wpa_config_write_reg_string(hk, "device_type", config->device_type); + if (WPA_GET_BE32(config->os_version)) { + char vbuf[10]; + os_snprintf(vbuf, sizeof(vbuf), "%08x", + WPA_GET_BE32(config->os_version)); + wpa_config_write_reg_string(hk, "os_version", vbuf); + } + wpa_config_write_reg_dword(hk, TEXT("wps_cred_processing"), + config->wps_cred_processing, 0); +#endif /* CONFIG_WPS */ + + return 0; +} + + +static int wpa_config_delete_subkeys(HKEY hk, const TCHAR *key) +{ + HKEY nhk; + int i, errors = 0; + LONG ret; + + ret = RegOpenKeyEx(hk, key, 0, KEY_ENUMERATE_SUB_KEYS | DELETE, &nhk); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "WINREG: Could not open key '" TSTR + "' for subkey deletion: error 0x%x (%d)", key, + (unsigned int) ret, (int) GetLastError()); + return 0; + } + + for (i = 0; ; i++) { + TCHAR name[255]; + DWORD namelen; + + namelen = 255; + ret = RegEnumKeyEx(nhk, i, name, &namelen, NULL, NULL, NULL, + NULL); + + if (ret == ERROR_NO_MORE_ITEMS) + break; + + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "RegEnumKeyEx failed: 0x%x (%d)", + (unsigned int) ret, (int) GetLastError()); + break; + } + + if (namelen >= 255) + namelen = 255 - 1; + name[namelen] = TEXT('\0'); + + ret = RegDeleteKey(nhk, name); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "RegDeleteKey failed: 0x%x (%d)", + (unsigned int) ret, (int) GetLastError()); + errors++; + } + } + + RegCloseKey(nhk); + + return errors ? -1 : 0; +} + + +static void write_str(HKEY hk, const char *field, struct wpa_ssid *ssid) +{ + char *value = wpa_config_get(ssid, field); + if (value == NULL) + return; + wpa_config_write_reg_string(hk, field, value); + os_free(value); +} + + +static void write_int(HKEY hk, const char *field, int value, int def) +{ + char val[20]; + if (value == def) + return; + os_snprintf(val, sizeof(val), "%d", value); + wpa_config_write_reg_string(hk, field, val); +} + + +static void write_bssid(HKEY hk, struct wpa_ssid *ssid) +{ + char *value = wpa_config_get(ssid, "bssid"); + if (value == NULL) + return; + wpa_config_write_reg_string(hk, "bssid", value); + os_free(value); +} + + +static void write_psk(HKEY hk, struct wpa_ssid *ssid) +{ + char *value = wpa_config_get(ssid, "psk"); + if (value == NULL) + return; + wpa_config_write_reg_string(hk, "psk", value); + os_free(value); +} + + +static void write_proto(HKEY hk, struct wpa_ssid *ssid) +{ + char *value; + + if (ssid->proto == DEFAULT_PROTO) + return; + + value = wpa_config_get(ssid, "proto"); + if (value == NULL) + return; + if (value[0]) + wpa_config_write_reg_string(hk, "proto", value); + os_free(value); +} + + +static void write_key_mgmt(HKEY hk, struct wpa_ssid *ssid) +{ + char *value; + + if (ssid->key_mgmt == DEFAULT_KEY_MGMT) + return; + + value = wpa_config_get(ssid, "key_mgmt"); + if (value == NULL) + return; + if (value[0]) + wpa_config_write_reg_string(hk, "key_mgmt", value); + os_free(value); +} + + +static void write_pairwise(HKEY hk, struct wpa_ssid *ssid) +{ + char *value; + + if (ssid->pairwise_cipher == DEFAULT_PAIRWISE) + return; + + value = wpa_config_get(ssid, "pairwise"); + if (value == NULL) + return; + if (value[0]) + wpa_config_write_reg_string(hk, "pairwise", value); + os_free(value); +} + + +static void write_group(HKEY hk, struct wpa_ssid *ssid) +{ + char *value; + + if (ssid->group_cipher == DEFAULT_GROUP) + return; + + value = wpa_config_get(ssid, "group"); + if (value == NULL) + return; + if (value[0]) + wpa_config_write_reg_string(hk, "group", value); + os_free(value); +} + + +static void write_auth_alg(HKEY hk, struct wpa_ssid *ssid) +{ + char *value; + + if (ssid->auth_alg == 0) + return; + + value = wpa_config_get(ssid, "auth_alg"); + if (value == NULL) + return; + if (value[0]) + wpa_config_write_reg_string(hk, "auth_alg", value); + os_free(value); +} + + +#ifdef IEEE8021X_EAPOL +static void write_eap(HKEY hk, struct wpa_ssid *ssid) +{ + char *value; + + value = wpa_config_get(ssid, "eap"); + if (value == NULL) + return; + + if (value[0]) + wpa_config_write_reg_string(hk, "eap", value); + os_free(value); +} +#endif /* IEEE8021X_EAPOL */ + + +static void write_wep_key(HKEY hk, int idx, struct wpa_ssid *ssid) +{ + char field[20], *value; + + os_snprintf(field, sizeof(field), "wep_key%d", idx); + value = wpa_config_get(ssid, field); + if (value) { + wpa_config_write_reg_string(hk, field, value); + os_free(value); + } +} + + +static int wpa_config_write_network(HKEY hk, struct wpa_ssid *ssid, int id) +{ + int i, errors = 0; + HKEY nhk, netw; + LONG ret; + TCHAR name[5]; + + ret = RegOpenKeyEx(hk, TEXT("networks"), 0, KEY_CREATE_SUB_KEY, &nhk); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "WINREG: Could not open networks key " + "for subkey addition: error 0x%x (%d)", + (unsigned int) ret, (int) GetLastError()); + return 0; + } + +#ifdef UNICODE + wsprintf(name, L"%04d", id); +#else /* UNICODE */ + os_snprintf(name, sizeof(name), "%04d", id); +#endif /* UNICODE */ + ret = RegCreateKeyEx(nhk, name, 0, NULL, 0, KEY_WRITE, NULL, &netw, + NULL); + RegCloseKey(nhk); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "WINREG: Could not add network key '%s':" + " error 0x%x (%d)", + name, (unsigned int) ret, (int) GetLastError()); + return -1; + } + +#define STR(t) write_str(netw, #t, ssid) +#define INT(t) write_int(netw, #t, ssid->t, 0) +#define INTe(t) write_int(netw, #t, ssid->eap.t, 0) +#define INT_DEF(t, def) write_int(netw, #t, ssid->t, def) +#define INT_DEFe(t, def) write_int(netw, #t, ssid->eap.t, def) + + STR(ssid); + INT(scan_ssid); + write_bssid(netw, ssid); + write_psk(netw, ssid); + write_proto(netw, ssid); + write_key_mgmt(netw, ssid); + write_pairwise(netw, ssid); + write_group(netw, ssid); + write_auth_alg(netw, ssid); +#ifdef IEEE8021X_EAPOL + write_eap(netw, ssid); + STR(identity); + STR(anonymous_identity); + STR(password); + STR(ca_cert); + STR(ca_path); + STR(client_cert); + STR(private_key); + STR(private_key_passwd); + STR(dh_file); + STR(subject_match); + STR(altsubject_match); + STR(ca_cert2); + STR(ca_path2); + STR(client_cert2); + STR(private_key2); + STR(private_key2_passwd); + STR(dh_file2); + STR(subject_match2); + STR(altsubject_match2); + STR(phase1); + STR(phase2); + STR(pcsc); + STR(pin); + STR(engine_id); + STR(key_id); + STR(cert_id); + STR(ca_cert_id); + STR(key2_id); + STR(pin2); + STR(engine2_id); + STR(cert2_id); + STR(ca_cert2_id); + INTe(engine); + INTe(engine2); + INT_DEF(eapol_flags, DEFAULT_EAPOL_FLAGS); +#endif /* IEEE8021X_EAPOL */ + for (i = 0; i < 4; i++) + write_wep_key(netw, i, ssid); + INT(wep_tx_keyidx); + INT(priority); +#ifdef IEEE8021X_EAPOL + INT_DEF(eap_workaround, DEFAULT_EAP_WORKAROUND); + STR(pac_file); + INT_DEFe(fragment_size, DEFAULT_FRAGMENT_SIZE); +#endif /* IEEE8021X_EAPOL */ + INT(mode); + INT(proactive_key_caching); + INT(disabled); + INT(peerkey); +#ifdef CONFIG_IEEE80211W + INT(ieee80211w); +#endif /* CONFIG_IEEE80211W */ + STR(id_str); + +#undef STR +#undef INT +#undef INT_DEF + + RegCloseKey(netw); + + return errors ? -1 : 0; +} + + +static int wpa_config_write_blob(HKEY hk, struct wpa_config_blob *blob) +{ + HKEY bhk; + LONG ret; + TCHAR *name; + + ret = RegCreateKeyEx(hk, TEXT("blobs"), 0, NULL, 0, KEY_WRITE, NULL, + &bhk, NULL); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "WINREG: Could not add blobs key: " + "error 0x%x (%d)", + (unsigned int) ret, (int) GetLastError()); + return -1; + } + + name = wpa_strdup_tchar(blob->name); + ret = RegSetValueEx(bhk, name, 0, REG_BINARY, blob->data, + blob->len); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_ERROR, "WINREG: Failed to set blob %s': " + "error 0x%x (%d)", blob->name, (unsigned int) ret, + (int) GetLastError()); + RegCloseKey(bhk); + os_free(name); + return -1; + } + os_free(name); + + RegCloseKey(bhk); + + return 0; +} + + +int wpa_config_write(const char *name, struct wpa_config *config) +{ + TCHAR buf[256]; + HKEY hk; + LONG ret; + int errors = 0; + struct wpa_ssid *ssid; + struct wpa_config_blob *blob; + int id; + + wpa_printf(MSG_DEBUG, "Writing configuration file '%s'", name); + +#ifdef UNICODE + _snwprintf(buf, 256, WPA_KEY_PREFIX TEXT("\\configs\\%S"), name); +#else /* UNICODE */ + os_snprintf(buf, 256, WPA_KEY_PREFIX TEXT("\\configs\\%s"), name); +#endif /* UNICODE */ + + ret = RegOpenKeyEx(WPA_KEY_ROOT, buf, 0, KEY_SET_VALUE | DELETE, &hk); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_ERROR, "Could not open wpa_supplicant " + "configuration registry %s: error %d", buf, + (int) GetLastError()); + return -1; + } + + if (wpa_config_write_global(config, hk)) { + wpa_printf(MSG_ERROR, "Failed to write global configuration " + "data"); + errors++; + } + + wpa_config_delete_subkeys(hk, TEXT("networks")); + for (ssid = config->ssid, id = 0; ssid; ssid = ssid->next, id++) { + if (ssid->key_mgmt == WPA_KEY_MGMT_WPS) + continue; /* do not save temporary WPS networks */ + if (wpa_config_write_network(hk, ssid, id)) + errors++; + } + + RegDeleteKey(hk, TEXT("blobs")); + for (blob = config->blobs; blob; blob = blob->next) { + if (wpa_config_write_blob(hk, blob)) + errors++; + } + + RegCloseKey(hk); + + wpa_printf(MSG_DEBUG, "Configuration '%s' written %ssuccessfully", + name, errors ? "un" : ""); + return errors ? -1 : 0; +} diff --git a/wpa_supplicant/ctrl_iface_dbus.c b/wpa_supplicant/ctrl_iface_dbus.c index c4e329c..8e69f4d 100644 --- a/wpa_supplicant/ctrl_iface_dbus.c +++ b/wpa_supplicant/ctrl_iface_dbus.c @@ -540,6 +540,8 @@ static DBusHandlerResult wpas_iface_message_handler(DBusConnection *connection, wpa_s); else if (!strcmp(method, "state")) reply = wpas_dbus_iface_get_state(message, wpa_s); + else if (!strcmp(method, "scanning")) + reply = wpas_dbus_iface_get_scanning(message, wpa_s); else if (!strcmp(method, "setBlobs")) reply = wpas_dbus_iface_set_blobs(message, wpa_s); else if (!strcmp(method, "removeBlobs")) @@ -603,6 +605,9 @@ static DBusHandlerResult wpas_message_handler(DBusConnection *connection, } else if (!strcmp(method, "getInterface")) { reply = wpas_dbus_global_get_interface( message, ctrl_iface->global); + } else if (!strcmp(method, "setDebugParams")) { + reply = wpas_dbus_global_set_debugparams( + message, ctrl_iface->global); } } @@ -741,6 +746,58 @@ out: } +/** + * wpa_supplicant_dbus_notify_scanning - send scanning status + * @wpa_s: %wpa_supplicant network interface data + * Returns: 0 on success, -1 on failure + * + * Notify listeners of interface scanning state changes + */ +void wpa_supplicant_dbus_notify_scanning(struct wpa_supplicant *wpa_s) +{ + struct ctrl_iface_dbus_priv *iface = wpa_s->global->dbus_ctrl_iface; + DBusMessage *_signal; + const char *path; + dbus_bool_t scanning = wpa_s->scanning ? TRUE : FALSE; + + /* Do nothing if the control interface is not turned on */ + if (iface == NULL) + return; + + path = wpa_supplicant_get_dbus_path(wpa_s); + if (path == NULL) { + perror("wpa_supplicant_dbus_notify_scanning[dbus]: interface " + "didn't have a dbus path"); + wpa_printf(MSG_ERROR, + "%s[dbus]: interface didn't have a dbus path; " + "can't send scanning signal.", __FUNCTION__); + return; + } + _signal = dbus_message_new_signal(path, WPAS_DBUS_IFACE_INTERFACE, + "Scanning"); + if (_signal == NULL) { + perror("wpa_supplicant_dbus_notify_scanning[dbus]: couldn't " + "create dbus signal; likely out of memory"); + wpa_printf(MSG_ERROR, "%s[dbus]: dbus control interface: not " + "enough memory to send scan results signal.", + __FUNCTION__); + return; + } + + if (dbus_message_append_args(_signal, + DBUS_TYPE_BOOLEAN, &scanning, + DBUS_TYPE_INVALID)) { + dbus_connection_send(iface->con, _signal, NULL); + } else { + perror("wpa_supplicant_dbus_notify_scanning[dbus]: not enough " + "memory to construct signal."); + wpa_printf(MSG_ERROR, "%s[dbus]: not enough memory to " + "construct signal.", __FUNCTION__); + } + dbus_message_unref(_signal); +} + + #ifdef CONFIG_WPS void wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s, const struct wps_credential *cred) @@ -795,6 +852,11 @@ void wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s, out: dbus_message_unref(_signal); } +#else /* CONFIG_WPS */ +void wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s, + const struct wps_credential *cred) +{ +} #endif /* CONFIG_WPS */ diff --git a/wpa_supplicant/ctrl_iface_dbus.h b/wpa_supplicant/ctrl_iface_dbus.h index 68919de..059a373 100644 --- a/wpa_supplicant/ctrl_iface_dbus.h +++ b/wpa_supplicant/ctrl_iface_dbus.h @@ -83,6 +83,7 @@ struct ctrl_iface_dbus_priv * wpa_supplicant_dbus_ctrl_iface_init(struct wpa_global *global); void wpa_supplicant_dbus_ctrl_iface_deinit(struct ctrl_iface_dbus_priv *iface); void wpa_supplicant_dbus_notify_scan_results(struct wpa_supplicant *wpa_s); +void wpa_supplicant_dbus_notify_scanning(struct wpa_supplicant *wpa_s); void wpa_supplicant_dbus_notify_state_change(struct wpa_supplicant *wpa_s, wpa_states new_state, wpa_states old_state); @@ -127,6 +128,11 @@ wpa_supplicant_dbus_notify_scan_results(struct wpa_supplicant *wpa_s) } static inline void +wpa_supplicant_dbus_notify_scanning(struct wpa_supplicant *wpa_s) +{ +} + +static inline void wpa_supplicant_dbus_notify_state_change(struct wpa_supplicant *wpa_s, wpa_states new_state, wpa_states old_state) diff --git a/wpa_supplicant/ctrl_iface_dbus_handlers.c b/wpa_supplicant/ctrl_iface_dbus_handlers.c index 3c29804..d3250d3 100644 --- a/wpa_supplicant/ctrl_iface_dbus_handlers.c +++ b/wpa_supplicant/ctrl_iface_dbus_handlers.c @@ -24,7 +24,11 @@ #include "ieee802_11_defs.h" #include "wpas_glue.h" #include "eapol_supp/eapol_supp_sm.h" +#include "wpa.h" +extern int wpa_debug_level; +extern int wpa_debug_show_keys; +extern int wpa_debug_timestamp; /** * wpas_dbus_new_invalid_opts_error - Return a new invalid options error message @@ -277,6 +281,51 @@ out: return reply; } +/** + * wpas_dbus_global_set_debugparams- Set the debug params + * @message: Pointer to incoming dbus message + * @global: %wpa_supplicant global data structure + * Returns: a dbus message containing a UINT32 indicating success (1) or + * failure (0), or returns a dbus error message with more information + * + * Handler function for "setDebugParams" method call. Handles requests + * by dbus clients for the object path of an specific network interface. + */ +DBusMessage * wpas_dbus_global_set_debugparams(DBusMessage *message, + struct wpa_global *global) +{ + DBusMessage *reply = NULL; + int debug_level; + dbus_bool_t debug_timestamp; + dbus_bool_t debug_show_keys; + + if (!dbus_message_get_args(message, NULL, + DBUS_TYPE_INT32, &debug_level, + DBUS_TYPE_BOOLEAN, &debug_timestamp, + DBUS_TYPE_BOOLEAN, &debug_show_keys, + DBUS_TYPE_INVALID)) { + reply = wpas_dbus_new_invalid_opts_error(message, NULL); + goto out; + } + + /* check for allowed debuglevels */ + if (debug_level != MSG_MSGDUMP && + debug_level != MSG_DEBUG && + debug_level != MSG_INFO && + debug_level != MSG_WARNING && + debug_level != MSG_ERROR) { + reply = wpas_dbus_new_invalid_opts_error(message, NULL); + goto out; + } + + wpa_debug_level = debug_level; + wpa_debug_timestamp = debug_timestamp ? 1 : 0; + wpa_debug_show_keys = debug_show_keys ? 1 : 0; + reply = wpas_dbus_new_success_reply(message); + +out: + return reply; +} /** * wpas_dbus_iface_scan - Request a wireless scan on an interface @@ -1246,8 +1295,11 @@ DBusMessage * wpas_dbus_iface_set_smartcard_modules( wpa_s->conf->pkcs11_module_path = pkcs11_module_path; #endif /* EAP_TLS_OPENSSL */ + wpa_sm_set_eapol(wpa_s->wpa, NULL); eapol_sm_deinit(wpa_s->eapol); + wpa_s->eapol = NULL; wpa_supplicant_init_eapol(wpa_s); + wpa_sm_set_eapol(wpa_s->wpa, wpa_s->eapol); return wpas_dbus_new_success_reply(message); @@ -1285,6 +1337,35 @@ DBusMessage * wpas_dbus_iface_get_state(DBusMessage *message, /** + * wpas_dbus_iface_get_scanning - Get interface scanning state + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: A dbus message containing whether the interface is scanning + * + * Handler function for "scanning" method call. + */ +DBusMessage * wpas_dbus_iface_get_scanning(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessage *reply = NULL; + dbus_bool_t scanning = wpa_s->scanning ? TRUE : FALSE; + + reply = dbus_message_new_method_return(message); + if (reply != NULL) { + dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &scanning, + DBUS_TYPE_INVALID); + } else { + perror("wpas_dbus_iface_get_scanning[dbus]: out of " + "memory."); + wpa_printf(MSG_ERROR, "dbus control interface: not enough " + "memory to return scanning state."); + } + + return reply; +} + + +/** * wpas_dbus_iface_set_blobs - Store named binary blobs (ie, for certificates) * @message: Pointer to incoming dbus message * @wpa_s: %wpa_supplicant data structure diff --git a/wpa_supplicant/ctrl_iface_dbus_handlers.h b/wpa_supplicant/ctrl_iface_dbus_handlers.h index 9660f95..376d835 100644 --- a/wpa_supplicant/ctrl_iface_dbus_handlers.h +++ b/wpa_supplicant/ctrl_iface_dbus_handlers.h @@ -28,6 +28,9 @@ DBusMessage * wpas_dbus_global_remove_interface(DBusMessage *message, DBusMessage * wpas_dbus_global_get_interface(DBusMessage *message, struct wpa_global *global); +DBusMessage * wpas_dbus_global_set_debugparams(DBusMessage *message, + struct wpa_global *global); + DBusMessage * wpas_dbus_iface_scan(DBusMessage *message, struct wpa_supplicant *wpa_s); @@ -74,6 +77,9 @@ DBusMessage * wpas_dbus_iface_set_smartcard_modules( DBusMessage * wpas_dbus_iface_get_state(DBusMessage *message, struct wpa_supplicant *wpa_s); +DBusMessage * wpas_dbus_iface_get_scanning(DBusMessage *message, + struct wpa_supplicant *wpa_s); + DBusMessage * wpas_dbus_iface_set_blobs(DBusMessage *message, struct wpa_supplicant *wpa_s); diff --git a/wpa_supplicant/ctrl_iface_named_pipe.c b/wpa_supplicant/ctrl_iface_named_pipe.c new file mode 100644 index 0000000..e8b53b1 --- /dev/null +++ b/wpa_supplicant/ctrl_iface_named_pipe.c @@ -0,0 +1,835 @@ +/* + * WPA Supplicant / Windows Named Pipe -based control interface + * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "config.h" +#include "eapol_supp/eapol_supp_sm.h" +#include "wpa_supplicant_i.h" +#include "ctrl_iface.h" +#include "wpa_ctrl.h" + +#ifdef __MINGW32_VERSION +/* mingw-w32api v3.1 does not yet include sddl.h, so define needed parts here + */ +#define SDDL_REVISION_1 1 +BOOL WINAPI ConvertStringSecurityDescriptorToSecurityDescriptorA( + LPCSTR, DWORD, PSECURITY_DESCRIPTOR *, PULONG); +BOOL WINAPI ConvertStringSecurityDescriptorToSecurityDescriptorW( + LPCWSTR, DWORD, PSECURITY_DESCRIPTOR *, PULONG); +#ifdef UNICODE +#define ConvertStringSecurityDescriptorToSecurityDescriptor \ +ConvertStringSecurityDescriptorToSecurityDescriptorW +#else +#define ConvertStringSecurityDescriptorToSecurityDescriptor \ +ConvertStringSecurityDescriptorToSecurityDescriptorA +#endif +#else /* __MINGW32_VERSION */ +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0500 +#endif +#include <sddl.h> +#endif /* __MINGW32_VERSION */ + +#ifndef WPA_SUPPLICANT_NAMED_PIPE +#define WPA_SUPPLICANT_NAMED_PIPE "WpaSupplicant" +#endif +#define NAMED_PIPE_PREFIX TEXT("\\\\.\\pipe\\") TEXT(WPA_SUPPLICANT_NAMED_PIPE) + +/* Per-interface ctrl_iface */ + +#define REQUEST_BUFSIZE 256 +#define REPLY_BUFSIZE 4096 + +struct ctrl_iface_priv; + +/** + * struct wpa_ctrl_dst - Internal data structure of control interface clients + * + * This structure is used to store information about registered control + * interface monitors into struct wpa_supplicant. This data is private to + * ctrl_iface_named_pipe.c and should not be touched directly from other files. + */ +struct wpa_ctrl_dst { + /* Note: OVERLAPPED must be the first member of struct wpa_ctrl_dst */ + OVERLAPPED overlap; + struct wpa_ctrl_dst *next, *prev; + struct ctrl_iface_priv *priv; + HANDLE pipe; + int attached; + int debug_level; + int errors; + char req_buf[REQUEST_BUFSIZE]; + char *rsp_buf; + int used; +}; + + +struct ctrl_iface_priv { + struct wpa_supplicant *wpa_s; + struct wpa_ctrl_dst *ctrl_dst; + SECURITY_ATTRIBUTES attr; + int sec_attr_set; +}; + + +static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, + int level, const char *buf, + size_t len); + +static void ctrl_close_pipe(struct wpa_ctrl_dst *dst); +static void wpa_supplicant_ctrl_iface_receive(void *, void *); +static VOID WINAPI ctrl_iface_read_completed(DWORD err, DWORD bytes, + LPOVERLAPPED overlap); + +struct wpa_global_dst; +static void global_close_pipe(struct wpa_global_dst *dst); +static void wpa_supplicant_global_iface_receive(void *eloop_data, + void *user_ctx); +static VOID WINAPI global_iface_read_completed(DWORD err, DWORD bytes, + LPOVERLAPPED overlap); + + +static int ctrl_broken_pipe(HANDLE pipe, int used) +{ + DWORD err; + + if (PeekNamedPipe(pipe, NULL, 0, NULL, NULL, NULL)) + return 0; + + err = GetLastError(); + if (err == ERROR_BROKEN_PIPE || (err == ERROR_BAD_PIPE && used)) + return 1; + return 0; +} + + +static void ctrl_flush_broken_pipes(struct ctrl_iface_priv *priv) +{ + struct wpa_ctrl_dst *dst, *next; + + dst = priv->ctrl_dst; + + while (dst) { + next = dst->next; + if (ctrl_broken_pipe(dst->pipe, dst->used)) { + wpa_printf(MSG_DEBUG, "CTRL: closing broken pipe %p", + dst); + ctrl_close_pipe(dst); + } + dst = next; + } +} + + +static int ctrl_open_pipe(struct ctrl_iface_priv *priv) +{ + struct wpa_ctrl_dst *dst; + DWORD err; + TCHAR name[256]; + + dst = os_zalloc(sizeof(*dst)); + if (dst == NULL) + return -1; + wpa_printf(MSG_DEBUG, "CTRL: Open pipe %p", dst); + + dst->priv = priv; + dst->debug_level = MSG_INFO; + dst->pipe = INVALID_HANDLE_VALUE; + + dst->overlap.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); + if (dst->overlap.hEvent == NULL) { + wpa_printf(MSG_ERROR, "CTRL: CreateEvent failed: %d", + (int) GetLastError()); + goto fail; + } + + eloop_register_event(dst->overlap.hEvent, + sizeof(dst->overlap.hEvent), + wpa_supplicant_ctrl_iface_receive, dst, NULL); + +#ifdef UNICODE + _snwprintf(name, 256, NAMED_PIPE_PREFIX TEXT("-%S"), + priv->wpa_s->ifname); +#else /* UNICODE */ + os_snprintf(name, 256, NAMED_PIPE_PREFIX "-%s", + priv->wpa_s->ifname); +#endif /* UNICODE */ + + /* TODO: add support for configuring access list for the pipe */ + dst->pipe = CreateNamedPipe(name, + PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_MESSAGE | + PIPE_READMODE_MESSAGE | + PIPE_WAIT, + 15, REPLY_BUFSIZE, REQUEST_BUFSIZE, + 1000, + priv->sec_attr_set ? &priv->attr : NULL); + if (dst->pipe == INVALID_HANDLE_VALUE) { + wpa_printf(MSG_ERROR, "CTRL: CreateNamedPipe failed: %d", + (int) GetLastError()); + goto fail; + } + + if (ConnectNamedPipe(dst->pipe, &dst->overlap)) { + wpa_printf(MSG_ERROR, "CTRL: ConnectNamedPipe failed: %d", + (int) GetLastError()); + CloseHandle(dst->pipe); + os_free(dst); + return -1; + } + + err = GetLastError(); + switch (err) { + case ERROR_IO_PENDING: + wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe: connection in " + "progress"); + break; + case ERROR_PIPE_CONNECTED: + wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe: already " + "connected"); + if (SetEvent(dst->overlap.hEvent)) + break; + /* fall through */ + default: + wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe error: %d", + (int) err); + CloseHandle(dst->pipe); + os_free(dst); + return -1; + } + + dst->next = priv->ctrl_dst; + if (dst->next) + dst->next->prev = dst; + priv->ctrl_dst = dst; + + return 0; + +fail: + ctrl_close_pipe(dst); + return -1; +} + + +static void ctrl_close_pipe(struct wpa_ctrl_dst *dst) +{ + wpa_printf(MSG_DEBUG, "CTRL: close pipe %p", dst); + + if (dst->overlap.hEvent) { + eloop_unregister_event(dst->overlap.hEvent, + sizeof(dst->overlap.hEvent)); + CloseHandle(dst->overlap.hEvent); + } + + if (dst->pipe != INVALID_HANDLE_VALUE) { + /* + * Could use FlushFileBuffers() here to guarantee that all data + * gets delivered to the client, but that can block, so let's + * not do this for now. + * FlushFileBuffers(dst->pipe); + */ + CloseHandle(dst->pipe); + } + + if (dst->prev) + dst->prev->next = dst->next; + else + dst->priv->ctrl_dst = dst->next; + if (dst->next) + dst->next->prev = dst->prev; + + os_free(dst->rsp_buf); + os_free(dst); +} + + +static VOID WINAPI ctrl_iface_write_completed(DWORD err, DWORD bytes, + LPOVERLAPPED overlap) +{ + struct wpa_ctrl_dst *dst = (struct wpa_ctrl_dst *) overlap; + wpa_printf(MSG_DEBUG, "CTRL: Overlapped write completed: dst=%p " + "err=%d bytes=%d", dst, (int) err, (int) bytes); + if (err) { + ctrl_close_pipe(dst); + return; + } + + os_free(dst->rsp_buf); + dst->rsp_buf = NULL; + + if (!ReadFileEx(dst->pipe, dst->req_buf, sizeof(dst->req_buf), + &dst->overlap, ctrl_iface_read_completed)) { + wpa_printf(MSG_DEBUG, "CTRL: ReadFileEx failed: %d", + (int) GetLastError()); + ctrl_close_pipe(dst); + return; + } + wpa_printf(MSG_DEBUG, "CTRL: Overlapped read started for %p", dst); +} + + +static void wpa_supplicant_ctrl_iface_rx(struct wpa_ctrl_dst *dst, size_t len) +{ + struct wpa_supplicant *wpa_s = dst->priv->wpa_s; + char *reply = NULL, *send_buf; + size_t reply_len = 0, send_len; + int new_attached = 0; + char *buf = dst->req_buf; + + dst->used = 1; + if (len >= REQUEST_BUFSIZE) + len = REQUEST_BUFSIZE - 1; + buf[len] = '\0'; + + if (os_strcmp(buf, "ATTACH") == 0) { + dst->attached = 1; + wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor attached"); + new_attached = 1; + reply_len = 2; + } else if (os_strcmp(buf, "DETACH") == 0) { + dst->attached = 0; + wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached"); + reply_len = 2; + } else if (os_strncmp(buf, "LEVEL ", 6) == 0) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", buf + 6); + dst->debug_level = atoi(buf + 6); + reply_len = 2; + } else { + reply = wpa_supplicant_ctrl_iface_process(wpa_s, buf, + &reply_len); + } + + if (reply) { + send_buf = reply; + send_len = reply_len; + } else if (reply_len == 2) { + send_buf = "OK\n"; + send_len = 3; + } else { + send_buf = "FAIL\n"; + send_len = 5; + } + + os_free(dst->rsp_buf); + dst->rsp_buf = os_malloc(send_len); + if (dst->rsp_buf == NULL) { + ctrl_close_pipe(dst); + os_free(reply); + return; + } + os_memcpy(dst->rsp_buf, send_buf, send_len); + os_free(reply); + + if (!WriteFileEx(dst->pipe, dst->rsp_buf, send_len, &dst->overlap, + ctrl_iface_write_completed)) { + wpa_printf(MSG_DEBUG, "CTRL: WriteFileEx failed: %d", + (int) GetLastError()); + ctrl_close_pipe(dst); + } else { + wpa_printf(MSG_DEBUG, "CTRL: Overlapped write started for %p", + dst); + } + + if (new_attached) + eapol_sm_notify_ctrl_attached(wpa_s->eapol); +} + + +static VOID WINAPI ctrl_iface_read_completed(DWORD err, DWORD bytes, + LPOVERLAPPED overlap) +{ + struct wpa_ctrl_dst *dst = (struct wpa_ctrl_dst *) overlap; + wpa_printf(MSG_DEBUG, "CTRL: Overlapped read completed: dst=%p err=%d " + "bytes=%d", dst, (int) err, (int) bytes); + if (err == 0 && bytes > 0) + wpa_supplicant_ctrl_iface_rx(dst, bytes); +} + + +static void wpa_supplicant_ctrl_iface_receive(void *eloop_data, void *user_ctx) +{ + struct wpa_ctrl_dst *dst = eloop_data; + struct ctrl_iface_priv *priv = dst->priv; + DWORD bytes; + + wpa_printf(MSG_DEBUG, "CTRL: wpa_supplicant_ctrl_iface_receive"); + ResetEvent(dst->overlap.hEvent); + + if (!GetOverlappedResult(dst->pipe, &dst->overlap, &bytes, FALSE)) { + wpa_printf(MSG_DEBUG, "CTRL: GetOverlappedResult failed: %d", + (int) GetLastError()); + return; + } + wpa_printf(MSG_DEBUG, "CTRL: GetOverlappedResult: New client " + "connected"); + + /* Open a new named pipe for the next client. */ + ctrl_open_pipe(priv); + + /* Use write completion function to start reading a command */ + ctrl_iface_write_completed(0, 0, &dst->overlap); + + ctrl_flush_broken_pipes(priv); +} + + +static int ctrl_iface_parse(struct ctrl_iface_priv *priv, const char *params) +{ + const char *sddl = NULL; + TCHAR *t_sddl; + + if (os_strncmp(params, "SDDL=", 5) == 0) + sddl = params + 5; + if (!sddl) { + sddl = os_strstr(params, " SDDL="); + if (sddl) + sddl += 6; + } + + if (!sddl) + return 0; + + wpa_printf(MSG_DEBUG, "CTRL: SDDL='%s'", sddl); + os_memset(&priv->attr, 0, sizeof(priv->attr)); + priv->attr.nLength = sizeof(priv->attr); + priv->attr.bInheritHandle = FALSE; + t_sddl = wpa_strdup_tchar(sddl); + if (t_sddl == NULL) + return -1; + if (!ConvertStringSecurityDescriptorToSecurityDescriptor( + t_sddl, SDDL_REVISION_1, + (PSECURITY_DESCRIPTOR *) (void *) + &priv->attr.lpSecurityDescriptor, + NULL)) { + os_free(t_sddl); + wpa_printf(MSG_ERROR, "CTRL: SDDL='%s' - could not convert to " + "security descriptor: %d", + sddl, (int) GetLastError()); + return -1; + } + os_free(t_sddl); + + priv->sec_attr_set = 1; + + return 0; +} + + +static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level, + const char *txt, size_t len) +{ + struct wpa_supplicant *wpa_s = ctx; + if (wpa_s == NULL || wpa_s->ctrl_iface == NULL) + return; + wpa_supplicant_ctrl_iface_send(wpa_s->ctrl_iface, level, txt, len); +} + + +struct ctrl_iface_priv * +wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) +{ + struct ctrl_iface_priv *priv; + + priv = os_zalloc(sizeof(*priv)); + if (priv == NULL) + return NULL; + priv->wpa_s = wpa_s; + + if (wpa_s->conf->ctrl_interface == NULL) + return priv; + + if (ctrl_iface_parse(priv, wpa_s->conf->ctrl_interface) < 0) { + os_free(priv); + return NULL; + } + + if (ctrl_open_pipe(priv) < 0) { + os_free(priv); + return NULL; + } + + wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb); + + return priv; +} + + +void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv) +{ + while (priv->ctrl_dst) + ctrl_close_pipe(priv->ctrl_dst); + if (priv->sec_attr_set) + LocalFree(priv->attr.lpSecurityDescriptor); + os_free(priv); +} + + +static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, + int level, const char *buf, + size_t len) +{ + struct wpa_ctrl_dst *dst, *next; + char levelstr[10]; + int idx; + char *sbuf; + int llen; + DWORD written; + + dst = priv->ctrl_dst; + if (dst == NULL) + return; + + os_snprintf(levelstr, sizeof(levelstr), "<%d>", level); + + llen = os_strlen(levelstr); + sbuf = os_malloc(llen + len); + if (sbuf == NULL) + return; + + os_memcpy(sbuf, levelstr, llen); + os_memcpy(sbuf + llen, buf, len); + + idx = 0; + while (dst) { + next = dst->next; + if (dst->attached && level >= dst->debug_level) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor send %p", + dst); + if (!WriteFile(dst->pipe, sbuf, llen + len, &written, + NULL)) { + wpa_printf(MSG_DEBUG, "CTRL: WriteFile to dst " + "%p failed: %d", + dst, (int) GetLastError()); + dst->errors++; + if (dst->errors > 10) + ctrl_close_pipe(dst); + } else + dst->errors = 0; + } + idx++; + dst = next; + } + os_free(sbuf); +} + + +void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv) +{ + wpa_printf(MSG_DEBUG, "CTRL_IFACE - %s - wait for monitor", + priv->wpa_s->ifname); + if (priv->ctrl_dst == NULL) + return; + WaitForSingleObject(priv->ctrl_dst->pipe, INFINITE); +} + + +/* Global ctrl_iface */ + +struct ctrl_iface_global_priv; + +struct wpa_global_dst { + /* Note: OVERLAPPED must be the first member of struct wpa_global_dst + */ + OVERLAPPED overlap; + struct wpa_global_dst *next, *prev; + struct ctrl_iface_global_priv *priv; + HANDLE pipe; + char req_buf[REQUEST_BUFSIZE]; + char *rsp_buf; + int used; +}; + +struct ctrl_iface_global_priv { + struct wpa_global *global; + struct wpa_global_dst *ctrl_dst; +}; + + +static void global_flush_broken_pipes(struct ctrl_iface_global_priv *priv) +{ + struct wpa_global_dst *dst, *next; + + dst = priv->ctrl_dst; + + while (dst) { + next = dst->next; + if (ctrl_broken_pipe(dst->pipe, dst->used)) { + wpa_printf(MSG_DEBUG, "CTRL: closing broken pipe %p", + dst); + global_close_pipe(dst); + } + dst = next; + } +} + + +static int global_open_pipe(struct ctrl_iface_global_priv *priv) +{ + struct wpa_global_dst *dst; + DWORD err; + + dst = os_zalloc(sizeof(*dst)); + if (dst == NULL) + return -1; + wpa_printf(MSG_DEBUG, "CTRL: Open pipe %p", dst); + + dst->priv = priv; + dst->pipe = INVALID_HANDLE_VALUE; + + dst->overlap.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); + if (dst->overlap.hEvent == NULL) { + wpa_printf(MSG_ERROR, "CTRL: CreateEvent failed: %d", + (int) GetLastError()); + goto fail; + } + + eloop_register_event(dst->overlap.hEvent, + sizeof(dst->overlap.hEvent), + wpa_supplicant_global_iface_receive, dst, NULL); + + /* TODO: add support for configuring access list for the pipe */ + dst->pipe = CreateNamedPipe(NAMED_PIPE_PREFIX, + PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_MESSAGE | + PIPE_READMODE_MESSAGE | + PIPE_WAIT, + 10, REPLY_BUFSIZE, REQUEST_BUFSIZE, + 1000, NULL); + if (dst->pipe == INVALID_HANDLE_VALUE) { + wpa_printf(MSG_ERROR, "CTRL: CreateNamedPipe failed: %d", + (int) GetLastError()); + goto fail; + } + + if (ConnectNamedPipe(dst->pipe, &dst->overlap)) { + wpa_printf(MSG_ERROR, "CTRL: ConnectNamedPipe failed: %d", + (int) GetLastError()); + CloseHandle(dst->pipe); + os_free(dst); + return -1; + } + + err = GetLastError(); + switch (err) { + case ERROR_IO_PENDING: + wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe: connection in " + "progress"); + break; + case ERROR_PIPE_CONNECTED: + wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe: already " + "connected"); + if (SetEvent(dst->overlap.hEvent)) + break; + /* fall through */ + default: + wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe error: %d", + (int) err); + CloseHandle(dst->pipe); + os_free(dst); + return -1; + } + + dst->next = priv->ctrl_dst; + if (dst->next) + dst->next->prev = dst; + priv->ctrl_dst = dst; + + return 0; + +fail: + global_close_pipe(dst); + return -1; +} + + +static void global_close_pipe(struct wpa_global_dst *dst) +{ + wpa_printf(MSG_DEBUG, "CTRL: close pipe %p", dst); + + if (dst->overlap.hEvent) { + eloop_unregister_event(dst->overlap.hEvent, + sizeof(dst->overlap.hEvent)); + CloseHandle(dst->overlap.hEvent); + } + + if (dst->pipe != INVALID_HANDLE_VALUE) { + /* + * Could use FlushFileBuffers() here to guarantee that all data + * gets delivered to the client, but that can block, so let's + * not do this for now. + * FlushFileBuffers(dst->pipe); + */ + CloseHandle(dst->pipe); + } + + if (dst->prev) + dst->prev->next = dst->next; + else + dst->priv->ctrl_dst = dst->next; + if (dst->next) + dst->next->prev = dst->prev; + + os_free(dst->rsp_buf); + os_free(dst); +} + + +static VOID WINAPI global_iface_write_completed(DWORD err, DWORD bytes, + LPOVERLAPPED overlap) +{ + struct wpa_global_dst *dst = (struct wpa_global_dst *) overlap; + wpa_printf(MSG_DEBUG, "CTRL: Overlapped write completed: dst=%p " + "err=%d bytes=%d", dst, (int) err, (int) bytes); + if (err) { + global_close_pipe(dst); + return; + } + + os_free(dst->rsp_buf); + dst->rsp_buf = NULL; + + if (!ReadFileEx(dst->pipe, dst->req_buf, sizeof(dst->req_buf), + &dst->overlap, global_iface_read_completed)) { + wpa_printf(MSG_DEBUG, "CTRL: ReadFileEx failed: %d", + (int) GetLastError()); + global_close_pipe(dst); + /* FIX: if this was the pipe waiting for new global + * connections, at this point there are no open global pipes.. + * Should try to open a new pipe.. */ + return; + } + wpa_printf(MSG_DEBUG, "CTRL: Overlapped read started for %p", dst); +} + + +static void wpa_supplicant_global_iface_rx(struct wpa_global_dst *dst, + size_t len) +{ + struct wpa_global *global = dst->priv->global; + char *reply = NULL, *send_buf; + size_t reply_len = 0, send_len; + char *buf = dst->req_buf; + + dst->used = 1; + if (len >= REQUEST_BUFSIZE) + len = REQUEST_BUFSIZE - 1; + buf[len] = '\0'; + + reply = wpa_supplicant_global_ctrl_iface_process(global, buf, + &reply_len); + if (reply) { + send_buf = reply; + send_len = reply_len; + } else if (reply_len) { + send_buf = "FAIL\n"; + send_len = 5; + } else { + os_free(dst->rsp_buf); + dst->rsp_buf = NULL; + return; + } + + os_free(dst->rsp_buf); + dst->rsp_buf = os_malloc(send_len); + if (dst->rsp_buf == NULL) { + global_close_pipe(dst); + os_free(reply); + return; + } + os_memcpy(dst->rsp_buf, send_buf, send_len); + os_free(reply); + + if (!WriteFileEx(dst->pipe, dst->rsp_buf, send_len, &dst->overlap, + global_iface_write_completed)) { + wpa_printf(MSG_DEBUG, "CTRL: WriteFileEx failed: %d", + (int) GetLastError()); + global_close_pipe(dst); + } else { + wpa_printf(MSG_DEBUG, "CTRL: Overlapped write started for %p", + dst); + } +} + + +static VOID WINAPI global_iface_read_completed(DWORD err, DWORD bytes, + LPOVERLAPPED overlap) +{ + struct wpa_global_dst *dst = (struct wpa_global_dst *) overlap; + wpa_printf(MSG_DEBUG, "CTRL: Overlapped read completed: dst=%p err=%d " + "bytes=%d", dst, (int) err, (int) bytes); + if (err == 0 && bytes > 0) + wpa_supplicant_global_iface_rx(dst, bytes); +} + + +static void wpa_supplicant_global_iface_receive(void *eloop_data, + void *user_ctx) +{ + struct wpa_global_dst *dst = eloop_data; + struct ctrl_iface_global_priv *priv = dst->priv; + DWORD bytes; + + wpa_printf(MSG_DEBUG, "CTRL: wpa_supplicant_global_iface_receive"); + ResetEvent(dst->overlap.hEvent); + + if (!GetOverlappedResult(dst->pipe, &dst->overlap, &bytes, FALSE)) { + wpa_printf(MSG_DEBUG, "CTRL: GetOverlappedResult failed: %d", + (int) GetLastError()); + return; + } + wpa_printf(MSG_DEBUG, "CTRL: GetOverlappedResult: New client " + "connected"); + + /* Open a new named pipe for the next client. */ + if (global_open_pipe(priv) < 0) { + wpa_printf(MSG_DEBUG, "CTRL: global_open_pipe failed"); + return; + } + + /* Use write completion function to start reading a command */ + global_iface_write_completed(0, 0, &dst->overlap); + + global_flush_broken_pipes(priv); +} + + +struct ctrl_iface_global_priv * +wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global) +{ + struct ctrl_iface_global_priv *priv; + + priv = os_zalloc(sizeof(*priv)); + if (priv == NULL) + return NULL; + priv->global = global; + + if (global_open_pipe(priv) < 0) { + os_free(priv); + return NULL; + } + + return priv; +} + + +void +wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv) +{ + while (priv->ctrl_dst) + global_close_pipe(priv->ctrl_dst); + os_free(priv); +} diff --git a/wpa_supplicant/ctrl_iface_unix.c b/wpa_supplicant/ctrl_iface_unix.c index bf6328e..2a62713 100644 --- a/wpa_supplicant/ctrl_iface_unix.c +++ b/wpa_supplicant/ctrl_iface_unix.c @@ -16,6 +16,7 @@ #include <sys/un.h> #include <sys/stat.h> #include <grp.h> +#include <stddef.h> #include "common.h" #include "eloop.h" @@ -69,7 +70,8 @@ static int wpa_supplicant_ctrl_iface_attach(struct ctrl_iface_priv *priv, dst->next = priv->ctrl_dst; priv->ctrl_dst = dst; wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor attached", - (u8 *) from->sun_path, fromlen - sizeof(from->sun_family)); + (u8 *) from->sun_path, + fromlen - offsetof(struct sockaddr_un, sun_path)); return 0; } @@ -84,7 +86,8 @@ static int wpa_supplicant_ctrl_iface_detach(struct ctrl_iface_priv *priv, while (dst) { if (fromlen == dst->addrlen && os_memcmp(from->sun_path, dst->addr.sun_path, - fromlen - sizeof(from->sun_family)) == 0) { + fromlen - offsetof(struct sockaddr_un, sun_path)) + == 0) { if (prev == NULL) priv->ctrl_dst = dst->next; else @@ -92,7 +95,8 @@ static int wpa_supplicant_ctrl_iface_detach(struct ctrl_iface_priv *priv, os_free(dst); wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor detached", (u8 *) from->sun_path, - fromlen - sizeof(from->sun_family)); + fromlen - + offsetof(struct sockaddr_un, sun_path)); return 0; } prev = dst; @@ -115,10 +119,12 @@ static int wpa_supplicant_ctrl_iface_level(struct ctrl_iface_priv *priv, while (dst) { if (fromlen == dst->addrlen && os_memcmp(from->sun_path, dst->addr.sun_path, - fromlen - sizeof(from->sun_family)) == 0) { + fromlen - offsetof(struct sockaddr_un, sun_path)) + == 0) { wpa_hexdump(MSG_DEBUG, "CTRL_IFACE changed monitor " "level", (u8 *) from->sun_path, - fromlen - sizeof(from->sun_family)); + fromlen - + offsetof(struct sockaddr_un, sun_path)); dst->debug_level = atoi(level); return 0; } @@ -326,6 +332,14 @@ wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) goto fail; } + /* Make sure the group can enter and read the directory */ + if (gid_set && + chmod(dir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP) < 0) { + wpa_printf(MSG_ERROR, "CTRL: chmod[ctrl_interface]: %s", + strerror(errno)); + goto fail; + } + if (os_strlen(dir) + 1 + os_strlen(wpa_s->ifname) >= sizeof(addr.sun_path)) { wpa_printf(MSG_ERROR, "ctrl_iface path limit exceeded"); @@ -339,6 +353,9 @@ wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) } os_memset(&addr, 0, sizeof(addr)); +#ifdef __FreeBSD__ + addr.sun_len = sizeof(addr); +#endif /* __FreeBSD__ */ addr.sun_family = AF_UNIX; fname = wpa_supplicant_ctrl_iface_path(wpa_s); if (fname == NULL) @@ -510,13 +527,16 @@ static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, if (level >= dst->debug_level) { wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor send", (u8 *) dst->addr.sun_path, dst->addrlen - - sizeof(dst->addr.sun_family)); + offsetof(struct sockaddr_un, sun_path)); msg.msg_name = (void *) &dst->addr; msg.msg_namelen = dst->addrlen; if (sendmsg(priv->sock, &msg, 0) < 0) { - perror("sendmsg(CTRL_IFACE monitor)"); + int _errno = errno; + wpa_printf(MSG_INFO, "CTRL_IFACE monitor[%d]: " + "%d - %s", + idx, errno, strerror(errno)); dst->errors++; - if (dst->errors > 10) { + if (dst->errors > 10 || _errno == ENOENT) { wpa_supplicant_ctrl_iface_detach( priv, &dst->addr, dst->addrlen); @@ -637,6 +657,9 @@ wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global) } os_memset(&addr, 0, sizeof(addr)); +#ifdef __FreeBSD__ + addr.sun_len = sizeof(addr); +#endif /* __FreeBSD__ */ addr.sun_family = AF_UNIX; os_strlcpy(addr.sun_path, global->params.ctrl_interface, sizeof(addr.sun_path)); diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig index dd9460d..4d0aa8a 100644 --- a/wpa_supplicant/defconfig +++ b/wpa_supplicant/defconfig @@ -50,6 +50,7 @@ CONFIG_DRIVER_HOSTAP=y #CFLAGS += -I../../include/wireless # Driver interface for madwifi driver +# Deprecated; use CONFIG_DRIVER_WEXT=y instead. #CONFIG_DRIVER_MADWIFI=y # Set include directory to the madwifi source tree #CFLAGS += -I../../madwifi @@ -60,6 +61,7 @@ CONFIG_DRIVER_HOSTAP=y #CONFIG_DRIVER_PRISM54=y # Driver interface for ndiswrapper +# Deprecated; use CONFIG_DRIVER_WEXT=y instead. #CONFIG_DRIVER_NDISWRAPPER=y # Driver interface for Atmel driver @@ -74,6 +76,7 @@ CONFIG_DRIVER_ATMEL=y #CFLAGS += -I/opt/WRT54GS/release/src/include # Driver interface for Intel ipw2100/2200 driver +# Deprecated; use CONFIG_DRIVER_WEXT=y instead. #CONFIG_DRIVER_IPW=y # Driver interface for Ralink driver diff --git a/wpa_supplicant/doc/.gitignore b/wpa_supplicant/doc/.gitignore deleted file mode 100644 index 59e4eb8..0000000 --- a/wpa_supplicant/doc/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -html -latex -wpa_supplicant.eps -wpa_supplicant.png diff --git a/wpa_supplicant/doc/docbook/.gitignore b/wpa_supplicant/doc/docbook/.gitignore deleted file mode 100644 index 8c3945c..0000000 --- a/wpa_supplicant/doc/docbook/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -manpage.links -manpage.refs -*.8 -*.5 -*.html -*.pdf diff --git a/wpa_supplicant/doc/docbook/wpa_background.8 b/wpa_supplicant/doc/docbook/wpa_background.8 index 3bda3f4..81f771e 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" "15 February 2009" "" "" +.TH "WPA_BACKGROUND" "8" "12 January 2010" "" "" .SH NAME wpa_background \- Background information on Wi-Fi Protected Access and IEEE 802.11i diff --git a/wpa_supplicant/doc/docbook/wpa_cli.8 b/wpa_supplicant/doc/docbook/wpa_cli.8 index 4e4aa46..286cf06 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" "15 February 2009" "" "" +.TH "WPA_CLI" "8" "12 January 2010" "" "" .SH NAME wpa_cli \- WPA command line client diff --git a/wpa_supplicant/doc/docbook/wpa_gui.8 b/wpa_supplicant/doc/docbook/wpa_gui.8 index 2f4f638..66a279d 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" "15 February 2009" "" "" +.TH "WPA_GUI" "8" "12 January 2010" "" "" .SH NAME wpa_gui \- WPA Graphical User Interface diff --git a/wpa_supplicant/doc/docbook/wpa_passphrase.8 b/wpa_supplicant/doc/docbook/wpa_passphrase.8 index b123daa..d1d1800 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" "15 February 2009" "" "" +.TH "WPA_PASSPHRASE" "8" "12 January 2010" "" "" .SH NAME wpa_passphrase \- Generate a WPA PSK from an ASCII passphrase for a SSID diff --git a/wpa_supplicant/doc/docbook/wpa_priv.8 b/wpa_supplicant/doc/docbook/wpa_priv.8 index 2191cec..a5fa2ea 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" "15 February 2009" "" "" +.TH "WPA_PRIV" "8" "12 January 2010" "" "" .SH NAME wpa_priv \- wpa_supplicant privilege separation helper diff --git a/wpa_supplicant/doc/docbook/wpa_supplicant.8 b/wpa_supplicant/doc/docbook/wpa_supplicant.8 index 0106c69..69b8d2b 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" "15 February 2009" "" "" +.TH "WPA_SUPPLICANT" "8" "12 January 2010" "" "" .SH NAME wpa_supplicant \- Wi-Fi Protected Access client and IEEE 802.1X supplicant diff --git a/wpa_supplicant/doc/docbook/wpa_supplicant.conf.5 b/wpa_supplicant/doc/docbook/wpa_supplicant.conf.5 index 7a01ea2..796d891 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" "15 February 2009" "" "" +.TH "WPA_SUPPLICANT.CONF" "5" "12 January 2010" "" "" .SH NAME wpa_supplicant.conf \- configuration file for wpa_supplicant diff --git a/wpa_supplicant/doc/porting.doxygen b/wpa_supplicant/doc/porting.doxygen index 0311134..7ea6a34 100644 --- a/wpa_supplicant/doc/porting.doxygen +++ b/wpa_supplicant/doc/porting.doxygen @@ -5,9 +5,9 @@ hardware (board, CPU) and software (OS, drivers) targets. It is already used with number of operating systems and numerous wireless card models and drivers. The main %wpa_supplicant repository includes -support for Linux, FreeBSD, and Windows. In addition, at least VxWorks, -PalmOS, Windows CE, and Windows Mobile are supported in separate -repositories. On the hardware +support for Linux, FreeBSD, and Windows. In addition, the code has been +ported to number of other operating systems like VxWorks, PalmOS, +Windows CE, and Windows Mobile. On the hardware side, %wpa_supplicant is used on various systems: desktops, laptops, PDAs, and embedded devices with CPUs including x86, PowerPC, arm/xscale, and MIPS. Both big and little endian configurations are diff --git a/wpa_supplicant/eapol_test.c b/wpa_supplicant/eapol_test.c index d87aad7..b188549 100644 --- a/wpa_supplicant/eapol_test.c +++ b/wpa_supplicant/eapol_test.c @@ -617,7 +617,8 @@ static void ieee802_1x_decapsulate_radius(struct eapol_test_data *e) static void ieee802_1x_get_keys(struct eapol_test_data *e, struct radius_msg *msg, struct radius_msg *req, - u8 *shared_secret, size_t shared_secret_len) + const u8 *shared_secret, + size_t shared_secret_len) { struct radius_ms_mppe_keys *keys; @@ -664,7 +665,7 @@ static void ieee802_1x_get_keys(struct eapol_test_data *e, /* Process the RADIUS frames from Authentication Server */ static RadiusRxResult ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, - u8 *shared_secret, size_t shared_secret_len, + const u8 *shared_secret, size_t shared_secret_len, void *data) { struct eapol_test_data *e = data; diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index dd4595a..39efedd 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -251,6 +251,11 @@ static int wpa_supplicant_match_privacy(struct wpa_scan_res *bss, if (ssid->mixed_cell) return 1; +#ifdef CONFIG_WPS + if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) + return 1; +#endif /* CONFIG_WPS */ + for (i = 0; i < NUM_WEP_KEYS; i++) { if (ssid->wep_key_len[i]) { privacy = 1; @@ -410,6 +415,11 @@ wpa_supplicant_select_bss_wpa(struct wpa_supplicant *wpa_s, continue; } + if (ssid_len == 0) { + wpa_printf(MSG_DEBUG, " skip - SSID not known"); + continue; + } + if (wpa_ie_len == 0 && rsn_ie_len == 0) { wpa_printf(MSG_DEBUG, " skip - no WPA/RSN IE"); continue; @@ -500,6 +510,11 @@ wpa_supplicant_select_bss_non_wpa(struct wpa_supplicant *wpa_s, continue; } + if (ssid_len == 0) { + wpa_printf(MSG_DEBUG, " skip - SSID not known"); + continue; + } + for (ssid = group; ssid; ssid = ssid->pnext) { int check_ssid = ssid->ssid_len != 0; @@ -536,7 +551,7 @@ wpa_supplicant_select_bss_non_wpa(struct wpa_supplicant *wpa_s, "BSSID mismatch"); continue; } - + if (!(ssid->key_mgmt & WPA_KEY_MGMT_NONE) && !(ssid->key_mgmt & WPA_KEY_MGMT_WPS) && !(ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) @@ -546,7 +561,7 @@ wpa_supplicant_select_bss_non_wpa(struct wpa_supplicant *wpa_s, continue; } - if ((ssid->key_mgmt & + if ((ssid->key_mgmt & (WPA_KEY_MGMT_IEEE8021X | WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_FT_PSK | WPA_KEY_MGMT_IEEE8021X_SHA256 | @@ -608,6 +623,8 @@ static void wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s) struct wpa_scan_res *selected = NULL; struct wpa_ssid *ssid = NULL; + wpa_supplicant_notify_scanning(wpa_s, 0); + if (wpa_supplicant_get_scan_results(wpa_s) < 0) { if (wpa_s->conf->ap_scan == 2) return; @@ -626,15 +643,20 @@ static void wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s) wpa_msg(wpa_s, MSG_DEBUG, "Cached scan results are " "empty - not posting"); } else { - wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS); + wpa_printf(MSG_DEBUG, "New scan results available"); + wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS); wpa_supplicant_dbus_notify_scan_results(wpa_s); wpas_wps_notify_scan_results(wpa_s); } - if ((wpa_s->conf->ap_scan == 2 && !wpas_wps_searching(wpa_s)) || - wpa_s->disconnected) + if ((wpa_s->conf->ap_scan == 2 && !wpas_wps_searching(wpa_s))) return; + if (wpa_s->disconnected) { + wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); + return; + } + while (selected == NULL) { for (prio = 0; prio < wpa_s->conf->num_prio; prio++) { selected = wpa_supplicant_select_bss( @@ -696,6 +718,14 @@ req_scan: */ wpa_s->scan_res_tried++; timeout = 0; + } else if (!wpa_supplicant_enabled_networks(wpa_s->conf)) { + /* + * No networks are enabled; short-circuit request so + * we don't wait timeout seconds before transitioning + * to INACTIVE state. + */ + wpa_supplicant_set_state(wpa_s, WPA_INACTIVE); + return; } wpa_supplicant_req_scan(wpa_s, timeout, 0); } @@ -864,6 +894,25 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, eapol_sm_notify_portValid(wpa_s->eapol, TRUE); eapol_sm_notify_eap_success(wpa_s->eapol, TRUE); } + + if (wpa_s->pending_eapol_rx) { + struct os_time now, age; + os_get_time(&now); + os_time_sub(&now, &wpa_s->pending_eapol_rx_time, &age); + if (age.sec == 0 && age.usec < 100000 && + os_memcmp(wpa_s->pending_eapol_rx_src, bssid, ETH_ALEN) == + 0) { + wpa_printf(MSG_DEBUG, "Process pending EAPOL frame " + "that was received just before association " + "notification"); + wpa_supplicant_rx_eapol( + wpa_s, wpa_s->pending_eapol_rx_src, + wpabuf_head(wpa_s->pending_eapol_rx), + wpabuf_len(wpa_s->pending_eapol_rx)); + } + wpabuf_free(wpa_s->pending_eapol_rx); + wpa_s->pending_eapol_rx = NULL; + } } diff --git a/wpa_supplicant/main_none.c b/wpa_supplicant/main_none.c new file mode 100644 index 0000000..993338a --- /dev/null +++ b/wpa_supplicant/main_none.c @@ -0,0 +1,46 @@ +/* + * WPA Supplicant / Example program entrypoint + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "wpa_supplicant_i.h" + +int main(int argc, char *argv[]) +{ + struct wpa_interface iface; + int exitcode = 0; + struct wpa_params params; + struct wpa_global *global; + + memset(¶ms, 0, sizeof(params)); + params.wpa_debug_level = MSG_INFO; + + global = wpa_supplicant_init(¶ms); + if (global == NULL) + return -1; + + memset(&iface, 0, sizeof(iface)); + /* TODO: set interface parameters */ + + if (wpa_supplicant_add_iface(global, &iface) == NULL) + exitcode = -1; + + if (exitcode == 0) + exitcode = wpa_supplicant_run(global); + + wpa_supplicant_deinit(global); + + return exitcode; +} diff --git a/wpa_supplicant/main_symbian.cpp b/wpa_supplicant/main_symbian.cpp new file mode 100644 index 0000000..4ff364b --- /dev/null +++ b/wpa_supplicant/main_symbian.cpp @@ -0,0 +1,48 @@ +/* + * WPA Supplicant / Program entrypoint for Symbian + * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +extern "C" { +#include "common.h" +#include "wpa_supplicant_i.h" +} + +GLDEF_C TInt E32Main(void) +{ + struct wpa_interface iface; + int exitcode = 0; + struct wpa_params params; + struct wpa_global *global; + + memset(¶ms, 0, sizeof(params)); + params.wpa_debug_level = MSG_INFO; + + global = wpa_supplicant_init(¶ms); + if (global == NULL) + return -1; + + memset(&iface, 0, sizeof(iface)); + /* TODO: set interface parameters */ + + if (wpa_supplicant_add_iface(global, &iface) == NULL) + exitcode = -1; + + if (exitcode == 0) + exitcode = wpa_supplicant_run(global); + + wpa_supplicant_deinit(global); + + return exitcode; +} diff --git a/wpa_supplicant/main_winmain.c b/wpa_supplicant/main_winmain.c new file mode 100644 index 0000000..19d9950 --- /dev/null +++ b/wpa_supplicant/main_winmain.c @@ -0,0 +1,84 @@ +/* + * WPA Supplicant / WinMain() function for Windows-based applications + * Copyright (c) 2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "wpa_supplicant_i.h" + +#ifdef _WIN32_WCE +#define CMDLINE LPWSTR +#else /* _WIN32_WCE */ +#define CMDLINE LPSTR +#endif /* _WIN32_WCE */ + + +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, + CMDLINE lpCmdLine, int nShowCmd) +{ + int i; + struct wpa_interface *ifaces, *iface; + int iface_count, exitcode = -1; + struct wpa_params params; + struct wpa_global *global; + + if (os_program_init()) + return -1; + + os_memset(¶ms, 0, sizeof(params)); + params.wpa_debug_level = MSG_MSGDUMP; + params.wpa_debug_file_path = "\\Temp\\wpa_supplicant-log.txt"; + params.wpa_debug_show_keys = 1; + + iface = ifaces = os_zalloc(sizeof(struct wpa_interface)); + if (ifaces == NULL) + return -1; + iface_count = 1; + + iface->confname = "default"; + iface->driver = "ndis"; + iface->ifname = ""; + + exitcode = 0; + global = wpa_supplicant_init(¶ms); + if (global == NULL) { + printf("Failed to initialize wpa_supplicant\n"); + exitcode = -1; + } + + for (i = 0; exitcode == 0 && i < iface_count; i++) { + if ((ifaces[i].confname == NULL && + ifaces[i].ctrl_interface == NULL) || + ifaces[i].ifname == NULL) { + if (iface_count == 1 && (params.ctrl_interface || + params.dbus_ctrl_interface)) + break; + exitcode = -1; + break; + } + if (wpa_supplicant_add_iface(global, &ifaces[i]) == NULL) + exitcode = -1; + } + + if (exitcode == 0) + exitcode = wpa_supplicant_run(global); + + wpa_supplicant_deinit(global); + + os_free(ifaces); + + os_program_deinit(); + + return exitcode; +} diff --git a/wpa_supplicant/main_winsvc.c b/wpa_supplicant/main_winsvc.c new file mode 100644 index 0000000..4a46ed5 --- /dev/null +++ b/wpa_supplicant/main_winsvc.c @@ -0,0 +1,464 @@ +/* + * WPA Supplicant / main() function for Win32 service + * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * 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' and 'config' 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>'. After + * this, it 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'. + */ + +#include "includes.h" +#include <windows.h> + +#include "common.h" +#include "wpa_supplicant_i.h" +#include "eloop.h" + +#ifndef WPASVC_NAME +#define WPASVC_NAME TEXT("wpasvc") +#endif +#ifndef WPASVC_DISPLAY_NAME +#define WPASVC_DISPLAY_NAME TEXT("wpa_supplicant service") +#endif +#ifndef WPASVC_DESCRIPTION +#define WPASVC_DESCRIPTION \ +TEXT("Provides IEEE 802.1X and WPA/WPA2 supplicant functionality") +#endif + +static HANDLE kill_svc; + +static SERVICE_STATUS_HANDLE svc_status_handle; +static SERVICE_STATUS svc_status; + + +#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 + +#ifdef UNICODE +#define TSTR "%S" +#else /* UNICODE */ +#define TSTR "%s" +#endif /* UNICODE */ + + +static int read_interface(struct wpa_global *global, HKEY _hk, + const TCHAR *name) +{ + HKEY hk; +#define TBUFLEN 255 + TCHAR adapter[TBUFLEN], config[TBUFLEN], ctrl_interface[TBUFLEN]; + DWORD buflen, val; + LONG ret; + struct wpa_interface iface; + int skip_on_error = 0; + + ret = RegOpenKeyEx(_hk, name, 0, KEY_QUERY_VALUE, &hk); + if (ret != ERROR_SUCCESS) { + printf("Could not open wpa_supplicant interface key\n"); + return -1; + } + + os_memset(&iface, 0, sizeof(iface)); + iface.driver = "ndis"; + + buflen = sizeof(ctrl_interface); + ret = RegQueryValueEx(hk, TEXT("ctrl_interface"), NULL, NULL, + (LPBYTE) ctrl_interface, &buflen); + if (ret == ERROR_SUCCESS) { + ctrl_interface[TBUFLEN - 1] = TEXT('\0'); + wpa_unicode2ascii_inplace(ctrl_interface); + printf("ctrl_interface[len=%d] '%s'\n", + (int) buflen, (char *) ctrl_interface); + iface.ctrl_interface = (char *) ctrl_interface; + } + + buflen = sizeof(adapter); + ret = RegQueryValueEx(hk, TEXT("adapter"), NULL, NULL, + (LPBYTE) adapter, &buflen); + if (ret == ERROR_SUCCESS) { + adapter[TBUFLEN - 1] = TEXT('\0'); + wpa_unicode2ascii_inplace(adapter); + printf("adapter[len=%d] '%s'\n", + (int) buflen, (char *) adapter); + iface.ifname = (char *) adapter; + } + + buflen = sizeof(config); + ret = RegQueryValueEx(hk, TEXT("config"), NULL, NULL, + (LPBYTE) config, &buflen); + if (ret == ERROR_SUCCESS) { + config[sizeof(config) - 1] = '\0'; + wpa_unicode2ascii_inplace(config); + printf("config[len=%d] '%s'\n", + (int) buflen, (char *) config); + iface.confname = (char *) config; + } + + buflen = sizeof(val); + ret = RegQueryValueEx(hk, TEXT("skip_on_error"), NULL, NULL, + (LPBYTE) &val, &buflen); + if (ret == ERROR_SUCCESS && buflen == sizeof(val)) + skip_on_error = val; + + RegCloseKey(hk); + + if (wpa_supplicant_add_iface(global, &iface) == NULL) { + if (skip_on_error) + wpa_printf(MSG_DEBUG, "Skipped interface '%s' due to " + "initialization failure", iface.ifname); + else + return -1; + } + + return 0; +} + + +static int wpa_supplicant_thread(void) +{ + int exitcode; + struct wpa_params params; + struct wpa_global *global; + HKEY hk, ihk; + DWORD val, buflen, i; + LONG ret; + + if (os_program_init()) + return -1; + + os_memset(¶ms, 0, sizeof(params)); + params.wpa_debug_level = MSG_INFO; + + ret = RegOpenKeyEx(WPA_KEY_ROOT, WPA_KEY_PREFIX, + 0, KEY_QUERY_VALUE, &hk); + if (ret != ERROR_SUCCESS) { + printf("Could not open wpa_supplicant registry key\n"); + return -1; + } + + buflen = sizeof(val); + ret = RegQueryValueEx(hk, TEXT("debug_level"), NULL, NULL, + (LPBYTE) &val, &buflen); + if (ret == ERROR_SUCCESS && buflen == sizeof(val)) { + params.wpa_debug_level = val; + } + + buflen = sizeof(val); + ret = RegQueryValueEx(hk, TEXT("debug_show_keys"), NULL, NULL, + (LPBYTE) &val, &buflen); + if (ret == ERROR_SUCCESS && buflen == sizeof(val)) { + params.wpa_debug_show_keys = val; + } + + buflen = sizeof(val); + ret = RegQueryValueEx(hk, TEXT("debug_timestamp"), NULL, NULL, + (LPBYTE) &val, &buflen); + if (ret == ERROR_SUCCESS && buflen == sizeof(val)) { + params.wpa_debug_timestamp = val; + } + + buflen = sizeof(val); + ret = RegQueryValueEx(hk, TEXT("debug_use_file"), NULL, NULL, + (LPBYTE) &val, &buflen); + if (ret == ERROR_SUCCESS && buflen == sizeof(val) && val) { + params.wpa_debug_file_path = "\\Temp\\wpa_supplicant-log.txt"; + } + + exitcode = 0; + global = wpa_supplicant_init(¶ms); + if (global == NULL) { + printf("Failed to initialize wpa_supplicant\n"); + exitcode = -1; + } + + ret = RegOpenKeyEx(hk, TEXT("interfaces"), 0, KEY_ENUMERATE_SUB_KEYS, + &ihk); + RegCloseKey(hk); + if (ret != ERROR_SUCCESS) { + printf("Could not open wpa_supplicant interfaces registry " + "key\n"); + return -1; + } + + for (i = 0; ; i++) { + TCHAR name[255]; + DWORD namelen; + + namelen = 255; + ret = RegEnumKeyEx(ihk, i, name, &namelen, NULL, NULL, NULL, + NULL); + + if (ret == ERROR_NO_MORE_ITEMS) + break; + + if (ret != ERROR_SUCCESS) { + printf("RegEnumKeyEx failed: 0x%x\n", + (unsigned int) ret); + break; + } + + if (namelen >= 255) + namelen = 255 - 1; + name[namelen] = '\0'; + + wpa_printf(MSG_DEBUG, "interface %d: %s\n", (int) i, name); + if (read_interface(global, ihk, name) < 0) + exitcode = -1; + } + + RegCloseKey(ihk); + + if (exitcode == 0) + exitcode = wpa_supplicant_run(global); + + wpa_supplicant_deinit(global); + + os_program_deinit(); + + return exitcode; +} + + +static DWORD svc_thread(LPDWORD param) +{ + int ret = wpa_supplicant_thread(); + + svc_status.dwCurrentState = SERVICE_STOPPED; + svc_status.dwWaitHint = 0; + if (!SetServiceStatus(svc_status_handle, &svc_status)) { + printf("SetServiceStatus() failed: %d\n", + (int) GetLastError()); + } + + return ret; +} + + +static int register_service(const TCHAR *exe) +{ + SC_HANDLE svc, scm; + SERVICE_DESCRIPTION sd; + + printf("Registering service: " TSTR "\n", WPASVC_NAME); + + scm = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE); + if (!scm) { + printf("OpenSCManager failed: %d\n", (int) GetLastError()); + return -1; + } + + svc = CreateService(scm, WPASVC_NAME, WPASVC_DISPLAY_NAME, + SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, + SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, + exe, NULL, NULL, NULL, NULL, NULL); + + if (!svc) { + printf("CreateService failed: %d\n\n", (int) GetLastError()); + CloseServiceHandle(scm); + return -1; + } + + os_memset(&sd, 0, sizeof(sd)); + sd.lpDescription = WPASVC_DESCRIPTION; + if (!ChangeServiceConfig2(svc, SERVICE_CONFIG_DESCRIPTION, &sd)) { + printf("ChangeServiceConfig2 failed: %d\n", + (int) GetLastError()); + /* This is not a fatal error, so continue anyway. */ + } + + CloseServiceHandle(svc); + CloseServiceHandle(scm); + + printf("Service registered successfully.\n"); + + return 0; +} + + +static int unregister_service(void) +{ + SC_HANDLE svc, scm; + SERVICE_STATUS status; + + printf("Unregistering service: " TSTR "\n", WPASVC_NAME); + + scm = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE); + if (!scm) { + printf("OpenSCManager failed: %d\n", (int) GetLastError()); + return -1; + } + + svc = OpenService(scm, WPASVC_NAME, SERVICE_ALL_ACCESS | DELETE); + if (!svc) { + printf("OpenService failed: %d\n\n", (int) GetLastError()); + CloseServiceHandle(scm); + return -1; + } + + if (QueryServiceStatus(svc, &status)) { + if (status.dwCurrentState != SERVICE_STOPPED) { + printf("Service currently active - stopping " + "service...\n"); + if (!ControlService(svc, SERVICE_CONTROL_STOP, + &status)) { + printf("ControlService failed: %d\n", + (int) GetLastError()); + } + Sleep(500); + } + } + + if (DeleteService(svc)) { + printf("Service unregistered successfully.\n"); + } else { + printf("DeleteService failed: %d\n", (int) GetLastError()); + } + + CloseServiceHandle(svc); + CloseServiceHandle(scm); + + return 0; +} + + +static void WINAPI service_ctrl_handler(DWORD control_code) +{ + switch (control_code) { + case SERVICE_CONTROL_INTERROGATE: + break; + case SERVICE_CONTROL_SHUTDOWN: + case SERVICE_CONTROL_STOP: + svc_status.dwCurrentState = SERVICE_STOP_PENDING; + svc_status.dwWaitHint = 2000; + eloop_terminate(); + SetEvent(kill_svc); + break; + } + + if (!SetServiceStatus(svc_status_handle, &svc_status)) { + printf("SetServiceStatus() failed: %d\n", + (int) GetLastError()); + } +} + + +static void WINAPI service_start(DWORD argc, LPTSTR *argv) +{ + DWORD id; + + svc_status_handle = RegisterServiceCtrlHandler(WPASVC_NAME, + service_ctrl_handler); + if (svc_status_handle == (SERVICE_STATUS_HANDLE) 0) { + printf("RegisterServiceCtrlHandler failed: %d\n", + (int) GetLastError()); + return; + } + + os_memset(&svc_status, 0, sizeof(svc_status)); + svc_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + svc_status.dwCurrentState = SERVICE_START_PENDING; + svc_status.dwWaitHint = 1000; + + if (!SetServiceStatus(svc_status_handle, &svc_status)) { + printf("SetServiceStatus() failed: %d\n", + (int) GetLastError()); + return; + } + + kill_svc = CreateEvent(0, TRUE, FALSE, 0); + if (!kill_svc) { + printf("CreateEvent failed: %d\n", (int) GetLastError()); + return; + } + + if (CreateThread(0, 0, (LPTHREAD_START_ROUTINE) svc_thread, 0, 0, &id) + == 0) { + printf("CreateThread failed: %d\n", (int) GetLastError()); + return; + } + + if (svc_status.dwCurrentState == SERVICE_START_PENDING) { + svc_status.dwCurrentState = SERVICE_RUNNING; + svc_status.dwWaitHint = 0; + svc_status.dwControlsAccepted = SERVICE_ACCEPT_STOP | + SERVICE_ACCEPT_SHUTDOWN; + } + + if (!SetServiceStatus(svc_status_handle, &svc_status)) { + printf("SetServiceStatus() failed: %d\n", + (int) GetLastError()); + return; + } + + /* wait until service gets killed */ + WaitForSingleObject(kill_svc, INFINITE); +} + + +int main(int argc, char *argv[]) +{ + SERVICE_TABLE_ENTRY dt[] = { + { WPASVC_NAME, service_start }, + { NULL, NULL } + }; + + if (argc > 1) { + if (os_strcmp(argv[1], "reg") == 0) { + TCHAR *path; + int ret; + + if (argc < 3) { + path = os_malloc(MAX_PATH * sizeof(TCHAR)); + if (path == NULL) + return -1; + if (!GetModuleFileName(NULL, path, MAX_PATH)) { + printf("GetModuleFileName failed: " + "%d\n", (int) GetLastError()); + os_free(path); + return -1; + } + } else { + path = wpa_strdup_tchar(argv[2]); + if (path == NULL) + return -1; + } + ret = register_service(path); + os_free(path); + return ret; + } else if (os_strcmp(argv[1], "unreg") == 0) { + return unregister_service(); + } else if (os_strcmp(argv[1], "app") == 0) { + return wpa_supplicant_thread(); + } + } + + if (!StartServiceCtrlDispatcher(dt)) { + printf("StartServiceCtrlDispatcher failed: %d\n", + (int) GetLastError()); + } + + return 0; +} diff --git a/wpa_supplicant/mlme.c b/wpa_supplicant/mlme.c index 0c63067..9885e19 100644 --- a/wpa_supplicant/mlme.c +++ b/wpa_supplicant/mlme.c @@ -459,9 +459,9 @@ static void ieee80211_send_assoc(struct wpa_supplicant *wpa_s) *pos++ = 0x00; /* Microsoft OUI 00:50:F2 */ *pos++ = 0x50; *pos++ = 0xf2; - *pos++ = 2; /* WME */ - *pos++ = 0; /* WME info */ - *pos++ = 1; /* WME ver */ + *pos++ = 2; /* WMM */ + *pos++ = 0; /* WMM info */ + *pos++ = 1; /* WMM ver */ *pos++ = 0; } @@ -1175,9 +1175,9 @@ static void ieee80211_rx_mgmt_assoc_resp(struct wpa_supplicant *wpa_s, #if 0 /* FIX? */ sta->assoc_ap = 1; - if (elems.wme && wpa_s->mlme.wmm_enabled) { - sta->flags |= WLAN_STA_WME; - ieee80211_sta_wmm_params(wpa_s, elems.wme, elems.wme_len); + if (elems.wmm && wpa_s->mlme.wmm_enabled) { + sta->flags |= WLAN_STA_WMM; + ieee80211_sta_wmm_params(wpa_s, elems.wmm, elems.wmm_len); } #endif @@ -1488,18 +1488,18 @@ static void ieee80211_bss_info(struct wpa_supplicant *wpa_s, bss->rsn_ie_len = 0; } - if (elems.wme && - (bss->wmm_ie == NULL || bss->wmm_ie_len != elems.wme_len || - os_memcmp(bss->wmm_ie, elems.wme, elems.wme_len))) { + if (elems.wmm && + (bss->wmm_ie == NULL || bss->wmm_ie_len != elems.wmm_len || + os_memcmp(bss->wmm_ie, elems.wmm, elems.wmm_len))) { os_free(bss->wmm_ie); - bss->wmm_ie = os_malloc(elems.wme_len + 2); + bss->wmm_ie = os_malloc(elems.wmm_len + 2); if (bss->wmm_ie) { - os_memcpy(bss->wmm_ie, elems.wme - 2, - elems.wme_len + 2); - bss->wmm_ie_len = elems.wme_len + 2; + os_memcpy(bss->wmm_ie, elems.wmm - 2, + elems.wmm_len + 2); + bss->wmm_ie_len = elems.wmm_len + 2; } else bss->wmm_ie_len = 0; - } else if (!elems.wme && bss->wmm_ie) { + } else if (!elems.wmm && bss->wmm_ie) { os_free(bss->wmm_ie); bss->wmm_ie = NULL; bss->wmm_ie_len = 0; @@ -1595,9 +1595,9 @@ static void ieee80211_rx_mgmt_beacon(struct wpa_supplicant *wpa_s, wpa_s->mlme.cts_protect_erp_frames = use_protection; } - if (elems.wme && wpa_s->mlme.wmm_enabled) { - ieee80211_sta_wmm_params(wpa_s, elems.wme, - elems.wme_len); + if (elems.wmm && wpa_s->mlme.wmm_enabled) { + ieee80211_sta_wmm_params(wpa_s, elems.wmm, + elems.wmm_len); } } @@ -1709,7 +1709,6 @@ static void ieee80211_rx_mgmt_ft_action(struct wpa_supplicant *wpa_s, wpa_printf(MSG_DEBUG, "MLME: Foreign STA Address " MACSTR " in FT Action Response", MAC2STR(sta_addr)); return; - } if (status) { diff --git a/wpa_supplicant/nmake.mak b/wpa_supplicant/nmake.mak new file mode 100644 index 0000000..5e39c11 --- /dev/null +++ b/wpa_supplicant/nmake.mak @@ -0,0 +1,228 @@ +# Makefile for Microsoft nmake to build wpa_supplicant + +# This can be run in Visual Studio 2005 Command Prompt + +# Note: Make sure that cl.exe is configured to include Platform SDK +# include and lib directories (vsvars32.bat) + +all: wpa_supplicant.exe wpa_cli.exe wpa_passphrase.exe wpasvc.exe win_if_list.exe + +# Root directory for WinPcap developer's pack +# (http://www.winpcap.org/install/bin/WpdPack_3_1.zip) +WINPCAPDIR=C:\dev\WpdPack + +# Root directory for OpenSSL +# (http://www.openssl.org/source/openssl-0.9.8a.tar.gz) +# Build and installed following instructions in INSTALL.W32 +# Note: If EAP-FAST is included in the build, OpenSSL needs to be patched to +# support it (openssl-tls-extensions.patch) +# Alternatively, see README-Windows.txt for information about binary +# installation package for OpenSSL. +OPENSSLDIR=C:\dev\openssl + +CC = cl +OBJDIR = objs + +CFLAGS = /DCONFIG_NATIVE_WINDOWS +CFLAGS = $(CFLAGS) /DCONFIG_NDIS_EVENTS_INTEGRATED +CFLAGS = $(CFLAGS) /DCONFIG_ANSI_C_EXTRA +CFLAGS = $(CFLAGS) /DCONFIG_WINPCAP +CFLAGS = $(CFLAGS) /DIEEE8021X_EAPOL +CFLAGS = $(CFLAGS) /DEAP_TLS_FUNCS +CFLAGS = $(CFLAGS) /DPKCS12_FUNCS +CFLAGS = $(CFLAGS) /DEAP_MD5 +CFLAGS = $(CFLAGS) /DEAP_TLS +CFLAGS = $(CFLAGS) /DEAP_MSCHAPv2 +CFLAGS = $(CFLAGS) /DEAP_PEAP +CFLAGS = $(CFLAGS) /DEAP_TTLS +CFLAGS = $(CFLAGS) /DEAP_GTC +CFLAGS = $(CFLAGS) /DEAP_OTP +CFLAGS = $(CFLAGS) /DEAP_SIM +CFLAGS = $(CFLAGS) /DEAP_LEAP +CFLAGS = $(CFLAGS) /DEAP_PSK +CFLAGS = $(CFLAGS) /DEAP_AKA +#CFLAGS = $(CFLAGS) /DEAP_FAST +CFLAGS = $(CFLAGS) /DEAP_PAX +CFLAGS = $(CFLAGS) /DEAP_TNC +CFLAGS = $(CFLAGS) /DPCSC_FUNCS +CFLAGS = $(CFLAGS) /DCONFIG_CTRL_IFACE +CFLAGS = $(CFLAGS) /DCONFIG_CTRL_IFACE_NAMED_PIPE +CFLAGS = $(CFLAGS) /DCONFIG_DRIVER_NDIS +CFLAGS = $(CFLAGS) /I..\src /I..\src\utils /I..\src\common /I..\src\crypto +CFLAGS = $(CFLAGS) /I..\src\rsn_supp /I..\src\eapol_supp /I. +CFLAGS = $(CFLAGS) /DWIN32 +CFLAGS = $(CFLAGS) /Fo$(OBJDIR)\\ /c +CFLAGS = $(CFLAGS) /W3 + +#CFLAGS = $(CFLAGS) /WX + +# VS 2005 complains about lot of deprecated string functions; let's ignore them +# at least for now since snprintf and strncpy can be used in a safe way +CFLAGS = $(CFLAGS) /D_CRT_SECURE_NO_DEPRECATE + +OBJS = \ + $(OBJDIR)\os_win32.obj \ + $(OBJDIR)\eloop_win.obj \ + $(OBJDIR)\sha1.obj \ + $(OBJDIR)\md5.obj \ + $(OBJDIR)\rc4.obj \ + $(OBJDIR)\aes_wrap.obj \ + $(OBJDIR)\common.obj \ + $(OBJDIR)\wpa_debug.obj \ + $(OBJDIR)\wpabuf.obj \ + $(OBJDIR)\wpa_supplicant.obj \ + $(OBJDIR)\wpa.obj \ + $(OBJDIR)\wpa_common.obj \ + $(OBJDIR)\wpa_ie.obj \ + $(OBJDIR)\preauth.obj \ + $(OBJDIR)\pmksa_cache.obj \ + $(OBJDIR)\eapol_supp_sm.obj \ + $(OBJDIR)\eap.obj \ + $(OBJDIR)\eap_common.obj \ + $(OBJDIR)\chap.obj \ + $(OBJDIR)\eap_methods.obj \ + $(OBJDIR)\eap_md5.obj \ + $(OBJDIR)\eap_tls.obj \ + $(OBJDIR)\eap_tls_common.obj \ + $(OBJDIR)\eap_mschapv2.obj \ + $(OBJDIR)\mschapv2.obj \ + $(OBJDIR)\eap_peap.obj \ + $(OBJDIR)\eap_peap_common.obj \ + $(OBJDIR)\eap_ttls.obj \ + $(OBJDIR)\eap_gtc.obj \ + $(OBJDIR)\eap_otp.obj \ + $(OBJDIR)\eap_leap.obj \ + $(OBJDIR)\eap_sim.obj \ + $(OBJDIR)\eap_sim_common.obj \ + $(OBJDIR)\eap_aka.obj \ + $(OBJDIR)\eap_pax.obj \ + $(OBJDIR)\eap_pax_common.obj \ + $(OBJDIR)\eap_psk.obj \ + $(OBJDIR)\eap_psk_common.obj \ + $(OBJDIR)\eap_tnc.obj \ + $(OBJDIR)\tncc.obj \ + $(OBJDIR)\base64.obj \ + $(OBJDIR)\ctrl_iface.obj \ + $(OBJDIR)\ctrl_iface_named_pipe.obj \ + $(OBJDIR)\driver_ndis.obj \ + $(OBJDIR)\driver_ndis_.obj \ + $(OBJDIR)\scan_helpers.obj \ + $(OBJDIR)\events.obj \ + $(OBJDIR)\blacklist.obj \ + $(OBJDIR)\scan.obj \ + $(OBJDIR)\wpas_glue.obj \ + $(OBJDIR)\config.obj \ + $(OBJDIR)\l2_packet_winpcap.obj \ + $(OBJDIR)\tls_openssl.obj \ + $(OBJDIR)\ms_funcs.obj \ + $(OBJDIR)\crypto_openssl.obj \ + $(OBJDIR)\pcsc_funcs.obj \ + $(OBJDIR)\ndis_events.obj + +# OBJS = $(OBJS) $(OBJDIR)\eap_fast.obj + +OBJS_t = $(OBJS) \ + $(OBJDIR)\eapol_test.obj \ + $(OBJDIR)\radius.obj \ + $(OBJDIR)\radius_client.obj \ + $(OBJDIR)\config_file.obj $(OBJDIR)\base64.obj + +OBJS_t2 = $(OBJS) \ + $(OBJDIR)\preauth_test.obj \ + $(OBJDIR)\config_file.obj $(OBJDIR)\base64.obj + +OBJS2 = $(OBJDIR)\drivers.obj \ + $(OBJDIR)\config_file.obj \ + $(OBJS2) $(OBJDIR)\main.obj + +OBJS3 = $(OBJDIR)\drivers.obj \ + $(OBJDIR)\config_winreg.obj \ + $(OBJS3) $(OBJDIR)\main_winsvc.obj + +OBJS_c = \ + $(OBJDIR)\os_win32.obj \ + $(OBJDIR)\wpa_cli.obj \ + $(OBJDIR)\wpa_ctrl.obj \ + $(OBJDIR)\common.obj + +OBJS_p = \ + $(OBJDIR)\os_win32.obj \ + $(OBJDIR)\common.obj \ + $(OBJDIR)\sha1.obj \ + $(OBJDIR)\md5.obj \ + $(OBJDIR)\crypto_openssl.obj \ + $(OBJDIR)\wpa_passphrase.obj + +LIBS = wbemuuid.lib libcmt.lib kernel32.lib uuid.lib ole32.lib oleaut32.lib \ + ws2_32.lib Advapi32.lib Crypt32.lib Winscard.lib \ + Packet.lib wpcap.lib \ + libeay32.lib ssleay32.lib +# If using Win32 OpenSSL binary installation from Shining Light Productions, +# replace the last line with this for dynamic libraries +# libeay32MT.lib ssleay32MT.lib +# and this for static libraries +# libeay32MT.lib ssleay32MT.lib Gdi32.lib User32.lib + +CFLAGS = $(CFLAGS) /I"$(WINPCAPDIR)/Include" /I"$(OPENSSLDIR)\include" +LFLAGS = /libpath:"$(WINPCAPDIR)\Lib" /libpath:"$(OPENSSLDIR)\lib" + +wpa_supplicant.exe: $(OBJDIR) $(OBJS) $(OBJS2) + link.exe /out:wpa_supplicant.exe $(LFLAGS) $(OBJS) $(OBJS2) $(LIBS) + +wpasvc.exe: $(OBJDIR) $(OBJS) $(OBJS3) + link.exe /out:wpasvc.exe $(LFLAGS) $(OBJS) $(OBJS3) $(LIBS) + +wpa_cli.exe: $(OBJDIR) $(OBJS_c) + link.exe /out:wpa_cli.exe $(LFLAGS) $(OBJS_c) $(LIBS) + +wpa_passphrase.exe: $(OBJDIR) $(OBJS_p) + link.exe /out:wpa_passphrase.exe $(LFLAGS) $(OBJS_p) $(LIBS) + +eapol_test.exe: $(OBJDIR) $(OBJS_t) + link.exe /out:eapol_test.exe $(LFLAGS) $(OBJS_t) $(LIBS) + +preauth_test.exe: $(OBJDIR) $(OBJS_t2) + link.exe /out:preauth_test.exe $(LFLAGS) $(OBJS_t2) $(LIBS) + +win_if_list.exe: $(OBJDIR) $(OBJDIR)\win_if_list.obj + link.exe /out:win_if_list.exe $(LFLAGS) $(OBJDIR)\win_if_list.obj $(LIBS) + + +{..\src\utils}.c{$(OBJDIR)}.obj:: + $(CC) $(CFLAGS) $< + +{..\src\common}.c{$(OBJDIR)}.obj:: + $(CC) $(CFLAGS) $< + +{..\src\rsn_supp}.c{$(OBJDIR)}.obj:: + $(CC) $(CFLAGS) $< + +{..\src\eapol_supp}.c{$(OBJDIR)}.obj:: + $(CC) $(CFLAGS) $< + +{..\src\crypto}.c{$(OBJDIR)}.obj:: + $(CC) $(CFLAGS) $< + +{..\src\eap_peer}.c{$(OBJDIR)}.obj:: + $(CC) $(CFLAGS) $< + +{..\src\eap_common}.c{$(OBJDIR)}.obj:: + $(CC) $(CFLAGS) $< + +{..\src\drivers}.c{$(OBJDIR)}.obj:: + $(CC) $(CFLAGS) $< + +{..\src\l2_packet}.c{$(OBJDIR)}.obj:: + $(CC) $(CFLAGS) $< + +{.\}.c{$(OBJDIR)}.obj:: + $(CC) $(CFLAGS) $< + +{.\}.cpp{$(OBJDIR)}.obj:: + $(CC) $(CFLAGS) $< + +$(OBJDIR): + if not exist "$(OBJDIR)" mkdir "$(OBJDIR)" + +clean: + erase $(OBJDIR)\*.obj wpa_supplicant.exe diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c index 67e2282..8cb7a42 100644 --- a/wpa_supplicant/scan.c +++ b/wpa_supplicant/scan.c @@ -20,6 +20,7 @@ #include "wpa_supplicant_i.h" #include "mlme.h" #include "wps_supplicant.h" +#include "ctrl_iface_dbus.h" static void wpa_supplicant_gen_assoc_event(struct wpa_supplicant *wpa_s) @@ -65,11 +66,24 @@ static int wpas_wps_in_use(struct wpa_config *conf, } #endif /* CONFIG_WPS */ + +int wpa_supplicant_enabled_networks(struct wpa_config *conf) +{ + struct wpa_ssid *ssid = conf->ssid; + while (ssid) { + if (!ssid->disabled) + return 1; + ssid = ssid->next; + } + return 0; +} + + static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; struct wpa_ssid *ssid; - int enabled, scan_req = 0, ret; + int scan_req = 0, ret; struct wpabuf *wps_ie = NULL; const u8 *extra_ie = NULL; size_t extra_ie_len = 0; @@ -78,19 +92,13 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) enum wps_request_type req_type = WPS_REQ_ENROLLEE_INFO; #endif /* CONFIG_WPS */ - if (wpa_s->disconnected && !wpa_s->scan_req) + if (wpa_s->disconnected && !wpa_s->scan_req) { + wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); return; - - enabled = 0; - ssid = wpa_s->conf->ssid; - while (ssid) { - if (!ssid->disabled) { - enabled++; - break; - } - ssid = ssid->next; } - if (!enabled && !wpa_s->scan_req) { + + if (!wpa_supplicant_enabled_networks(wpa_s->conf) && + !wpa_s->scan_req) { wpa_printf(MSG_DEBUG, "No enabled networks - do not scan"); wpa_supplicant_set_state(wpa_s, WPA_INACTIVE); return; @@ -189,6 +197,8 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) } #endif /* CONFIG_WPS */ + wpa_supplicant_notify_scanning(wpa_s, 1); + if (wpa_s->use_client_mlme) { ieee80211_sta_set_probe_req_ie(wpa_s, extra_ie, extra_ie_len); ret = ieee80211_sta_req_scan(wpa_s, ssid ? ssid->ssid : NULL, @@ -203,6 +213,7 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) if (ret) { wpa_printf(MSG_WARNING, "Failed to initiate AP scan."); + wpa_supplicant_notify_scanning(wpa_s, 0); wpa_supplicant_req_scan(wpa_s, 10, 0); } else wpa_s->scan_runs++; @@ -261,3 +272,14 @@ void wpa_supplicant_cancel_scan(struct wpa_supplicant *wpa_s) wpa_msg(wpa_s, MSG_DEBUG, "Cancelling scan request"); eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL); } + + +void wpa_supplicant_notify_scanning(struct wpa_supplicant *wpa_s, + int scanning) +{ + if (wpa_s->scanning != scanning) { + wpa_s->scanning = scanning; + wpa_supplicant_dbus_notify_scanning(wpa_s); + } +} + diff --git a/wpa_supplicant/symbian/README.symbian b/wpa_supplicant/symbian/README.symbian new file mode 100644 index 0000000..9d3b811 --- /dev/null +++ b/wpa_supplicant/symbian/README.symbian @@ -0,0 +1,24 @@ +wpa_supplicant for Symbian +========================== + +Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> and +contributors +All Rights Reserved. + +This program is dual-licensed under both the GPL version 2 and BSD +license. Either license may be used at your option. + + +This directory includes project files for testing experimental Symbian +(e.g., Nokia S60 3rd Ed) builds. The Symbian port is not really +complete or expected to work, but these files can be used to verify +that the build itself can be completed successfully. + +These files have been successfully tested with Nokia S60 3rd Edition +MR SDK. + +Build files can be created and a phone release build can be run with +following commands: + +bldmake bldfiles +abld build gcce urel diff --git a/wpa_supplicant/symbian/bld.inf b/wpa_supplicant/symbian/bld.inf new file mode 100644 index 0000000..a1fc582 --- /dev/null +++ b/wpa_supplicant/symbian/bld.inf @@ -0,0 +1,8 @@ +PRJ_PLATFORMS +WINSCW GCCE + +PRJ_EXPORTS + +PRJ_MMPFILES + +wpa_supplicant.mmp diff --git a/wpa_supplicant/symbian/wpa_supplicant.mmp b/wpa_supplicant/symbian/wpa_supplicant.mmp new file mode 100644 index 0000000..fad9626 --- /dev/null +++ b/wpa_supplicant/symbian/wpa_supplicant.mmp @@ -0,0 +1,38 @@ +TARGET wpa_supplicant.exe +UID 0x0 0x0 +VENDORID 0 +TARGETTYPE exe + +SYSTEMINCLUDE \epoc32\include \epoc32\include\variant \epoc32\include\ecom \epoc32\include\libc + +USERINCLUDE .. ..\..\src ..\..\src\utils ..\..\src\common ..\..\src\crypto ..\..\src\rsn_supp + +SOURCEPATH .. +SOURCE main_symbian.cpp +SOURCE config.c config_file.c +SOURCE eapol_sm.c +SOURCE wpa_supplicant.c events.c +SOURCEPATH ..\..\src\rsn_supp +SOURCE wpa.c preauth.c pmksa_cache.c peerkey.c wpa_ie.c +SOURCEPATH ..\..\src\drivers +SOURCE drivers.c +SOURCEPATH ..\..\src\common +SOURCE wpa_common.c +SOURCEPATH ..\..\src\utils +SOURCE os_none.c common.c wpa_debug.c eloop_none.c base64.c +SOURCEPATH ..\..\src\crypto +SOURCE sha1.c md5.c rc4.c md4.c des.c aes_wrap.c aes.c ms_funcs.c +SOURCE tls_internal.c crypto_internal.c +SOURCEPATH ..\..\src\tls +SOURCE asn1.c bignum.c rsa.c x509v3.c tlsv1_client.c tlsv1_common.c +SOURCEPATH ..\..\src\l2_packet +SOURCE l2_packet_none.c +SOURCEPATH ..\..\src\eap_peer +SOURCE eap.c eap_methods.c +SOURCE eap_md5.c eap_tls.c eap_mschapv2.c eap_peap.c eap_gtc.c +SOURCE eap_ttls.c eap_otp.c eap_leap.c eap_tls_common.c eap_tlv.c +SOURCE eap_fast.c eap_fast_pac.c +SOURCEPATH ..\..\src\eap_common +SOURCE eap_common.c + +LIBRARY euser.lib estlib.lib 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 0000000..9c46240 --- /dev/null +++ b/wpa_supplicant/vs2005/eapol_test/eapol_test.vcproj @@ -0,0 +1,425 @@ +<?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;..\..\..\src\common;..\..\..\src\crypto;..\..\..\src\rsn_supp;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;..\..\..\src\common;..\..\..\src\crypto;..\..\..\src\rsn_supp;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_wrap.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\utils\base64.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\blacklist.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\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="..\..\..\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\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="..\..\..\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="..\..\..\src\crypto\rc4.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\scan.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\drivers\scan_helpers.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\sha1.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 0000000..e79fc0f --- /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 0000000..6d36cba --- /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\utils;..\..\..\src\common"
+ 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\utils;..\..\..\src\common"
+ 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 0000000..1a3618b --- /dev/null +++ b/wpa_supplicant/vs2005/wpa_passphrase/wpa_passphrase.vcproj @@ -0,0 +1,220 @@ +<?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\utils;..\..\..\src\crypto;C:\dev\openssl\include"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS;INTERNAL_SHA1;INTERNAL_MD5"
+ 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\utils;..\..\..\src\crypto;C:\dev\openssl\include"
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS;INTERNAL_SHA1;INTERNAL_MD5"
+ 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\utils\os_win32.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\sha1.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 0000000..df89e31 --- /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 0000000..a733fea --- /dev/null +++ b/wpa_supplicant/vs2005/wpa_supplicant/wpa_supplicant.vcproj @@ -0,0 +1,421 @@ +<?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;..\..\..\src\common;..\..\..\src\crypto;..\..\..\src\rsn_supp;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;..\..\..\src\common;..\..\..\src\crypto;..\..\..\src\rsn_supp;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_wrap.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\utils\base64.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\blacklist.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_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="..\..\..\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\md4.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="..\..\..\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\crypto\rc4.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\scan.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\drivers\scan_helpers.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\sha1.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 0000000..9f9e438 --- /dev/null +++ b/wpa_supplicant/vs2005/wpasvc/wpasvc.vcproj @@ -0,0 +1,421 @@ +<?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;..\..\..\src\common;..\..\..\src\crypto;..\..\..\src\rsn_supp;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;..\..\..\src\common;..\..\..\src\crypto;..\..\..\src\rsn_supp;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_wrap.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\utils\base64.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\blacklist.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_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="..\..\..\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\md4.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="..\..\..\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\crypto\rc4.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\scan.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\drivers\scan_helpers.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\sha1.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/win_example.reg b/wpa_supplicant/win_example.reg new file mode 100755 index 0000000..875d4ef --- /dev/null +++ b/wpa_supplicant/win_example.reg @@ -0,0 +1,42 @@ +REGEDIT4
+
+[HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant]
+"debug_level"=dword:00000000
+"debug_show_keys"=dword:00000001
+"debug_timestamp"=dword:00000000
+"debug_use_file"=dword:00000000
+
+[HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant\configs]
+
+[HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant\configs\test]
+"ap_scan"=dword:00000002
+"update_config"=dword:00000001
+"uuid"="12345678-9abc-def0-1234-56789abcdef0"
+"device_name"="Wireless Client"
+"manufacturer"="Company"
+"model_name"="cmodel"
+"serial_number"="12345"
+"device_type"="1-0050F204-1"
+"os_version"="01020300"
+
+[HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant\configs\test\blobs]
+"testblob"=hex:01,02,03,04,05
+
+[HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant\configs\test\networks]
+
+[HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant\configs\test\networks\0000]
+"ssid"="\"example network\""
+"key_mgmt"="WPA-PSK"
+"psk"="\"secret password\""
+"pairwise"="CCMP"
+"group"="CCMP"
+"proto"="WPA"
+
+[HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant\interfaces]
+
+[HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant\interfaces\0000]
+"adapter"="{A7627643-C310-49E5-BD89-7E77709C04AB}"
+"config"="test"
+"ctrl_interface"=""
+"skip_on_error"=dword:00000000
+
diff --git a/wpa_supplicant/win_if_list.c b/wpa_supplicant/win_if_list.c new file mode 100644 index 0000000..0e1532e --- /dev/null +++ b/wpa_supplicant/win_if_list.c @@ -0,0 +1,179 @@ +/* + * win_if_list - Display network interfaces with description (for Windows) + * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * This small tool is for the Windows build to provide an easy way of fetching + * a list of available network interfaces. + */ + +#include "includes.h" +#include <stdio.h> +#ifdef CONFIG_USE_NDISUIO +#include <winsock2.h> +#include <ntddndis.h> +#else /* CONFIG_USE_NDISUIO */ +#include "pcap.h" +#include <winsock.h> +#endif /* CONFIG_USE_NDISUIO */ + +#ifdef CONFIG_USE_NDISUIO + +/* from nuiouser.h */ +#define FSCTL_NDISUIO_BASE FILE_DEVICE_NETWORK + +#define _NDISUIO_CTL_CODE(_Function, _Method, _Access) \ + CTL_CODE(FSCTL_NDISUIO_BASE, _Function, _Method, _Access) + +#define IOCTL_NDISUIO_QUERY_BINDING \ + _NDISUIO_CTL_CODE(0x203, METHOD_BUFFERED, \ + FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +#define IOCTL_NDISUIO_BIND_WAIT \ + _NDISUIO_CTL_CODE(0x204, METHOD_BUFFERED, \ + FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +typedef struct _NDISUIO_QUERY_BINDING +{ + ULONG BindingIndex; + ULONG DeviceNameOffset; + ULONG DeviceNameLength; + ULONG DeviceDescrOffset; + ULONG DeviceDescrLength; +} NDISUIO_QUERY_BINDING, *PNDISUIO_QUERY_BINDING; + + +static HANDLE ndisuio_open(void) +{ + DWORD written; + HANDLE h; + + h = CreateFile(TEXT("\\\\.\\\\Ndisuio"), + GENERIC_READ | GENERIC_WRITE, 0, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, + INVALID_HANDLE_VALUE); + if (h == INVALID_HANDLE_VALUE) + return h; + +#ifndef _WIN32_WCE + if (!DeviceIoControl(h, IOCTL_NDISUIO_BIND_WAIT, NULL, 0, NULL, 0, + &written, NULL)) { + printf("IOCTL_NDISUIO_BIND_WAIT failed: %d", + (int) GetLastError()); + CloseHandle(h); + return INVALID_HANDLE_VALUE; + } +#endif /* _WIN32_WCE */ + + return h; +} + + +static void ndisuio_query_bindings(HANDLE ndisuio) +{ + NDISUIO_QUERY_BINDING *b; + size_t blen = sizeof(*b) + 1024; + int i, error; + DWORD written; + char name[256], desc[256]; + WCHAR *pos; + size_t j, len; + + b = malloc(blen); + if (b == NULL) + return; + + for (i = 0; ; i++) { + memset(b, 0, blen); + b->BindingIndex = i; + if (!DeviceIoControl(ndisuio, IOCTL_NDISUIO_QUERY_BINDING, + b, sizeof(NDISUIO_QUERY_BINDING), b, + (DWORD) blen, &written, NULL)) { + error = (int) GetLastError(); + if (error == ERROR_NO_MORE_ITEMS) + break; + printf("IOCTL_NDISUIO_QUERY_BINDING failed: %d", + error); + break; + } + + pos = (WCHAR *) ((char *) b + b->DeviceNameOffset); + len = b->DeviceNameLength; + if (len >= sizeof(name)) + len = sizeof(name) - 1; + for (j = 0; j < len; j++) + name[j] = (char) pos[j]; + name[len] = '\0'; + + pos = (WCHAR *) ((char *) b + b->DeviceDescrOffset); + len = b->DeviceDescrLength; + if (len >= sizeof(desc)) + len = sizeof(desc) - 1; + for (j = 0; j < len; j++) + desc[j] = (char) pos[j]; + desc[len] = '\0'; + + printf("ifname: %s\ndescription: %s\n\n", name, desc); + } + + free(b); +} + + +static void ndisuio_enum_bindings(void) +{ + HANDLE ndisuio = ndisuio_open(); + if (ndisuio == INVALID_HANDLE_VALUE) + return; + + ndisuio_query_bindings(ndisuio); + CloseHandle(ndisuio); +} + +#else /* CONFIG_USE_NDISUIO */ + +static void show_dev(pcap_if_t *dev) +{ + printf("ifname: %s\ndescription: %s\n\n", + dev->name, dev->description); +} + + +static void pcap_enum_devs(void) +{ + pcap_if_t *devs, *dev; + char err[PCAP_ERRBUF_SIZE + 1]; + + if (pcap_findalldevs(&devs, err) < 0) { + fprintf(stderr, "Error - pcap_findalldevs: %s\n", err); + return; + } + + for (dev = devs; dev; dev = dev->next) { + show_dev(dev); + } + + pcap_freealldevs(devs); +} + +#endif /* CONFIG_USE_NDISUIO */ + + +int main(int argc, char *argv[]) +{ +#ifdef CONFIG_USE_NDISUIO + ndisuio_enum_bindings(); +#else /* CONFIG_USE_NDISUIO */ + pcap_enum_devs(); +#endif /* CONFIG_USE_NDISUIO */ + + return 0; +} diff --git a/wpa_supplicant/wpa_gui-qt4/addinterface.cpp b/wpa_supplicant/wpa_gui-qt4/addinterface.cpp new file mode 100644 index 0000000..02fecfe --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/addinterface.cpp @@ -0,0 +1,245 @@ +/* + * wpa_gui - AddInterface class + * Copyright (c) 2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <cstdio> +#include "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("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, "driver"); + interfaceWidget->headerItem()->setText(1, "interface"); + interfaceWidget->headerItem()->setText(2, "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).toAscii().constData(), + "default", + sel->text(0).toAscii().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", + "Add interface command could not be " + "completed."); + return; + } + + buf[len] = '\0'; + if (buf[0] != 'O' || buf[1] != 'K') { + QMessageBox::warning(this, "wpa_gui", + "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", + "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 0000000..9d9476a --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/addinterface.h @@ -0,0 +1,45 @@ +/* + * wpa_gui - AddInterface class + * Copyright (c) 2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef ADDINTERFACE_H +#define ADDINTERFACE_H + +#include <QObject> + +#include <QtGui/QDialog> +#include <QtGui/QTreeWidget> +#include <QtGui/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 0000000..46deb96a --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/eventhistory.cpp @@ -0,0 +1,130 @@ +/* + * wpa_gui - EventHistory class + * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#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("Timestamp"); + case 1: + return QString("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::WFlags) + : 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 0000000..40dff6d --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/eventhistory.h @@ -0,0 +1,63 @@ +/* + * wpa_gui - EventHistory class + * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#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::WFlags 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 0000000..afe9149 --- /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 0000000..93e94fc --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/icons.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/icons" > + <file alias="wpa_gui.svg">icons/wpa_gui.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 0000000..cb5c65e --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/icons/Makefile @@ -0,0 +1,27 @@ +#!/usr/bin/make -f + +NAME := wpa_gui +SVG := $(NAME).svg +SIZES := 16x16 22x22 32x32 48x48 64x64 128x128 +ICONS := $(addsuffix .png,$(SIZES)) +ICONS += $(addsuffix .xpm,$(NAME) $(NAME)-16) + +all: $(ICONS) + +%.png: + mkdir -p hicolor/$(@:.png=)/apps/ + inkscape $(SVG) --without-gui \ + --export-width=$(word 1,$(subst x, ,$(@:.png=))) \ + --export-height=$(word 2,$(subst x, ,$(@:.png=))) \ + --export-png=hicolor/$(@:.png=)/apps/$(NAME).png + +$(NAME).xpm: + mkdir -p pixmaps/ + convert hicolor/32x32/apps/$(NAME).png pixmaps/$@ + +$(NAME)-16.xpm: + mkdir -p pixmaps/ + convert hicolor/16x16/apps/$(NAME).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 0000000..1584eb5 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/icons/README @@ -0,0 +1,7 @@ +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. + +To convert the svg icon to other formats, make sure inkscape and imagemagick +are installed and use `make' to create various sized png and xpm icons. 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 0000000..b3abf0a --- /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 0000000..09f3d96 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/icons_png.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/icons" > + <file alias="wpa_gui.png">icons/hicolor/16x16/apps/wpa_gui.png</file> + </qresource> +</RCC> diff --git a/wpa_supplicant/wpa_gui-qt4/main.cpp b/wpa_supplicant/wpa_gui-qt4/main.cpp new file mode 100644 index 0000000..c5e285f --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/main.cpp @@ -0,0 +1,70 @@ +/* + * wpa_gui - Application startup + * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifdef CONFIG_NATIVE_WINDOWS +#include <winsock.h> +#endif /* CONFIG_NATIVE_WINDOWS */ +#include <QApplication> +#include "wpagui.h" + + +class WpaGuiApp : public QApplication +{ +public: + WpaGuiApp(int &argc, char **argv); + +#ifndef QT_NO_SESSIONMANAGER + virtual void saveState(QSessionManager &manager); +#endif + + WpaGui *w; +}; + +WpaGuiApp::WpaGuiApp(int &argc, char **argv) : QApplication(argc, argv) +{ +} + +#ifndef QT_NO_SESSIONMANAGER +void WpaGuiApp::saveState(QSessionManager &manager) +{ + QApplication::saveState(manager); + w->saveState(); +} +#endif + + +int main(int argc, char *argv[]) +{ + WpaGuiApp app(argc, argv); + WpaGui w(&app); + int ret; + +#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 0000000..dae9edd --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/networkconfig.cpp @@ -0,0 +1,823 @@ +/* + * wpa_gui - NetworkConfig class + * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <cstdio> +#include <QMessageBox> + +#include "networkconfig.h" +#include "wpagui.h" + +enum { + AUTH_NONE = 0, + AUTH_IEEE8021X = 1, + AUTH_WPA_PSK = 2, + AUTH_WPA_EAP = 3, + AUTH_WPA2_PSK = 4, + AUTH_WPA2_EAP = 5 +}; + +#define WPA_GUI_KEY_DATA "[key is configured]" + + +NetworkConfig::NetworkConfig(QWidget *parent, const char *, bool, Qt::WFlags) + : QDialog(parent) +{ + setupUi(this); + + 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; + + if (flags.indexOf("-CCMP") >= 0) + encr = 1; + else if (flags.indexOf("-TKIP") >= 0) + encr = 0; + else if (flags.indexOf("WEP") >= 0) + encr = 1; + else + encr = 0; + + authSelect->setCurrentIndex(auth); + authChanged(auth); + encrSelect->setCurrentIndex(encr); + + wepEnabled(auth == AUTH_NONE && encr == 1); + + getEapCapa(); + + if (flags.indexOf("[WPS") >= 0) + useWpsButton->setEnabled(true); + bssid = sel->text(1); +} + + +void NetworkConfig::authChanged(int sel) +{ + 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 || sel == AUTH_IEEE8021X) { + encrSelect->addItem("None"); + encrSelect->addItem("WEP"); + encrSelect->setCurrentIndex(sel == AUTH_NONE ? 0 : 1); + } else { + encrSelect->addItem("TKIP"); + encrSelect->addItem("CCMP"); + encrSelect->setCurrentIndex((sel == AUTH_WPA2_PSK || + sel == AUTH_WPA2_EAP) ? 1 : 0); + } + + wepEnabled(sel == AUTH_IEEE8021X); +} + + +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, "WPA Pre-Shared Key Error", + "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, "Network ID Error", + "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", "Failed to add " + "network to wpa_supplicant\n" + "configuration."); + return; + } + id = atoi(reply); + } else + id = edit_network_id; + + setNetworkParam(id, "ssid", ssidEdit->text().toAscii().constData(), + true); + + const char *key_mgmt = NULL, *proto = NULL, *pairwise = NULL; + switch (auth) { + case AUTH_NONE: + 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_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().toAscii().constData(), + WPA_GUI_KEY_DATA) != 0) + setNetworkParam(id, "psk", + pskEdit->text().toAscii().constData(), + psklen != 64); + if (eapSelect->isEnabled()) { + const char *eap = + eapSelect->currentText().toAscii().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). + toAscii().constData()); + } else if (eap.compare("TTLS") == 0) { + if (inner.startsWith("EAP-")) + snprintf(phase2, sizeof(phase2), "autheap=%s", + inner.right(inner.size() - 4). + toAscii().constData()); + else + snprintf(phase2, sizeof(phase2), "auth=%s", + inner.toAscii().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). + toAscii().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().toAscii().constData(), + true); + else + setNetworkParam(id, "identity", "NULL", false); + if (passwordEdit->isEnabled() && passwordEdit->text().length() > 0 && + strcmp(passwordEdit->text().toAscii().constData(), + WPA_GUI_KEY_DATA) != 0) + setNetworkParam(id, "password", + passwordEdit->text().toAscii().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().toAscii().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().toAscii().constData(), + true); + else + setNetworkParam(id, "id_str", "NULL", false); + + if (prioritySpinBox->isEnabled()) { + QString prio; + prio = prio.setNum(prioritySpinBox->value()); + setNetworkParam(id, "priority", prio.toAscii().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", "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 &sel) +{ + wepEnabled(sel.indexOf("WEP") == 0); +} + + +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().toAscii().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, 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) + 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 || auth == AUTH_IEEE8021X) + encr = 1; + + wepEdit->setText(reply + 1); + } else if (res >= 0 && key_value_isset(reply, reply_len)) { + if (auth == AUTH_NONE || auth == AUTH_IEEE8021X) + encr = 1; + wepEdit->setText(WPA_GUI_KEY_DATA); + } + } + + 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); + if (auth == AUTH_NONE || auth == AUTH_IEEE8021X) + wepEnabled(encr == 1); + + removeButton->setEnabled(true); + addButton->setText("Save"); +} + + +void NetworkConfig::removeNetwork() +{ + char reply[10], cmd[256]; + size_t reply_len; + + if (QMessageBox::information(this, "wpa_gui", + "This will permanently remove the " + "network\n" + "from the configuration. Do you really " + "want\n" + "to remove this network?", "Yes", "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", + "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 0000000..0ceeb41 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/networkconfig.h @@ -0,0 +1,61 @@ +/* + * wpa_gui - NetworkConfig class + * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#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::WFlags 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 0000000..ede462f --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/networkconfig.ui @@ -0,0 +1,425 @@ +<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 or static WEP</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/scanresults.cpp b/wpa_supplicant/wpa_gui-qt4/scanresults.cpp new file mode 100644 index 0000000..459aa8c --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/scanresults.cpp @@ -0,0 +1,144 @@ +/* + * wpa_gui - ScanResults class + * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <cstdio> + +#include "scanresults.h" +#include "wpagui.h" +#include "networkconfig.h" + + +ScanResults::ScanResults(QWidget *parent, const char *, bool, Qt::WFlags) + : 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); +} + + +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("qual=")) + signal = (*it).mid(pos); + else if ((*it).startsWith("flags=")) + flags = (*it).mid(pos); + else if ((*it).startsWith("ssid=")) + ssid = (*it).mid(pos); + } + + QTreeWidgetItem *item = new QTreeWidgetItem(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 0000000..2c4a1b0 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/scanresults.h @@ -0,0 +1,46 @@ +/* + * wpa_gui - ScanResults class + * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#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::WFlags 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 0000000..81e405e --- /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/userdatarequest.cpp b/wpa_supplicant/wpa_gui-qt4/userdatarequest.cpp new file mode 100644 index 0000000..42fbdbc --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/userdatarequest.cpp @@ -0,0 +1,100 @@ +/* + * wpa_gui - UserDataRequest class + * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "userdatarequest.h" +#include "wpagui.h" +#include "wpa_ctrl.h" + + +UserDataRequest::UserDataRequest(QWidget *parent, const char *, bool, + Qt::WFlags) + : 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("Password: "); + queryEdit->setEchoMode(QLineEdit::Password); + } else if (strcmp(tmp, "NEW_PASSWORD") == 0) { + queryField->setText("New password: "); + queryEdit->setEchoMode(QLineEdit::Password); + } else if (strcmp(tmp, "IDENTITY") == 0) + queryField->setText("Identity: "); + else if (strcmp(tmp, "PASSPHRASE") == 0) { + queryField->setText("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.toAscii().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 0000000..2b6e837 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/userdatarequest.h @@ -0,0 +1,46 @@ +/* + * wpa_gui - UserDataRequest class + * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#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::WFlags 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 0000000..1de2a26 --- /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 0000000..ccc7d87 --- /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 0000000..2317cbd --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/wpa_gui.pro @@ -0,0 +1,62 @@ +TEMPLATE = app +LANGUAGE = C++ + +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/utils ../../src/common + +HEADERS += wpamsg.h \ + wpagui.h \ + eventhistory.h \ + scanresults.h \ + userdatarequest.h \ + networkconfig.h \ + addinterface.h + +SOURCES += main.cpp \ + wpagui.cpp \ + eventhistory.cpp \ + scanresults.cpp \ + userdatarequest.cpp \ + networkconfig.cpp \ + addinterface.cpp \ + ../../src/common/wpa_ctrl.c + +RESOURCES += icons.qrc + +FORMS = wpagui.ui \ + eventhistory.ui \ + scanresults.ui \ + userdatarequest.ui \ + networkconfig.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 0000000..a3fbf72 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/wpagui.cpp @@ -0,0 +1,1706 @@ +/* + * wpa_gui - WpaGui class + * Copyright (c) 2005-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifdef __MINGW32__ +/* Need to get getopt() */ +#include <unistd.h> +#endif + +#ifdef CONFIG_NATIVE_WINDOWS +#include <windows.h> +#endif /* CONFIG_NATIVE_WINDOWS */ + +#include <cstdio> +#include <QMessageBox> +#include <QCloseEvent> +#include <QImageReader> +#include <QSettings> + +#include "wpagui.h" +#include "dirent.h" +#include "wpa_ctrl.h" +#include "userdatarequest.h" +#include "networkconfig.h" + +#if 1 +/* Silence stdout */ +#define printf wpagui_printf +static int wpagui_printf(const char *, ...) +{ + return 0; +} +#endif + +WpaGui::WpaGui(QApplication *_app, QWidget *parent, const char *, Qt::WFlags) + : QMainWindow(parent), app(_app) +{ + setupUi(this); + +#ifdef CONFIG_NATIVE_WINDOWS + fileStopServiceAction = new QAction(this); + fileStopServiceAction->setObjectName("Stop Service"); + fileStopServiceAction->setIconText("Stop Service"); + fileMenu->insertAction(actionWPS, fileStopServiceAction); + + fileStartServiceAction = new QAction(this); + fileStartServiceAction->setObjectName("Start Service"); + fileStartServiceAction->setIconText("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("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(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; + add_iface = NULL; + udr = NULL; + tray_icon = NULL; + startInTray = false; + ctrl_iface = NULL; + ctrl_conn = NULL; + monitor_conn = NULL; + msgNotifier = NULL; + ctrl_iface_dir = strdup("/var/run/wpa_supplicant"); + + 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("connecting to wpa_supplicant"); + timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), SLOT(ping())); + timer->setSingleShot(FALSE); + timer->start(1000); + + if (openCtrlConnection(ctrl_iface) < 0) { + printf("Failed to open control connection to " + "wpa_supplicant.\n"); + } + + 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 (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; + for (;;) { + c = getopt(qApp->argc(), qApp->argv(), "i:p:t"); + if (c < 0) + break; + switch (c) { + case 'i': + free(ctrl_iface); + ctrl_iface = strdup(optarg); + break; + case 'p': + free(ctrl_iface_dir); + ctrl_iface_dir = strdup(optarg); + break; + case 't': + startInTray = 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; + printf("Selected interface '%s'\n", + 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(), + "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; + } + + printf("Trying to connect to '%s'\n", 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)) { + printf("Failed to attach to wpa_supplicant\n"); + 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; +} + + +static void wpa_gui_msg_cb(char *msg, size_t) +{ + /* This should not happen anymore since two control connections are + * used. */ + printf("missed message: %s\n", msg); +} + + +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, + wpa_gui_msg_cb); + if (ret == -2) + printf("'%s' command timed out.\n", cmd); + else if (ret < 0) + printf("'%s' command failed.\n", cmd); + + return ret; +} + + +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("Could not get status from " + "wpa_supplicant"); + textAuthentication->clear(); + textEncryption->clear(); + textSsid->clear(); + textBssid->clear(); + textIpAddress->clear(); + +#ifdef CONFIG_NATIVE_WINDOWS + static bool first = true; + if (first && connectedToService && + (ctrl_iface == NULL || *ctrl_iface == '\0')) { + first = false; + if (QMessageBox::information( + this, qAppName(), + "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; + + 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); + } 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(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; + } + } + + if (last) + break; + start = end + 1; + } + + 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 (!status_updated) + textStatus->clear(); + if (!auth_updated) + textAuthentication->clear(); + if (!ssid_updated) + textSsid->clear(); + if (!bssid_updated) + textBssid->clear(); + if (!ipaddr_updated) + textIpAddress->clear(); +} + + +void WpaGui::updateNetworks() +{ + char buf[2048], *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'; + + 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("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() +{ + printf("helpIndex\n"); +} + + +void WpaGui::helpContents() +{ + printf("helpContents\n"); +} + + +void WpaGui::helpAbout() +{ + QMessageBox::about(this, "wpa_gui for wpa_supplicant", + "Copyright (c) 2003-2008,\n" + "Jouni Malinen <j@w1.fi>\n" + "and contributors.\n" + "\n" + "This program is free software. You can\n" + "distribute it and/or modify it under the terms " + "of\n" + "the GNU General Public License version 2.\n" + "\n" + "Alternatively, this software may be distributed\n" + "under the terms of the BSD license.\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) { + printf("PING failed - trying to reconnect\n"); + if (openCtrlConnection(ctrl_iface) >= 0) { + printf("Reconnected successfully\n"); + 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 */ +} + + +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); + 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, + "Disconnected from network."); + else if (str_match(pos, WPA_EVENT_CONNECTED)) { + showTrayMessage(QSystemTrayIcon::Information, 3, + "Connection to network established."); + QTimer::singleShot(5 * 1000, this, SLOT(showTrayStatus())); + stopWpsRun(true); + } else if (str_match(pos, WPS_EVENT_AP_AVAILABLE_PBC)) { + showTrayMessage(QSystemTrayIcon::Information, 3, + "Wi-Fi Protected Setup (WPS) AP\n" + "in active PBC mode found."); + wpsStatusText->setText("WPS AP in active PBC mode found"); + if (textStatus->text() == "INACTIVE" || + textStatus->text() == "DISCONNECTED") + wpaguiTab->setCurrentWidget(wpsTab); + wpsInstructions->setText("Press the PBC button on the screen " + "to start registration"); + } else if (str_match(pos, WPS_EVENT_AP_AVAILABLE_PIN)) { + showTrayMessage(QSystemTrayIcon::Information, 3, + "Wi-Fi Protected Setup (WPS) AP\n" + " in active PIN mode found."); + wpsStatusText->setText("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)) { + showTrayMessage(QSystemTrayIcon::Information, 3, + "Wi-Fi Protected Setup (WPS)\n" + "AP detected."); + wpsStatusText->setText("WPS AP detected"); + } else if (str_match(pos, WPS_EVENT_OVERLAP)) { + showTrayMessage(QSystemTrayIcon::Information, 3, + "Wi-Fi Protected Setup (WPS)\n" + "PBC mode overlap detected."); + wpsStatusText->setText("PBC mode overlap detected"); + wpsInstructions->setText("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("Network configuration received"); + wpaguiTab->setCurrentWidget(wpsTab); + } else if (str_match(pos, WPA_EVENT_EAP_METHOD)) { + if (strstr(pos, "(WSC)")) + wpsStatusText->setText("Registration started"); + } else if (str_match(pos, WPS_EVENT_M2D)) { + wpsStatusText->setText("Registrar does not yet know PIN"); + } else if (str_match(pos, WPS_EVENT_FAIL)) { + wpsStatusText->setText("Registration failed"); + } else if (str_match(pos, WPS_EVENT_SUCCESS)) { + wpsStatusText->setText("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.startsWith("Select any")) { + cmd = "any"; + } else { + int pos = cmd.indexOf(':'); + if (pos < 0) { + printf("Invalid selectNetwork '%s'\n", + cmd.toAscii().constData()); + return; + } + cmd.truncate(pos); + } + cmd.prepend("SELECT_NETWORK "); + ctrlRequest(cmd.toAscii().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.startsWith("all")) { + int pos = cmd.indexOf(':'); + if (pos < 0) { + printf("Invalid enableNetwork '%s'\n", + cmd.toAscii().constData()); + return; + } + cmd.truncate(pos); + } + cmd.prepend("ENABLE_NETWORK "); + ctrlRequest(cmd.toAscii().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.startsWith("all")) { + int pos = cmd.indexOf(':'); + if (pos < 0) { + printf("Invalid disableNetwork '%s'\n", + cmd.toAscii().constData()); + return; + } + cmd.truncate(pos); + } + cmd.prepend("DISABLE_NETWORK "); + ctrlRequest(cmd.toAscii().constData(), reply, &reply_len); + triggerUpdate(); +} + + +void WpaGui::editNetwork(const QString &sel) +{ + QString cmd(sel); + int id = -1; + + if (!cmd.startsWith("Select any")) { + int pos = sel.indexOf(':'); + if (pos < 0) { + printf("Invalid editNetwork '%s'\n", + cmd.toAscii().constData()); + return; + } + cmd.truncate(pos); + 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, "No Networks", + "There are no networks to edit.\n"); + return; + } + QString sel(networkSelect->currentText()); + editNetwork(sel); +} + + +void WpaGui::editListedNetwork() +{ + if (networkList->currentRow() < 0) { + QMessageBox::information(this, "Select A Network", + "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.startsWith("Select any")) + return; + + if (!cmd.startsWith("all")) { + int pos = cmd.indexOf(':'); + if (pos < 0) { + printf("Invalid removeNetwork '%s'\n", + cmd.toAscii().constData()); + return; + } + cmd.truncate(pos); + } + cmd.prepend("REMOVE_NETWORK "); + ctrlRequest(cmd.toAscii().constData(), reply, &reply_len); + triggerUpdate(); +} + + +void WpaGui::removeSelectedNetwork() +{ + if (networkSelect->count() < 1) { + QMessageBox::information(this, "No Networks", + "There are no networks to remove.\n"); + return; + } + QString sel(networkSelect->currentText()); + removeNetwork(sel); +} + + +void WpaGui::removeListedNetwork() +{ + if (networkList->currentRow() < 0) { + QMessageBox::information(this, "Select A Network", + "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) { + printf("Invalid getNetworkDisabled '%s'\n", + cmd.toAscii().constData()); + return -1; + } + cmd.truncate(pos); + cmd.prepend("GET_NETWORK "); + cmd.append(" disabled"); + + if (ctrlRequest(cmd.toAscii().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, "Failed to save configuration", + "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, "Saved configuration", + "The current configuration was saved." + "\n"); +} + + +void WpaGui::selectAdapter( const QString & sel ) +{ + if (openCtrlConnection(sel.toAscii().constData()) < 0) + printf("Failed to open control connection to " + "wpa_supplicant.\n"); + updateStatus(); + updateNetworks(); +} + + +void WpaGui::createTrayIcon(bool trayOnly) +{ + QApplication::setQuitOnLastWindowClosed(false); + + tray_icon = new QSystemTrayIcon(this); + tray_icon->setToolTip(qAppName() + " - wpa_supplicant user interface"); + if (QImageReader::supportedImageFormats().contains(QByteArray("svg"))) + tray_icon->setIcon(QIcon(":/icons/wpa_gui.svg")); + else + tray_icon->setIcon(QIcon(":/icons/wpa_gui.png")); + + connect(tray_icon, + SIGNAL(activated(QSystemTrayIcon::ActivationReason)), + this, SLOT(trayActivated(QSystemTrayIcon::ActivationReason))); + + ackTrayIcon = false; + + tray_menu = new QMenu(this); + + disconnectAction = new QAction("&Disconnect", this); + reconnectAction = new QAction("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("&Event History", this); + scanAction = new QAction("Scan &Results", this); + statAction = new QAction("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("&Show Window", this); + hideAction = new QAction("&Hide Window", this); + quitAction = new QAction("&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()) + 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::closeEvent(QCloseEvent *event) +{ + if (eh) { + eh->close(); + delete eh; + eh = NULL; + } + + if (scanres) { + scanres->close(); + delete scanres; + scanres = 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() + " will keep running in " + "the system tray."); + } else { + QMessageBox::information(this, qAppName() + " systray", + "The program will keep " + "running in the system " + "tray."); + } + ackTrayIcon = true; + } + + event->accept(); +} + + +void WpaGui::wpsDialog() +{ + wpaguiTab->setCurrentWidget(wpsTab); +} + + +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("WPS AP in active PBC mode found")) { + wpsInstructions->setText("Press the push button on the AP to " + "start the PBC mode."); + } else { + wpsInstructions->setText("If you have not yet done so, press " + "the push button on the AP to start " + "the PBC mode."); + } + wpsStatusText->setText("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("Enter the generated PIN into the Registrar " + "(either the internal one in the AP or an " + "external one)."); + wpsStatusText->setText("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("WPS AP selected from scan results"); + wpsInstructions->setText("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.toAscii().constData(), reply, &reply_len) < 0) + return; + + wpsStatusText->setText("Waiting for AP/Enrollee"); + wpsRunning = true; +} + + +void WpaGui::stopWpsRun(bool success) +{ + if (wpsRunning) + wpsStatusText->setText(success ? "Connected to the network" : + "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("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("OpenSCManager failed"); + return; + } + + svc = OpenService(scm, WPASVC_NAME, SERVICE_START); + if (!svc) { + ErrorMsg(this).showMsg("OpenService failed"); + CloseServiceHandle(scm); + return; + } + + if (!StartService(svc, 0, NULL)) { + ErrorMsg(this).showMsg("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("OpenSCManager failed"); + return; + } + + svc = OpenService(scm, WPASVC_NAME, SERVICE_STOP); + if (!svc) { + ErrorMsg(this).showMsg("OpenService failed"); + CloseServiceHandle(scm); + return; + } + + if (!ControlService(svc, SERVICE_CONTROL_STOP, &status)) { + ErrorMsg(this).showMsg("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) { + printf("OpenSCManager failed: %d\n", (int) GetLastError()); + return false; + } + + svc = OpenService(scm, WPASVC_NAME, SERVICE_QUERY_STATUS); + if (!svc) { + printf("OpenService failed: %d\n\n", (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 0000000..741cf17 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/wpagui.h @@ -0,0 +1,147 @@ +/* + * wpa_gui - WpaGui class + * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef WPAGUI_H +#define WPAGUI_H + +#include <QSystemTrayIcon> +#include <QObject> +#include "ui_wpagui.h" +#include "addinterface.h" + +class UserDataRequest; + + +class WpaGui : public QMainWindow, public Ui::WpaGui +{ + Q_OBJECT + +public: + WpaGui(QApplication *app, QWidget *parent = 0, const char *name = 0, + Qt::WFlags 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 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 wpsDialog(); + 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; + 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; + void createTrayIcon(bool); + bool ackTrayIcon; + bool startInTray; + + int openCtrlConnection(const char *ifname); + + bool wpsRunning; + + QString bssFromScan; + + void stopWpsRun(bool success); + +#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 0000000..cd67ecb --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/wpagui.ui @@ -0,0 +1,517 @@ +<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="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> + </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> + </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 0000000..4950b21 --- /dev/null +++ b/wpa_supplicant/wpa_gui-qt4/wpamsg.h @@ -0,0 +1,41 @@ +/* + * wpa_gui - WpaMsg class for storing event messages + * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#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_gui/eventhistory.ui b/wpa_supplicant/wpa_gui/eventhistory.ui new file mode 100644 index 0000000..3735fb7 --- /dev/null +++ b/wpa_supplicant/wpa_gui/eventhistory.ui @@ -0,0 +1,125 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>EventHistory</class> +<widget class="QDialog"> + <property name="name"> + <cstring>EventHistory</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>533</width> + <height>285</height> + </rect> + </property> + <property name="caption"> + <string>Event history</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QListView"> + <column> + <property name="text"> + <string>Timestamp</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>Message</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <property name="name"> + <cstring>eventListView</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>7</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="resizePolicy"> + <enum>Manual</enum> + </property> + <property name="selectionMode"> + <enum>NoSelection</enum> + </property> + <property name="resizeMode"> + <enum>LastColumn</enum> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout30</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer3</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>closeButton</cstring> + </property> + <property name="text"> + <string>Close</string> + </property> + </widget> + </hbox> + </widget> + </vbox> +</widget> +<connections> + <connection> + <sender>closeButton</sender> + <signal>clicked()</signal> + <receiver>EventHistory</receiver> + <slot>close()</slot> + </connection> +</connections> +<includes> + <include location="local" impldecl="in declaration">wpamsg.h</include> + <include location="local" impldecl="in implementation">eventhistory.ui.h</include> +</includes> +<slots> + <slot>addEvents( WpaMsgList msgs )</slot> + <slot>addEvent( WpaMsg msg )</slot> +</slots> +<functions> + <function access="private" specifier="non virtual">init()</function> + <function access="private" specifier="non virtual">destroy()</function> +</functions> +<pixmapinproject/> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/wpa_supplicant/wpa_gui/eventhistory.ui.h b/wpa_supplicant/wpa_gui/eventhistory.ui.h new file mode 100644 index 0000000..cb2caab --- /dev/null +++ b/wpa_supplicant/wpa_gui/eventhistory.ui.h @@ -0,0 +1,41 @@ +/**************************************************************************** +** ui.h extension file, included from the uic-generated form implementation. +** +** If you want to add, delete, or rename functions or slots, use +** Qt Designer to update this file, preserving your code. +** +** You should not define a constructor or destructor in this file. +** Instead, write your code in functions called init() and destroy(). +** These will automatically be called by the form's constructor and +** destructor. +*****************************************************************************/ + +void EventHistory::init() +{ +} + + +void EventHistory::destroy() +{ +} + + +void EventHistory::addEvents(WpaMsgList msgs) +{ + WpaMsgList::iterator it; + for (it = msgs.begin(); it != msgs.end(); it++) { + addEvent(*it); + } +} + + +void EventHistory::addEvent(WpaMsg msg) +{ + Q3ListViewItem *item; + item = new Q3ListViewItem(eventListView, + msg.getTimestamp().toString("yyyy-MM-dd hh:mm:ss.zzz"), + msg.getMsg()); + if (item == NULL) + return; + eventListView->setSelected(item, false); +} diff --git a/wpa_supplicant/wpa_gui/main.cpp b/wpa_supplicant/wpa_gui/main.cpp new file mode 100644 index 0000000..a78473a --- /dev/null +++ b/wpa_supplicant/wpa_gui/main.cpp @@ -0,0 +1,30 @@ +#ifdef CONFIG_NATIVE_WINDOWS +#include <winsock.h> +#endif /* CONFIG_NATIVE_WINDOWS */ +#include <qapplication.h> +#include "wpagui.h" + +int main( int argc, char ** argv ) +{ + QApplication a( argc, argv ); + WpaGui w; + int ret; + +#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 */ + + w.show(); + a.connect( &a, SIGNAL( lastWindowClosed() ), &a, SLOT( quit() ) ); + ret = a.exec(); + +#ifdef CONFIG_NATIVE_WINDOWS + WSACleanup(); +#endif /* CONFIG_NATIVE_WINDOWS */ + + return ret; +} diff --git a/wpa_supplicant/wpa_gui/networkconfig.ui b/wpa_supplicant/wpa_gui/networkconfig.ui new file mode 100644 index 0000000..019ecf7 --- /dev/null +++ b/wpa_supplicant/wpa_gui/networkconfig.ui @@ -0,0 +1,475 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>NetworkConfig</class> +<widget class="QDialog"> + <property name="name"> + <cstring>NetworkConfig</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>380</width> + <height>430</height> + </rect> + </property> + <property name="caption"> + <string>NetworkConfig</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QPushButton" row="1" column="3"> + <property name="name"> + <cstring>cancelButton</cstring> + </property> + <property name="text"> + <string>Cancel</string> + </property> + </widget> + <widget class="QFrame" row="0" column="0" rowspan="1" colspan="4"> + <property name="name"> + <cstring>frame9</cstring> + </property> + <property name="frameShape"> + <enum>StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>Raised</enum> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel0</cstring> + </property> + <property name="text"> + <string>SSID</string> + </property> + </widget> + <widget class="QLineEdit" row="0" column="1"> + <property name="name"> + <cstring>ssidEdit</cstring> + </property> + <property name="text"> + <string></string> + </property> + <property name="toolTip" stdset="0"> + <string>Network name (Service Set IDentifier)</string> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Network ID</string> + </property> + </widget> + <widget class="QLineEdit" row="1" column="1"> + <property name="name"> + <cstring>idstrEdit</cstring> + </property> + <property name="text"> + <string></string> + </property> + <property name="toolTip" stdset="0"> + <string>Network Identification String</string> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>Authentication</string> + </property> + </widget> + <widget class="QComboBox" row="2" column="1"> + <item> + <property name="text"> + <string>Plaintext or static WEP</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> + <property name="name"> + <cstring>authSelect</cstring> + </property> + </widget> + <widget class="QLabel" row="3" column="0"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="text"> + <string>Encryption</string> + </property> + </widget> + <widget class="QComboBox" row="3" column="1"> + <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> + <property name="name"> + <cstring>encrSelect</cstring> + </property> + </widget> + <widget class="QLabel" row="4" column="0"> + <property name="name"> + <cstring>textLabel4</cstring> + </property> + <property name="text"> + <string>PSK</string> + </property> + </widget> + <widget class="QLineEdit" row="4" column="1"> + <property name="name"> + <cstring>pskEdit</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="echoMode"> + <enum>Password</enum> + </property> + <property name="toolTip" stdset="0"> + <string>WPA/WPA2 pre-shared key or passphrase</string> + </property> + <property name="whatsThis" stdset="0"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="5" column="0"> + <property name="name"> + <cstring>textLabel5</cstring> + </property> + <property name="text"> + <string>EAP method</string> + </property> + </widget> + <widget class="QComboBox" row="5" column="1"> + <property name="name"> + <cstring>eapSelect</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="QLabel" row="6" column="0"> + <property name="name"> + <cstring>textLabel6</cstring> + </property> + <property name="text"> + <string>Identity</string> + </property> + </widget> + <widget class="QLineEdit" row="6" column="1"> + <property name="name"> + <cstring>identityEdit</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="toolTip" stdset="0"> + <string>Username/Identity for EAP methods</string> + </property> + </widget> + <widget class="QLabel" row="7" column="0"> + <property name="name"> + <cstring>textLabel7</cstring> + </property> + <property name="text"> + <string>Password</string> + </property> + </widget> + <widget class="QLineEdit" row="7" column="1"> + <property name="name"> + <cstring>passwordEdit</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="echoMode"> + <enum>Password</enum> + </property> + <property name="toolTip" stdset="0"> + <string>Password for EAP methods</string> + </property> + </widget> + <widget class="QLabel" row="8" column="0"> + <property name="name"> + <cstring>textLabel1_2</cstring> + </property> + <property name="text"> + <string>CA certificate</string> + </property> + </widget> + <widget class="QLineEdit" row="8" column="1"> + <property name="name"> + <cstring>cacertEdit</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="QButtonGroup" row="9" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>buttonGroup1</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="title"> + <string>WEP keys</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QRadioButton" row="0" column="0"> + <property name="name"> + <cstring>wep0Radio</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>key 0</string> + </property> + </widget> + <widget class="QRadioButton" row="1" column="0"> + <property name="name"> + <cstring>wep1Radio</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>key 1</string> + </property> + </widget> + <widget class="QRadioButton" row="3" column="0"> + <property name="name"> + <cstring>wep3Radio</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>key 3</string> + </property> + </widget> + <widget class="QRadioButton" row="2" column="0"> + <property name="name"> + <cstring>wep2Radio</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>key 2</string> + </property> + </widget> + <widget class="QLineEdit" row="0" column="1"> + <property name="name"> + <cstring>wep0Edit</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="QLineEdit" row="1" column="1"> + <property name="name"> + <cstring>wep1Edit</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="QLineEdit" row="2" column="1"> + <property name="name"> + <cstring>wep2Edit</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="QLineEdit" row="3" column="1"> + <property name="name"> + <cstring>wep3Edit</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + </grid> + </widget> + </grid> + </widget> + <spacer row="1" column="0"> + <property name="name"> + <cstring>spacer5</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>130</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QPushButton" row="1" column="1"> + <property name="name"> + <cstring>addButton</cstring> + </property> + <property name="text"> + <string>Add</string> + </property> + </widget> + <widget class="QPushButton" row="1" column="2"> + <property name="name"> + <cstring>removeButton</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Remove</string> + </property> + </widget> + </grid> +</widget> +<connections> + <connection> + <sender>authSelect</sender> + <signal>activated(int)</signal> + <receiver>NetworkConfig</receiver> + <slot>authChanged(int)</slot> + </connection> + <connection> + <sender>cancelButton</sender> + <signal>clicked()</signal> + <receiver>NetworkConfig</receiver> + <slot>close()</slot> + </connection> + <connection> + <sender>addButton</sender> + <signal>clicked()</signal> + <receiver>NetworkConfig</receiver> + <slot>addNetwork()</slot> + </connection> + <connection> + <sender>encrSelect</sender> + <signal>activated(const QString&)</signal> + <receiver>NetworkConfig</receiver> + <slot>encrChanged(const QString&)</slot> + </connection> + <connection> + <sender>removeButton</sender> + <signal>clicked()</signal> + <receiver>NetworkConfig</receiver> + <slot>removeNetwork()</slot> + </connection> +</connections> +<tabstops> + <tabstop>ssidEdit</tabstop> + <tabstop>idstrEdit</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>wep1Radio</tabstop> + <tabstop>wep2Radio</tabstop> + <tabstop>wep3Radio</tabstop> + <tabstop>wep0Edit</tabstop> + <tabstop>wep1Edit</tabstop> + <tabstop>wep2Edit</tabstop> + <tabstop>wep3Edit</tabstop> + <tabstop>addButton</tabstop> + <tabstop>removeButton</tabstop> + <tabstop>cancelButton</tabstop> +</tabstops> +<includes> + <include location="global" impldecl="in declaration">qlistview.h</include> + <include location="global" impldecl="in implementation">qmessagebox.h</include> + <include location="local" impldecl="in implementation">wpagui.h</include> + <include location="local" impldecl="in implementation">networkconfig.ui.h</include> +</includes> +<forwards> + <forward>class WpaGui;</forward> +</forwards> +<variables> + <variable access="private">WpaGui *wpagui;</variable> + <variable access="private">int edit_network_id;</variable> + <variable access="private">bool new_network;</variable> +</variables> +<slots> + <slot>authChanged( int sel )</slot> + <slot>addNetwork()</slot> + <slot>encrChanged( const QString & sel )</slot> + <slot>writeWepKey( int network_id, QLineEdit * edit, int id )</slot> + <slot>removeNetwork()</slot> +</slots> +<functions> + <function access="private" specifier="non virtual">init()</function> + <function>paramsFromScanResults( QListViewItem * sel )</function> + <function>setWpaGui( WpaGui * _wpagui )</function> + <function returnType="int">setNetworkParam( int id, const char * field, const char * value, bool quote )</function> + <function access="private">wepEnabled( bool enabled )</function> + <function>paramsFromConfig( int network_id )</function> + <function>newNetwork()</function> + <function access="private">getEapCapa()</function> +</functions> +<pixmapinproject/> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/wpa_supplicant/wpa_gui/networkconfig.ui.h b/wpa_supplicant/wpa_gui/networkconfig.ui.h new file mode 100644 index 0000000..501d5d2 --- /dev/null +++ b/wpa_supplicant/wpa_gui/networkconfig.ui.h @@ -0,0 +1,552 @@ +/**************************************************************************** +** ui.h extension file, included from the uic-generated form implementation. +** +** If you want to add, delete, or rename functions or slots, use +** Qt Designer to update this file, preserving your code. +** +** You should not define a constructor or destructor in this file. +** Instead, write your code in functions called init() and destroy(). +** These will automatically be called by the form's constructor and +** destructor. +*****************************************************************************/ + +#include <stdlib.h> + +enum { + AUTH_NONE = 0, + AUTH_IEEE8021X = 1, + AUTH_WPA_PSK = 2, + AUTH_WPA_EAP = 3, + AUTH_WPA2_PSK = 4, + AUTH_WPA2_EAP = 5 +}; + +#define WPA_GUI_KEY_DATA "[key is configured]" + +void NetworkConfig::init() +{ + wpagui = NULL; + new_network = false; +} + +void NetworkConfig::paramsFromScanResults(Q3ListViewItem *sel) +{ + new_network = true; + + /* SSID BSSID frequency signal flags */ + setCaption(sel->text(0)); + ssidEdit->setText(sel->text(0)); + + QString flags = sel->text(4); + int auth, encr = 0; + if (flags.find("[WPA2-EAP") >= 0) + auth = AUTH_WPA2_EAP; + else if (flags.find("[WPA-EAP") >= 0) + auth = AUTH_WPA_EAP; + else if (flags.find("[WPA2-PSK") >= 0) + auth = AUTH_WPA2_PSK; + else if (flags.find("[WPA-PSK") >= 0) + auth = AUTH_WPA_PSK; + else + auth = AUTH_NONE; + + if (flags.find("-CCMP") >= 0) + encr = 1; + else if (flags.find("-TKIP") >= 0) + encr = 0; + else if (flags.find("WEP") >= 0) + encr = 1; + else + encr = 0; + + authSelect->setCurrentItem(auth); + authChanged(auth); + encrSelect->setCurrentItem(encr); + + getEapCapa(); +} + + +void NetworkConfig::authChanged(int sel) +{ + 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); + + while (encrSelect->count()) + encrSelect->removeItem(0); + + if (sel == AUTH_NONE || sel == AUTH_IEEE8021X) { + encrSelect->insertItem("None"); + encrSelect->insertItem("WEP"); + encrSelect->setCurrentItem(sel == AUTH_NONE ? 0 : 1); + } else { + encrSelect->insertItem("TKIP"); + encrSelect->insertItem("CCMP"); + encrSelect->setCurrentItem((sel == AUTH_WPA2_PSK || + sel == AUTH_WPA2_EAP) ? 1 : 0); + } + + wepEnabled(sel == AUTH_IEEE8021X); +} + + +void NetworkConfig::addNetwork() +{ + char reply[10], cmd[256]; + size_t reply_len; + int id; + int psklen = pskEdit->text().length(); + int auth = authSelect->currentItem(); + + if (auth == AUTH_WPA_PSK || auth == AUTH_WPA2_PSK) { + if (psklen < 8 || psklen > 64) { + QMessageBox::warning(this, "wpa_gui", "WPA-PSK requires a passphrase " + "of 8 to 63 characters\n" + "or 64 hex digit PSK"); + 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", "Failed to add network to wpa_supplicant\n" + "configuration."); + return; + } + id = atoi(reply); + } else { + id = edit_network_id; + } + + setNetworkParam(id, "ssid", ssidEdit->text().ascii(), true); + + if (idstrEdit->isEnabled()) + setNetworkParam(id, "id_str", idstrEdit->text().ascii(), true); + + const char *key_mgmt = NULL, *proto = NULL, *pairwise = NULL; + switch (auth) { + case AUTH_NONE: + 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_WPA_PSK || auth == AUTH_WPA_EAP || + auth == AUTH_WPA2_PSK || auth == AUTH_WPA2_EAP) { + int encr = encrSelect->currentItem(); + 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().ascii(), WPA_GUI_KEY_DATA) != 0) + setNetworkParam(id, "psk", pskEdit->text().ascii(), psklen != 64); + if (eapSelect->isEnabled()) + setNetworkParam(id, "eap", eapSelect->currentText().ascii(), false); + if (identityEdit->isEnabled()) + setNetworkParam(id, "identity", identityEdit->text().ascii(), true); + if (passwordEdit->isEnabled() && + strcmp(passwordEdit->text().ascii(), WPA_GUI_KEY_DATA) != 0) + setNetworkParam(id, "password", passwordEdit->text().ascii(), true); + if (cacertEdit->isEnabled()) + setNetworkParam(id, "ca_cert", cacertEdit->text().ascii(), true); + 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); + + 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", "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 &sel ) +{ + wepEnabled(sel.find("WEP") == 0); +} + + +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().ascii(); + 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 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 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, 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) + 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); + } + + 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->text(i).compare(reply) == 0) { + eapSelect->setCurrentItem(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 || auth == AUTH_IEEE8021X) + encr = 1; + + wepEdit->setText(reply + 1); + } else if (res >= 0 && key_value_isset(reply, reply_len)) { + if (auth == AUTH_NONE || auth == AUTH_IEEE8021X) + encr = 1; + wepEdit->setText(WPA_GUI_KEY_DATA); + } + } + + 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; + } + } + + authSelect->setCurrentItem(auth); + authChanged(auth); + encrSelect->setCurrentItem(encr); + if (auth == AUTH_NONE || auth == AUTH_IEEE8021X) + wepEnabled(encr == 1); + + removeButton->setEnabled(true); + addButton->setText("Save"); +} + + +void NetworkConfig::removeNetwork() +{ + char reply[10], cmd[256]; + size_t reply_len; + + if (QMessageBox::information(this, "wpa_gui", + "This will permanently remove the network\n" + "from the configuration. Do you really want\n" + "to remove this network?", "Yes", "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", + "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 = QStringList::split(QChar(' '), res); + eapSelect->insertStringList(types); +} diff --git a/wpa_supplicant/wpa_gui/scanresults.ui b/wpa_supplicant/wpa_gui/scanresults.ui new file mode 100644 index 0000000..66c8b4b --- /dev/null +++ b/wpa_supplicant/wpa_gui/scanresults.ui @@ -0,0 +1,179 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>ScanResults</class> +<widget class="QDialog"> + <property name="name"> + <cstring>ScanResults</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>452</width> + <height>225</height> + </rect> + </property> + <property name="caption"> + <string>Scan results</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QListView"> + <column> + <property name="text"> + <string>SSID</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>BSSID</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>frequency</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>signal</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>flags</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <property name="name"> + <cstring>scanResultsView</cstring> + </property> + <property name="frameShape"> + <enum>StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>Sunken</enum> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout24</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer6</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>50</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>scanButton</cstring> + </property> + <property name="text"> + <string>Scan</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>closeButton</cstring> + </property> + <property name="text"> + <string>Close</string> + </property> + </widget> + </hbox> + </widget> + </vbox> +</widget> +<connections> + <connection> + <sender>closeButton</sender> + <signal>clicked()</signal> + <receiver>ScanResults</receiver> + <slot>close()</slot> + </connection> + <connection> + <sender>scanButton</sender> + <signal>clicked()</signal> + <receiver>ScanResults</receiver> + <slot>scanRequest()</slot> + </connection> + <connection> + <sender>scanResultsView</sender> + <signal>doubleClicked(QListViewItem*)</signal> + <receiver>ScanResults</receiver> + <slot>bssSelected(QListViewItem*)</slot> + </connection> +</connections> +<includes> + <include location="local" impldecl="in implementation">wpa_ctrl.h</include> + <include location="local" impldecl="in implementation">wpagui.h</include> + <include location="local" impldecl="in implementation">networkconfig.h</include> + <include location="local" impldecl="in implementation">scanresults.ui.h</include> +</includes> +<forwards> + <forward>class WpaGui;</forward> +</forwards> +<variables> + <variable access="private">WpaGui *wpagui;</variable> + <variable access="private">QTimer *timer;</variable> +</variables> +<slots> + <slot>setWpaGui( WpaGui * _wpagui )</slot> + <slot>updateResults()</slot> + <slot>scanRequest()</slot> + <slot>getResults()</slot> + <slot>bssSelected( QListViewItem * sel )</slot> +</slots> +<functions> + <function access="private" specifier="non virtual">init()</function> + <function access="private" specifier="non virtual">destroy()</function> +</functions> +<pixmapinproject/> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/wpa_supplicant/wpa_gui/scanresults.ui.h b/wpa_supplicant/wpa_gui/scanresults.ui.h new file mode 100644 index 0000000..530d2e6 --- /dev/null +++ b/wpa_supplicant/wpa_gui/scanresults.ui.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** ui.h extension file, included from the uic-generated form implementation. +** +** If you want to add, delete, or rename functions or slots, use +** Qt Designer to update this file, preserving your code. +** +** You should not define a constructor or destructor in this file. +** Instead, write your code in functions called init() and destroy(). +** These will automatically be called by the form's constructor and +** destructor. +*****************************************************************************/ + +void ScanResults::init() +{ + wpagui = NULL; +} + + +void ScanResults::destroy() +{ + delete timer; +} + + +void ScanResults::setWpaGui(WpaGui *_wpagui) +{ + wpagui = _wpagui; + updateResults(); + + timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), SLOT(getResults())); + timer->start(10000, FALSE); +} + + +void ScanResults::updateResults() +{ + char reply[8192]; + size_t reply_len; + + if (wpagui == NULL) + return; + + reply_len = sizeof(reply) - 1; + if (wpagui->ctrlRequest("SCAN_RESULTS", reply, &reply_len) < 0) + return; + reply[reply_len] = '\0'; + + scanResultsView->clear(); + + QString res(reply); + QStringList lines = QStringList::split(QChar('\n'), res); + bool first = true; + for (QStringList::Iterator it = lines.begin(); it != lines.end(); it++) { + if (first) { + first = false; + continue; + } + + QStringList cols = QStringList::split(QChar('\t'), *it, true); + QString ssid, bssid, freq, signal, flags; + bssid = cols.count() > 0 ? cols[0] : ""; + freq = cols.count() > 1 ? cols[1] : ""; + signal = cols.count() > 2 ? cols[2] : ""; + flags = cols.count() > 3 ? cols[3] : ""; + ssid = cols.count() > 4 ? cols[4] : ""; + new Q3ListViewItem(scanResultsView, ssid, bssid, freq, signal, flags); + } +} + + +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( Q3ListViewItem * 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/setup-mingw-cross-compiling b/wpa_supplicant/wpa_gui/setup-mingw-cross-compiling new file mode 100755 index 0000000..e173b00 --- /dev/null +++ b/wpa_supplicant/wpa_gui/setup-mingw-cross-compiling @@ -0,0 +1,11 @@ +#!/bin/sh + +# qmake seems to be forcing include and lib paths from the original build +# and I have no idea how to change these. For now, just override the +# directories in the Makefile.Release file after qmake run. + +qmake -spec /q/jm/qt4-win/4.0.0/mkspecs/win32-g++ wpa_gui.pro -o Makefile +cat Makefile.Release | + sed s%qt4/lib%qt4-win/4.0.0/lib%g | + sed s%qt4/include%qt4-win/4.0.0/include%g > tmp.Makefile.Release && +mv -f tmp.Makefile.Release Makefile.Release diff --git a/wpa_supplicant/wpa_gui/userdatarequest.ui b/wpa_supplicant/wpa_gui/userdatarequest.ui new file mode 100644 index 0000000..c3d545f --- /dev/null +++ b/wpa_supplicant/wpa_gui/userdatarequest.ui @@ -0,0 +1,163 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>UserDataRequest</class> +<widget class="QDialog"> + <property name="name"> + <cstring>UserDataRequest</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>216</width> + <height>103</height> + </rect> + </property> + <property name="caption"> + <string>Authentication credentials required</string> + </property> + <property name="sizeGripEnabled"> + <bool>true</bool> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>queryInfo</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout28</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>queryField</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>queryEdit</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="echoMode"> + <enum>Password</enum> + </property> + </widget> + </hbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout27</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer4</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>buttonOk</cstring> + </property> + <property name="text"> + <string>&OK</string> + </property> + <property name="accel"> + <string></string> + </property> + <property name="autoDefault"> + <bool>true</bool> + </property> + <property name="default"> + <bool>true</bool> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>buttonCancel</cstring> + </property> + <property name="text"> + <string>&Cancel</string> + </property> + <property name="accel"> + <string></string> + </property> + <property name="autoDefault"> + <bool>true</bool> + </property> + </widget> + </hbox> + </widget> + </vbox> +</widget> +<connections> + <connection> + <sender>buttonOk</sender> + <signal>clicked()</signal> + <receiver>UserDataRequest</receiver> + <slot>sendReply()</slot> + </connection> + <connection> + <sender>buttonCancel</sender> + <signal>clicked()</signal> + <receiver>UserDataRequest</receiver> + <slot>reject()</slot> + </connection> + <connection> + <sender>queryEdit</sender> + <signal>returnPressed()</signal> + <receiver>UserDataRequest</receiver> + <slot>sendReply()</slot> + </connection> +</connections> +<includes> + <include location="local" impldecl="in implementation">wpa_ctrl.h</include> + <include location="local" impldecl="in implementation">wpagui.h</include> + <include location="local" impldecl="in implementation">userdatarequest.ui.h</include> +</includes> +<forwards> + <forward>class WpaGui;</forward> +</forwards> +<variables> + <variable access="private">WpaGui *wpagui;</variable> + <variable access="private">int networkid;</variable> + <variable access="private">QString field;</variable> +</variables> +<slots> + <slot>sendReply()</slot> +</slots> +<functions> + <function specifier="non virtual" returnType="int">setParams( WpaGui * _wpagui, const char * reqMsg )</function> +</functions> +<pixmapinproject/> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/wpa_supplicant/wpa_gui/userdatarequest.ui.h b/wpa_supplicant/wpa_gui/userdatarequest.ui.h new file mode 100644 index 0000000..66d4478 --- /dev/null +++ b/wpa_supplicant/wpa_gui/userdatarequest.ui.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** ui.h extension file, included from the uic-generated form implementation. +** +** If you want to add, delete, or rename functions or slots, use +** Qt Designer to update this file, preserving your code. +** +** You should not define a constructor or destructor in this file. +** Instead, write your code in functions called init() and destroy(). +** These will automatically be called by the form's constructor and +** destructor. +*****************************************************************************/ + +#include <stdlib.h> + +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("Password: "); + queryEdit->setEchoMode(QLineEdit::Password); + } else if (strcmp(tmp, "NEW_PASSWORD") == 0) { + queryField->setText("New password: "); + queryEdit->setEchoMode(QLineEdit::Password); + } else if (strcmp(tmp, "IDENTITY") == 0) + queryField->setText("Identity: "); + else if (strcmp(tmp, "PASSPHRASE") == 0) { + queryField->setText("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.ascii(), reply, &reply_len); + accept(); +} diff --git a/wpa_supplicant/wpa_gui/wpa_gui.pro b/wpa_supplicant/wpa_gui/wpa_gui.pro new file mode 100644 index 0000000..6363db9 --- /dev/null +++ b/wpa_supplicant/wpa_gui/wpa_gui.pro @@ -0,0 +1,50 @@ +TEMPLATE = app +LANGUAGE = C++ + +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 + DEFINES += CONFIG_NATIVE_WINDOWS CONFIG_CTRL_IFACE_NAMED_PIPE + SOURCES += ../../src/utils/os_win32.c +} else { + DEFINES += CONFIG_CTRL_IFACE_UNIX + SOURCES += ../../src/utils/os_unix.c +} + +INCLUDEPATH += . .. ../../src/utils ../../src/common + +HEADERS += wpamsg.h + +SOURCES += main.cpp \ + ../../src/common/wpa_ctrl.c + +FORMS = wpagui.ui \ + eventhistory.ui \ + scanresults.ui \ + userdatarequest.ui \ + networkconfig.ui + + +unix { + UI_DIR = .ui + MOC_DIR = .moc + OBJECTS_DIR = .obj +} + +qtver = $$[QT_VERSION] +isEmpty( qtver ) { + message(Compiling for Qt 3.x) + DEFINES += Q3ListViewItem=QListViewItem +} else { + message(Compiling for Qt $$qtver) + QT += qt3support + CONFIG += uic3 +} diff --git a/wpa_supplicant/wpa_gui/wpagui.ui b/wpa_supplicant/wpa_gui/wpagui.ui new file mode 100644 index 0000000..01666a3 --- /dev/null +++ b/wpa_supplicant/wpa_gui/wpagui.ui @@ -0,0 +1,471 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>WpaGui</class> +<widget class="QMainWindow"> + <property name="name"> + <cstring>WpaGui</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>279</width> + <height>308</height> + </rect> + </property> + <property name="caption"> + <string>wpa_gui</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>textLabel16</cstring> + </property> + <property name="text"> + <string>Adapter:</string> + </property> + </widget> + <widget class="QComboBox" row="0" column="2" rowspan="1" colspan="2"> + <property name="name"> + <cstring>adapterSelect</cstring> + </property> + </widget> + <widget class="QLabel" row="1" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>textLabel8</cstring> + </property> + <property name="text"> + <string>Network:</string> + </property> + </widget> + <widget class="QComboBox" row="1" column="2" rowspan="1" colspan="2"> + <property name="name"> + <cstring>networkSelect</cstring> + </property> + </widget> + <widget class="QFrame" row="2" column="0" rowspan="1" colspan="4"> + <property name="name"> + <cstring>frame3</cstring> + </property> + <property name="frameShape"> + <enum>StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>Raised</enum> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Status:</string> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>Last message:</string> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="text"> + <string>Authentication:</string> + </property> + </widget> + <widget class="QLabel" row="3" column="0"> + <property name="name"> + <cstring>textLabel4</cstring> + </property> + <property name="text"> + <string>Encryption:</string> + </property> + </widget> + <widget class="QLabel" row="4" column="0"> + <property name="name"> + <cstring>textLabel5</cstring> + </property> + <property name="text"> + <string>SSID:</string> + </property> + </widget> + <widget class="QLabel" row="5" column="0"> + <property name="name"> + <cstring>textLabel6</cstring> + </property> + <property name="text"> + <string>BSSID:</string> + </property> + </widget> + <widget class="QLabel" row="6" column="0"> + <property name="name"> + <cstring>textLabel7</cstring> + </property> + <property name="text"> + <string>IP address:</string> + </property> + </widget> + <widget class="QLabel" row="0" column="1"> + <property name="name"> + <cstring>textStatus</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="1" column="1" rowspan="1" colspan="3"> + <property name="name"> + <cstring>textLastMessage</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="2" column="1"> + <property name="name"> + <cstring>textAuthentication</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="3" column="1"> + <property name="name"> + <cstring>textEncryption</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="4" column="1"> + <property name="name"> + <cstring>textSsid</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="5" column="1"> + <property name="name"> + <cstring>textBssid</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QLabel" row="6" column="1"> + <property name="name"> + <cstring>textIpAddress</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + </grid> + </widget> + <spacer row="3" column="0"> + <property name="name"> + <cstring>spacer7</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>16</width> + <height>16</height> + </size> + </property> + </spacer> + <widget class="QPushButton" row="3" column="1"> + <property name="name"> + <cstring>connectButton</cstring> + </property> + <property name="text"> + <string>Connect</string> + </property> + </widget> + <widget class="QPushButton" row="3" column="2"> + <property name="name"> + <cstring>disconnectButton</cstring> + </property> + <property name="text"> + <string>Disconnect</string> + </property> + </widget> + <widget class="QPushButton" row="3" column="3"> + <property name="name"> + <cstring>scanButton</cstring> + </property> + <property name="text"> + <string>Scan</string> + </property> + </widget> + </grid> +</widget> +<menubar> + <property name="name"> + <cstring>MenuBar</cstring> + </property> + <item text="&File" name="fileMenu"> + <separator/> + <action name="fileEventHistoryAction"/> + <action name="fileAdd_NetworkAction"/> + <action name="fileEdit_networkAction"/> + <separator/> + <action name="fileExitAction"/> + </item> + <item text="&Help" name="helpMenu"> + <action name="helpContentsAction"/> + <action name="helpIndexAction"/> + <separator/> + <action name="helpAboutAction"/> + </item> +</menubar> +<toolbars> +</toolbars> +<actions> + <action> + <property name="name"> + <cstring>fileExitAction</cstring> + </property> + <property name="text"> + <string>Exit</string> + </property> + <property name="menuText"> + <string>E&xit</string> + </property> + <property name="accel"> + <string>Ctrl+Q</string> + </property> + </action> + <action> + <property name="name"> + <cstring>helpContentsAction</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Contents</string> + </property> + <property name="menuText"> + <string>&Contents...</string> + </property> + <property name="accel"> + <string></string> + </property> + </action> + <action> + <property name="name"> + <cstring>helpIndexAction</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Index</string> + </property> + <property name="menuText"> + <string>&Index...</string> + </property> + <property name="accel"> + <string></string> + </property> + </action> + <action> + <property name="name"> + <cstring>helpAboutAction</cstring> + </property> + <property name="text"> + <string>About</string> + </property> + <property name="menuText"> + <string>&About</string> + </property> + <property name="accel"> + <string></string> + </property> + </action> + <action> + <property name="name"> + <cstring>fileEventHistoryAction</cstring> + </property> + <property name="text"> + <string>Event History</string> + </property> + <property name="menuText"> + <string>Event &History</string> + </property> + </action> + <action> + <property name="name"> + <cstring>fileAdd_NetworkAction</cstring> + </property> + <property name="text"> + <string>Add Network</string> + </property> + <property name="menuText"> + <string>&Add Network</string> + </property> + </action> + <action> + <property name="name"> + <cstring>fileEdit_networkAction</cstring> + </property> + <property name="text"> + <string>Edit Network</string> + </property> + <property name="menuText"> + <string>&Edit Network</string> + </property> + </action> +</actions> +<connections> + <connection> + <sender>helpIndexAction</sender> + <signal>activated()</signal> + <receiver>WpaGui</receiver> + <slot>helpIndex()</slot> + </connection> + <connection> + <sender>helpContentsAction</sender> + <signal>activated()</signal> + <receiver>WpaGui</receiver> + <slot>helpContents()</slot> + </connection> + <connection> + <sender>helpAboutAction</sender> + <signal>activated()</signal> + <receiver>WpaGui</receiver> + <slot>helpAbout()</slot> + </connection> + <connection> + <sender>fileExitAction</sender> + <signal>activated()</signal> + <receiver>WpaGui</receiver> + <slot>close()</slot> + </connection> + <connection> + <sender>disconnectButton</sender> + <signal>clicked()</signal> + <receiver>WpaGui</receiver> + <slot>disconnect()</slot> + </connection> + <connection> + <sender>scanButton</sender> + <signal>clicked()</signal> + <receiver>WpaGui</receiver> + <slot>scan()</slot> + </connection> + <connection> + <sender>connectButton</sender> + <signal>clicked()</signal> + <receiver>WpaGui</receiver> + <slot>connectB()</slot> + </connection> + <connection> + <sender>fileEventHistoryAction</sender> + <signal>activated()</signal> + <receiver>WpaGui</receiver> + <slot>eventHistory()</slot> + </connection> + <connection> + <sender>networkSelect</sender> + <signal>activated(const QString&)</signal> + <receiver>WpaGui</receiver> + <slot>selectNetwork(const QString&)</slot> + </connection> + <connection> + <sender>fileEdit_networkAction</sender> + <signal>activated()</signal> + <receiver>WpaGui</receiver> + <slot>editNetwork()</slot> + </connection> + <connection> + <sender>fileAdd_NetworkAction</sender> + <signal>activated()</signal> + <receiver>WpaGui</receiver> + <slot>addNetwork()</slot> + </connection> + <connection> + <sender>adapterSelect</sender> + <signal>activated(const QString&)</signal> + <receiver>WpaGui</receiver> + <slot>selectAdapter(const QString&)</slot> + </connection> +</connections> +<includes> + <include location="global" impldecl="in declaration">qtimer.h</include> + <include location="global" impldecl="in declaration">qsocketnotifier.h</include> + <include location="local" impldecl="in declaration">wpamsg.h</include> + <include location="local" impldecl="in declaration">eventhistory.h</include> + <include location="local" impldecl="in declaration">scanresults.h</include> + <include location="local" impldecl="in implementation">wpa_ctrl.h</include> + <include location="global" impldecl="in implementation">dirent.h</include> + <include location="global" impldecl="in implementation">qmessagebox.h</include> + <include location="global" impldecl="in implementation">qapplication.h</include> + <include location="local" impldecl="in implementation">userdatarequest.h</include> + <include location="local" impldecl="in implementation">networkconfig.h</include> + <include location="local" impldecl="in implementation">wpagui.ui.h</include> +</includes> +<forwards> + <forward>class UserDataRequest;</forward> +</forwards> +<variables> + <variable access="private">ScanResults *scanres;</variable> + <variable access="private">bool networkMayHaveChanged;</variable> + <variable access="private">char *ctrl_iface;</variable> + <variable access="private">EventHistory *eh;</variable> + <variable access="private">struct wpa_ctrl *ctrl_conn;</variable> + <variable access="private">QSocketNotifier *msgNotifier;</variable> + <variable access="private">QTimer *timer;</variable> + <variable access="private">int pingsToStatusUpdate;</variable> + <variable access="private">WpaMsgList msgs;</variable> + <variable access="private">char *ctrl_iface_dir;</variable> + <variable access="private">struct wpa_ctrl *monitor_conn;</variable> + <variable access="private">UserDataRequest *udr;</variable> +</variables> +<slots> + <slot>parse_argv()</slot> + <slot>updateStatus()</slot> + <slot>updateNetworks()</slot> + <slot>helpIndex()</slot> + <slot>helpContents()</slot> + <slot>helpAbout()</slot> + <slot>disconnect()</slot> + <slot>scan()</slot> + <slot>eventHistory()</slot> + <slot>ping()</slot> + <slot>processMsg( char * msg )</slot> + <slot>processCtrlReq( const char * req )</slot> + <slot>receiveMsgs()</slot> + <slot>connectB()</slot> + <slot>selectNetwork( const QString & sel )</slot> + <slot>editNetwork()</slot> + <slot>addNetwork()</slot> + <slot>selectAdapter( const QString & sel )</slot> +</slots> +<functions> + <function access="private" specifier="non virtual">init()</function> + <function access="private" specifier="non virtual">destroy()</function> + <function access="private" specifier="non virtual" returnType="int">openCtrlConnection( const char * ifname )</function> + <function returnType="int">ctrlRequest( const char * cmd, char * buf, size_t * buflen )</function> + <function>triggerUpdate()</function> +</functions> +<pixmapinproject/> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/wpa_supplicant/wpa_gui/wpagui.ui.h b/wpa_supplicant/wpa_gui/wpagui.ui.h new file mode 100644 index 0000000..678ff1b --- /dev/null +++ b/wpa_supplicant/wpa_gui/wpagui.ui.h @@ -0,0 +1,730 @@ +/**************************************************************************** +** ui.h extension file, included from the uic-generated form implementation. +** +** If you want to add, delete, or rename functions or slots, use +** Qt Designer to update this file, preserving your code. +** +** You should not define a constructor or destructor in this file. +** Instead, write your code in functions called init() and destroy(). +** These will automatically be called by the form's constructor and +** destructor. +*****************************************************************************/ + + +#ifdef __MINGW32__ +/* Need to get getopt() */ +#include <unistd.h> +#endif + +#include <stdlib.h> + +void WpaGui::init() +{ + eh = NULL; + scanres = NULL; + udr = NULL; + ctrl_iface = NULL; + ctrl_conn = NULL; + monitor_conn = NULL; + msgNotifier = NULL; + ctrl_iface_dir = strdup("/var/run/wpa_supplicant"); + + parse_argv(); + + textStatus->setText("connecting to wpa_supplicant"); + timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), SLOT(ping())); + timer->start(1000, FALSE); + + if (openCtrlConnection(ctrl_iface) < 0) { + printf("Failed to open control connection to wpa_supplicant.\n"); + } + + updateStatus(); + networkMayHaveChanged = true; + updateNetworks(); +} + + +void WpaGui::destroy() +{ + 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 (udr) { + udr->close(); + delete udr; + udr = NULL; + } + + free(ctrl_iface); + ctrl_iface = NULL; + + free(ctrl_iface_dir); + ctrl_iface_dir = NULL; +} + + +void WpaGui::parse_argv() +{ + int c; + for (;;) { + c = getopt(qApp->argc(), qApp->argv(), "i:p:"); + if (c < 0) + break; + switch (c) { + case 'i': + free(ctrl_iface); + ctrl_iface = strdup(optarg); + break; + case 'p': + free(ctrl_iface_dir); + ctrl_iface_dir = strdup(optarg); + 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; + printf("Selected interface '%s'\n", 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) { + 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) + 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; + } + + printf("Trying to connect to '%s'\n", 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)) { + printf("Failed to attach to wpa_supplicant\n"); + 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->insertItem(ctrl_iface); + adapterSelect->setCurrentItem(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->insertItem(pos); + if (pos2) + pos = pos2 + 1; + else + break; + } + } + + return 0; +} + + +static void wpa_gui_msg_cb(char *msg, size_t) +{ + /* This should not happen anymore since two control connections are used. */ + printf("missed message: %s\n", msg); +} + + +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, + wpa_gui_msg_cb); + if (ret == -2) { + printf("'%s' command timed out.\n", cmd); + } else if (ret < 0) { + printf("'%s' command failed.\n", cmd); + } + + return ret; +} + + +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("Could not get status from wpa_supplicant"); + textAuthentication->clear(); + textEncryption->clear(); + textSsid->clear(); + textBssid->clear(); + textIpAddress->clear(); + 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; + + 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); + } 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(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; + } + } + + if (last) + break; + start = end + 1; + } + + 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 (!status_updated) + textStatus->clear(); + if (!auth_updated) + textAuthentication->clear(); + if (!ssid_updated) + textSsid->clear(); + if (!bssid_updated) + textBssid->clear(); + if (!ipaddr_updated) + textIpAddress->clear(); +} + + +void WpaGui::updateNetworks() +{ + char buf[2048], *start, *end, *id, *ssid, *bssid, *flags; + size_t len; + int first_active = -1; + bool selected = false; + + if (!networkMayHaveChanged) + return; + + networkSelect->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'; + + QString network(id); + network.append(": "); + network.append(ssid); + networkSelect->insertItem(network); + + if (strstr(flags, "[CURRENT]")) { + networkSelect->setCurrentItem(networkSelect->count() - 1); + selected = true; + } else if (first_active < 0 && strstr(flags, "[DISABLED]") == NULL) + first_active = networkSelect->count() - 1; + + if (last) + break; + start = end + 1; + } + + if (!selected && first_active >= 0) + networkSelect->setCurrentItem(first_active); + + networkMayHaveChanged = false; +} + + +void WpaGui::helpIndex() +{ + printf("helpIndex\n"); +} + + +void WpaGui::helpContents() +{ + printf("helpContents\n"); +} + + +void WpaGui::helpAbout() +{ + QMessageBox::about(this, "wpa_gui for wpa_supplicant", + "Copyright (c) 2003-2008,\n" + "Jouni Malinen <j@w1.fi>\n" + "and contributors.\n" + "\n" + "This program is free software. You can\n" + "distribute it and/or modify it under the terms of\n" + "the GNU General Public License version 2.\n" + "\n" + "Alternatively, this software may be distributed\n" + "under the terms of the BSD license.\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); +} + + +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) { + printf("PING failed - trying to reconnect\n"); + if (openCtrlConnection(ctrl_iface) >= 0) { + printf("Reconnected successfully\n"); + pingsToStatusUpdate = 0; + } + } + + pingsToStatusUpdate--; + if (pingsToStatusUpdate <= 0) { + updateStatus(); + updateNetworks(); + } +} + + +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); + 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)); +} + + +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); + + int pos = cmd.find(':'); + if (pos < 0) { + printf("Invalid selectNetwork '%s'\n", cmd.ascii()); + return; + } + cmd.truncate(pos); + cmd.prepend("SELECT_NETWORK "); + ctrlRequest(cmd.ascii(), reply, &reply_len); +} + + +void WpaGui::editNetwork() +{ + QString sel(networkSelect->currentText()); + int pos = sel.find(':'); + if (pos < 0) { + printf("Invalid selectNetwork '%s'\n", sel.ascii()); + return; + } + sel.truncate(pos); + + NetworkConfig *nc = new NetworkConfig(); + if (nc == NULL) + return; + nc->setWpaGui(this); + + nc->paramsFromConfig(sel.toInt()); + nc->show(); + nc->exec(); +} + + +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::selectAdapter( const QString & sel ) +{ + if (openCtrlConnection(sel.ascii()) < 0) + printf("Failed to open control connection to wpa_supplicant.\n"); + updateStatus(); + updateNetworks(); +} diff --git a/wpa_supplicant/wpa_gui/wpamsg.h b/wpa_supplicant/wpa_gui/wpamsg.h new file mode 100644 index 0000000..f3fce06 --- /dev/null +++ b/wpa_supplicant/wpa_gui/wpamsg.h @@ -0,0 +1,34 @@ +#ifndef WPAMSG_H +#define WPAMSG_H + +class WpaMsg; + +#if QT_VERSION >= 0x040000 +#include <QDateTime> +#include <QLinkedList> +typedef QLinkedList<WpaMsg> WpaMsgList; +#else +#include <qdatetime.h> +typedef QValueList<WpaMsg> WpaMsgList; +#endif + +class WpaMsg { +public: + WpaMsg() {} + 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; +}; + +#endif /* WPAMSG_H */ diff --git a/wpa_supplicant/wpa_priv.c b/wpa_supplicant/wpa_priv.c index 4a27125..4ff0284 100644 --- a/wpa_supplicant/wpa_priv.c +++ b/wpa_supplicant/wpa_priv.c @@ -172,12 +172,12 @@ static void wpa_priv_get_scan_results2(struct wpa_priv_interface *iface, sizeof(*from)); os_free(buf); - os_free(res); + wpa_scan_results_free(res); return; fail: os_free(buf); - os_free(res); + wpa_scan_results_free(res); sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, sizeof(*from)); } diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index c1f95cb..3a5c7cb 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -385,6 +385,9 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) ieee80211_sta_deinit(wpa_s); wpas_wps_deinit(wpa_s); + + wpabuf_free(wpa_s->pending_eapol_rx); + wpa_s->pending_eapol_rx = NULL; } @@ -418,6 +421,10 @@ void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr) wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 1, 0, NULL, 0, NULL, 0); wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 2, 0, NULL, 0, NULL, 0); wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 3, 0, NULL, 0, NULL, 0); +#ifdef CONFIG_IEEE80211W + wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 4, 0, NULL, 0, NULL, 0); + wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 5, 0, NULL, 0, NULL, 0); +#endif /* CONFIG_IEEE80211W */ if (addr) { wpa_drv_set_key(wpa_s, WPA_ALG_NONE, addr, 0, 0, NULL, 0, NULL, 0); @@ -475,6 +482,9 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, wpa_states state) wpa_supplicant_state_txt(wpa_s->wpa_state), wpa_supplicant_state_txt(state)); + if (state != WPA_SCANNING) + wpa_supplicant_notify_scanning(wpa_s, 0); + wpa_supplicant_dbus_notify_state_change(wpa_s, state, wpa_s->wpa_state); @@ -1226,7 +1236,6 @@ void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s, int reason_code) { u8 *addr = NULL; - wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); if (!is_zero_ether_addr(wpa_s->bssid)) { if (wpa_s->use_client_mlme) ieee80211_sta_deauthenticate(wpa_s, reason_code); @@ -1236,11 +1245,10 @@ void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s, addr = wpa_s->bssid; } wpa_clear_keys(wpa_s, addr); + wpa_supplicant_mark_disassoc(wpa_s); wpa_s->current_ssid = NULL; wpa_sm_set_config(wpa_s->wpa, NULL); eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); - eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE); - eapol_sm_notify_portValid(wpa_s->eapol, FALSE); } @@ -1471,12 +1479,14 @@ static int wpa_supplicant_set_driver(struct wpa_supplicant *wpa_s, if (name == NULL) { /* default to first driver in the list */ wpa_s->driver = wpa_supplicant_drivers[0]; + wpa_s->global_drv_priv = wpa_s->global->drv_priv[0]; return 0; } for (i = 0; wpa_supplicant_drivers[i]; i++) { if (os_strcmp(name, wpa_supplicant_drivers[i]->name) == 0) { wpa_s->driver = wpa_supplicant_drivers[i]; + wpa_s->global_drv_priv = wpa_s->global->drv_priv[i]; return 0; } } @@ -1494,6 +1504,27 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, wpa_printf(MSG_DEBUG, "RX EAPOL from " MACSTR, MAC2STR(src_addr)); wpa_hexdump(MSG_MSGDUMP, "RX EAPOL", buf, len); + if (wpa_s->wpa_state < WPA_ASSOCIATED) { + /* + * There is possible race condition between receiving the + * association event and the EAPOL frame since they are coming + * through different paths from the driver. In order to avoid + * issues in trying to process the EAPOL frame before receiving + * association information, lets queue it for processing until + * the association event is received. + */ + wpa_printf(MSG_DEBUG, "Not associated - Delay processing of " + "received EAPOL frame"); + wpabuf_free(wpa_s->pending_eapol_rx); + wpa_s->pending_eapol_rx = wpabuf_alloc_copy(buf, len); + if (wpa_s->pending_eapol_rx) { + os_get_time(&wpa_s->pending_eapol_rx_time); + os_memcpy(wpa_s->pending_eapol_rx_src, src_addr, + ETH_ALEN); + } + return; + } + if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE) { wpa_printf(MSG_DEBUG, "Ignored received EAPOL frame since " "no key management is configured"); @@ -1915,6 +1946,8 @@ struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global, if (wpa_s == NULL) return NULL; + wpa_s->global = global; + if (wpa_supplicant_init_iface(wpa_s, iface) || wpa_supplicant_init_iface2(wpa_s)) { wpa_printf(MSG_DEBUG, "Failed to add interface %s", @@ -1924,15 +1957,13 @@ struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global, return NULL; } - wpa_s->global = global; - /* Register the interface with the dbus control interface */ if (wpas_dbus_register_iface(wpa_s)) { wpa_supplicant_deinit_iface(wpa_s); os_free(wpa_s); return NULL; } - + wpa_s->next = global->ifaces; global->ifaces = wpa_s; diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 5e4dcc1..b441cbb 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -305,6 +305,7 @@ struct wpa_supplicant { int mgmt_group_cipher; void *drv_priv; /* private data used by driver_ops */ + void *global_drv_priv; struct wpa_ssid *prev_scan_ssid; /* previously scanned SSID; * NULL = not yet initialized (start @@ -325,6 +326,7 @@ struct wpa_supplicant { struct ctrl_iface_priv *ctrl_iface; wpa_states wpa_state; + int scanning; int new_connection; int reassociated_connection; @@ -358,6 +360,10 @@ struct wpa_supplicant { struct wps_context *wps; int wps_success; /* WPS success event received */ int blacklist_cleared; + + struct wpabuf *pending_eapol_rx; + struct os_time pending_eapol_rx_time; + u8 pending_eapol_rx_src[ETH_ALEN]; }; @@ -404,8 +410,11 @@ int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); /* scan.c */ +int wpa_supplicant_enabled_networks(struct wpa_config *conf); void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec); void wpa_supplicant_cancel_scan(struct wpa_supplicant *wpa_s); +void wpa_supplicant_notify_scanning(struct wpa_supplicant *wpa_s, + int scanning); /* events.c */ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s); @@ -415,7 +424,8 @@ static inline void * wpa_drv_init(struct wpa_supplicant *wpa_s, const char *ifname) { if (wpa_s->driver->init2) - return wpa_s->driver->init2(wpa_s, ifname, wpa_s->global); + return wpa_s->driver->init2(wpa_s, ifname, + wpa_s->global_drv_priv); if (wpa_s->driver->init) { return wpa_s->driver->init(wpa_s, ifname); } diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c index 2b96aa7..fc72cb8 100644 --- a/wpa_supplicant/wpas_glue.c +++ b/wpa_supplicant/wpas_glue.c @@ -402,7 +402,7 @@ static void _wpa_supplicant_disassociate(void *wpa_s, int reason_code) { wpa_supplicant_disassociate(wpa_s, reason_code); /* Schedule a scan to make sure we continue looking for networks */ - wpa_supplicant_req_scan(wpa_s, 0, 0); + wpa_supplicant_req_scan(wpa_s, 5, 0); } @@ -410,7 +410,7 @@ static void _wpa_supplicant_deauthenticate(void *wpa_s, int reason_code) { wpa_supplicant_deauthenticate(wpa_s, reason_code); /* Schedule a scan to make sure we continue looking for networks */ - wpa_supplicant_req_scan(wpa_s, 0, 0); + wpa_supplicant_req_scan(wpa_s, 5, 0); } diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c index 9b73601..9422b1b 100644 --- a/wpa_supplicant/wps_supplicant.c +++ b/wpa_supplicant/wps_supplicant.c @@ -26,8 +26,10 @@ #include "ctrl_iface_dbus.h" #include "eap_common/eap_wsc_common.h" #include "blacklist.h" +#include "wpa.h" #include "wps_supplicant.h" + #define WPS_PIN_SCAN_IGNORE_SEL_REG 3 static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx); @@ -83,11 +85,109 @@ int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s) } +static void wpas_wps_security_workaround(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + const struct wps_credential *cred) +{ + struct wpa_driver_capa capa; + size_t i; + struct wpa_scan_res *bss; + const u8 *ie; + struct wpa_ie_data adv; + int wpa2 = 0, ccmp = 0; + + /* + * Many existing WPS APs do not know how to negotiate WPA2 or CCMP in + * case they are configured for mixed mode operation (WPA+WPA2 and + * TKIP+CCMP). Try to use scan results to figure out whether the AP + * actually supports stronger security and select that if the client + * has support for it, too. + */ + + if (wpa_drv_get_capa(wpa_s, &capa)) + return; /* Unknown what driver supports */ + + if (wpa_supplicant_get_scan_results(wpa_s) || wpa_s->scan_res == NULL) + return; /* Could not get scan results for checking advertised + * parameters */ + + for (i = 0; i < wpa_s->scan_res->num; i++) { + bss = wpa_s->scan_res->res[i]; + if (os_memcmp(bss->bssid, cred->mac_addr, ETH_ALEN) != 0) + continue; + ie = wpa_scan_get_ie(bss, WLAN_EID_SSID); + if (ie == NULL) + continue; + if (ie[1] != ssid->ssid_len || ssid->ssid == NULL || + os_memcmp(ie + 2, ssid->ssid, ssid->ssid_len) != 0) + continue; + + wpa_printf(MSG_DEBUG, "WPS: AP found from scan results"); + break; + } + + if (i == wpa_s->scan_res->num) { + wpa_printf(MSG_DEBUG, "WPS: The AP was not found from scan " + "results - use credential as-is"); + return; + } + + ie = wpa_scan_get_ie(bss, WLAN_EID_RSN); + if (ie && wpa_parse_wpa_ie(ie, 2 + ie[1], &adv) == 0) { + wpa2 = 1; + if (adv.pairwise_cipher & WPA_CIPHER_CCMP) + ccmp = 1; + } else { + ie = wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); + if (ie && wpa_parse_wpa_ie(ie, 2 + ie[1], &adv) == 0 && + adv.pairwise_cipher & WPA_CIPHER_CCMP) + ccmp = 1; + } + + if (ie == NULL && (ssid->proto & WPA_PROTO_WPA) && + (ssid->pairwise_cipher & WPA_CIPHER_TKIP)) { + /* + * TODO: This could be the initial AP configuration and the + * Beacon contents could change shortly. Should request a new + * scan and delay addition of the network until the updated + * scan results are available. + */ + wpa_printf(MSG_DEBUG, "WPS: The AP did not yet advertise WPA " + "support - use credential as-is"); + return; + } + + if (ccmp && !(ssid->pairwise_cipher & WPA_CIPHER_CCMP) && + (ssid->pairwise_cipher & WPA_CIPHER_TKIP) && + (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) { + wpa_printf(MSG_DEBUG, "WPS: Add CCMP into the credential " + "based on scan results"); + if (wpa_s->conf->ap_scan == 1) + ssid->pairwise_cipher |= WPA_CIPHER_CCMP; + else + ssid->pairwise_cipher = WPA_CIPHER_CCMP; + } + + if (wpa2 && !(ssid->proto & WPA_PROTO_RSN) && + (ssid->proto & WPA_PROTO_WPA) && + (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP)) { + wpa_printf(MSG_DEBUG, "WPS: Add WPA2 into the credential " + "based on scan results"); + if (wpa_s->conf->ap_scan == 1) + ssid->proto |= WPA_PROTO_RSN; + else + ssid->proto = WPA_PROTO_RSN; + } +} + + static int wpa_supplicant_wps_cred(void *ctx, const struct wps_credential *cred) { struct wpa_supplicant *wpa_s = ctx; struct wpa_ssid *ssid = wpa_s->current_ssid; + u8 key_idx = 0; + u16 auth_type; if ((wpa_s->conf->wps_cred_processing == 1 || wpa_s->conf->wps_cred_processing == 2) && cred->cred_attr) { @@ -110,13 +210,30 @@ static int wpa_supplicant_wps_cred(void *ctx, if (wpa_s->conf->wps_cred_processing == 1) return 0; - if (cred->auth_type != WPS_AUTH_OPEN && - cred->auth_type != WPS_AUTH_SHARED && - cred->auth_type != WPS_AUTH_WPAPSK && - cred->auth_type != WPS_AUTH_WPA2PSK) { + wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID", cred->ssid, cred->ssid_len); + wpa_printf(MSG_DEBUG, "WPS: Authentication Type 0x%x", + cred->auth_type); + wpa_printf(MSG_DEBUG, "WPS: Encryption Type 0x%x", cred->encr_type); + wpa_printf(MSG_DEBUG, "WPS: Network Key Index %d", cred->key_idx); + wpa_hexdump_key(MSG_DEBUG, "WPS: Network Key", + cred->key, cred->key_len); + wpa_printf(MSG_DEBUG, "WPS: MAC Address " MACSTR, + MAC2STR(cred->mac_addr)); + + auth_type = cred->auth_type; + if (auth_type == (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) { + wpa_printf(MSG_DEBUG, "WPS: Workaround - convert mixed-mode " + "auth_type into WPA2PSK"); + auth_type = WPS_AUTH_WPA2PSK; + } + + if (auth_type != WPS_AUTH_OPEN && + auth_type != WPS_AUTH_SHARED && + auth_type != WPS_AUTH_WPAPSK && + auth_type != WPS_AUTH_WPA2PSK) { wpa_printf(MSG_DEBUG, "WPS: Ignored credentials for " - "unsupported authentication type %d", - cred->auth_type); + "unsupported authentication type 0x%x", + auth_type); return 0; } @@ -151,13 +268,36 @@ static int wpa_supplicant_wps_cred(void *ctx, case WPS_ENCR_NONE: break; case WPS_ENCR_WEP: - if (cred->key_len > 0 && cred->key_len <= MAX_WEP_KEY_LEN && - cred->key_idx < NUM_WEP_KEYS) { - os_memcpy(ssid->wep_key[cred->key_idx], cred->key, + if (cred->key_len <= 0) + break; + if (cred->key_len != 5 && cred->key_len != 13 && + cred->key_len != 10 && cred->key_len != 26) { + wpa_printf(MSG_ERROR, "WPS: Invalid WEP Key length " + "%lu", (unsigned long) cred->key_len); + return -1; + } + if (cred->key_idx > NUM_WEP_KEYS) { + wpa_printf(MSG_ERROR, "WPS: Invalid WEP Key index %d", + cred->key_idx); + return -1; + } + if (cred->key_idx) + key_idx = cred->key_idx - 1; + if (cred->key_len == 10 || cred->key_len == 26) { + if (hexstr2bin((char *) cred->key, + ssid->wep_key[key_idx], + cred->key_len / 2) < 0) { + wpa_printf(MSG_ERROR, "WPS: Invalid WEP Key " + "%d", key_idx); + return -1; + } + ssid->wep_key_len[key_idx] = cred->key_len / 2; + } else { + os_memcpy(ssid->wep_key[key_idx], cred->key, cred->key_len); - ssid->wep_key_len[cred->key_idx] = cred->key_len; - ssid->wep_tx_keyidx = cred->key_idx; + ssid->wep_key_len[key_idx] = cred->key_len; } + ssid->wep_tx_keyidx = key_idx; break; case WPS_ENCR_TKIP: ssid->pairwise_cipher = WPA_CIPHER_TKIP; @@ -167,7 +307,7 @@ static int wpa_supplicant_wps_cred(void *ctx, break; } - switch (cred->auth_type) { + switch (auth_type) { case WPS_AUTH_OPEN: ssid->auth_alg = WPA_AUTH_ALG_OPEN; ssid->key_mgmt = WPA_KEY_MGMT_NONE; @@ -225,6 +365,8 @@ static int wpa_supplicant_wps_cred(void *ctx, } } + wpas_wps_security_workaround(wpa_s, ssid, cred); + #ifndef CONFIG_NO_CONFIG_WRITE if (wpa_s->conf->update_config && wpa_config_write(wpa_s->confname, wpa_s->conf)) { @@ -277,6 +419,10 @@ static void wpa_supplicant_wps_event(void *ctx, enum wps_event event, break; case WPS_EV_PWD_AUTH_FAIL: break; + case WPS_EV_PBC_OVERLAP: + break; + case WPS_EV_PBC_TIMEOUT: + break; } } @@ -343,7 +489,7 @@ static struct wpa_ssid * wpas_wps_add_network(struct wpa_supplicant *wpa_s, if (bssid) { size_t i; - struct wpa_scan_res *res; + int count = 0; os_memcpy(ssid->bssid, bssid, ETH_ALEN); ssid->bssid_set = 1; @@ -355,6 +501,7 @@ static struct wpa_ssid * wpas_wps_add_network(struct wpa_supplicant *wpa_s, for (i = 0; i < wpa_s->scan_res->num; i++) { const u8 *ie; + struct wpa_scan_res *res; res = wpa_s->scan_res->res[i]; if (os_memcmp(bssid, res->bssid, ETH_ALEN) != 0) @@ -369,7 +516,18 @@ static struct wpa_ssid * wpas_wps_add_network(struct wpa_supplicant *wpa_s, break; os_memcpy(ssid->ssid, ie + 2, ie[1]); ssid->ssid_len = ie[1]; - break; + wpa_hexdump_ascii(MSG_DEBUG, "WPS: Picked SSID from " + "scan results", + ssid->ssid, ssid->ssid_len); + count++; + } + + if (count > 1) { + wpa_printf(MSG_DEBUG, "WPS: More than one SSID found " + "for the AP; use wildcard"); + os_free(ssid->ssid); + ssid->ssid = NULL; + ssid->ssid_len = 0; } } |